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_rewrite.h"
49 : #include "catalog/pg_statistic_ext.h"
50 : #include "catalog/pg_tablespace.h"
51 : #include "catalog/pg_trigger.h"
52 : #include "catalog/pg_type.h"
53 : #include "catalog/storage.h"
54 : #include "catalog/storage_xlog.h"
55 : #include "catalog/toasting.h"
56 : #include "commands/cluster.h"
57 : #include "commands/comment.h"
58 : #include "commands/defrem.h"
59 : #include "commands/event_trigger.h"
60 : #include "commands/sequence.h"
61 : #include "commands/tablecmds.h"
62 : #include "commands/tablespace.h"
63 : #include "commands/trigger.h"
64 : #include "commands/typecmds.h"
65 : #include "commands/user.h"
66 : #include "commands/vacuum.h"
67 : #include "executor/executor.h"
68 : #include "foreign/fdwapi.h"
69 : #include "foreign/foreign.h"
70 : #include "miscadmin.h"
71 : #include "nodes/makefuncs.h"
72 : #include "nodes/nodeFuncs.h"
73 : #include "nodes/parsenodes.h"
74 : #include "optimizer/optimizer.h"
75 : #include "parser/parse_coerce.h"
76 : #include "parser/parse_collate.h"
77 : #include "parser/parse_expr.h"
78 : #include "parser/parse_relation.h"
79 : #include "parser/parse_type.h"
80 : #include "parser/parse_utilcmd.h"
81 : #include "parser/parser.h"
82 : #include "partitioning/partbounds.h"
83 : #include "partitioning/partdesc.h"
84 : #include "pgstat.h"
85 : #include "rewrite/rewriteDefine.h"
86 : #include "rewrite/rewriteHandler.h"
87 : #include "rewrite/rewriteManip.h"
88 : #include "storage/bufmgr.h"
89 : #include "storage/lmgr.h"
90 : #include "storage/lock.h"
91 : #include "storage/predicate.h"
92 : #include "storage/smgr.h"
93 : #include "tcop/utility.h"
94 : #include "utils/acl.h"
95 : #include "utils/builtins.h"
96 : #include "utils/fmgroids.h"
97 : #include "utils/inval.h"
98 : #include "utils/lsyscache.h"
99 : #include "utils/memutils.h"
100 : #include "utils/partcache.h"
101 : #include "utils/relcache.h"
102 : #include "utils/ruleutils.h"
103 : #include "utils/snapmgr.h"
104 : #include "utils/syscache.h"
105 : #include "utils/timestamp.h"
106 : #include "utils/typcache.h"
107 : #include "utils/usercontext.h"
108 :
109 : /*
110 : * ON COMMIT action list
111 : */
112 : typedef struct OnCommitItem
113 : {
114 : Oid relid; /* relid of relation */
115 : OnCommitAction oncommit; /* what to do at end of xact */
116 :
117 : /*
118 : * If this entry was created during the current transaction,
119 : * creating_subid is the ID of the creating subxact; if created in a prior
120 : * transaction, creating_subid is zero. If deleted during the current
121 : * transaction, deleting_subid is the ID of the deleting subxact; if no
122 : * deletion request is pending, deleting_subid is zero.
123 : */
124 : SubTransactionId creating_subid;
125 : SubTransactionId deleting_subid;
126 : } OnCommitItem;
127 :
128 : static List *on_commits = NIL;
129 :
130 :
131 : /*
132 : * State information for ALTER TABLE
133 : *
134 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
135 : * structs, one for each table modified by the operation (the named table
136 : * plus any child tables that are affected). We save lists of subcommands
137 : * to apply to this table (possibly modified by parse transformation steps);
138 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
139 : * necessary information is stored in the constraints and newvals lists.
140 : *
141 : * Phase 2 is divided into multiple passes; subcommands are executed in
142 : * a pass determined by subcommand type.
143 : */
144 :
145 : typedef enum AlterTablePass
146 : {
147 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
148 : AT_PASS_DROP, /* DROP (all flavors) */
149 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
150 : AT_PASS_ADD_COL, /* ADD COLUMN */
151 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
152 : AT_PASS_OLD_COL_ATTRS, /* re-install attnotnull */
153 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
154 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
155 : /* We could support a RENAME COLUMN pass here, but not currently used */
156 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
157 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
158 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
159 : AT_PASS_ADD_INDEX, /* ADD indexes */
160 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
161 : AT_PASS_MISC, /* other stuff */
162 : } AlterTablePass;
163 :
164 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
165 :
166 : typedef struct AlteredTableInfo
167 : {
168 : /* Information saved before any work commences: */
169 : Oid relid; /* Relation to work on */
170 : char relkind; /* Its relkind */
171 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
172 :
173 : /*
174 : * Transiently set during Phase 2, normally set to NULL.
175 : *
176 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
177 : * returns control. This can be exploited by ATExecCmd subroutines to
178 : * close/reopen across transaction boundaries.
179 : */
180 : Relation rel;
181 :
182 : /* Information saved by Phase 1 for Phase 2: */
183 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
184 : /* Information saved by Phases 1/2 for Phase 3: */
185 : List *constraints; /* List of NewConstraint */
186 : List *newvals; /* List of NewColumnValue */
187 : List *afterStmts; /* List of utility command parsetrees */
188 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
189 : int rewrite; /* Reason for forced rewrite, if any */
190 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
191 : Oid newAccessMethod; /* new access method; 0 means no change,
192 : * if above is true */
193 : Oid newTableSpace; /* new tablespace; 0 means no change */
194 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
195 : char newrelpersistence; /* if above is true */
196 : Expr *partition_constraint; /* for attach partition validation */
197 : /* true, if validating default due to some other attach/detach */
198 : bool validate_default;
199 : /* Objects to rebuild after completing ALTER TYPE operations */
200 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
201 : List *changedConstraintDefs; /* string definitions of same */
202 : List *changedIndexOids; /* OIDs of indexes to rebuild */
203 : List *changedIndexDefs; /* string definitions of same */
204 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
205 : char *clusterOnIndex; /* index to use for CLUSTER */
206 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
207 : List *changedStatisticsDefs; /* string definitions of same */
208 : } AlteredTableInfo;
209 :
210 : /* Struct describing one new constraint to check in Phase 3 scan */
211 : /* Note: new not-null constraints are handled elsewhere */
212 : typedef struct NewConstraint
213 : {
214 : char *name; /* Constraint name, or NULL if none */
215 : ConstrType contype; /* CHECK or FOREIGN */
216 : Oid refrelid; /* PK rel, if FOREIGN */
217 : Oid refindid; /* OID of PK's index, if FOREIGN */
218 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
219 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
220 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
221 : ExprState *qualstate; /* Execution state for CHECK expr */
222 : } NewConstraint;
223 :
224 : /*
225 : * Struct describing one new column value that needs to be computed during
226 : * Phase 3 copy (this could be either a new column with a non-null default, or
227 : * a column that we're changing the type of). Columns without such an entry
228 : * are just copied from the old table during ATRewriteTable. Note that the
229 : * expr is an expression over *old* table values, except when is_generated
230 : * is true; then it is an expression over columns of the *new* tuple.
231 : */
232 : typedef struct NewColumnValue
233 : {
234 : AttrNumber attnum; /* which column */
235 : Expr *expr; /* expression to compute */
236 : ExprState *exprstate; /* execution state */
237 : bool is_generated; /* is it a GENERATED expression? */
238 : } NewColumnValue;
239 :
240 : /*
241 : * Error-reporting support for RemoveRelations
242 : */
243 : struct dropmsgstrings
244 : {
245 : char kind;
246 : int nonexistent_code;
247 : const char *nonexistent_msg;
248 : const char *skipping_msg;
249 : const char *nota_msg;
250 : const char *drophint_msg;
251 : };
252 :
253 : static const struct dropmsgstrings dropmsgstringarray[] = {
254 : {RELKIND_RELATION,
255 : ERRCODE_UNDEFINED_TABLE,
256 : gettext_noop("table \"%s\" does not exist"),
257 : gettext_noop("table \"%s\" does not exist, skipping"),
258 : gettext_noop("\"%s\" is not a table"),
259 : gettext_noop("Use DROP TABLE to remove a table.")},
260 : {RELKIND_SEQUENCE,
261 : ERRCODE_UNDEFINED_TABLE,
262 : gettext_noop("sequence \"%s\" does not exist"),
263 : gettext_noop("sequence \"%s\" does not exist, skipping"),
264 : gettext_noop("\"%s\" is not a sequence"),
265 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
266 : {RELKIND_VIEW,
267 : ERRCODE_UNDEFINED_TABLE,
268 : gettext_noop("view \"%s\" does not exist"),
269 : gettext_noop("view \"%s\" does not exist, skipping"),
270 : gettext_noop("\"%s\" is not a view"),
271 : gettext_noop("Use DROP VIEW to remove a view.")},
272 : {RELKIND_MATVIEW,
273 : ERRCODE_UNDEFINED_TABLE,
274 : gettext_noop("materialized view \"%s\" does not exist"),
275 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
276 : gettext_noop("\"%s\" is not a materialized view"),
277 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
278 : {RELKIND_INDEX,
279 : ERRCODE_UNDEFINED_OBJECT,
280 : gettext_noop("index \"%s\" does not exist"),
281 : gettext_noop("index \"%s\" does not exist, skipping"),
282 : gettext_noop("\"%s\" is not an index"),
283 : gettext_noop("Use DROP INDEX to remove an index.")},
284 : {RELKIND_COMPOSITE_TYPE,
285 : ERRCODE_UNDEFINED_OBJECT,
286 : gettext_noop("type \"%s\" does not exist"),
287 : gettext_noop("type \"%s\" does not exist, skipping"),
288 : gettext_noop("\"%s\" is not a type"),
289 : gettext_noop("Use DROP TYPE to remove a type.")},
290 : {RELKIND_FOREIGN_TABLE,
291 : ERRCODE_UNDEFINED_OBJECT,
292 : gettext_noop("foreign table \"%s\" does not exist"),
293 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
294 : gettext_noop("\"%s\" is not a foreign table"),
295 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
296 : {RELKIND_PARTITIONED_TABLE,
297 : ERRCODE_UNDEFINED_TABLE,
298 : gettext_noop("table \"%s\" does not exist"),
299 : gettext_noop("table \"%s\" does not exist, skipping"),
300 : gettext_noop("\"%s\" is not a table"),
301 : gettext_noop("Use DROP TABLE to remove a table.")},
302 : {RELKIND_PARTITIONED_INDEX,
303 : ERRCODE_UNDEFINED_OBJECT,
304 : gettext_noop("index \"%s\" does not exist"),
305 : gettext_noop("index \"%s\" does not exist, skipping"),
306 : gettext_noop("\"%s\" is not an index"),
307 : gettext_noop("Use DROP INDEX to remove an index.")},
308 : {'\0', 0, NULL, NULL, NULL, NULL}
309 : };
310 :
311 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
312 : struct DropRelationCallbackState
313 : {
314 : /* These fields are set by RemoveRelations: */
315 : char expected_relkind;
316 : LOCKMODE heap_lockmode;
317 : /* These fields are state to track which subsidiary locks are held: */
318 : Oid heapOid;
319 : Oid partParentOid;
320 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
321 : char actual_relkind;
322 : char actual_relpersistence;
323 : };
324 :
325 : /* Alter table target-type flags for ATSimplePermissions */
326 : #define ATT_TABLE 0x0001
327 : #define ATT_VIEW 0x0002
328 : #define ATT_MATVIEW 0x0004
329 : #define ATT_INDEX 0x0008
330 : #define ATT_COMPOSITE_TYPE 0x0010
331 : #define ATT_FOREIGN_TABLE 0x0020
332 : #define ATT_PARTITIONED_INDEX 0x0040
333 : #define ATT_SEQUENCE 0x0080
334 :
335 : /*
336 : * ForeignTruncateInfo
337 : *
338 : * Information related to truncation of foreign tables. This is used for
339 : * the elements in a hash table. It uses the server OID as lookup key,
340 : * and includes a per-server list of all foreign tables involved in the
341 : * truncation.
342 : */
343 : typedef struct ForeignTruncateInfo
344 : {
345 : Oid serverid;
346 : List *rels;
347 : } ForeignTruncateInfo;
348 :
349 : /*
350 : * Partition tables are expected to be dropped when the parent partitioned
351 : * table gets dropped. Hence for partitioning we use AUTO dependency.
352 : * Otherwise, for regular inheritance use NORMAL dependency.
353 : */
354 : #define child_dependency_type(child_is_partition) \
355 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
356 :
357 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
358 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
359 : static void truncate_check_activity(Relation rel);
360 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
361 : Oid relId, Oid oldRelId, void *arg);
362 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
363 : bool is_partition, List **supconstr,
364 : List **supnotnulls);
365 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
366 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
367 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
368 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
369 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
370 : static void StoreCatalogInheritance(Oid relationId, List *supers,
371 : bool child_is_partition);
372 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
373 : int32 seqNumber, Relation inhRelation,
374 : bool child_is_partition);
375 : static int findAttrByName(const char *attributeName, const List *columns);
376 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
377 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
378 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
379 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
380 : LOCKMODE lockmode);
381 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
382 : bool recurse, bool recursing, LOCKMODE lockmode);
383 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
384 : Relation rel, HeapTuple contuple, List **otherrelids,
385 : LOCKMODE lockmode);
386 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
387 : Relation rel, char *constrName,
388 : bool recurse, bool recursing, LOCKMODE lockmode);
389 : static int transformColumnNameList(Oid relId, List *colList,
390 : int16 *attnums, Oid *atttypids);
391 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
392 : List **attnamelist,
393 : int16 *attnums, Oid *atttypids,
394 : Oid *opclasses, bool *pk_has_without_overlaps);
395 : static Oid transformFkeyCheckAttrs(Relation pkrel,
396 : int numattrs, int16 *attnums,
397 : bool with_period, Oid *opclasses,
398 : bool *pk_has_without_overlaps);
399 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
400 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
401 : Oid *funcid);
402 : static void validateForeignKeyConstraint(char *conname,
403 : Relation rel, Relation pkrel,
404 : Oid pkindOid, Oid constraintOid, bool hasperiod);
405 : static void ATController(AlterTableStmt *parsetree,
406 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
407 : AlterTableUtilityContext *context);
408 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
409 : bool recurse, bool recursing, LOCKMODE lockmode,
410 : AlterTableUtilityContext *context);
411 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
412 : AlterTableUtilityContext *context);
413 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
414 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
415 : AlterTableUtilityContext *context);
416 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
417 : Relation rel, AlterTableCmd *cmd,
418 : bool recurse, LOCKMODE lockmode,
419 : AlterTablePass cur_pass,
420 : AlterTableUtilityContext *context);
421 : static void ATRewriteTables(AlterTableStmt *parsetree,
422 : List **wqueue, LOCKMODE lockmode,
423 : AlterTableUtilityContext *context);
424 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
425 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
426 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
427 : static void ATSimpleRecursion(List **wqueue, Relation rel,
428 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
429 : AlterTableUtilityContext *context);
430 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
431 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
432 : LOCKMODE lockmode,
433 : AlterTableUtilityContext *context);
434 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
435 : DropBehavior behavior);
436 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
437 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
438 : AlterTableUtilityContext *context);
439 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
440 : Relation rel, AlterTableCmd **cmd,
441 : bool recurse, bool recursing,
442 : LOCKMODE lockmode, AlterTablePass cur_pass,
443 : AlterTableUtilityContext *context);
444 : static bool check_for_column_name_collision(Relation rel, const char *colname,
445 : bool if_not_exists);
446 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
447 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
448 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
449 : LOCKMODE lockmode);
450 : static bool set_attnotnull(List **wqueue, Relation rel,
451 : AttrNumber attnum, bool recurse, LOCKMODE lockmode);
452 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
453 : char *constrname, char *colName,
454 : bool recurse, bool recursing,
455 : List **readyRels, LOCKMODE lockmode);
456 : static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel,
457 : const char *colName, LOCKMODE lockmode);
458 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
459 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
460 : List *testConstraint, List *provenConstraint);
461 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
462 : Node *newDefault, LOCKMODE lockmode);
463 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
464 : Node *newDefault);
465 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
466 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
467 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
468 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
469 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
470 : bool recurse, bool recursing);
471 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
472 : Node *newExpr, LOCKMODE lockmode);
473 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
474 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
475 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
476 : Node *newValue, LOCKMODE lockmode);
477 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
478 : Node *options, bool isReset, LOCKMODE lockmode);
479 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
480 : Node *newValue, LOCKMODE lockmode);
481 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
482 : AlterTableCmd *cmd, LOCKMODE lockmode,
483 : AlterTableUtilityContext *context);
484 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
485 : DropBehavior behavior,
486 : bool recurse, bool recursing,
487 : bool missing_ok, LOCKMODE lockmode,
488 : ObjectAddresses *addrs);
489 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
490 : LOCKMODE lockmode, AlterTableUtilityContext *context);
491 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
492 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
493 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
494 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
495 : static ObjectAddress ATExecAddConstraint(List **wqueue,
496 : AlteredTableInfo *tab, Relation rel,
497 : Constraint *newConstraint, bool recurse, bool is_readd,
498 : LOCKMODE lockmode);
499 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
500 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
501 : IndexStmt *stmt, LOCKMODE lockmode);
502 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
503 : AlteredTableInfo *tab, Relation rel,
504 : Constraint *constr,
505 : bool recurse, bool recursing, bool is_readd,
506 : LOCKMODE lockmode);
507 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
508 : Relation rel, Constraint *fkconstraint,
509 : bool recurse, bool recursing,
510 : LOCKMODE lockmode);
511 : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
512 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
513 : int numfks, int16 *pkattnum, int16 *fkattnum,
514 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
515 : int numfkdelsetcols, int16 *fkdelsetcols,
516 : bool old_check_ok,
517 : Oid parentDelTrigger, Oid parentUpdTrigger,
518 : bool with_period);
519 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
520 : int numfksetcols, const int16 *fksetcolsattnums,
521 : List *fksetcols);
522 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
523 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
524 : int numfks, int16 *pkattnum, int16 *fkattnum,
525 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
526 : int numfkdelsetcols, int16 *fkdelsetcols,
527 : bool old_check_ok, LOCKMODE lockmode,
528 : Oid parentInsTrigger, Oid parentUpdTrigger,
529 : bool with_period);
530 :
531 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
532 : Relation partitionRel);
533 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
534 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
535 : Relation partRel);
536 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
537 : Constraint *fkconstraint, Oid constraintOid,
538 : Oid indexOid,
539 : Oid parentInsTrigger, Oid parentUpdTrigger,
540 : Oid *insertTrigOid, Oid *updateTrigOid);
541 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
542 : Constraint *fkconstraint, Oid constraintOid,
543 : Oid indexOid,
544 : Oid parentDelTrigger, Oid parentUpdTrigger,
545 : Oid *deleteTrigOid, Oid *updateTrigOid);
546 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
547 : Oid partRelid,
548 : Oid parentConstrOid, int numfks,
549 : AttrNumber *mapped_conkey, AttrNumber *confkey,
550 : Oid *conpfeqop,
551 : Oid parentInsTrigger,
552 : Oid parentUpdTrigger,
553 : Relation trigrel);
554 : static void GetForeignKeyActionTriggers(Relation trigrel,
555 : Oid conoid, Oid confrelid, Oid conrelid,
556 : Oid *deleteTriggerOid,
557 : Oid *updateTriggerOid);
558 : static void GetForeignKeyCheckTriggers(Relation trigrel,
559 : Oid conoid, Oid confrelid, Oid conrelid,
560 : Oid *insertTriggerOid,
561 : Oid *updateTriggerOid);
562 : static void ATExecDropConstraint(Relation rel, const char *constrName,
563 : DropBehavior behavior, bool recurse,
564 : bool missing_ok, LOCKMODE lockmode);
565 : static ObjectAddress dropconstraint_internal(Relation rel,
566 : HeapTuple constraintTup, DropBehavior behavior,
567 : bool recurse, bool recursing,
568 : bool missing_ok, List **readyRels,
569 : LOCKMODE lockmode);
570 : static void ATPrepAlterColumnType(List **wqueue,
571 : AlteredTableInfo *tab, Relation rel,
572 : bool recurse, bool recursing,
573 : AlterTableCmd *cmd, LOCKMODE lockmode,
574 : AlterTableUtilityContext *context);
575 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
576 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
577 : AlterTableCmd *cmd, LOCKMODE lockmode);
578 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
579 : Relation rel, AttrNumber attnum, const char *colName);
580 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
581 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
582 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
583 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
584 : LOCKMODE lockmode);
585 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
586 : char *cmd, List **wqueue, LOCKMODE lockmode,
587 : bool rewrite);
588 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
589 : Oid objid, Relation rel, List *domname,
590 : const char *conname);
591 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
592 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
593 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
594 : List *options, LOCKMODE lockmode);
595 : static void change_owner_fix_column_acls(Oid relationOid,
596 : Oid oldOwnerId, Oid newOwnerId);
597 : static void change_owner_recurse_to_sequences(Oid relationOid,
598 : Oid newOwnerId, LOCKMODE lockmode);
599 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
600 : LOCKMODE lockmode);
601 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
602 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
603 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
604 : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
605 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
606 : const char *tablespacename, LOCKMODE lockmode);
607 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
608 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
609 : static void ATExecSetRelOptions(Relation rel, List *defList,
610 : AlterTableType operation,
611 : LOCKMODE lockmode);
612 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
613 : char fires_when, bool skip_system, bool recurse,
614 : LOCKMODE lockmode);
615 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
616 : char fires_when, LOCKMODE lockmode);
617 : static void ATPrepAddInherit(Relation child_rel);
618 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
619 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
620 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
621 : DependencyType deptype);
622 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
623 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
624 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
625 : static void ATExecGenericOptions(Relation rel, List *options);
626 : static void ATExecSetRowSecurity(Relation rel, bool rls);
627 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
628 : static ObjectAddress ATExecSetCompression(Relation rel,
629 : const char *column, Node *newValue, LOCKMODE lockmode);
630 :
631 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
632 : static const char *storage_name(char c);
633 :
634 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
635 : Oid oldRelOid, void *arg);
636 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
637 : Oid oldrelid, void *arg);
638 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
639 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
640 : List **partexprs, Oid *partopclass, Oid *partcollation,
641 : PartitionStrategy strategy);
642 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
643 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
644 : bool expect_detached);
645 : static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
646 : int inhcount);
647 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
648 : PartitionCmd *cmd,
649 : AlterTableUtilityContext *context);
650 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
651 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
652 : List *partConstraint,
653 : bool validate_default);
654 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
655 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
656 : static void DropClonedTriggersFromPartition(Oid partitionId);
657 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
658 : Relation rel, RangeVar *name,
659 : bool concurrent);
660 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
661 : bool concurrent, Oid defaultPartOid);
662 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
663 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
664 : RangeVar *name);
665 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
666 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
667 : Relation partitionTbl);
668 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
669 : static List *GetParentedForeignKeyRefs(Relation partition);
670 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
671 : static char GetAttributeCompression(Oid atttypid, const char *compression);
672 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
673 :
674 : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
675 : Relation rel, PartitionCmd *cmd,
676 : AlterTableUtilityContext *context);
677 : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
678 : PartitionCmd *cmd, AlterTableUtilityContext *context);
679 :
680 : /* ----------------------------------------------------------------
681 : * DefineRelation
682 : * Creates a new relation.
683 : *
684 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
685 : * The other arguments are used to extend the behavior for other cases:
686 : * relkind: relkind to assign to the new relation
687 : * ownerId: if not InvalidOid, use this as the new relation's owner.
688 : * typaddress: if not null, it's set to the pg_type entry's address.
689 : * queryString: for error reporting
690 : *
691 : * Note that permissions checks are done against current user regardless of
692 : * ownerId. A nonzero ownerId is used when someone is creating a relation
693 : * "on behalf of" someone else, so we still want to see that the current user
694 : * has permissions to do it.
695 : *
696 : * If successful, returns the address of the new relation.
697 : * ----------------------------------------------------------------
698 : */
699 : ObjectAddress
700 53208 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
701 : ObjectAddress *typaddress, const char *queryString)
702 : {
703 : char relname[NAMEDATALEN];
704 : Oid namespaceId;
705 : Oid relationId;
706 : Oid tablespaceId;
707 : Relation rel;
708 : TupleDesc descriptor;
709 : List *inheritOids;
710 : List *old_constraints;
711 : List *old_notnulls;
712 : List *rawDefaults;
713 : List *cookedDefaults;
714 : List *nncols;
715 : Datum reloptions;
716 : ListCell *listptr;
717 : AttrNumber attnum;
718 : bool partitioned;
719 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
720 : Oid ofTypeId;
721 : ObjectAddress address;
722 : LOCKMODE parentLockmode;
723 53208 : Oid accessMethodId = InvalidOid;
724 :
725 : /*
726 : * Truncate relname to appropriate length (probably a waste of time, as
727 : * parser should have done this already).
728 : */
729 53208 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
730 :
731 : /*
732 : * Check consistency of arguments
733 : */
734 53208 : if (stmt->oncommit != ONCOMMIT_NOOP
735 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
736 12 : ereport(ERROR,
737 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
738 : errmsg("ON COMMIT can only be used on temporary tables")));
739 :
740 53196 : if (stmt->partspec != NULL)
741 : {
742 4882 : if (relkind != RELKIND_RELATION)
743 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
744 :
745 4882 : relkind = RELKIND_PARTITIONED_TABLE;
746 4882 : partitioned = true;
747 : }
748 : else
749 48314 : partitioned = false;
750 :
751 : /*
752 : * Look up the namespace in which we are supposed to create the relation,
753 : * check we have permission to create there, lock it against concurrent
754 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
755 : * namespace is selected.
756 : */
757 : namespaceId =
758 53196 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
759 :
760 : /*
761 : * Security check: disallow creating temp tables from security-restricted
762 : * code. This is needed because calling code might not expect untrusted
763 : * tables to appear in pg_temp at the front of its search path.
764 : */
765 53196 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
766 2960 : && InSecurityRestrictedOperation())
767 0 : ereport(ERROR,
768 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
769 : errmsg("cannot create temporary table within security-restricted operation")));
770 :
771 : /*
772 : * Determine the lockmode to use when scanning parents. A self-exclusive
773 : * lock is needed here.
774 : *
775 : * For regular inheritance, if two backends attempt to add children to the
776 : * same parent simultaneously, and that parent has no pre-existing
777 : * children, then both will attempt to update the parent's relhassubclass
778 : * field, leading to a "tuple concurrently updated" error. Also, this
779 : * interlocks against a concurrent ANALYZE on the parent table, which
780 : * might otherwise be attempting to clear the parent's relhassubclass
781 : * field, if its previous children were recently dropped.
782 : *
783 : * If the child table is a partition, then we instead grab an exclusive
784 : * lock on the parent because its partition descriptor will be changed by
785 : * addition of the new partition.
786 : */
787 53196 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
788 : ShareUpdateExclusiveLock);
789 :
790 : /* Determine the list of OIDs of the parents. */
791 53196 : inheritOids = NIL;
792 63566 : foreach(listptr, stmt->inhRelations)
793 : {
794 10370 : RangeVar *rv = (RangeVar *) lfirst(listptr);
795 : Oid parentOid;
796 :
797 10370 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
798 :
799 : /*
800 : * Reject duplications in the list of parents.
801 : */
802 10370 : if (list_member_oid(inheritOids, parentOid))
803 0 : ereport(ERROR,
804 : (errcode(ERRCODE_DUPLICATE_TABLE),
805 : errmsg("relation \"%s\" would be inherited from more than once",
806 : get_rel_name(parentOid))));
807 :
808 10370 : inheritOids = lappend_oid(inheritOids, parentOid);
809 : }
810 :
811 : /*
812 : * Select tablespace to use: an explicitly indicated one, or (in the case
813 : * of a partitioned table) the parent's, if it has one.
814 : */
815 53196 : if (stmt->tablespacename)
816 : {
817 110 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
818 :
819 104 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
820 6 : ereport(ERROR,
821 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
822 : errmsg("cannot specify default tablespace for partitioned relations")));
823 : }
824 53086 : else if (stmt->partbound)
825 : {
826 : Assert(list_length(inheritOids) == 1);
827 8268 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
828 : }
829 : else
830 44818 : tablespaceId = InvalidOid;
831 :
832 : /* still nothing? use the default */
833 53184 : if (!OidIsValid(tablespaceId))
834 53064 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
835 : partitioned);
836 :
837 : /* Check permissions except when using database's default */
838 53178 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
839 : {
840 : AclResult aclresult;
841 :
842 138 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
843 : ACL_CREATE);
844 138 : if (aclresult != ACLCHECK_OK)
845 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
846 6 : get_tablespace_name(tablespaceId));
847 : }
848 :
849 : /* In all cases disallow placing user relations in pg_global */
850 53172 : if (tablespaceId == GLOBALTABLESPACE_OID)
851 18 : ereport(ERROR,
852 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
853 : errmsg("only shared relations can be placed in pg_global tablespace")));
854 :
855 : /* Identify user ID that will own the table */
856 53154 : if (!OidIsValid(ownerId))
857 52924 : ownerId = GetUserId();
858 :
859 : /*
860 : * Parse and validate reloptions, if any.
861 : */
862 53154 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
863 : true, false);
864 :
865 53136 : switch (relkind)
866 : {
867 13370 : case RELKIND_VIEW:
868 13370 : (void) view_reloptions(reloptions, true);
869 13352 : break;
870 4864 : case RELKIND_PARTITIONED_TABLE:
871 4864 : (void) partitioned_table_reloptions(reloptions, true);
872 4858 : break;
873 34902 : default:
874 34902 : (void) heap_reloptions(relkind, reloptions, true);
875 : }
876 :
877 53016 : if (stmt->ofTypename)
878 : {
879 : AclResult aclresult;
880 :
881 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
882 :
883 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
884 86 : if (aclresult != ACLCHECK_OK)
885 6 : aclcheck_error_type(aclresult, ofTypeId);
886 : }
887 : else
888 52930 : ofTypeId = InvalidOid;
889 :
890 : /*
891 : * Look up inheritance ancestors and generate relation schema, including
892 : * inherited attributes. (Note that stmt->tableElts is destructively
893 : * modified by MergeAttributes.)
894 : */
895 52842 : stmt->tableElts =
896 53010 : MergeAttributes(stmt->tableElts, inheritOids,
897 53010 : stmt->relation->relpersistence,
898 53010 : stmt->partbound != NULL,
899 : &old_constraints, &old_notnulls);
900 :
901 : /*
902 : * Create a tuple descriptor from the relation schema. Note that this
903 : * deals with column names, types, and in-descriptor NOT NULL flags, but
904 : * not default values, NOT NULL or CHECK constraints; we handle those
905 : * below.
906 : */
907 52842 : descriptor = BuildDescForRelation(stmt->tableElts);
908 :
909 : /*
910 : * Find columns with default values and prepare for insertion of the
911 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
912 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
913 : * while raw defaults go into a list of RawColumnDefault structs that will
914 : * be processed by AddRelationNewConstraints. (We can't deal with raw
915 : * expressions until we can do transformExpr.)
916 : *
917 : * We can set the atthasdef flags now in the tuple descriptor; this just
918 : * saves StoreAttrDefault from having to do an immediate update of the
919 : * pg_attribute rows.
920 : */
921 52794 : rawDefaults = NIL;
922 52794 : cookedDefaults = NIL;
923 52794 : attnum = 0;
924 :
925 267308 : foreach(listptr, stmt->tableElts)
926 : {
927 214514 : ColumnDef *colDef = lfirst(listptr);
928 : Form_pg_attribute attr;
929 :
930 214514 : attnum++;
931 214514 : attr = TupleDescAttr(descriptor, attnum - 1);
932 :
933 214514 : if (colDef->raw_default != NULL)
934 : {
935 : RawColumnDefault *rawEnt;
936 :
937 : Assert(colDef->cooked_default == NULL);
938 :
939 2400 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
940 2400 : rawEnt->attnum = attnum;
941 2400 : rawEnt->raw_default = colDef->raw_default;
942 2400 : rawEnt->missingMode = false;
943 2400 : rawEnt->generated = colDef->generated;
944 2400 : rawDefaults = lappend(rawDefaults, rawEnt);
945 2400 : attr->atthasdef = true;
946 : }
947 212114 : else if (colDef->cooked_default != NULL)
948 : {
949 : CookedConstraint *cooked;
950 :
951 442 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
952 442 : cooked->contype = CONSTR_DEFAULT;
953 442 : cooked->conoid = InvalidOid; /* until created */
954 442 : cooked->name = NULL;
955 442 : cooked->attnum = attnum;
956 442 : cooked->expr = colDef->cooked_default;
957 442 : cooked->skip_validation = false;
958 442 : cooked->is_local = true; /* not used for defaults */
959 442 : cooked->inhcount = 0; /* ditto */
960 442 : cooked->is_no_inherit = false;
961 442 : cookedDefaults = lappend(cookedDefaults, cooked);
962 442 : attr->atthasdef = true;
963 : }
964 : }
965 :
966 : /*
967 : * For relations with table AM and partitioned tables, select access
968 : * method to use: an explicitly indicated one, or (in the case of a
969 : * partitioned table) the parent's, if it has one.
970 : */
971 52794 : if (stmt->accessMethod != NULL)
972 : {
973 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
974 116 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
975 : }
976 52678 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
977 : {
978 36540 : if (stmt->partbound)
979 : {
980 : Assert(list_length(inheritOids) == 1);
981 8110 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
982 : }
983 :
984 36540 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
985 31662 : accessMethodId = get_table_am_oid(default_table_access_method, false);
986 : }
987 :
988 : /*
989 : * Create the relation. Inherited defaults and constraints are passed in
990 : * for immediate handling --- since they don't need parsing, they can be
991 : * stored immediately.
992 : */
993 52776 : relationId = heap_create_with_catalog(relname,
994 : namespaceId,
995 : tablespaceId,
996 : InvalidOid,
997 : InvalidOid,
998 : ofTypeId,
999 : ownerId,
1000 : accessMethodId,
1001 : descriptor,
1002 : list_concat(cookedDefaults,
1003 : old_constraints),
1004 : relkind,
1005 52776 : stmt->relation->relpersistence,
1006 : false,
1007 : false,
1008 : stmt->oncommit,
1009 : reloptions,
1010 : true,
1011 : allowSystemTableMods,
1012 : false,
1013 : InvalidOid,
1014 : typaddress);
1015 :
1016 : /*
1017 : * We must bump the command counter to make the newly-created relation
1018 : * tuple visible for opening.
1019 : */
1020 52752 : CommandCounterIncrement();
1021 :
1022 : /*
1023 : * Open the new relation and acquire exclusive lock on it. This isn't
1024 : * really necessary for locking out other backends (since they can't see
1025 : * the new rel anyway until we commit), but it keeps the lock manager from
1026 : * complaining about deadlock risks.
1027 : */
1028 52752 : rel = relation_open(relationId, AccessExclusiveLock);
1029 :
1030 : /*
1031 : * Now add any newly specified column default and generation expressions
1032 : * to the new relation. These are passed to us in the form of raw
1033 : * parsetrees; we need to transform them to executable expression trees
1034 : * before they can be added. The most convenient way to do that is to
1035 : * apply the parser's transformExpr routine, but transformExpr doesn't
1036 : * work unless we have a pre-existing relation. So, the transformation has
1037 : * to be postponed to this final step of CREATE TABLE.
1038 : *
1039 : * This needs to be before processing the partitioning clauses because
1040 : * those could refer to generated columns.
1041 : */
1042 52752 : if (rawDefaults)
1043 2036 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1044 : true, true, false, queryString);
1045 :
1046 : /*
1047 : * Make column generation expressions visible for use by partitioning.
1048 : */
1049 52650 : CommandCounterIncrement();
1050 :
1051 : /* Process and store partition bound, if any. */
1052 52650 : if (stmt->partbound)
1053 : {
1054 : PartitionBoundSpec *bound;
1055 : ParseState *pstate;
1056 8214 : Oid parentId = linitial_oid(inheritOids),
1057 : defaultPartOid;
1058 : Relation parent,
1059 8214 : defaultRel = NULL;
1060 : ParseNamespaceItem *nsitem;
1061 :
1062 : /* Already have strong enough lock on the parent */
1063 8214 : parent = table_open(parentId, NoLock);
1064 :
1065 : /*
1066 : * We are going to try to validate the partition bound specification
1067 : * against the partition key of parentRel, so it better have one.
1068 : */
1069 8214 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1070 18 : ereport(ERROR,
1071 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1072 : errmsg("\"%s\" is not partitioned",
1073 : RelationGetRelationName(parent))));
1074 :
1075 : /*
1076 : * The partition constraint of the default partition depends on the
1077 : * partition bounds of every other partition. It is possible that
1078 : * another backend might be about to execute a query on the default
1079 : * partition table, and that the query relies on previously cached
1080 : * default partition constraints. We must therefore take a table lock
1081 : * strong enough to prevent all queries on the default partition from
1082 : * proceeding until we commit and send out a shared-cache-inval notice
1083 : * that will make them update their index lists.
1084 : *
1085 : * Order of locking: The relation being added won't be visible to
1086 : * other backends until it is committed, hence here in
1087 : * DefineRelation() the order of locking the default partition and the
1088 : * relation being added does not matter. But at all other places we
1089 : * need to lock the default relation before we lock the relation being
1090 : * added or removed i.e. we should take the lock in same order at all
1091 : * the places such that lock parent, lock default partition and then
1092 : * lock the partition so as to avoid a deadlock.
1093 : */
1094 : defaultPartOid =
1095 8196 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1096 : true));
1097 8196 : if (OidIsValid(defaultPartOid))
1098 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1099 :
1100 : /* Transform the bound values */
1101 8196 : pstate = make_parsestate(NULL);
1102 8196 : pstate->p_sourcetext = queryString;
1103 :
1104 : /*
1105 : * Add an nsitem containing this relation, so that transformExpr
1106 : * called on partition bound expressions is able to report errors
1107 : * using a proper context.
1108 : */
1109 8196 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1110 : NULL, false, false);
1111 8196 : addNSItemToQuery(pstate, nsitem, false, true, true);
1112 :
1113 8196 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1114 :
1115 : /*
1116 : * Check first that the new partition's bound is valid and does not
1117 : * overlap with any of existing partitions of the parent.
1118 : */
1119 7992 : check_new_partition_bound(relname, parent, bound, pstate);
1120 :
1121 : /*
1122 : * If the default partition exists, its partition constraints will
1123 : * change after the addition of this new partition such that it won't
1124 : * allow any row that qualifies for this new partition. So, check that
1125 : * the existing data in the default partition satisfies the constraint
1126 : * as it will exist after adding this partition.
1127 : */
1128 7878 : if (OidIsValid(defaultPartOid))
1129 : {
1130 348 : check_default_partition_contents(parent, defaultRel, bound);
1131 : /* Keep the lock until commit. */
1132 330 : table_close(defaultRel, NoLock);
1133 : }
1134 :
1135 : /* Update the pg_class entry. */
1136 7860 : StorePartitionBound(rel, parent, bound);
1137 :
1138 7860 : table_close(parent, NoLock);
1139 : }
1140 :
1141 : /* Store inheritance information for new rel. */
1142 52296 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1143 :
1144 : /*
1145 : * Process the partitioning specification (if any) and store the partition
1146 : * key information into the catalog.
1147 : */
1148 52296 : if (partitioned)
1149 : {
1150 : ParseState *pstate;
1151 : int partnatts;
1152 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1153 : Oid partopclass[PARTITION_MAX_KEYS];
1154 : Oid partcollation[PARTITION_MAX_KEYS];
1155 4858 : List *partexprs = NIL;
1156 :
1157 4858 : pstate = make_parsestate(NULL);
1158 4858 : pstate->p_sourcetext = queryString;
1159 :
1160 4858 : partnatts = list_length(stmt->partspec->partParams);
1161 :
1162 : /* Protect fixed-size arrays here and in executor */
1163 4858 : if (partnatts > PARTITION_MAX_KEYS)
1164 0 : ereport(ERROR,
1165 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1166 : errmsg("cannot partition using more than %d columns",
1167 : PARTITION_MAX_KEYS)));
1168 :
1169 : /*
1170 : * We need to transform the raw parsetrees corresponding to partition
1171 : * expressions into executable expression trees. Like column defaults
1172 : * and CHECK constraints, we could not have done the transformation
1173 : * earlier.
1174 : */
1175 4858 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1176 :
1177 4828 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1178 : partattrs, &partexprs, partopclass,
1179 4828 : partcollation, stmt->partspec->strategy);
1180 :
1181 4744 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1182 : partexprs,
1183 : partopclass, partcollation);
1184 :
1185 : /* make it all visible */
1186 4744 : CommandCounterIncrement();
1187 : }
1188 :
1189 : /*
1190 : * If we're creating a partition, create now all the indexes, triggers,
1191 : * FKs defined in the parent.
1192 : *
1193 : * We can't do it earlier, because DefineIndex wants to know the partition
1194 : * key which we just stored.
1195 : */
1196 52182 : if (stmt->partbound)
1197 : {
1198 7854 : Oid parentId = linitial_oid(inheritOids);
1199 : Relation parent;
1200 : List *idxlist;
1201 : ListCell *cell;
1202 :
1203 : /* Already have strong enough lock on the parent */
1204 7854 : parent = table_open(parentId, NoLock);
1205 7854 : idxlist = RelationGetIndexList(parent);
1206 :
1207 : /*
1208 : * For each index in the parent table, create one in the partition
1209 : */
1210 9276 : foreach(cell, idxlist)
1211 : {
1212 1440 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1213 : AttrMap *attmap;
1214 : IndexStmt *idxstmt;
1215 : Oid constraintOid;
1216 :
1217 1440 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1218 : {
1219 36 : if (idxRel->rd_index->indisunique)
1220 12 : ereport(ERROR,
1221 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1222 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1223 : RelationGetRelationName(parent)),
1224 : errdetail("Table \"%s\" contains indexes that are unique.",
1225 : RelationGetRelationName(parent))));
1226 : else
1227 : {
1228 24 : index_close(idxRel, AccessShareLock);
1229 24 : continue;
1230 : }
1231 : }
1232 :
1233 1404 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1234 : RelationGetDescr(parent),
1235 : false);
1236 : idxstmt =
1237 1404 : generateClonedIndexStmt(NULL, idxRel,
1238 : attmap, &constraintOid);
1239 1404 : DefineIndex(RelationGetRelid(rel),
1240 : idxstmt,
1241 : InvalidOid,
1242 : RelationGetRelid(idxRel),
1243 : constraintOid,
1244 : -1,
1245 : false, false, false, false, false);
1246 :
1247 1398 : index_close(idxRel, AccessShareLock);
1248 : }
1249 :
1250 7836 : list_free(idxlist);
1251 :
1252 : /*
1253 : * If there are any row-level triggers, clone them to the new
1254 : * partition.
1255 : */
1256 7836 : if (parent->trigdesc != NULL)
1257 426 : CloneRowTriggersToPartition(parent, rel);
1258 :
1259 : /*
1260 : * And foreign keys too. Note that because we're freshly creating the
1261 : * table, there is no need to verify these new constraints.
1262 : */
1263 7836 : CloneForeignKeyConstraints(NULL, parent, rel);
1264 :
1265 7836 : table_close(parent, NoLock);
1266 : }
1267 :
1268 : /*
1269 : * Now add any newly specified CHECK constraints to the new relation. Same
1270 : * as for defaults above, but these need to come after partitioning is set
1271 : * up.
1272 : */
1273 52164 : if (stmt->constraints)
1274 634 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1275 : true, true, false, queryString);
1276 :
1277 : /*
1278 : * Finally, merge the not-null constraints that are declared directly with
1279 : * those that come from parent relations (making sure to count inheritance
1280 : * appropriately for each), create them, and set the attnotnull flag on
1281 : * columns that don't yet have it.
1282 : */
1283 52146 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1284 : old_notnulls);
1285 59660 : foreach(listptr, nncols)
1286 7526 : set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
1287 :
1288 52134 : ObjectAddressSet(address, RelationRelationId, relationId);
1289 :
1290 : /*
1291 : * Clean up. We keep lock on new relation (although it shouldn't be
1292 : * visible to anyone else anyway, until commit).
1293 : */
1294 52134 : relation_close(rel, NoLock);
1295 :
1296 52134 : return address;
1297 : }
1298 :
1299 : /*
1300 : * BuildDescForRelation
1301 : *
1302 : * Given a list of ColumnDef nodes, build a TupleDesc.
1303 : *
1304 : * Note: tdtypeid will need to be filled in later on.
1305 : */
1306 : TupleDesc
1307 55520 : BuildDescForRelation(const List *columns)
1308 : {
1309 : int natts;
1310 : AttrNumber attnum;
1311 : ListCell *l;
1312 : TupleDesc desc;
1313 : bool has_not_null;
1314 : char *attname;
1315 : Oid atttypid;
1316 : int32 atttypmod;
1317 : Oid attcollation;
1318 : int attdim;
1319 :
1320 : /*
1321 : * allocate a new tuple descriptor
1322 : */
1323 55520 : natts = list_length(columns);
1324 55520 : desc = CreateTemplateTupleDesc(natts);
1325 55520 : has_not_null = false;
1326 :
1327 55520 : attnum = 0;
1328 :
1329 272976 : foreach(l, columns)
1330 : {
1331 217516 : ColumnDef *entry = lfirst(l);
1332 : AclResult aclresult;
1333 : Form_pg_attribute att;
1334 :
1335 : /*
1336 : * for each entry in the list, get the name and type information from
1337 : * the list and have TupleDescInitEntry fill in the attribute
1338 : * information we need.
1339 : */
1340 217516 : attnum++;
1341 :
1342 217516 : attname = entry->colname;
1343 217516 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1344 :
1345 217516 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1346 217516 : if (aclresult != ACLCHECK_OK)
1347 42 : aclcheck_error_type(aclresult, atttypid);
1348 :
1349 217474 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1350 217474 : attdim = list_length(entry->typeName->arrayBounds);
1351 217474 : if (attdim > PG_INT16_MAX)
1352 0 : ereport(ERROR,
1353 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1354 : errmsg("too many array dimensions"));
1355 :
1356 217474 : if (entry->typeName->setof)
1357 0 : ereport(ERROR,
1358 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1359 : errmsg("column \"%s\" cannot be declared SETOF",
1360 : attname)));
1361 :
1362 217474 : TupleDescInitEntry(desc, attnum, attname,
1363 : atttypid, atttypmod, attdim);
1364 217474 : att = TupleDescAttr(desc, attnum - 1);
1365 :
1366 : /* Override TupleDescInitEntry's settings as requested */
1367 217474 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1368 :
1369 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1370 217474 : att->attnotnull = entry->is_not_null;
1371 217474 : has_not_null |= entry->is_not_null;
1372 217474 : att->attislocal = entry->is_local;
1373 217474 : att->attinhcount = entry->inhcount;
1374 217474 : att->attidentity = entry->identity;
1375 217474 : att->attgenerated = entry->generated;
1376 217474 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1377 217462 : if (entry->storage)
1378 22700 : att->attstorage = entry->storage;
1379 194762 : else if (entry->storage_name)
1380 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1381 : }
1382 :
1383 55460 : if (has_not_null)
1384 : {
1385 12128 : TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1386 :
1387 12128 : constr->has_not_null = true;
1388 12128 : constr->has_generated_stored = false;
1389 12128 : constr->defval = NULL;
1390 12128 : constr->missing = NULL;
1391 12128 : constr->num_defval = 0;
1392 12128 : constr->check = NULL;
1393 12128 : constr->num_check = 0;
1394 12128 : desc->constr = constr;
1395 : }
1396 : else
1397 : {
1398 43332 : desc->constr = NULL;
1399 : }
1400 :
1401 55460 : return desc;
1402 : }
1403 :
1404 : /*
1405 : * Emit the right error or warning message for a "DROP" command issued on a
1406 : * non-existent relation
1407 : */
1408 : static void
1409 1080 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1410 : {
1411 : const struct dropmsgstrings *rentry;
1412 :
1413 1200 : if (rel->schemaname != NULL &&
1414 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1415 : {
1416 42 : if (!missing_ok)
1417 : {
1418 0 : ereport(ERROR,
1419 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1420 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1421 : }
1422 : else
1423 : {
1424 42 : ereport(NOTICE,
1425 : (errmsg("schema \"%s\" does not exist, skipping",
1426 : rel->schemaname)));
1427 : }
1428 42 : return;
1429 : }
1430 :
1431 1358 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1432 : {
1433 1358 : if (rentry->kind == rightkind)
1434 : {
1435 1038 : if (!missing_ok)
1436 : {
1437 132 : ereport(ERROR,
1438 : (errcode(rentry->nonexistent_code),
1439 : errmsg(rentry->nonexistent_msg, rel->relname)));
1440 : }
1441 : else
1442 : {
1443 906 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1444 906 : break;
1445 : }
1446 : }
1447 : }
1448 :
1449 : Assert(rentry->kind != '\0'); /* Should be impossible */
1450 : }
1451 :
1452 : /*
1453 : * Emit the right error message for a "DROP" command issued on a
1454 : * relation of the wrong type
1455 : */
1456 : static void
1457 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1458 : {
1459 : const struct dropmsgstrings *rentry;
1460 : const struct dropmsgstrings *wentry;
1461 :
1462 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1463 0 : if (rentry->kind == rightkind)
1464 0 : break;
1465 : Assert(rentry->kind != '\0');
1466 :
1467 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1468 0 : if (wentry->kind == wrongkind)
1469 0 : break;
1470 : /* wrongkind could be something we don't have in our table... */
1471 :
1472 0 : ereport(ERROR,
1473 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1474 : errmsg(rentry->nota_msg, relname),
1475 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1476 : }
1477 :
1478 : /*
1479 : * RemoveRelations
1480 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1481 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1482 : */
1483 : void
1484 16204 : RemoveRelations(DropStmt *drop)
1485 : {
1486 : ObjectAddresses *objects;
1487 : char relkind;
1488 : ListCell *cell;
1489 16204 : int flags = 0;
1490 16204 : LOCKMODE lockmode = AccessExclusiveLock;
1491 :
1492 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1493 16204 : if (drop->concurrent)
1494 : {
1495 : /*
1496 : * Note that for temporary relations this lock may get upgraded later
1497 : * on, but as no other session can access a temporary relation, this
1498 : * is actually fine.
1499 : */
1500 122 : lockmode = ShareUpdateExclusiveLock;
1501 : Assert(drop->removeType == OBJECT_INDEX);
1502 122 : if (list_length(drop->objects) != 1)
1503 6 : ereport(ERROR,
1504 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1505 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1506 116 : if (drop->behavior == DROP_CASCADE)
1507 0 : ereport(ERROR,
1508 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1509 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1510 : }
1511 :
1512 : /*
1513 : * First we identify all the relations, then we delete them in a single
1514 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1515 : * RESTRICT errors if one of the relations depends on another.
1516 : */
1517 :
1518 : /* Determine required relkind */
1519 16198 : switch (drop->removeType)
1520 : {
1521 14110 : case OBJECT_TABLE:
1522 14110 : relkind = RELKIND_RELATION;
1523 14110 : break;
1524 :
1525 768 : case OBJECT_INDEX:
1526 768 : relkind = RELKIND_INDEX;
1527 768 : break;
1528 :
1529 172 : case OBJECT_SEQUENCE:
1530 172 : relkind = RELKIND_SEQUENCE;
1531 172 : break;
1532 :
1533 880 : case OBJECT_VIEW:
1534 880 : relkind = RELKIND_VIEW;
1535 880 : break;
1536 :
1537 120 : case OBJECT_MATVIEW:
1538 120 : relkind = RELKIND_MATVIEW;
1539 120 : break;
1540 :
1541 148 : case OBJECT_FOREIGN_TABLE:
1542 148 : relkind = RELKIND_FOREIGN_TABLE;
1543 148 : break;
1544 :
1545 0 : default:
1546 0 : elog(ERROR, "unrecognized drop object type: %d",
1547 : (int) drop->removeType);
1548 : relkind = 0; /* keep compiler quiet */
1549 : break;
1550 : }
1551 :
1552 : /* Lock and validate each relation; build a list of object addresses */
1553 16198 : objects = new_object_addresses();
1554 :
1555 35920 : foreach(cell, drop->objects)
1556 : {
1557 19880 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1558 : Oid relOid;
1559 : ObjectAddress obj;
1560 : struct DropRelationCallbackState state;
1561 :
1562 : /*
1563 : * These next few steps are a great deal like relation_openrv, but we
1564 : * don't bother building a relcache entry since we don't need it.
1565 : *
1566 : * Check for shared-cache-inval messages before trying to access the
1567 : * relation. This is needed to cover the case where the name
1568 : * identifies a rel that has been dropped and recreated since the
1569 : * start of our transaction: if we don't flush the old syscache entry,
1570 : * then we'll latch onto that entry and suffer an error later.
1571 : */
1572 19880 : AcceptInvalidationMessages();
1573 :
1574 : /* Look up the appropriate relation using namespace search. */
1575 19880 : state.expected_relkind = relkind;
1576 39760 : state.heap_lockmode = drop->concurrent ?
1577 19880 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1578 : /* We must initialize these fields to show that no locks are held: */
1579 19880 : state.heapOid = InvalidOid;
1580 19880 : state.partParentOid = InvalidOid;
1581 :
1582 19880 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1583 : RangeVarCallbackForDropRelation,
1584 : (void *) &state);
1585 :
1586 : /* Not there? */
1587 19860 : if (!OidIsValid(relOid))
1588 : {
1589 1080 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1590 948 : continue;
1591 : }
1592 :
1593 : /*
1594 : * Decide if concurrent mode needs to be used here or not. The
1595 : * callback retrieved the rel's persistence for us.
1596 : */
1597 18780 : if (drop->concurrent &&
1598 110 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1599 : {
1600 : Assert(list_length(drop->objects) == 1 &&
1601 : drop->removeType == OBJECT_INDEX);
1602 92 : flags |= PERFORM_DELETION_CONCURRENTLY;
1603 : }
1604 :
1605 : /*
1606 : * Concurrent index drop cannot be used with partitioned indexes,
1607 : * either.
1608 : */
1609 18780 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1610 92 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1611 6 : ereport(ERROR,
1612 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1613 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1614 : rel->relname)));
1615 :
1616 : /*
1617 : * If we're told to drop a partitioned index, we must acquire lock on
1618 : * all the children of its parent partitioned table before proceeding.
1619 : * Otherwise we'd try to lock the child index partitions before their
1620 : * tables, leading to potential deadlock against other sessions that
1621 : * will lock those objects in the other order.
1622 : */
1623 18774 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1624 70 : (void) find_all_inheritors(state.heapOid,
1625 : state.heap_lockmode,
1626 : NULL);
1627 :
1628 : /* OK, we're ready to delete this one */
1629 18774 : obj.classId = RelationRelationId;
1630 18774 : obj.objectId = relOid;
1631 18774 : obj.objectSubId = 0;
1632 :
1633 18774 : add_exact_object_address(&obj, objects);
1634 : }
1635 :
1636 16040 : performMultipleDeletions(objects, drop->behavior, flags);
1637 :
1638 15904 : free_object_addresses(objects);
1639 15904 : }
1640 :
1641 : /*
1642 : * Before acquiring a table lock, check whether we have sufficient rights.
1643 : * In the case of DROP INDEX, also try to lock the table before the index.
1644 : * Also, if the table to be dropped is a partition, we try to lock the parent
1645 : * first.
1646 : */
1647 : static void
1648 20032 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1649 : void *arg)
1650 : {
1651 : HeapTuple tuple;
1652 : struct DropRelationCallbackState *state;
1653 : char expected_relkind;
1654 : bool is_partition;
1655 : Form_pg_class classform;
1656 : LOCKMODE heap_lockmode;
1657 20032 : bool invalid_system_index = false;
1658 :
1659 20032 : state = (struct DropRelationCallbackState *) arg;
1660 20032 : heap_lockmode = state->heap_lockmode;
1661 :
1662 : /*
1663 : * If we previously locked some other index's heap, and the name we're
1664 : * looking up no longer refers to that relation, release the now-useless
1665 : * lock.
1666 : */
1667 20032 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1668 : {
1669 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1670 0 : state->heapOid = InvalidOid;
1671 : }
1672 :
1673 : /*
1674 : * Similarly, if we previously locked some other partition's heap, and the
1675 : * name we're looking up no longer refers to that relation, release the
1676 : * now-useless lock.
1677 : */
1678 20032 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1679 : {
1680 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1681 0 : state->partParentOid = InvalidOid;
1682 : }
1683 :
1684 : /* Didn't find a relation, so no need for locking or permission checks. */
1685 20032 : if (!OidIsValid(relOid))
1686 1082 : return;
1687 :
1688 18950 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1689 18950 : if (!HeapTupleIsValid(tuple))
1690 0 : return; /* concurrently dropped, so nothing to do */
1691 18950 : classform = (Form_pg_class) GETSTRUCT(tuple);
1692 18950 : is_partition = classform->relispartition;
1693 :
1694 : /* Pass back some data to save lookups in RemoveRelations */
1695 18950 : state->actual_relkind = classform->relkind;
1696 18950 : state->actual_relpersistence = classform->relpersistence;
1697 :
1698 : /*
1699 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1700 : * but RemoveRelations() can only pass one relkind for a given relation.
1701 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1702 : * That means we must be careful before giving the wrong type error when
1703 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1704 : * exists with indexes.
1705 : */
1706 18950 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1707 2964 : expected_relkind = RELKIND_RELATION;
1708 15986 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1709 78 : expected_relkind = RELKIND_INDEX;
1710 : else
1711 15908 : expected_relkind = classform->relkind;
1712 :
1713 18950 : if (state->expected_relkind != expected_relkind)
1714 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1715 0 : state->expected_relkind);
1716 :
1717 : /* Allow DROP to either table owner or schema owner */
1718 18950 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1719 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1720 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1721 18 : get_relkind_objtype(classform->relkind),
1722 18 : rel->relname);
1723 :
1724 : /*
1725 : * Check the case of a system index that might have been invalidated by a
1726 : * failed concurrent process and allow its drop. For the time being, this
1727 : * only concerns indexes of toast relations that became invalid during a
1728 : * REINDEX CONCURRENTLY process.
1729 : */
1730 18932 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1731 : {
1732 : HeapTuple locTuple;
1733 : Form_pg_index indexform;
1734 : bool indisvalid;
1735 :
1736 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1737 0 : if (!HeapTupleIsValid(locTuple))
1738 : {
1739 0 : ReleaseSysCache(tuple);
1740 0 : return;
1741 : }
1742 :
1743 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1744 0 : indisvalid = indexform->indisvalid;
1745 0 : ReleaseSysCache(locTuple);
1746 :
1747 : /* Mark object as being an invalid index of system catalogs */
1748 0 : if (!indisvalid)
1749 0 : invalid_system_index = true;
1750 : }
1751 :
1752 : /* In the case of an invalid index, it is fine to bypass this check */
1753 18932 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1754 2 : ereport(ERROR,
1755 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1756 : errmsg("permission denied: \"%s\" is a system catalog",
1757 : rel->relname)));
1758 :
1759 18930 : ReleaseSysCache(tuple);
1760 :
1761 : /*
1762 : * In DROP INDEX, attempt to acquire lock on the parent table before
1763 : * locking the index. index_drop() will need this anyway, and since
1764 : * regular queries lock tables before their indexes, we risk deadlock if
1765 : * we do it the other way around. No error if we don't find a pg_index
1766 : * entry, though --- the relation may have been dropped. Note that this
1767 : * code will execute for either plain or partitioned indexes.
1768 : */
1769 18930 : if (expected_relkind == RELKIND_INDEX &&
1770 : relOid != oldRelOid)
1771 : {
1772 756 : state->heapOid = IndexGetRelation(relOid, true);
1773 756 : if (OidIsValid(state->heapOid))
1774 756 : LockRelationOid(state->heapOid, heap_lockmode);
1775 : }
1776 :
1777 : /*
1778 : * Similarly, if the relation is a partition, we must acquire lock on its
1779 : * parent before locking the partition. That's because queries lock the
1780 : * parent before its partitions, so we risk deadlock if we do it the other
1781 : * way around.
1782 : */
1783 18930 : if (is_partition && relOid != oldRelOid)
1784 : {
1785 594 : state->partParentOid = get_partition_parent(relOid, true);
1786 594 : if (OidIsValid(state->partParentOid))
1787 594 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1788 : }
1789 : }
1790 :
1791 : /*
1792 : * ExecuteTruncate
1793 : * Executes a TRUNCATE command.
1794 : *
1795 : * This is a multi-relation truncate. We first open and grab exclusive
1796 : * lock on all relations involved, checking permissions and otherwise
1797 : * verifying that the relation is OK for truncation. Note that if relations
1798 : * are foreign tables, at this stage, we have not yet checked that their
1799 : * foreign data in external data sources are OK for truncation. These are
1800 : * checked when foreign data are actually truncated later. In CASCADE mode,
1801 : * relations having FK references to the targeted relations are automatically
1802 : * added to the group; in RESTRICT mode, we check that all FK references are
1803 : * internal to the group that's being truncated. Finally all the relations
1804 : * are truncated and reindexed.
1805 : */
1806 : void
1807 1444 : ExecuteTruncate(TruncateStmt *stmt)
1808 : {
1809 1444 : List *rels = NIL;
1810 1444 : List *relids = NIL;
1811 1444 : List *relids_logged = NIL;
1812 : ListCell *cell;
1813 :
1814 : /*
1815 : * Open, exclusive-lock, and check all the explicitly-specified relations
1816 : */
1817 3114 : foreach(cell, stmt->relations)
1818 : {
1819 1718 : RangeVar *rv = lfirst(cell);
1820 : Relation rel;
1821 1718 : bool recurse = rv->inh;
1822 : Oid myrelid;
1823 1718 : LOCKMODE lockmode = AccessExclusiveLock;
1824 :
1825 1718 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1826 : 0, RangeVarCallbackForTruncate,
1827 : NULL);
1828 :
1829 : /* don't throw error for "TRUNCATE foo, foo" */
1830 1682 : if (list_member_oid(relids, myrelid))
1831 2 : continue;
1832 :
1833 : /* open the relation, we already hold a lock on it */
1834 1680 : rel = table_open(myrelid, NoLock);
1835 :
1836 : /*
1837 : * RangeVarGetRelidExtended() has done most checks with its callback,
1838 : * but other checks with the now-opened Relation remain.
1839 : */
1840 1680 : truncate_check_activity(rel);
1841 :
1842 1680 : rels = lappend(rels, rel);
1843 1680 : relids = lappend_oid(relids, myrelid);
1844 :
1845 : /* Log this relation only if needed for logical decoding */
1846 1680 : if (RelationIsLogicallyLogged(rel))
1847 64 : relids_logged = lappend_oid(relids_logged, myrelid);
1848 :
1849 1680 : if (recurse)
1850 : {
1851 : ListCell *child;
1852 : List *children;
1853 :
1854 1618 : children = find_all_inheritors(myrelid, lockmode, NULL);
1855 :
1856 4968 : foreach(child, children)
1857 : {
1858 3350 : Oid childrelid = lfirst_oid(child);
1859 :
1860 3350 : if (list_member_oid(relids, childrelid))
1861 1618 : continue;
1862 :
1863 : /* find_all_inheritors already got lock */
1864 1732 : rel = table_open(childrelid, NoLock);
1865 :
1866 : /*
1867 : * It is possible that the parent table has children that are
1868 : * temp tables of other backends. We cannot safely access
1869 : * such tables (because of buffering issues), and the best
1870 : * thing to do is to silently ignore them. Note that this
1871 : * check is the same as one of the checks done in
1872 : * truncate_check_activity() called below, still it is kept
1873 : * here for simplicity.
1874 : */
1875 1732 : if (RELATION_IS_OTHER_TEMP(rel))
1876 : {
1877 8 : table_close(rel, lockmode);
1878 8 : continue;
1879 : }
1880 :
1881 : /*
1882 : * Inherited TRUNCATE commands perform access permission
1883 : * checks on the parent table only. So we skip checking the
1884 : * children's permissions and don't call
1885 : * truncate_check_perms() here.
1886 : */
1887 1724 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1888 1724 : truncate_check_activity(rel);
1889 :
1890 1724 : rels = lappend(rels, rel);
1891 1724 : relids = lappend_oid(relids, childrelid);
1892 :
1893 : /* Log this relation only if needed for logical decoding */
1894 1724 : if (RelationIsLogicallyLogged(rel))
1895 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1896 : }
1897 : }
1898 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1899 12 : ereport(ERROR,
1900 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1901 : errmsg("cannot truncate only a partitioned table"),
1902 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1903 : }
1904 :
1905 1396 : ExecuteTruncateGuts(rels, relids, relids_logged,
1906 1396 : stmt->behavior, stmt->restart_seqs, false);
1907 :
1908 : /* And close the rels */
1909 4552 : foreach(cell, rels)
1910 : {
1911 3238 : Relation rel = (Relation) lfirst(cell);
1912 :
1913 3238 : table_close(rel, NoLock);
1914 : }
1915 1314 : }
1916 :
1917 : /*
1918 : * ExecuteTruncateGuts
1919 : *
1920 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1921 : * command (see above) as well as replication subscribers that execute a
1922 : * replicated TRUNCATE action.
1923 : *
1924 : * explicit_rels is the list of Relations to truncate that the command
1925 : * specified. relids is the list of Oids corresponding to explicit_rels.
1926 : * relids_logged is the list of Oids (a subset of relids) that require
1927 : * WAL-logging. This is all a bit redundant, but the existing callers have
1928 : * this information handy in this form.
1929 : */
1930 : void
1931 1434 : ExecuteTruncateGuts(List *explicit_rels,
1932 : List *relids,
1933 : List *relids_logged,
1934 : DropBehavior behavior, bool restart_seqs,
1935 : bool run_as_table_owner)
1936 : {
1937 : List *rels;
1938 1434 : List *seq_relids = NIL;
1939 1434 : HTAB *ft_htab = NULL;
1940 : EState *estate;
1941 : ResultRelInfo *resultRelInfos;
1942 : ResultRelInfo *resultRelInfo;
1943 : SubTransactionId mySubid;
1944 : ListCell *cell;
1945 : Oid *logrelids;
1946 :
1947 : /*
1948 : * Check the explicitly-specified relations.
1949 : *
1950 : * In CASCADE mode, suck in all referencing relations as well. This
1951 : * requires multiple iterations to find indirectly-dependent relations. At
1952 : * each phase, we need to exclusive-lock new rels before looking for their
1953 : * dependencies, else we might miss something. Also, we check each rel as
1954 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1955 : * time on a rel we have no permissions for.
1956 : */
1957 1434 : rels = list_copy(explicit_rels);
1958 1434 : if (behavior == DROP_CASCADE)
1959 : {
1960 : for (;;)
1961 40 : {
1962 : List *newrelids;
1963 :
1964 80 : newrelids = heap_truncate_find_FKs(relids);
1965 80 : if (newrelids == NIL)
1966 40 : break; /* nothing else to add */
1967 :
1968 134 : foreach(cell, newrelids)
1969 : {
1970 94 : Oid relid = lfirst_oid(cell);
1971 : Relation rel;
1972 :
1973 94 : rel = table_open(relid, AccessExclusiveLock);
1974 94 : ereport(NOTICE,
1975 : (errmsg("truncate cascades to table \"%s\"",
1976 : RelationGetRelationName(rel))));
1977 94 : truncate_check_rel(relid, rel->rd_rel);
1978 94 : truncate_check_perms(relid, rel->rd_rel);
1979 94 : truncate_check_activity(rel);
1980 94 : rels = lappend(rels, rel);
1981 94 : relids = lappend_oid(relids, relid);
1982 :
1983 : /* Log this relation only if needed for logical decoding */
1984 94 : if (RelationIsLogicallyLogged(rel))
1985 0 : relids_logged = lappend_oid(relids_logged, relid);
1986 : }
1987 : }
1988 : }
1989 :
1990 : /*
1991 : * Check foreign key references. In CASCADE mode, this should be
1992 : * unnecessary since we just pulled in all the references; but as a
1993 : * cross-check, do it anyway if in an Assert-enabled build.
1994 : */
1995 : #ifdef USE_ASSERT_CHECKING
1996 : heap_truncate_check_FKs(rels, false);
1997 : #else
1998 1434 : if (behavior == DROP_RESTRICT)
1999 1394 : heap_truncate_check_FKs(rels, false);
2000 : #endif
2001 :
2002 : /*
2003 : * If we are asked to restart sequences, find all the sequences, lock them
2004 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2005 : * We want to do this early since it's pointless to do all the truncation
2006 : * work only to fail on sequence permissions.
2007 : */
2008 1360 : if (restart_seqs)
2009 : {
2010 52 : foreach(cell, rels)
2011 : {
2012 26 : Relation rel = (Relation) lfirst(cell);
2013 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2014 : ListCell *seqcell;
2015 :
2016 62 : foreach(seqcell, seqlist)
2017 : {
2018 36 : Oid seq_relid = lfirst_oid(seqcell);
2019 : Relation seq_rel;
2020 :
2021 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2022 :
2023 : /* This check must match AlterSequence! */
2024 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2025 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2026 0 : RelationGetRelationName(seq_rel));
2027 :
2028 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2029 :
2030 36 : relation_close(seq_rel, NoLock);
2031 : }
2032 : }
2033 : }
2034 :
2035 : /* Prepare to catch AFTER triggers. */
2036 1360 : AfterTriggerBeginQuery();
2037 :
2038 : /*
2039 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2040 : * each relation. We don't need to call ExecOpenIndices, though.
2041 : *
2042 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2043 : * though we don't have a range table and don't populate the
2044 : * es_result_relations array. That's a bit bogus, but it's enough to make
2045 : * ExecGetTriggerResultRel() find them.
2046 : */
2047 1360 : estate = CreateExecutorState();
2048 : resultRelInfos = (ResultRelInfo *)
2049 1360 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2050 1360 : resultRelInfo = resultRelInfos;
2051 4770 : foreach(cell, rels)
2052 : {
2053 3410 : Relation rel = (Relation) lfirst(cell);
2054 :
2055 3410 : InitResultRelInfo(resultRelInfo,
2056 : rel,
2057 : 0, /* dummy rangetable index */
2058 : NULL,
2059 : 0);
2060 3410 : estate->es_opened_result_relations =
2061 3410 : lappend(estate->es_opened_result_relations, resultRelInfo);
2062 3410 : resultRelInfo++;
2063 : }
2064 :
2065 : /*
2066 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2067 : * truncating (this is because one of them might throw an error). Also, if
2068 : * we were to allow them to prevent statement execution, that would need
2069 : * to be handled here.
2070 : */
2071 1360 : resultRelInfo = resultRelInfos;
2072 4770 : foreach(cell, rels)
2073 : {
2074 : UserContext ucxt;
2075 :
2076 3410 : if (run_as_table_owner)
2077 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2078 : &ucxt);
2079 3410 : ExecBSTruncateTriggers(estate, resultRelInfo);
2080 3410 : if (run_as_table_owner)
2081 70 : RestoreUserContext(&ucxt);
2082 3410 : resultRelInfo++;
2083 : }
2084 :
2085 : /*
2086 : * OK, truncate each table.
2087 : */
2088 1360 : mySubid = GetCurrentSubTransactionId();
2089 :
2090 4770 : foreach(cell, rels)
2091 : {
2092 3410 : Relation rel = (Relation) lfirst(cell);
2093 :
2094 : /* Skip partitioned tables as there is nothing to do */
2095 3410 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2096 692 : continue;
2097 :
2098 : /*
2099 : * Build the lists of foreign tables belonging to each foreign server
2100 : * and pass each list to the foreign data wrapper's callback function,
2101 : * so that each server can truncate its all foreign tables in bulk.
2102 : * Each list is saved as a single entry in a hash table that uses the
2103 : * server OID as lookup key.
2104 : */
2105 2718 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2106 : {
2107 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2108 : bool found;
2109 : ForeignTruncateInfo *ft_info;
2110 :
2111 : /* First time through, initialize hashtable for foreign tables */
2112 34 : if (!ft_htab)
2113 : {
2114 : HASHCTL hctl;
2115 :
2116 30 : memset(&hctl, 0, sizeof(HASHCTL));
2117 30 : hctl.keysize = sizeof(Oid);
2118 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2119 30 : hctl.hcxt = CurrentMemoryContext;
2120 :
2121 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2122 : 32, /* start small and extend */
2123 : &hctl,
2124 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2125 : }
2126 :
2127 : /* Find or create cached entry for the foreign table */
2128 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2129 34 : if (!found)
2130 30 : ft_info->rels = NIL;
2131 :
2132 : /*
2133 : * Save the foreign table in the entry of the server that the
2134 : * foreign table belongs to.
2135 : */
2136 34 : ft_info->rels = lappend(ft_info->rels, rel);
2137 34 : continue;
2138 : }
2139 :
2140 : /*
2141 : * Normally, we need a transaction-safe truncation here. However, if
2142 : * the table was either created in the current (sub)transaction or has
2143 : * a new relfilenumber in the current (sub)transaction, then we can
2144 : * just truncate it in-place, because a rollback would cause the whole
2145 : * table or the current physical file to be thrown away anyway.
2146 : */
2147 2684 : if (rel->rd_createSubid == mySubid ||
2148 2658 : rel->rd_newRelfilelocatorSubid == mySubid)
2149 : {
2150 : /* Immediate, non-rollbackable truncation is OK */
2151 90 : heap_truncate_one_rel(rel);
2152 : }
2153 : else
2154 : {
2155 : Oid heap_relid;
2156 : Oid toast_relid;
2157 2594 : ReindexParams reindex_params = {0};
2158 :
2159 : /*
2160 : * This effectively deletes all rows in the table, and may be done
2161 : * in a serializable transaction. In that case we must record a
2162 : * rw-conflict in to this transaction from each transaction
2163 : * holding a predicate lock on the table.
2164 : */
2165 2594 : CheckTableForSerializableConflictIn(rel);
2166 :
2167 : /*
2168 : * Need the full transaction-safe pushups.
2169 : *
2170 : * Create a new empty storage file for the relation, and assign it
2171 : * as the relfilenumber value. The old storage file is scheduled
2172 : * for deletion at commit.
2173 : */
2174 2594 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2175 :
2176 2594 : heap_relid = RelationGetRelid(rel);
2177 :
2178 : /*
2179 : * The same for the toast table, if any.
2180 : */
2181 2594 : toast_relid = rel->rd_rel->reltoastrelid;
2182 2594 : if (OidIsValid(toast_relid))
2183 : {
2184 1596 : Relation toastrel = relation_open(toast_relid,
2185 : AccessExclusiveLock);
2186 :
2187 1596 : RelationSetNewRelfilenumber(toastrel,
2188 1596 : toastrel->rd_rel->relpersistence);
2189 1596 : table_close(toastrel, NoLock);
2190 : }
2191 :
2192 : /*
2193 : * Reconstruct the indexes to match, and we're done.
2194 : */
2195 2594 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2196 : &reindex_params);
2197 : }
2198 :
2199 2684 : pgstat_count_truncate(rel);
2200 : }
2201 :
2202 : /* Now go through the hash table, and truncate foreign tables */
2203 1360 : if (ft_htab)
2204 : {
2205 : ForeignTruncateInfo *ft_info;
2206 : HASH_SEQ_STATUS seq;
2207 :
2208 30 : hash_seq_init(&seq, ft_htab);
2209 :
2210 30 : PG_TRY();
2211 : {
2212 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2213 : {
2214 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2215 :
2216 : /* truncate_check_rel() has checked that already */
2217 : Assert(routine->ExecForeignTruncate != NULL);
2218 :
2219 30 : routine->ExecForeignTruncate(ft_info->rels,
2220 : behavior,
2221 : restart_seqs);
2222 : }
2223 : }
2224 8 : PG_FINALLY();
2225 : {
2226 30 : hash_destroy(ft_htab);
2227 : }
2228 30 : PG_END_TRY();
2229 : }
2230 :
2231 : /*
2232 : * Restart owned sequences if we were asked to.
2233 : */
2234 1388 : foreach(cell, seq_relids)
2235 : {
2236 36 : Oid seq_relid = lfirst_oid(cell);
2237 :
2238 36 : ResetSequence(seq_relid);
2239 : }
2240 :
2241 : /*
2242 : * Write a WAL record to allow this set of actions to be logically
2243 : * decoded.
2244 : *
2245 : * Assemble an array of relids so we can write a single WAL record for the
2246 : * whole action.
2247 : */
2248 1352 : if (relids_logged != NIL)
2249 : {
2250 : xl_heap_truncate xlrec;
2251 50 : int i = 0;
2252 :
2253 : /* should only get here if wal_level >= logical */
2254 : Assert(XLogLogicalInfoActive());
2255 :
2256 50 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2257 136 : foreach(cell, relids_logged)
2258 86 : logrelids[i++] = lfirst_oid(cell);
2259 :
2260 50 : xlrec.dbId = MyDatabaseId;
2261 50 : xlrec.nrelids = list_length(relids_logged);
2262 50 : xlrec.flags = 0;
2263 50 : if (behavior == DROP_CASCADE)
2264 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2265 50 : if (restart_seqs)
2266 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2267 :
2268 50 : XLogBeginInsert();
2269 50 : XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2270 50 : XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2271 :
2272 50 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2273 :
2274 50 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2275 : }
2276 :
2277 : /*
2278 : * Process all AFTER STATEMENT TRUNCATE triggers.
2279 : */
2280 1352 : resultRelInfo = resultRelInfos;
2281 4754 : foreach(cell, rels)
2282 : {
2283 : UserContext ucxt;
2284 :
2285 3402 : if (run_as_table_owner)
2286 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2287 : &ucxt);
2288 3402 : ExecASTruncateTriggers(estate, resultRelInfo);
2289 3402 : if (run_as_table_owner)
2290 70 : RestoreUserContext(&ucxt);
2291 3402 : resultRelInfo++;
2292 : }
2293 :
2294 : /* Handle queued AFTER triggers */
2295 1352 : AfterTriggerEndQuery(estate);
2296 :
2297 : /* We can clean up the EState now */
2298 1352 : FreeExecutorState(estate);
2299 :
2300 : /*
2301 : * Close any rels opened by CASCADE (can't do this while EState still
2302 : * holds refs)
2303 : */
2304 1352 : rels = list_difference_ptr(rels, explicit_rels);
2305 1446 : foreach(cell, rels)
2306 : {
2307 94 : Relation rel = (Relation) lfirst(cell);
2308 :
2309 94 : table_close(rel, NoLock);
2310 : }
2311 1352 : }
2312 :
2313 : /*
2314 : * Check that a given relation is safe to truncate. Subroutine for
2315 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2316 : */
2317 : static void
2318 3612 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2319 : {
2320 3612 : char *relname = NameStr(reltuple->relname);
2321 :
2322 : /*
2323 : * Only allow truncate on regular tables, foreign tables using foreign
2324 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2325 : * latter are only being included here for the following checks; no
2326 : * physical truncation will occur in their case.).
2327 : */
2328 3612 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2329 : {
2330 36 : Oid serverid = GetForeignServerIdByRelId(relid);
2331 36 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2332 :
2333 36 : if (!fdwroutine->ExecForeignTruncate)
2334 2 : ereport(ERROR,
2335 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2336 : errmsg("cannot truncate foreign table \"%s\"",
2337 : relname)));
2338 : }
2339 3576 : else if (reltuple->relkind != RELKIND_RELATION &&
2340 706 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2341 0 : ereport(ERROR,
2342 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2343 : errmsg("\"%s\" is not a table", relname)));
2344 :
2345 : /*
2346 : * Most system catalogs can't be truncated at all, or at least not unless
2347 : * allow_system_table_mods=on. As an exception, however, we allow
2348 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2349 : * to change its relfilenode to match the old cluster, and allowing a
2350 : * TRUNCATE command to be executed is the easiest way of doing that.
2351 : */
2352 3610 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2353 22 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2354 2 : ereport(ERROR,
2355 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2356 : errmsg("permission denied: \"%s\" is a system catalog",
2357 : relname)));
2358 :
2359 3608 : InvokeObjectTruncateHook(relid);
2360 3608 : }
2361 :
2362 : /*
2363 : * Check that current user has the permission to truncate given relation.
2364 : */
2365 : static void
2366 1884 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2367 : {
2368 1884 : char *relname = NameStr(reltuple->relname);
2369 : AclResult aclresult;
2370 :
2371 : /* Permissions checks */
2372 1884 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2373 1884 : if (aclresult != ACLCHECK_OK)
2374 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2375 : relname);
2376 1852 : }
2377 :
2378 : /*
2379 : * Set of extra sanity checks to check if a given relation is safe to
2380 : * truncate. This is split with truncate_check_rel() as
2381 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2382 : */
2383 : static void
2384 3498 : truncate_check_activity(Relation rel)
2385 : {
2386 : /*
2387 : * Don't allow truncate on temp tables of other backends ... their local
2388 : * buffer manager is not going to cope.
2389 : */
2390 3498 : if (RELATION_IS_OTHER_TEMP(rel))
2391 0 : ereport(ERROR,
2392 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2393 : errmsg("cannot truncate temporary tables of other sessions")));
2394 :
2395 : /*
2396 : * Also check for active uses of the relation in the current transaction,
2397 : * including open scans and pending AFTER trigger events.
2398 : */
2399 3498 : CheckTableNotInUse(rel, "TRUNCATE");
2400 3498 : }
2401 :
2402 : /*
2403 : * storage_name
2404 : * returns the name corresponding to a typstorage/attstorage enum value
2405 : */
2406 : static const char *
2407 24 : storage_name(char c)
2408 : {
2409 24 : switch (c)
2410 : {
2411 0 : case TYPSTORAGE_PLAIN:
2412 0 : return "PLAIN";
2413 0 : case TYPSTORAGE_EXTERNAL:
2414 0 : return "EXTERNAL";
2415 12 : case TYPSTORAGE_EXTENDED:
2416 12 : return "EXTENDED";
2417 12 : case TYPSTORAGE_MAIN:
2418 12 : return "MAIN";
2419 0 : default:
2420 0 : return "???";
2421 : }
2422 : }
2423 :
2424 : /*----------
2425 : * MergeAttributes
2426 : * Returns new schema given initial schema and superclasses.
2427 : *
2428 : * Input arguments:
2429 : * 'columns' is the column/attribute definition for the table. (It's a list
2430 : * of ColumnDef's.) It is destructively changed.
2431 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2432 : * 'relpersistence' is the persistence type of the table.
2433 : * 'is_partition' tells if the table is a partition.
2434 : *
2435 : * Output arguments:
2436 : * 'supconstr' receives a list of constraints belonging to the parents,
2437 : * updated as necessary to be valid for the child.
2438 : * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2439 : * constraints coming from inheritance parents.
2440 : *
2441 : * Return value:
2442 : * Completed schema list.
2443 : *
2444 : * Notes:
2445 : * The order in which the attributes are inherited is very important.
2446 : * Intuitively, the inherited attributes should come first. If a table
2447 : * inherits from multiple parents, the order of those attributes are
2448 : * according to the order of the parents specified in CREATE TABLE.
2449 : *
2450 : * Here's an example:
2451 : *
2452 : * create table person (name text, age int4, location point);
2453 : * create table emp (salary int4, manager text) inherits(person);
2454 : * create table student (gpa float8) inherits (person);
2455 : * create table stud_emp (percent int4) inherits (emp, student);
2456 : *
2457 : * The order of the attributes of stud_emp is:
2458 : *
2459 : * person {1:name, 2:age, 3:location}
2460 : * / \
2461 : * {6:gpa} student emp {4:salary, 5:manager}
2462 : * \ /
2463 : * stud_emp {7:percent}
2464 : *
2465 : * If the same attribute name appears multiple times, then it appears
2466 : * in the result table in the proper location for its first appearance.
2467 : *
2468 : * Constraints (including not-null constraints) for the child table
2469 : * are the union of all relevant constraints, from both the child schema
2470 : * and parent tables. In addition, in legacy inheritance, each column that
2471 : * appears in a primary key in any of the parents also gets a NOT NULL
2472 : * constraint (partitioning doesn't need this, because the PK itself gets
2473 : * inherited.)
2474 : *
2475 : * The default value for a child column is defined as:
2476 : * (1) If the child schema specifies a default, that value is used.
2477 : * (2) If neither the child nor any parent specifies a default, then
2478 : * the column will not have a default.
2479 : * (3) If conflicting defaults are inherited from different parents
2480 : * (and not overridden by the child), an error is raised.
2481 : * (4) Otherwise the inherited default is used.
2482 : *
2483 : * Note that the default-value infrastructure is used for generated
2484 : * columns' expressions too, so most of the preceding paragraph applies
2485 : * to generation expressions too. We insist that a child column be
2486 : * generated if and only if its parent(s) are, but it need not have
2487 : * the same generation expression.
2488 : *----------
2489 : */
2490 : static List *
2491 53010 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2492 : bool is_partition, List **supconstr, List **supnotnulls)
2493 : {
2494 53010 : List *inh_columns = NIL;
2495 53010 : List *constraints = NIL;
2496 53010 : List *nnconstraints = NIL;
2497 53010 : bool have_bogus_defaults = false;
2498 : int child_attno;
2499 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2500 53010 : List *saved_columns = NIL;
2501 : ListCell *lc;
2502 :
2503 : /*
2504 : * Check for and reject tables with too many columns. We perform this
2505 : * check relatively early for two reasons: (a) we don't run the risk of
2506 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2507 : * okay if we're processing <= 1600 columns, but could take minutes to
2508 : * execute if the user attempts to create a table with hundreds of
2509 : * thousands of columns.
2510 : *
2511 : * Note that we also need to check that we do not exceed this figure after
2512 : * including columns from inherited relations.
2513 : */
2514 53010 : if (list_length(columns) > MaxHeapAttributeNumber)
2515 0 : ereport(ERROR,
2516 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2517 : errmsg("tables can have at most %d columns",
2518 : MaxHeapAttributeNumber)));
2519 :
2520 : /*
2521 : * Check for duplicate names in the explicit list of attributes.
2522 : *
2523 : * Although we might consider merging such entries in the same way that we
2524 : * handle name conflicts for inherited attributes, it seems to make more
2525 : * sense to assume such conflicts are errors.
2526 : *
2527 : * We don't use foreach() here because we have two nested loops over the
2528 : * columns list, with possible element deletions in the inner one. If we
2529 : * used foreach_delete_current() it could only fix up the state of one of
2530 : * the loops, so it seems cleaner to use looping over list indexes for
2531 : * both loops. Note that any deletion will happen beyond where the outer
2532 : * loop is, so its index never needs adjustment.
2533 : */
2534 246980 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2535 : {
2536 193994 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2537 :
2538 193994 : if (!is_partition && coldef->typeName == NULL)
2539 : {
2540 : /*
2541 : * Typed table column option that does not belong to a column from
2542 : * the type. This works because the columns from the type come
2543 : * first in the list. (We omit this check for partition column
2544 : * lists; those are processed separately below.)
2545 : */
2546 6 : ereport(ERROR,
2547 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2548 : errmsg("column \"%s\" does not exist",
2549 : coldef->colname)));
2550 : }
2551 :
2552 : /* restpos scans all entries beyond coldef; incr is in loop body */
2553 6160764 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2554 : {
2555 5966794 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2556 :
2557 5966794 : if (strcmp(coldef->colname, restdef->colname) == 0)
2558 : {
2559 50 : if (coldef->is_from_type)
2560 : {
2561 : /*
2562 : * merge the column options into the column from the type
2563 : */
2564 32 : coldef->is_not_null = restdef->is_not_null;
2565 32 : coldef->raw_default = restdef->raw_default;
2566 32 : coldef->cooked_default = restdef->cooked_default;
2567 32 : coldef->constraints = restdef->constraints;
2568 32 : coldef->is_from_type = false;
2569 32 : columns = list_delete_nth_cell(columns, restpos);
2570 : }
2571 : else
2572 18 : ereport(ERROR,
2573 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2574 : errmsg("column \"%s\" specified more than once",
2575 : coldef->colname)));
2576 : }
2577 : else
2578 5966744 : restpos++;
2579 : }
2580 : }
2581 :
2582 : /*
2583 : * In case of a partition, there are no new column definitions, only dummy
2584 : * ColumnDefs created for column constraints. Set them aside for now and
2585 : * process them at the end.
2586 : */
2587 52986 : if (is_partition)
2588 : {
2589 8256 : saved_columns = columns;
2590 8256 : columns = NIL;
2591 : }
2592 :
2593 : /*
2594 : * Scan the parents left-to-right, and merge their attributes to form a
2595 : * list of inherited columns (inh_columns).
2596 : */
2597 52986 : child_attno = 0;
2598 63260 : foreach(lc, supers)
2599 : {
2600 10346 : Oid parent = lfirst_oid(lc);
2601 : Relation relation;
2602 : TupleDesc tupleDesc;
2603 : TupleConstr *constr;
2604 : AttrMap *newattmap;
2605 : List *inherited_defaults;
2606 : List *cols_with_defaults;
2607 : List *nnconstrs;
2608 : ListCell *lc1;
2609 : ListCell *lc2;
2610 : Bitmapset *pkattrs;
2611 10346 : Bitmapset *nncols = NULL;
2612 :
2613 : /* caller already got lock */
2614 10346 : relation = table_open(parent, NoLock);
2615 :
2616 : /*
2617 : * Check for active uses of the parent partitioned table in the
2618 : * current transaction, such as being used in some manner by an
2619 : * enclosing command.
2620 : */
2621 10346 : if (is_partition)
2622 8256 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2623 :
2624 : /*
2625 : * We do not allow partitioned tables and partitions to participate in
2626 : * regular inheritance.
2627 : */
2628 10340 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2629 6 : ereport(ERROR,
2630 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2631 : errmsg("cannot inherit from partitioned table \"%s\"",
2632 : RelationGetRelationName(relation))));
2633 10334 : if (relation->rd_rel->relispartition && !is_partition)
2634 6 : ereport(ERROR,
2635 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2636 : errmsg("cannot inherit from partition \"%s\"",
2637 : RelationGetRelationName(relation))));
2638 :
2639 10328 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2640 8252 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2641 8232 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2642 0 : ereport(ERROR,
2643 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2644 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2645 : RelationGetRelationName(relation))));
2646 :
2647 : /*
2648 : * If the parent is permanent, so must be all of its partitions. Note
2649 : * that inheritance allows that case.
2650 : */
2651 10328 : if (is_partition &&
2652 8250 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2653 : relpersistence == RELPERSISTENCE_TEMP)
2654 6 : ereport(ERROR,
2655 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2656 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2657 : RelationGetRelationName(relation))));
2658 :
2659 : /* Permanent rels cannot inherit from temporary ones */
2660 10322 : if (relpersistence != RELPERSISTENCE_TEMP &&
2661 9980 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2662 24 : ereport(ERROR,
2663 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2664 : errmsg(!is_partition
2665 : ? "cannot inherit from temporary relation \"%s\""
2666 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2667 : RelationGetRelationName(relation))));
2668 :
2669 : /* If existing rel is temp, it must belong to this session */
2670 10298 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2671 294 : !relation->rd_islocaltemp)
2672 0 : ereport(ERROR,
2673 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2674 : errmsg(!is_partition
2675 : ? "cannot inherit from temporary relation of another session"
2676 : : "cannot create as partition of temporary relation of another session")));
2677 :
2678 : /*
2679 : * We should have an UNDER permission flag for this, but for now,
2680 : * demand that creator of a child table own the parent.
2681 : */
2682 10298 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2683 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2684 0 : RelationGetRelationName(relation));
2685 :
2686 10298 : tupleDesc = RelationGetDescr(relation);
2687 10298 : constr = tupleDesc->constr;
2688 :
2689 : /*
2690 : * newattmap->attnums[] will contain the child-table attribute numbers
2691 : * for the attributes of this parent table. (They are not the same
2692 : * for parents after the first one, nor if we have dropped columns.)
2693 : */
2694 10298 : newattmap = make_attrmap(tupleDesc->natts);
2695 :
2696 : /* We can't process inherited defaults until newattmap is complete. */
2697 10298 : inherited_defaults = cols_with_defaults = NIL;
2698 :
2699 : /*
2700 : * All columns that are part of the parent's primary key need to be
2701 : * NOT NULL; if partition just the attnotnull bit, otherwise a full
2702 : * constraint (if they don't have one already). Also, we request
2703 : * attnotnull on columns that have a not-null constraint that's not
2704 : * marked NO INHERIT.
2705 : */
2706 10298 : pkattrs = RelationGetIndexAttrBitmap(relation,
2707 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
2708 10298 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation), true);
2709 11146 : foreach(lc1, nnconstrs)
2710 848 : nncols = bms_add_member(nncols,
2711 848 : ((CookedConstraint *) lfirst(lc1))->attnum);
2712 :
2713 32124 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2714 21826 : parent_attno++)
2715 : {
2716 21850 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2717 : parent_attno - 1);
2718 21850 : char *attributeName = NameStr(attribute->attname);
2719 : int exist_attno;
2720 : ColumnDef *newdef;
2721 : ColumnDef *mergeddef;
2722 :
2723 : /*
2724 : * Ignore dropped columns in the parent.
2725 : */
2726 21850 : if (attribute->attisdropped)
2727 192 : continue; /* leave newattmap->attnums entry as zero */
2728 :
2729 : /*
2730 : * Create new column definition
2731 : */
2732 21658 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2733 : attribute->atttypmod, attribute->attcollation);
2734 21658 : newdef->storage = attribute->attstorage;
2735 21658 : newdef->generated = attribute->attgenerated;
2736 21658 : if (CompressionMethodIsValid(attribute->attcompression))
2737 24 : newdef->compression =
2738 24 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2739 :
2740 : /*
2741 : * Regular inheritance children are independent enough not to
2742 : * inherit identity columns. But partitions are integral part of
2743 : * a partitioned table and inherit identity column.
2744 : */
2745 21658 : if (is_partition)
2746 17738 : newdef->identity = attribute->attidentity;
2747 :
2748 : /*
2749 : * Does it match some previously considered column from another
2750 : * parent?
2751 : */
2752 21658 : exist_attno = findAttrByName(attributeName, inh_columns);
2753 21658 : if (exist_attno > 0)
2754 : {
2755 : /*
2756 : * Yes, try to merge the two column definitions.
2757 : */
2758 296 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2759 :
2760 272 : newattmap->attnums[parent_attno - 1] = exist_attno;
2761 :
2762 : /*
2763 : * Partitions have only one parent, so conflict should never
2764 : * occur.
2765 : */
2766 : Assert(!is_partition);
2767 : }
2768 : else
2769 : {
2770 : /*
2771 : * No, create a new inherited column
2772 : */
2773 21362 : newdef->inhcount = 1;
2774 21362 : newdef->is_local = false;
2775 21362 : inh_columns = lappend(inh_columns, newdef);
2776 :
2777 21362 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2778 :
2779 21362 : mergeddef = newdef;
2780 : }
2781 :
2782 : /*
2783 : * mark attnotnull if parent has it and it's not NO INHERIT
2784 : */
2785 42420 : if (bms_is_member(parent_attno, nncols) ||
2786 20786 : bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2787 : pkattrs))
2788 2134 : mergeddef->is_not_null = true;
2789 :
2790 : /*
2791 : * In regular inheritance, columns in the parent's primary key get
2792 : * an extra not-null constraint. Partitioning doesn't need this,
2793 : * because the PK itself is going to be cloned to the partition.
2794 : */
2795 25530 : if (!is_partition &&
2796 3896 : bms_is_member(parent_attno -
2797 : FirstLowInvalidHeapAttributeNumber,
2798 : pkattrs))
2799 : {
2800 : CookedConstraint *nn;
2801 :
2802 256 : nn = palloc(sizeof(CookedConstraint));
2803 256 : nn->contype = CONSTR_NOTNULL;
2804 256 : nn->conoid = InvalidOid;
2805 256 : nn->name = NULL;
2806 256 : nn->attnum = newattmap->attnums[parent_attno - 1];
2807 256 : nn->expr = NULL;
2808 256 : nn->skip_validation = false;
2809 256 : nn->is_local = false;
2810 256 : nn->inhcount = 1;
2811 256 : nn->is_no_inherit = false;
2812 :
2813 256 : nnconstraints = lappend(nnconstraints, nn);
2814 : }
2815 :
2816 : /*
2817 : * Locate default/generation expression if any
2818 : */
2819 21634 : if (attribute->atthasdef)
2820 : {
2821 : Node *this_default;
2822 :
2823 650 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2824 650 : if (this_default == NULL)
2825 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2826 : parent_attno, RelationGetRelationName(relation));
2827 :
2828 : /*
2829 : * If it's a GENERATED default, it might contain Vars that
2830 : * need to be mapped to the inherited column(s)' new numbers.
2831 : * We can't do that till newattmap is ready, so just remember
2832 : * all the inherited default expressions for the moment.
2833 : */
2834 650 : inherited_defaults = lappend(inherited_defaults, this_default);
2835 650 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2836 : }
2837 : }
2838 :
2839 : /*
2840 : * Now process any inherited default expressions, adjusting attnos
2841 : * using the completed newattmap map.
2842 : */
2843 10924 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2844 : {
2845 650 : Node *this_default = (Node *) lfirst(lc1);
2846 650 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2847 : bool found_whole_row;
2848 :
2849 : /* Adjust Vars to match new table's column numbering */
2850 650 : this_default = map_variable_attnos(this_default,
2851 : 1, 0,
2852 : newattmap,
2853 : InvalidOid, &found_whole_row);
2854 :
2855 : /*
2856 : * For the moment we have to reject whole-row variables. We could
2857 : * convert them, if we knew the new table's rowtype OID, but that
2858 : * hasn't been assigned yet. (A variable could only appear in a
2859 : * generation expression, so the error message is correct.)
2860 : */
2861 650 : if (found_whole_row)
2862 0 : ereport(ERROR,
2863 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2864 : errmsg("cannot convert whole-row table reference"),
2865 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2866 : def->colname,
2867 : RelationGetRelationName(relation))));
2868 :
2869 : /*
2870 : * If we already had a default from some prior parent, check to
2871 : * see if they are the same. If so, no problem; if not, mark the
2872 : * column as having a bogus default. Below, we will complain if
2873 : * the bogus default isn't overridden by the child columns.
2874 : */
2875 : Assert(def->raw_default == NULL);
2876 650 : if (def->cooked_default == NULL)
2877 620 : def->cooked_default = this_default;
2878 30 : else if (!equal(def->cooked_default, this_default))
2879 : {
2880 24 : def->cooked_default = &bogus_marker;
2881 24 : have_bogus_defaults = true;
2882 : }
2883 : }
2884 :
2885 : /*
2886 : * Now copy the CHECK constraints of this parent, adjusting attnos
2887 : * using the completed newattmap map. Identically named constraints
2888 : * are merged if possible, else we throw error.
2889 : */
2890 10274 : if (constr && constr->num_check > 0)
2891 : {
2892 304 : ConstrCheck *check = constr->check;
2893 :
2894 638 : for (int i = 0; i < constr->num_check; i++)
2895 : {
2896 334 : char *name = check[i].ccname;
2897 : Node *expr;
2898 : bool found_whole_row;
2899 :
2900 : /* ignore if the constraint is non-inheritable */
2901 334 : if (check[i].ccnoinherit)
2902 48 : continue;
2903 :
2904 : /* Adjust Vars to match new table's column numbering */
2905 286 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2906 : 1, 0,
2907 : newattmap,
2908 : InvalidOid, &found_whole_row);
2909 :
2910 : /*
2911 : * For the moment we have to reject whole-row variables. We
2912 : * could convert them, if we knew the new table's rowtype OID,
2913 : * but that hasn't been assigned yet.
2914 : */
2915 286 : if (found_whole_row)
2916 0 : ereport(ERROR,
2917 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2918 : errmsg("cannot convert whole-row table reference"),
2919 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2920 : name,
2921 : RelationGetRelationName(relation))));
2922 :
2923 286 : constraints = MergeCheckConstraint(constraints, name, expr);
2924 : }
2925 : }
2926 :
2927 : /*
2928 : * Also copy the not-null constraints from this parent. The
2929 : * attnotnull markings were already installed above.
2930 : */
2931 11122 : foreach(lc1, nnconstrs)
2932 : {
2933 848 : CookedConstraint *nn = lfirst(lc1);
2934 :
2935 : Assert(nn->contype == CONSTR_NOTNULL);
2936 :
2937 848 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2938 848 : nn->is_local = false;
2939 848 : nn->inhcount = 1;
2940 :
2941 848 : nnconstraints = lappend(nnconstraints, nn);
2942 : }
2943 :
2944 10274 : free_attrmap(newattmap);
2945 :
2946 : /*
2947 : * Close the parent rel, but keep our lock on it until xact commit.
2948 : * That will prevent someone else from deleting or ALTERing the parent
2949 : * before the child is committed.
2950 : */
2951 10274 : table_close(relation, NoLock);
2952 : }
2953 :
2954 : /*
2955 : * If we had no inherited attributes, the result columns are just the
2956 : * explicitly declared columns. Otherwise, we need to merge the declared
2957 : * columns into the inherited column list. Although, we never have any
2958 : * explicitly declared columns if the table is a partition.
2959 : */
2960 52914 : if (inh_columns != NIL)
2961 : {
2962 9936 : int newcol_attno = 0;
2963 :
2964 10726 : foreach(lc, columns)
2965 : {
2966 838 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2967 838 : char *attributeName = newdef->colname;
2968 : int exist_attno;
2969 :
2970 : /*
2971 : * Partitions have only one parent and have no column definitions
2972 : * of their own, so conflict should never occur.
2973 : */
2974 : Assert(!is_partition);
2975 :
2976 838 : newcol_attno++;
2977 :
2978 : /*
2979 : * Does it match some inherited column?
2980 : */
2981 838 : exist_attno = findAttrByName(attributeName, inh_columns);
2982 838 : if (exist_attno > 0)
2983 : {
2984 : /*
2985 : * Yes, try to merge the two column definitions.
2986 : */
2987 280 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2988 : }
2989 : else
2990 : {
2991 : /*
2992 : * No, attach new column unchanged to result columns.
2993 : */
2994 558 : inh_columns = lappend(inh_columns, newdef);
2995 : }
2996 : }
2997 :
2998 9888 : columns = inh_columns;
2999 :
3000 : /*
3001 : * Check that we haven't exceeded the legal # of columns after merging
3002 : * in inherited columns.
3003 : */
3004 9888 : if (list_length(columns) > MaxHeapAttributeNumber)
3005 0 : ereport(ERROR,
3006 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3007 : errmsg("tables can have at most %d columns",
3008 : MaxHeapAttributeNumber)));
3009 : }
3010 :
3011 : /*
3012 : * Now that we have the column definition list for a partition, we can
3013 : * check whether the columns referenced in the column constraint specs
3014 : * actually exist. Also, merge column defaults.
3015 : */
3016 52866 : if (is_partition)
3017 : {
3018 8422 : foreach(lc, saved_columns)
3019 : {
3020 208 : ColumnDef *restdef = lfirst(lc);
3021 208 : bool found = false;
3022 : ListCell *l;
3023 :
3024 784 : foreach(l, columns)
3025 : {
3026 588 : ColumnDef *coldef = lfirst(l);
3027 :
3028 588 : if (strcmp(coldef->colname, restdef->colname) == 0)
3029 : {
3030 208 : found = true;
3031 :
3032 : /*
3033 : * Check for conflicts related to generated columns.
3034 : *
3035 : * Same rules as above: generated-ness has to match the
3036 : * parent, but the contents of the generation expression
3037 : * can be different.
3038 : */
3039 208 : if (coldef->generated)
3040 : {
3041 106 : if (restdef->raw_default && !restdef->generated)
3042 6 : ereport(ERROR,
3043 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3044 : errmsg("column \"%s\" inherits from generated column but specifies default",
3045 : restdef->colname)));
3046 100 : if (restdef->identity)
3047 0 : ereport(ERROR,
3048 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3049 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3050 : restdef->colname)));
3051 : }
3052 : else
3053 : {
3054 102 : if (restdef->generated)
3055 6 : ereport(ERROR,
3056 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3057 : errmsg("child column \"%s\" specifies generation expression",
3058 : restdef->colname),
3059 : errhint("A child table column cannot be generated unless its parent column is.")));
3060 : }
3061 :
3062 : /*
3063 : * Override the parent's default value for this column
3064 : * (coldef->cooked_default) with the partition's local
3065 : * definition (restdef->raw_default), if there's one. It
3066 : * should be physically impossible to get a cooked default
3067 : * in the local definition or a raw default in the
3068 : * inherited definition, but make sure they're nulls, for
3069 : * future-proofing.
3070 : */
3071 : Assert(restdef->cooked_default == NULL);
3072 : Assert(coldef->raw_default == NULL);
3073 196 : if (restdef->raw_default)
3074 : {
3075 124 : coldef->raw_default = restdef->raw_default;
3076 124 : coldef->cooked_default = NULL;
3077 : }
3078 : }
3079 : }
3080 :
3081 : /* complain for constraints on columns not in parent */
3082 196 : if (!found)
3083 0 : ereport(ERROR,
3084 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3085 : errmsg("column \"%s\" does not exist",
3086 : restdef->colname)));
3087 : }
3088 : }
3089 :
3090 : /*
3091 : * If we found any conflicting parent default values, check to make sure
3092 : * they were overridden by the child.
3093 : */
3094 52854 : if (have_bogus_defaults)
3095 : {
3096 54 : foreach(lc, columns)
3097 : {
3098 42 : ColumnDef *def = lfirst(lc);
3099 :
3100 42 : if (def->cooked_default == &bogus_marker)
3101 : {
3102 12 : if (def->generated)
3103 6 : ereport(ERROR,
3104 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3105 : errmsg("column \"%s\" inherits conflicting generation expressions",
3106 : def->colname),
3107 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3108 : else
3109 6 : ereport(ERROR,
3110 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3111 : errmsg("column \"%s\" inherits conflicting default values",
3112 : def->colname),
3113 : errhint("To resolve the conflict, specify a default explicitly.")));
3114 : }
3115 : }
3116 : }
3117 :
3118 52842 : *supconstr = constraints;
3119 52842 : *supnotnulls = nnconstraints;
3120 :
3121 52842 : return columns;
3122 : }
3123 :
3124 :
3125 : /*
3126 : * MergeCheckConstraint
3127 : * Try to merge an inherited CHECK constraint with previous ones
3128 : *
3129 : * If we inherit identically-named constraints from multiple parents, we must
3130 : * merge them, or throw an error if they don't have identical definitions.
3131 : *
3132 : * constraints is a list of CookedConstraint structs for previous constraints.
3133 : *
3134 : * If the new constraint matches an existing one, then the existing
3135 : * constraint's inheritance count is updated. If there is a conflict (same
3136 : * name but different expression), throw an error. If the constraint neither
3137 : * matches nor conflicts with an existing one, a new constraint is appended to
3138 : * the list.
3139 : */
3140 : static List *
3141 286 : MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3142 : {
3143 : ListCell *lc;
3144 : CookedConstraint *newcon;
3145 :
3146 316 : foreach(lc, constraints)
3147 : {
3148 72 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3149 :
3150 : Assert(ccon->contype == CONSTR_CHECK);
3151 :
3152 : /* Non-matching names never conflict */
3153 72 : if (strcmp(ccon->name, name) != 0)
3154 30 : continue;
3155 :
3156 42 : if (equal(expr, ccon->expr))
3157 : {
3158 : /* OK to merge constraint with existing */
3159 42 : ccon->inhcount++;
3160 42 : if (ccon->inhcount < 0)
3161 0 : ereport(ERROR,
3162 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3163 : errmsg("too many inheritance parents"));
3164 42 : return constraints;
3165 : }
3166 :
3167 0 : ereport(ERROR,
3168 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3169 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3170 : name)));
3171 : }
3172 :
3173 : /*
3174 : * Constraint couldn't be merged with an existing one and also didn't
3175 : * conflict with an existing one, so add it as a new one to the list.
3176 : */
3177 244 : newcon = palloc0_object(CookedConstraint);
3178 244 : newcon->contype = CONSTR_CHECK;
3179 244 : newcon->name = pstrdup(name);
3180 244 : newcon->expr = expr;
3181 244 : newcon->inhcount = 1;
3182 244 : return lappend(constraints, newcon);
3183 : }
3184 :
3185 : /*
3186 : * MergeChildAttribute
3187 : * Merge given child attribute definition into given inherited attribute.
3188 : *
3189 : * Input arguments:
3190 : * 'inh_columns' is the list of inherited ColumnDefs.
3191 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3192 : * 'newcol_attno' is the attribute number in child table's schema definition
3193 : * 'newdef' is the column/attribute definition from the child table.
3194 : *
3195 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3196 : * ColumnDef remains unchanged.
3197 : *
3198 : * Notes:
3199 : * - The attribute is merged according to the rules laid out in the prologue
3200 : * of MergeAttributes().
3201 : * - If matching inherited attribute exists but the child attribute can not be
3202 : * merged into it, the function throws respective errors.
3203 : * - A partition can not have its own column definitions. Hence this function
3204 : * is applicable only to a regular inheritance child.
3205 : */
3206 : static void
3207 280 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3208 : {
3209 280 : char *attributeName = newdef->colname;
3210 : ColumnDef *inhdef;
3211 : Oid inhtypeid,
3212 : newtypeid;
3213 : int32 inhtypmod,
3214 : newtypmod;
3215 : Oid inhcollid,
3216 : newcollid;
3217 :
3218 280 : if (exist_attno == newcol_attno)
3219 252 : ereport(NOTICE,
3220 : (errmsg("merging column \"%s\" with inherited definition",
3221 : attributeName)));
3222 : else
3223 28 : ereport(NOTICE,
3224 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3225 : errdetail("User-specified column moved to the position of the inherited column.")));
3226 :
3227 280 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3228 :
3229 : /*
3230 : * Must have the same type and typmod
3231 : */
3232 280 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3233 280 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3234 280 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3235 12 : ereport(ERROR,
3236 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3237 : errmsg("column \"%s\" has a type conflict",
3238 : attributeName),
3239 : errdetail("%s versus %s",
3240 : format_type_with_typemod(inhtypeid, inhtypmod),
3241 : format_type_with_typemod(newtypeid, newtypmod))));
3242 :
3243 : /*
3244 : * Must have the same collation
3245 : */
3246 268 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3247 268 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3248 268 : if (inhcollid != newcollid)
3249 6 : ereport(ERROR,
3250 : (errcode(ERRCODE_COLLATION_MISMATCH),
3251 : errmsg("column \"%s\" has a collation conflict",
3252 : attributeName),
3253 : errdetail("\"%s\" versus \"%s\"",
3254 : get_collation_name(inhcollid),
3255 : get_collation_name(newcollid))));
3256 :
3257 : /*
3258 : * Identity is never inherited by a regular inheritance child. Pick
3259 : * child's identity definition if there's one.
3260 : */
3261 262 : inhdef->identity = newdef->identity;
3262 :
3263 : /*
3264 : * Copy storage parameter
3265 : */
3266 262 : if (inhdef->storage == 0)
3267 0 : inhdef->storage = newdef->storage;
3268 262 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3269 6 : ereport(ERROR,
3270 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3271 : errmsg("column \"%s\" has a storage parameter conflict",
3272 : attributeName),
3273 : errdetail("%s versus %s",
3274 : storage_name(inhdef->storage),
3275 : storage_name(newdef->storage))));
3276 :
3277 : /*
3278 : * Copy compression parameter
3279 : */
3280 256 : if (inhdef->compression == NULL)
3281 250 : inhdef->compression = newdef->compression;
3282 6 : else if (newdef->compression != NULL)
3283 : {
3284 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3285 6 : ereport(ERROR,
3286 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3287 : errmsg("column \"%s\" has a compression method conflict",
3288 : attributeName),
3289 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3290 : }
3291 :
3292 : /*
3293 : * Merge of not-null constraints = OR 'em together
3294 : */
3295 250 : inhdef->is_not_null |= newdef->is_not_null;
3296 :
3297 : /*
3298 : * Check for conflicts related to generated columns.
3299 : *
3300 : * If the parent column is generated, the child column will be made a
3301 : * generated column if it isn't already. If it is a generated column,
3302 : * we'll take its generation expression in preference to the parent's. We
3303 : * must check that the child column doesn't specify a default value or
3304 : * identity, which matches the rules for a single column in
3305 : * parse_utilcmd.c.
3306 : *
3307 : * Conversely, if the parent column is not generated, the child column
3308 : * can't be either. (We used to allow that, but it results in being able
3309 : * to override the generation expression via UPDATEs through the parent.)
3310 : */
3311 250 : if (inhdef->generated)
3312 : {
3313 26 : if (newdef->raw_default && !newdef->generated)
3314 6 : ereport(ERROR,
3315 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3316 : errmsg("column \"%s\" inherits from generated column but specifies default",
3317 : inhdef->colname)));
3318 20 : if (newdef->identity)
3319 6 : ereport(ERROR,
3320 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3321 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3322 : inhdef->colname)));
3323 : }
3324 : else
3325 : {
3326 224 : if (newdef->generated)
3327 6 : ereport(ERROR,
3328 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3329 : errmsg("child column \"%s\" specifies generation expression",
3330 : inhdef->colname),
3331 : errhint("A child table column cannot be generated unless its parent column is.")));
3332 : }
3333 :
3334 : /*
3335 : * If new def has a default, override previous default
3336 : */
3337 232 : if (newdef->raw_default != NULL)
3338 : {
3339 18 : inhdef->raw_default = newdef->raw_default;
3340 18 : inhdef->cooked_default = newdef->cooked_default;
3341 : }
3342 :
3343 : /* Mark the column as locally defined */
3344 232 : inhdef->is_local = true;
3345 232 : }
3346 :
3347 : /*
3348 : * MergeInheritedAttribute
3349 : * Merge given parent attribute definition into specified attribute
3350 : * inherited from the previous parents.
3351 : *
3352 : * Input arguments:
3353 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3354 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3355 : * 'newdef' is the new parent column/attribute definition to be merged.
3356 : *
3357 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3358 : *
3359 : * Notes:
3360 : * - The attribute is merged according to the rules laid out in the prologue
3361 : * of MergeAttributes().
3362 : * - If matching inherited attribute exists but the new attribute can not be
3363 : * merged into it, the function throws respective errors.
3364 : * - A partition inherits from only a single parent. Hence this function is
3365 : * applicable only to a regular inheritance.
3366 : */
3367 : static ColumnDef *
3368 296 : MergeInheritedAttribute(List *inh_columns,
3369 : int exist_attno,
3370 : const ColumnDef *newdef)
3371 : {
3372 296 : char *attributeName = newdef->colname;
3373 : ColumnDef *prevdef;
3374 : Oid prevtypeid,
3375 : newtypeid;
3376 : int32 prevtypmod,
3377 : newtypmod;
3378 : Oid prevcollid,
3379 : newcollid;
3380 :
3381 296 : ereport(NOTICE,
3382 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3383 : attributeName)));
3384 296 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3385 :
3386 : /*
3387 : * Must have the same type and typmod
3388 : */
3389 296 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3390 296 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3391 296 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3392 0 : ereport(ERROR,
3393 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3394 : errmsg("inherited column \"%s\" has a type conflict",
3395 : attributeName),
3396 : errdetail("%s versus %s",
3397 : format_type_with_typemod(prevtypeid, prevtypmod),
3398 : format_type_with_typemod(newtypeid, newtypmod))));
3399 :
3400 : /*
3401 : * Must have the same collation
3402 : */
3403 296 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3404 296 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3405 296 : if (prevcollid != newcollid)
3406 0 : ereport(ERROR,
3407 : (errcode(ERRCODE_COLLATION_MISMATCH),
3408 : errmsg("inherited column \"%s\" has a collation conflict",
3409 : attributeName),
3410 : errdetail("\"%s\" versus \"%s\"",
3411 : get_collation_name(prevcollid),
3412 : get_collation_name(newcollid))));
3413 :
3414 : /*
3415 : * Copy/check storage parameter
3416 : */
3417 296 : if (prevdef->storage == 0)
3418 0 : prevdef->storage = newdef->storage;
3419 296 : else if (prevdef->storage != newdef->storage)
3420 6 : ereport(ERROR,
3421 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3422 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3423 : attributeName),
3424 : errdetail("%s versus %s",
3425 : storage_name(prevdef->storage),
3426 : storage_name(newdef->storage))));
3427 :
3428 : /*
3429 : * Copy/check compression parameter
3430 : */
3431 290 : if (prevdef->compression == NULL)
3432 284 : prevdef->compression = newdef->compression;
3433 6 : else if (strcmp(prevdef->compression, newdef->compression) != 0)
3434 6 : ereport(ERROR,
3435 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3436 : errmsg("column \"%s\" has a compression method conflict",
3437 : attributeName),
3438 : errdetail("%s versus %s", prevdef->compression, newdef->compression)));
3439 :
3440 : /*
3441 : * Check for GENERATED conflicts
3442 : */
3443 284 : if (prevdef->generated != newdef->generated)
3444 12 : ereport(ERROR,
3445 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3446 : errmsg("inherited column \"%s\" has a generation conflict",
3447 : attributeName)));
3448 :
3449 : /*
3450 : * Default and other constraints are handled by the caller.
3451 : */
3452 :
3453 272 : prevdef->inhcount++;
3454 272 : if (prevdef->inhcount < 0)
3455 0 : ereport(ERROR,
3456 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3457 : errmsg("too many inheritance parents"));
3458 :
3459 272 : return prevdef;
3460 : }
3461 :
3462 : /*
3463 : * StoreCatalogInheritance
3464 : * Updates the system catalogs with proper inheritance information.
3465 : *
3466 : * supers is a list of the OIDs of the new relation's direct ancestors.
3467 : */
3468 : static void
3469 52296 : StoreCatalogInheritance(Oid relationId, List *supers,
3470 : bool child_is_partition)
3471 : {
3472 : Relation relation;
3473 : int32 seqNumber;
3474 : ListCell *entry;
3475 :
3476 : /*
3477 : * sanity checks
3478 : */
3479 : Assert(OidIsValid(relationId));
3480 :
3481 52296 : if (supers == NIL)
3482 42750 : return;
3483 :
3484 : /*
3485 : * Store INHERITS information in pg_inherits using direct ancestors only.
3486 : * Also enter dependencies on the direct ancestors, and make sure they are
3487 : * marked with relhassubclass = true.
3488 : *
3489 : * (Once upon a time, both direct and indirect ancestors were found here
3490 : * and then entered into pg_ipl. Since that catalog doesn't exist
3491 : * anymore, there's no need to look for indirect ancestors.)
3492 : */
3493 9546 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3494 :
3495 9546 : seqNumber = 1;
3496 19358 : foreach(entry, supers)
3497 : {
3498 9812 : Oid parentOid = lfirst_oid(entry);
3499 :
3500 9812 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3501 : child_is_partition);
3502 9812 : seqNumber++;
3503 : }
3504 :
3505 9546 : table_close(relation, RowExclusiveLock);
3506 : }
3507 :
3508 : /*
3509 : * Make catalog entries showing relationId as being an inheritance child
3510 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3511 : */
3512 : static void
3513 12428 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3514 : int32 seqNumber, Relation inhRelation,
3515 : bool child_is_partition)
3516 : {
3517 : ObjectAddress childobject,
3518 : parentobject;
3519 :
3520 : /* store the pg_inherits row */
3521 12428 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3522 :
3523 : /*
3524 : * Store a dependency too
3525 : */
3526 12428 : parentobject.classId = RelationRelationId;
3527 12428 : parentobject.objectId = parentOid;
3528 12428 : parentobject.objectSubId = 0;
3529 12428 : childobject.classId = RelationRelationId;
3530 12428 : childobject.objectId = relationId;
3531 12428 : childobject.objectSubId = 0;
3532 :
3533 12428 : recordDependencyOn(&childobject, &parentobject,
3534 : child_dependency_type(child_is_partition));
3535 :
3536 : /*
3537 : * Post creation hook of this inheritance. Since object_access_hook
3538 : * doesn't take multiple object identifiers, we relay oid of parent
3539 : * relation using auxiliary_id argument.
3540 : */
3541 12428 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3542 : relationId, 0,
3543 : parentOid, false);
3544 :
3545 : /*
3546 : * Mark the parent as having subclasses.
3547 : */
3548 12428 : SetRelationHasSubclass(parentOid, true);
3549 12428 : }
3550 :
3551 : /*
3552 : * Look for an existing column entry with the given name.
3553 : *
3554 : * Returns the index (starting with 1) if attribute already exists in columns,
3555 : * 0 if it doesn't.
3556 : */
3557 : static int
3558 22496 : findAttrByName(const char *attributeName, const List *columns)
3559 : {
3560 : ListCell *lc;
3561 22496 : int i = 1;
3562 :
3563 42008 : foreach(lc, columns)
3564 : {
3565 20088 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3566 576 : return i;
3567 :
3568 19512 : i++;
3569 : }
3570 21920 : return 0;
3571 : }
3572 :
3573 :
3574 : /*
3575 : * SetRelationHasSubclass
3576 : * Set the value of the relation's relhassubclass field in pg_class.
3577 : *
3578 : * NOTE: caller must be holding an appropriate lock on the relation.
3579 : * ShareUpdateExclusiveLock is sufficient.
3580 : *
3581 : * NOTE: an important side-effect of this operation is that an SI invalidation
3582 : * message is sent out to all backends --- including me --- causing plans
3583 : * referencing the relation to be rebuilt with the new list of children.
3584 : * This must happen even if we find that no change is needed in the pg_class
3585 : * row.
3586 : */
3587 : void
3588 15738 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3589 : {
3590 : Relation relationRelation;
3591 : HeapTuple tuple;
3592 : Form_pg_class classtuple;
3593 :
3594 : /*
3595 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3596 : */
3597 15738 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3598 15738 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3599 15738 : if (!HeapTupleIsValid(tuple))
3600 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3601 15738 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3602 :
3603 15738 : if (classtuple->relhassubclass != relhassubclass)
3604 : {
3605 7404 : classtuple->relhassubclass = relhassubclass;
3606 7404 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3607 : }
3608 : else
3609 : {
3610 : /* no need to change tuple, but force relcache rebuild anyway */
3611 8334 : CacheInvalidateRelcacheByTuple(tuple);
3612 : }
3613 :
3614 15738 : heap_freetuple(tuple);
3615 15738 : table_close(relationRelation, RowExclusiveLock);
3616 15738 : }
3617 :
3618 : /*
3619 : * CheckRelationTableSpaceMove
3620 : * Check if relation can be moved to new tablespace.
3621 : *
3622 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3623 : *
3624 : * Returns true if the relation can be moved to the new tablespace; raises
3625 : * an error if it is not possible to do the move; returns false if the move
3626 : * would have no effect.
3627 : */
3628 : bool
3629 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3630 : {
3631 : Oid oldTableSpaceId;
3632 :
3633 : /*
3634 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3635 : * stored as 0.
3636 : */
3637 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3638 226 : if (newTableSpaceId == oldTableSpaceId ||
3639 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3640 10 : return false;
3641 :
3642 : /*
3643 : * We cannot support moving mapped relations into different tablespaces.
3644 : * (In particular this eliminates all shared catalogs.)
3645 : */
3646 216 : if (RelationIsMapped(rel))
3647 0 : ereport(ERROR,
3648 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3649 : errmsg("cannot move system relation \"%s\"",
3650 : RelationGetRelationName(rel))));
3651 :
3652 : /* Cannot move a non-shared relation into pg_global */
3653 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3654 12 : ereport(ERROR,
3655 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3656 : errmsg("only shared relations can be placed in pg_global tablespace")));
3657 :
3658 : /*
3659 : * Do not allow moving temp tables of other backends ... their local
3660 : * buffer manager is not going to cope.
3661 : */
3662 204 : if (RELATION_IS_OTHER_TEMP(rel))
3663 0 : ereport(ERROR,
3664 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3665 : errmsg("cannot move temporary tables of other sessions")));
3666 :
3667 204 : return true;
3668 : }
3669 :
3670 : /*
3671 : * SetRelationTableSpace
3672 : * Set new reltablespace and relfilenumber in pg_class entry.
3673 : *
3674 : * newTableSpaceId is the new tablespace for the relation, and
3675 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3676 : * InvalidRelFileNumber, this field is not updated.
3677 : *
3678 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3679 : *
3680 : * The caller of this routine had better check if a relation can be
3681 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3682 : * first, and is responsible for making the change visible with
3683 : * CommandCounterIncrement().
3684 : */
3685 : void
3686 204 : SetRelationTableSpace(Relation rel,
3687 : Oid newTableSpaceId,
3688 : RelFileNumber newRelFilenumber)
3689 : {
3690 : Relation pg_class;
3691 : HeapTuple tuple;
3692 : Form_pg_class rd_rel;
3693 204 : Oid reloid = RelationGetRelid(rel);
3694 :
3695 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3696 :
3697 : /* Get a modifiable copy of the relation's pg_class row. */
3698 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3699 :
3700 204 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3701 204 : if (!HeapTupleIsValid(tuple))
3702 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3703 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3704 :
3705 : /* Update the pg_class row. */
3706 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3707 204 : InvalidOid : newTableSpaceId;
3708 204 : if (RelFileNumberIsValid(newRelFilenumber))
3709 160 : rd_rel->relfilenode = newRelFilenumber;
3710 204 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3711 :
3712 : /*
3713 : * Record dependency on tablespace. This is only required for relations
3714 : * that have no physical storage.
3715 : */
3716 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3717 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3718 : rd_rel->reltablespace);
3719 :
3720 204 : heap_freetuple(tuple);
3721 204 : table_close(pg_class, RowExclusiveLock);
3722 204 : }
3723 :
3724 : /*
3725 : * renameatt_check - basic sanity checks before attribute rename
3726 : */
3727 : static void
3728 972 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3729 : {
3730 972 : char relkind = classform->relkind;
3731 :
3732 972 : if (classform->reloftype && !recursing)
3733 6 : ereport(ERROR,
3734 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3735 : errmsg("cannot rename column of typed table")));
3736 :
3737 : /*
3738 : * Renaming the columns of sequences or toast tables doesn't actually
3739 : * break anything from the system's point of view, since internal
3740 : * references are by attnum. But it doesn't seem right to allow users to
3741 : * change names that are hardcoded into the system, hence the following
3742 : * restriction.
3743 : */
3744 966 : if (relkind != RELKIND_RELATION &&
3745 86 : relkind != RELKIND_VIEW &&
3746 86 : relkind != RELKIND_MATVIEW &&
3747 38 : relkind != RELKIND_COMPOSITE_TYPE &&
3748 38 : relkind != RELKIND_INDEX &&
3749 38 : relkind != RELKIND_PARTITIONED_INDEX &&
3750 0 : relkind != RELKIND_FOREIGN_TABLE &&
3751 : relkind != RELKIND_PARTITIONED_TABLE)
3752 0 : ereport(ERROR,
3753 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3754 : errmsg("cannot rename columns of relation \"%s\"",
3755 : NameStr(classform->relname)),
3756 : errdetail_relkind_not_supported(relkind)));
3757 :
3758 : /*
3759 : * permissions checking. only the owner of a class can change its schema.
3760 : */
3761 966 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3762 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3763 0 : NameStr(classform->relname));
3764 966 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3765 2 : ereport(ERROR,
3766 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3767 : errmsg("permission denied: \"%s\" is a system catalog",
3768 : NameStr(classform->relname))));
3769 964 : }
3770 :
3771 : /*
3772 : * renameatt_internal - workhorse for renameatt
3773 : *
3774 : * Return value is the attribute number in the 'myrelid' relation.
3775 : */
3776 : static AttrNumber
3777 540 : renameatt_internal(Oid myrelid,
3778 : const char *oldattname,
3779 : const char *newattname,
3780 : bool recurse,
3781 : bool recursing,
3782 : int expected_parents,
3783 : DropBehavior behavior)
3784 : {
3785 : Relation targetrelation;
3786 : Relation attrelation;
3787 : HeapTuple atttup;
3788 : Form_pg_attribute attform;
3789 : AttrNumber attnum;
3790 :
3791 : /*
3792 : * Grab an exclusive lock on the target table, which we will NOT release
3793 : * until end of transaction.
3794 : */
3795 540 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3796 540 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3797 :
3798 : /*
3799 : * if the 'recurse' flag is set then we are supposed to rename this
3800 : * attribute in all classes that inherit from 'relname' (as well as in
3801 : * 'relname').
3802 : *
3803 : * any permissions or problems with duplicate attributes will cause the
3804 : * whole transaction to abort, which is what we want -- all or nothing.
3805 : */
3806 540 : if (recurse)
3807 : {
3808 : List *child_oids,
3809 : *child_numparents;
3810 : ListCell *lo,
3811 : *li;
3812 :
3813 : /*
3814 : * we need the number of parents for each child so that the recursive
3815 : * calls to renameatt() can determine whether there are any parents
3816 : * outside the inheritance hierarchy being processed.
3817 : */
3818 236 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3819 : &child_numparents);
3820 :
3821 : /*
3822 : * find_all_inheritors does the recursive search of the inheritance
3823 : * hierarchy, so all we have to do is process all of the relids in the
3824 : * list that it returns.
3825 : */
3826 710 : forboth(lo, child_oids, li, child_numparents)
3827 : {
3828 504 : Oid childrelid = lfirst_oid(lo);
3829 504 : int numparents = lfirst_int(li);
3830 :
3831 504 : if (childrelid == myrelid)
3832 236 : continue;
3833 : /* note we need not recurse again */
3834 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3835 : }
3836 : }
3837 : else
3838 : {
3839 : /*
3840 : * If we are told not to recurse, there had better not be any child
3841 : * tables; else the rename would put them out of step.
3842 : *
3843 : * expected_parents will only be 0 if we are not already recursing.
3844 : */
3845 340 : if (expected_parents == 0 &&
3846 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3847 12 : ereport(ERROR,
3848 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3849 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3850 : oldattname)));
3851 : }
3852 :
3853 : /* rename attributes in typed tables of composite type */
3854 498 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3855 : {
3856 : List *child_oids;
3857 : ListCell *lo;
3858 :
3859 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3860 24 : RelationGetRelationName(targetrelation),
3861 : behavior);
3862 :
3863 24 : foreach(lo, child_oids)
3864 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3865 : }
3866 :
3867 492 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3868 :
3869 492 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3870 492 : if (!HeapTupleIsValid(atttup))
3871 24 : ereport(ERROR,
3872 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3873 : errmsg("column \"%s\" does not exist",
3874 : oldattname)));
3875 468 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3876 :
3877 468 : attnum = attform->attnum;
3878 468 : if (attnum <= 0)
3879 0 : ereport(ERROR,
3880 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3881 : errmsg("cannot rename system column \"%s\"",
3882 : oldattname)));
3883 :
3884 : /*
3885 : * if the attribute is inherited, forbid the renaming. if this is a
3886 : * top-level call to renameatt(), then expected_parents will be 0, so the
3887 : * effect of this code will be to prohibit the renaming if the attribute
3888 : * is inherited at all. if this is a recursive call to renameatt(),
3889 : * expected_parents will be the number of parents the current relation has
3890 : * within the inheritance hierarchy being processed, so we'll prohibit the
3891 : * renaming only if there are additional parents from elsewhere.
3892 : */
3893 468 : if (attform->attinhcount > expected_parents)
3894 30 : ereport(ERROR,
3895 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3896 : errmsg("cannot rename inherited column \"%s\"",
3897 : oldattname)));
3898 :
3899 : /* new name should not already exist */
3900 438 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3901 :
3902 : /* apply the update */
3903 426 : namestrcpy(&(attform->attname), newattname);
3904 :
3905 426 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3906 :
3907 426 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3908 :
3909 426 : heap_freetuple(atttup);
3910 :
3911 426 : table_close(attrelation, RowExclusiveLock);
3912 :
3913 426 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3914 :
3915 426 : return attnum;
3916 : }
3917 :
3918 : /*
3919 : * Perform permissions and integrity checks before acquiring a relation lock.
3920 : */
3921 : static void
3922 394 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3923 : void *arg)
3924 : {
3925 : HeapTuple tuple;
3926 : Form_pg_class form;
3927 :
3928 394 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3929 394 : if (!HeapTupleIsValid(tuple))
3930 40 : return; /* concurrently dropped */
3931 354 : form = (Form_pg_class) GETSTRUCT(tuple);
3932 354 : renameatt_check(relid, form, false);
3933 346 : ReleaseSysCache(tuple);
3934 : }
3935 :
3936 : /*
3937 : * renameatt - changes the name of an attribute in a relation
3938 : *
3939 : * The returned ObjectAddress is that of the renamed column.
3940 : */
3941 : ObjectAddress
3942 304 : renameatt(RenameStmt *stmt)
3943 : {
3944 : Oid relid;
3945 : AttrNumber attnum;
3946 : ObjectAddress address;
3947 :
3948 : /* lock level taken here should match renameatt_internal */
3949 304 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3950 304 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3951 : RangeVarCallbackForRenameAttribute,
3952 : NULL);
3953 :
3954 290 : if (!OidIsValid(relid))
3955 : {
3956 24 : ereport(NOTICE,
3957 : (errmsg("relation \"%s\" does not exist, skipping",
3958 : stmt->relation->relname)));
3959 24 : return InvalidObjectAddress;
3960 : }
3961 :
3962 : attnum =
3963 266 : renameatt_internal(relid,
3964 266 : stmt->subname, /* old att name */
3965 266 : stmt->newname, /* new att name */
3966 266 : stmt->relation->inh, /* recursive? */
3967 : false, /* recursing? */
3968 : 0, /* expected inhcount */
3969 : stmt->behavior);
3970 :
3971 182 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3972 :
3973 182 : return address;
3974 : }
3975 :
3976 : /*
3977 : * same logic as renameatt_internal
3978 : */
3979 : static ObjectAddress
3980 84 : rename_constraint_internal(Oid myrelid,
3981 : Oid mytypid,
3982 : const char *oldconname,
3983 : const char *newconname,
3984 : bool recurse,
3985 : bool recursing,
3986 : int expected_parents)
3987 : {
3988 84 : Relation targetrelation = NULL;
3989 : Oid constraintOid;
3990 : HeapTuple tuple;
3991 : Form_pg_constraint con;
3992 : ObjectAddress address;
3993 :
3994 : Assert(!myrelid || !mytypid);
3995 :
3996 84 : if (mytypid)
3997 : {
3998 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3999 : }
4000 : else
4001 : {
4002 78 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4003 :
4004 : /*
4005 : * don't tell it whether we're recursing; we allow changing typed
4006 : * tables here
4007 : */
4008 78 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4009 :
4010 78 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4011 : }
4012 :
4013 84 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4014 84 : if (!HeapTupleIsValid(tuple))
4015 0 : elog(ERROR, "cache lookup failed for constraint %u",
4016 : constraintOid);
4017 84 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4018 :
4019 84 : if (myrelid &&
4020 78 : (con->contype == CONSTRAINT_CHECK ||
4021 18 : con->contype == CONSTRAINT_NOTNULL) &&
4022 60 : !con->connoinherit)
4023 : {
4024 48 : if (recurse)
4025 : {
4026 : List *child_oids,
4027 : *child_numparents;
4028 : ListCell *lo,
4029 : *li;
4030 :
4031 30 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4032 : &child_numparents);
4033 :
4034 72 : forboth(lo, child_oids, li, child_numparents)
4035 : {
4036 42 : Oid childrelid = lfirst_oid(lo);
4037 42 : int numparents = lfirst_int(li);
4038 :
4039 42 : if (childrelid == myrelid)
4040 30 : continue;
4041 :
4042 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4043 : }
4044 : }
4045 : else
4046 : {
4047 24 : if (expected_parents == 0 &&
4048 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4049 6 : ereport(ERROR,
4050 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4051 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4052 : oldconname)));
4053 : }
4054 :
4055 42 : if (con->coninhcount > expected_parents)
4056 6 : ereport(ERROR,
4057 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4058 : errmsg("cannot rename inherited constraint \"%s\"",
4059 : oldconname)));
4060 : }
4061 :
4062 72 : if (con->conindid
4063 18 : && (con->contype == CONSTRAINT_PRIMARY
4064 6 : || con->contype == CONSTRAINT_UNIQUE
4065 0 : || con->contype == CONSTRAINT_EXCLUSION))
4066 : /* rename the index; this renames the constraint as well */
4067 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4068 : else
4069 54 : RenameConstraintById(constraintOid, newconname);
4070 :
4071 72 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4072 :
4073 72 : ReleaseSysCache(tuple);
4074 :
4075 72 : if (targetrelation)
4076 : {
4077 : /*
4078 : * Invalidate relcache so as others can see the new constraint name.
4079 : */
4080 66 : CacheInvalidateRelcache(targetrelation);
4081 :
4082 66 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4083 : }
4084 :
4085 72 : return address;
4086 : }
4087 :
4088 : ObjectAddress
4089 78 : RenameConstraint(RenameStmt *stmt)
4090 : {
4091 78 : Oid relid = InvalidOid;
4092 78 : Oid typid = InvalidOid;
4093 :
4094 78 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4095 : {
4096 : Relation rel;
4097 : HeapTuple tup;
4098 :
4099 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4100 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4101 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4102 6 : if (!HeapTupleIsValid(tup))
4103 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4104 6 : checkDomainOwner(tup);
4105 6 : ReleaseSysCache(tup);
4106 6 : table_close(rel, NoLock);
4107 : }
4108 : else
4109 : {
4110 : /* lock level taken here should match rename_constraint_internal */
4111 72 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4112 72 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4113 : RangeVarCallbackForRenameAttribute,
4114 : NULL);
4115 72 : if (!OidIsValid(relid))
4116 : {
4117 6 : ereport(NOTICE,
4118 : (errmsg("relation \"%s\" does not exist, skipping",
4119 : stmt->relation->relname)));
4120 6 : return InvalidObjectAddress;
4121 : }
4122 : }
4123 :
4124 : return
4125 72 : rename_constraint_internal(relid, typid,
4126 72 : stmt->subname,
4127 72 : stmt->newname,
4128 138 : (stmt->relation &&
4129 66 : stmt->relation->inh), /* recursive? */
4130 : false, /* recursing? */
4131 : 0 /* expected inhcount */ );
4132 : }
4133 :
4134 : /*
4135 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4136 : * RENAME
4137 : */
4138 : ObjectAddress
4139 510 : RenameRelation(RenameStmt *stmt)
4140 : {
4141 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4142 : Oid relid;
4143 : ObjectAddress address;
4144 :
4145 : /*
4146 : * Grab an exclusive lock on the target table, index, sequence, view,
4147 : * materialized view, or foreign table, which we will NOT release until
4148 : * end of transaction.
4149 : *
4150 : * Lock level used here should match RenameRelationInternal, to avoid lock
4151 : * escalation. However, because ALTER INDEX can be used with any relation
4152 : * type, we mustn't believe without verification.
4153 : */
4154 : for (;;)
4155 12 : {
4156 : LOCKMODE lockmode;
4157 : char relkind;
4158 : bool obj_is_index;
4159 :
4160 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4161 :
4162 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4163 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4164 : RangeVarCallbackForAlterRelation,
4165 : (void *) stmt);
4166 :
4167 472 : if (!OidIsValid(relid))
4168 : {
4169 18 : ereport(NOTICE,
4170 : (errmsg("relation \"%s\" does not exist, skipping",
4171 : stmt->relation->relname)));
4172 18 : return InvalidObjectAddress;
4173 : }
4174 :
4175 : /*
4176 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4177 : * to rename a table), but we might've used the wrong lock level. If
4178 : * that happens, retry with the correct lock level. We don't bother
4179 : * if we already acquired AccessExclusiveLock with an index, however.
4180 : */
4181 454 : relkind = get_rel_relkind(relid);
4182 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4183 : relkind == RELKIND_PARTITIONED_INDEX);
4184 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4185 : break;
4186 :
4187 12 : UnlockRelationOid(relid, lockmode);
4188 12 : is_index_stmt = obj_is_index;
4189 : }
4190 :
4191 : /* Do the work */
4192 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4193 :
4194 430 : ObjectAddressSet(address, RelationRelationId, relid);
4195 :
4196 430 : return address;
4197 : }
4198 :
4199 : /*
4200 : * RenameRelationInternal - change the name of a relation
4201 : */
4202 : void
4203 1330 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4204 : {
4205 : Relation targetrelation;
4206 : Relation relrelation; /* for RELATION relation */
4207 : HeapTuple reltup;
4208 : Form_pg_class relform;
4209 : Oid namespaceId;
4210 :
4211 : /*
4212 : * Grab a lock on the target relation, which we will NOT release until end
4213 : * of transaction. We need at least a self-exclusive lock so that
4214 : * concurrent DDL doesn't overwrite the rename if they start updating
4215 : * while still seeing the old version. The lock also guards against
4216 : * triggering relcache reloads in concurrent sessions, which might not
4217 : * handle this information changing under them. For indexes, we can use a
4218 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4219 : * specially.
4220 : */
4221 1330 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4222 1330 : namespaceId = RelationGetNamespace(targetrelation);
4223 :
4224 : /*
4225 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4226 : */
4227 1330 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4228 :
4229 1330 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4230 1330 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4231 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4232 1330 : relform = (Form_pg_class) GETSTRUCT(reltup);
4233 :
4234 1330 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4235 12 : ereport(ERROR,
4236 : (errcode(ERRCODE_DUPLICATE_TABLE),
4237 : errmsg("relation \"%s\" already exists",
4238 : newrelname)));
4239 :
4240 : /*
4241 : * RenameRelation is careful not to believe the caller's idea of the
4242 : * relation kind being handled. We don't have to worry about this, but
4243 : * let's not be totally oblivious to it. We can process an index as
4244 : * not-an-index, but not the other way around.
4245 : */
4246 : Assert(!is_index ||
4247 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4248 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4249 :
4250 : /*
4251 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4252 : * because it's a copy...)
4253 : */
4254 1318 : namestrcpy(&(relform->relname), newrelname);
4255 :
4256 1318 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4257 :
4258 1318 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4259 : InvalidOid, is_internal);
4260 :
4261 1318 : heap_freetuple(reltup);
4262 1318 : table_close(relrelation, RowExclusiveLock);
4263 :
4264 : /*
4265 : * Also rename the associated type, if any.
4266 : */
4267 1318 : if (OidIsValid(targetrelation->rd_rel->reltype))
4268 154 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4269 : newrelname, namespaceId);
4270 :
4271 : /*
4272 : * Also rename the associated constraint, if any.
4273 : */
4274 1318 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4275 718 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4276 : {
4277 618 : Oid constraintId = get_index_constraint(myrelid);
4278 :
4279 618 : if (OidIsValid(constraintId))
4280 36 : RenameConstraintById(constraintId, newrelname);
4281 : }
4282 :
4283 : /*
4284 : * Close rel, but keep lock!
4285 : */
4286 1318 : relation_close(targetrelation, NoLock);
4287 1318 : }
4288 :
4289 : /*
4290 : * ResetRelRewrite - reset relrewrite
4291 : */
4292 : void
4293 412 : ResetRelRewrite(Oid myrelid)
4294 : {
4295 : Relation relrelation; /* for RELATION relation */
4296 : HeapTuple reltup;
4297 : Form_pg_class relform;
4298 :
4299 : /*
4300 : * Find relation's pg_class tuple.
4301 : */
4302 412 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4303 :
4304 412 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4305 412 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4306 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4307 412 : relform = (Form_pg_class) GETSTRUCT(reltup);
4308 :
4309 : /*
4310 : * Update pg_class tuple.
4311 : */
4312 412 : relform->relrewrite = InvalidOid;
4313 :
4314 412 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4315 :
4316 412 : heap_freetuple(reltup);
4317 412 : table_close(relrelation, RowExclusiveLock);
4318 412 : }
4319 :
4320 : /*
4321 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4322 : * any open reference to the target table besides the one just acquired by
4323 : * the calling command; this implies there's an open cursor or active plan.
4324 : * We need this check because our lock doesn't protect us against stomping
4325 : * on our own foot, only other people's feet!
4326 : *
4327 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4328 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4329 : * possibly be relaxed to only error out for certain types of alterations.
4330 : * But the use-case for allowing any of these things is not obvious, so we
4331 : * won't work hard at it for now.
4332 : *
4333 : * We also reject these commands if there are any pending AFTER trigger events
4334 : * for the rel. This is certainly necessary for the rewriting variants of
4335 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4336 : * events would try to fetch the wrong tuples. It might be overly cautious
4337 : * in other cases, but again it seems better to err on the side of paranoia.
4338 : *
4339 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4340 : * we are worried about active indexscans on the index. The trigger-event
4341 : * check can be skipped, since we are doing no damage to the parent table.
4342 : *
4343 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4344 : */
4345 : void
4346 137088 : CheckTableNotInUse(Relation rel, const char *stmt)
4347 : {
4348 : int expected_refcnt;
4349 :
4350 137088 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4351 137088 : if (rel->rd_refcnt != expected_refcnt)
4352 24 : ereport(ERROR,
4353 : (errcode(ERRCODE_OBJECT_IN_USE),
4354 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4355 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4356 : stmt, RelationGetRelationName(rel))));
4357 :
4358 137064 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4359 214940 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4360 106436 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4361 18 : ereport(ERROR,
4362 : (errcode(ERRCODE_OBJECT_IN_USE),
4363 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4364 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4365 : stmt, RelationGetRelationName(rel))));
4366 137046 : }
4367 :
4368 : /*
4369 : * AlterTableLookupRelation
4370 : * Look up, and lock, the OID for the relation named by an alter table
4371 : * statement.
4372 : */
4373 : Oid
4374 34618 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4375 : {
4376 69152 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4377 34618 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4378 : RangeVarCallbackForAlterRelation,
4379 : (void *) stmt);
4380 : }
4381 :
4382 : /*
4383 : * AlterTable
4384 : * Execute ALTER TABLE, which can be a list of subcommands
4385 : *
4386 : * ALTER TABLE is performed in three phases:
4387 : * 1. Examine subcommands and perform pre-transformation checking.
4388 : * 2. Validate and transform subcommands, and update system catalogs.
4389 : * 3. Scan table(s) to check new constraints, and optionally recopy
4390 : * the data into new table(s).
4391 : * Phase 3 is not performed unless one or more of the subcommands requires
4392 : * it. The intention of this design is to allow multiple independent
4393 : * updates of the table schema to be performed with only one pass over the
4394 : * data.
4395 : *
4396 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4397 : * each table to be affected (there may be multiple affected tables if the
4398 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4399 : * validation of the subcommands. Because earlier subcommands may change
4400 : * the catalog state seen by later commands, there are limits to what can
4401 : * be done in this phase. Generally, this phase acquires table locks,
4402 : * checks permissions and relkind, and recurses to find child tables.
4403 : *
4404 : * ATRewriteCatalogs performs phase 2 for each affected table.
4405 : * Certain subcommands need to be performed before others to avoid
4406 : * unnecessary conflicts; for example, DROP COLUMN should come before
4407 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4408 : * lists, one for each logical "pass" of phase 2.
4409 : *
4410 : * ATRewriteTables performs phase 3 for those tables that need it.
4411 : *
4412 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4413 : * since phase 1 already does it. However, for certain subcommand types
4414 : * it is only possible to determine how to recurse at phase 2 time; for
4415 : * those cases, phase 1 sets the cmd->recurse flag.
4416 : *
4417 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4418 : * the whole operation; we don't have to do anything special to clean up.
4419 : *
4420 : * The caller must lock the relation, with an appropriate lock level
4421 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4422 : * or higher. We pass the lock level down
4423 : * so that we can apply it recursively to inherited tables. Note that the
4424 : * lock level we want as we recurse might well be higher than required for
4425 : * that specific subcommand. So we pass down the overall lock requirement,
4426 : * rather than reassess it at lower levels.
4427 : *
4428 : * The caller also provides a "context" which is to be passed back to
4429 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4430 : * Some of the fields therein, such as the relid, are used here as well.
4431 : */
4432 : void
4433 34396 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4434 : AlterTableUtilityContext *context)
4435 : {
4436 : Relation rel;
4437 :
4438 : /* Caller is required to provide an adequate lock. */
4439 34396 : rel = relation_open(context->relid, NoLock);
4440 :
4441 34396 : CheckTableNotInUse(rel, "ALTER TABLE");
4442 :
4443 34378 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4444 31134 : }
4445 :
4446 : /*
4447 : * AlterTableInternal
4448 : *
4449 : * ALTER TABLE with target specified by OID
4450 : *
4451 : * We do not reject if the relation is already open, because it's quite
4452 : * likely that one or more layers of caller have it open. That means it
4453 : * is unsafe to use this entry point for alterations that could break
4454 : * existing query plans. On the assumption it's not used for such, we
4455 : * don't have to reject pending AFTER triggers, either.
4456 : *
4457 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4458 : * used for any subcommand types that require parse transformation or
4459 : * could generate subcommands that have to be passed to ProcessUtility.
4460 : */
4461 : void
4462 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4463 : {
4464 : Relation rel;
4465 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4466 :
4467 278 : rel = relation_open(relid, lockmode);
4468 :
4469 278 : EventTriggerAlterTableRelid(relid);
4470 :
4471 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4472 278 : }
4473 :
4474 : /*
4475 : * AlterTableGetLockLevel
4476 : *
4477 : * Sets the overall lock level required for the supplied list of subcommands.
4478 : * Policy for doing this set according to needs of AlterTable(), see
4479 : * comments there for overall explanation.
4480 : *
4481 : * Function is called before and after parsing, so it must give same
4482 : * answer each time it is called. Some subcommands are transformed
4483 : * into other subcommand types, so the transform must never be made to a
4484 : * lower lock level than previously assigned. All transforms are noted below.
4485 : *
4486 : * Since this is called before we lock the table we cannot use table metadata
4487 : * to influence the type of lock we acquire.
4488 : *
4489 : * There should be no lockmodes hardcoded into the subcommand functions. All
4490 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4491 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4492 : * and does not travel through this section of code and cannot be combined with
4493 : * any of the subcommands given here.
4494 : *
4495 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4496 : * so any changes that might affect SELECTs running on standbys need to use
4497 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4498 : * have a solution for that also.
4499 : *
4500 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4501 : * that takes a lock less than AccessExclusiveLock can change object definitions
4502 : * while pg_dump is running. Be careful to check that the appropriate data is
4503 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4504 : * otherwise we might end up with an inconsistent dump that can't restore.
4505 : */
4506 : LOCKMODE
4507 34896 : AlterTableGetLockLevel(List *cmds)
4508 : {
4509 : /*
4510 : * This only works if we read catalog tables using MVCC snapshots.
4511 : */
4512 : ListCell *lcmd;
4513 34896 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4514 :
4515 71796 : foreach(lcmd, cmds)
4516 : {
4517 36900 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4518 36900 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4519 :
4520 36900 : switch (cmd->subtype)
4521 : {
4522 : /*
4523 : * These subcommands rewrite the heap, so require full locks.
4524 : */
4525 3222 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4526 : * to SELECT */
4527 : case AT_SetAccessMethod: /* must rewrite heap */
4528 : case AT_SetTableSpace: /* must rewrite heap */
4529 : case AT_AlterColumnType: /* must rewrite heap */
4530 3222 : cmd_lockmode = AccessExclusiveLock;
4531 3222 : break;
4532 :
4533 : /*
4534 : * These subcommands may require addition of toast tables. If
4535 : * we add a toast table to a table currently being scanned, we
4536 : * might miss data added to the new toast table by concurrent
4537 : * insert transactions.
4538 : */
4539 212 : case AT_SetStorage: /* may add toast tables, see
4540 : * ATRewriteCatalogs() */
4541 212 : cmd_lockmode = AccessExclusiveLock;
4542 212 : break;
4543 :
4544 : /*
4545 : * Removing constraints can affect SELECTs that have been
4546 : * optimized assuming the constraint holds true. See also
4547 : * CloneFkReferenced.
4548 : */
4549 1268 : case AT_DropConstraint: /* as DROP INDEX */
4550 : case AT_DropNotNull: /* may change some SQL plans */
4551 1268 : cmd_lockmode = AccessExclusiveLock;
4552 1268 : break;
4553 :
4554 : /*
4555 : * Subcommands that may be visible to concurrent SELECTs
4556 : */
4557 1718 : case AT_DropColumn: /* change visible to SELECT */
4558 : case AT_AddColumnToView: /* CREATE VIEW */
4559 : case AT_DropOids: /* used to equiv to DropColumn */
4560 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4561 : case AT_EnableReplicaRule: /* may change SELECT rules */
4562 : case AT_EnableRule: /* may change SELECT rules */
4563 : case AT_DisableRule: /* may change SELECT rules */
4564 1718 : cmd_lockmode = AccessExclusiveLock;
4565 1718 : break;
4566 :
4567 : /*
4568 : * Changing owner may remove implicit SELECT privileges
4569 : */
4570 1820 : case AT_ChangeOwner: /* change visible to SELECT */
4571 1820 : cmd_lockmode = AccessExclusiveLock;
4572 1820 : break;
4573 :
4574 : /*
4575 : * Changing foreign table options may affect optimization.
4576 : */
4577 238 : case AT_GenericOptions:
4578 : case AT_AlterColumnGenericOptions:
4579 238 : cmd_lockmode = AccessExclusiveLock;
4580 238 : break;
4581 :
4582 : /*
4583 : * These subcommands affect write operations only.
4584 : */
4585 340 : case AT_EnableTrig:
4586 : case AT_EnableAlwaysTrig:
4587 : case AT_EnableReplicaTrig:
4588 : case AT_EnableTrigAll:
4589 : case AT_EnableTrigUser:
4590 : case AT_DisableTrig:
4591 : case AT_DisableTrigAll:
4592 : case AT_DisableTrigUser:
4593 340 : cmd_lockmode = ShareRowExclusiveLock;
4594 340 : break;
4595 :
4596 : /*
4597 : * These subcommands affect write operations only. XXX
4598 : * Theoretically, these could be ShareRowExclusiveLock.
4599 : */
4600 9640 : case AT_ColumnDefault:
4601 : case AT_CookedColumnDefault:
4602 : case AT_AlterConstraint:
4603 : case AT_AddIndex: /* from ADD CONSTRAINT */
4604 : case AT_AddIndexConstraint:
4605 : case AT_ReplicaIdentity:
4606 : case AT_SetNotNull:
4607 : case AT_SetAttNotNull:
4608 : case AT_EnableRowSecurity:
4609 : case AT_DisableRowSecurity:
4610 : case AT_ForceRowSecurity:
4611 : case AT_NoForceRowSecurity:
4612 : case AT_AddIdentity:
4613 : case AT_DropIdentity:
4614 : case AT_SetIdentity:
4615 : case AT_SetExpression:
4616 : case AT_DropExpression:
4617 : case AT_SetCompression:
4618 9640 : cmd_lockmode = AccessExclusiveLock;
4619 9640 : break;
4620 :
4621 12822 : case AT_AddConstraint:
4622 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4623 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4624 12822 : if (IsA(cmd->def, Constraint))
4625 : {
4626 12822 : Constraint *con = (Constraint *) cmd->def;
4627 :
4628 12822 : switch (con->contype)
4629 : {
4630 9502 : case CONSTR_EXCLUSION:
4631 : case CONSTR_PRIMARY:
4632 : case CONSTR_UNIQUE:
4633 :
4634 : /*
4635 : * Cases essentially the same as CREATE INDEX. We
4636 : * could reduce the lock strength to ShareLock if
4637 : * we can work out how to allow concurrent catalog
4638 : * updates. XXX Might be set down to
4639 : * ShareRowExclusiveLock but requires further
4640 : * analysis.
4641 : */
4642 9502 : cmd_lockmode = AccessExclusiveLock;
4643 9502 : break;
4644 2386 : case CONSTR_FOREIGN:
4645 :
4646 : /*
4647 : * We add triggers to both tables when we add a
4648 : * Foreign Key, so the lock level must be at least
4649 : * as strong as CREATE TRIGGER.
4650 : */
4651 2386 : cmd_lockmode = ShareRowExclusiveLock;
4652 2386 : break;
4653 :
4654 934 : default:
4655 934 : cmd_lockmode = AccessExclusiveLock;
4656 : }
4657 0 : }
4658 12822 : break;
4659 :
4660 : /*
4661 : * These subcommands affect inheritance behaviour. Queries
4662 : * started before us will continue to see the old inheritance
4663 : * behaviour, while queries started after we commit will see
4664 : * new behaviour. No need to prevent reads or writes to the
4665 : * subtable while we hook it up though. Changing the TupDesc
4666 : * may be a problem, so keep highest lock.
4667 : */
4668 382 : case AT_AddInherit:
4669 : case AT_DropInherit:
4670 382 : cmd_lockmode = AccessExclusiveLock;
4671 382 : break;
4672 :
4673 : /*
4674 : * These subcommands affect implicit row type conversion. They
4675 : * have affects similar to CREATE/DROP CAST on queries. don't
4676 : * provide for invalidating parse trees as a result of such
4677 : * changes, so we keep these at AccessExclusiveLock.
4678 : */
4679 72 : case AT_AddOf:
4680 : case AT_DropOf:
4681 72 : cmd_lockmode = AccessExclusiveLock;
4682 72 : break;
4683 :
4684 : /*
4685 : * Only used by CREATE OR REPLACE VIEW which must conflict
4686 : * with an SELECTs currently using the view.
4687 : */
4688 194 : case AT_ReplaceRelOptions:
4689 194 : cmd_lockmode = AccessExclusiveLock;
4690 194 : break;
4691 :
4692 : /*
4693 : * These subcommands affect general strategies for performance
4694 : * and maintenance, though don't change the semantic results
4695 : * from normal data reads and writes. Delaying an ALTER TABLE
4696 : * behind currently active writes only delays the point where
4697 : * the new strategy begins to take effect, so there is no
4698 : * benefit in waiting. In this case the minimum restriction
4699 : * applies: we don't currently allow concurrent catalog
4700 : * updates.
4701 : */
4702 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4703 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4704 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4705 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4706 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4707 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4708 234 : break;
4709 :
4710 88 : case AT_SetLogged:
4711 : case AT_SetUnLogged:
4712 88 : cmd_lockmode = AccessExclusiveLock;
4713 88 : break;
4714 :
4715 388 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4716 388 : cmd_lockmode = ShareUpdateExclusiveLock;
4717 388 : break;
4718 :
4719 : /*
4720 : * Rel options are more complex than first appears. Options
4721 : * are set here for tables, views and indexes; for historical
4722 : * reasons these can all be used with ALTER TABLE, so we can't
4723 : * decide between them using the basic grammar.
4724 : */
4725 740 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4726 : * getTables() */
4727 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4728 : * getTables() */
4729 740 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4730 740 : break;
4731 :
4732 2608 : case AT_AttachPartition:
4733 2608 : cmd_lockmode = ShareUpdateExclusiveLock;
4734 2608 : break;
4735 :
4736 528 : case AT_DetachPartition:
4737 528 : if (((PartitionCmd *) cmd->def)->concurrent)
4738 158 : cmd_lockmode = ShareUpdateExclusiveLock;
4739 : else
4740 370 : cmd_lockmode = AccessExclusiveLock;
4741 528 : break;
4742 :
4743 14 : case AT_DetachPartitionFinalize:
4744 14 : cmd_lockmode = ShareUpdateExclusiveLock;
4745 14 : break;
4746 :
4747 252 : case AT_SplitPartition:
4748 252 : cmd_lockmode = AccessExclusiveLock;
4749 252 : break;
4750 :
4751 120 : case AT_MergePartitions:
4752 120 : cmd_lockmode = AccessExclusiveLock;
4753 120 : break;
4754 :
4755 0 : default: /* oops */
4756 0 : elog(ERROR, "unrecognized alter table type: %d",
4757 : (int) cmd->subtype);
4758 : break;
4759 : }
4760 :
4761 : /*
4762 : * Take the greatest lockmode from any subcommand
4763 : */
4764 36900 : if (cmd_lockmode > lockmode)
4765 30940 : lockmode = cmd_lockmode;
4766 : }
4767 :
4768 34896 : return lockmode;
4769 : }
4770 :
4771 : /*
4772 : * ATController provides top level control over the phases.
4773 : *
4774 : * parsetree is passed in to allow it to be passed to event triggers
4775 : * when requested.
4776 : */
4777 : static void
4778 34656 : ATController(AlterTableStmt *parsetree,
4779 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4780 : AlterTableUtilityContext *context)
4781 : {
4782 34656 : List *wqueue = NIL;
4783 : ListCell *lcmd;
4784 :
4785 : /* Phase 1: preliminary examination of commands, create work queue */
4786 71038 : foreach(lcmd, cmds)
4787 : {
4788 36654 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4789 :
4790 36654 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4791 : }
4792 :
4793 : /* Close the relation, but keep lock until commit */
4794 34384 : relation_close(rel, NoLock);
4795 :
4796 : /* Phase 2: update system catalogs */
4797 34384 : ATRewriteCatalogs(&wqueue, lockmode, context);
4798 :
4799 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4800 31748 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4801 31412 : }
4802 :
4803 : /*
4804 : * ATPrepCmd
4805 : *
4806 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4807 : * recursion and permission checks.
4808 : *
4809 : * Caller must have acquired appropriate lock type on relation already.
4810 : * This lock should be held until commit.
4811 : */
4812 : static void
4813 37294 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4814 : bool recurse, bool recursing, LOCKMODE lockmode,
4815 : AlterTableUtilityContext *context)
4816 : {
4817 : AlteredTableInfo *tab;
4818 37294 : AlterTablePass pass = AT_PASS_UNSET;
4819 :
4820 : /* Find or create work queue entry for this table */
4821 37294 : tab = ATGetQueueEntry(wqueue, rel);
4822 :
4823 : /*
4824 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4825 : * partitions that are pending detach.
4826 : */
4827 37294 : if (rel->rd_rel->relispartition &&
4828 2628 : cmd->subtype != AT_DetachPartitionFinalize &&
4829 1314 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4830 2 : ereport(ERROR,
4831 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4832 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4833 : RelationGetRelationName(rel)),
4834 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4835 :
4836 : /*
4837 : * Copy the original subcommand for each table, so we can scribble on it.
4838 : * This avoids conflicts when different child tables need to make
4839 : * different parse transformations (for example, the same column may have
4840 : * different column numbers in different children).
4841 : */
4842 37292 : cmd = copyObject(cmd);
4843 :
4844 : /*
4845 : * Do permissions and relkind checking, recursion to child tables if
4846 : * needed, and any additional phase-1 processing needed. (But beware of
4847 : * adding any processing that looks at table details that another
4848 : * subcommand could change. In some cases we reject multiple subcommands
4849 : * that could try to change the same state in contrary ways.)
4850 : */
4851 37292 : switch (cmd->subtype)
4852 : {
4853 1962 : case AT_AddColumn: /* ADD COLUMN */
4854 1962 : ATSimplePermissions(cmd->subtype, rel,
4855 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4856 1962 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4857 : lockmode, context);
4858 : /* Recursion occurs during execution phase */
4859 1950 : pass = AT_PASS_ADD_COL;
4860 1950 : break;
4861 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4862 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4863 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4864 : lockmode, context);
4865 : /* Recursion occurs during execution phase */
4866 24 : pass = AT_PASS_ADD_COL;
4867 24 : break;
4868 590 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4869 :
4870 : /*
4871 : * We allow defaults on views so that INSERT into a view can have
4872 : * default-ish behavior. This works because the rewriter
4873 : * substitutes default values into INSERTs before it expands
4874 : * rules.
4875 : */
4876 590 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4877 590 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4878 : /* No command-specific prep needed */
4879 590 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4880 590 : break;
4881 110 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4882 : /* This is currently used only in CREATE TABLE */
4883 : /* (so the permission check really isn't necessary) */
4884 110 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4885 : /* This command never recurses */
4886 110 : pass = AT_PASS_ADD_OTHERCONSTR;
4887 110 : break;
4888 156 : case AT_AddIdentity:
4889 156 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4890 : /* Set up recursion for phase 2; no other prep needed */
4891 156 : if (recurse)
4892 150 : cmd->recurse = true;
4893 156 : pass = AT_PASS_ADD_OTHERCONSTR;
4894 156 : break;
4895 62 : case AT_SetIdentity:
4896 62 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4897 : /* Set up recursion for phase 2; no other prep needed */
4898 62 : if (recurse)
4899 56 : cmd->recurse = true;
4900 : /* This should run after AddIdentity, so do it in MISC pass */
4901 62 : pass = AT_PASS_MISC;
4902 62 : break;
4903 62 : case AT_DropIdentity:
4904 62 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4905 : /* Set up recursion for phase 2; no other prep needed */
4906 62 : if (recurse)
4907 56 : cmd->recurse = true;
4908 62 : pass = AT_PASS_DROP;
4909 62 : break;
4910 262 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4911 262 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4912 : /* Set up recursion for phase 2; no other prep needed */
4913 256 : if (recurse)
4914 244 : cmd->recurse = true;
4915 256 : pass = AT_PASS_DROP;
4916 256 : break;
4917 372 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4918 372 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4919 : /* Set up recursion for phase 2; no other prep needed */
4920 366 : if (recurse)
4921 336 : cmd->recurse = true;
4922 366 : pass = AT_PASS_COL_ATTRS;
4923 366 : break;
4924 7350 : case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4925 : * a constraint */
4926 7350 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4927 : /* Need command-specific recursion decision */
4928 7350 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4929 7350 : pass = AT_PASS_COL_ATTRS;
4930 7350 : break;
4931 84 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4932 84 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4933 84 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4934 84 : pass = AT_PASS_SET_EXPRESSION;
4935 84 : break;
4936 44 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4937 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4938 44 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4939 44 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4940 32 : pass = AT_PASS_DROP;
4941 32 : break;
4942 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4943 164 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4944 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4945 : /* No command-specific prep needed */
4946 164 : pass = AT_PASS_MISC;
4947 164 : break;
4948 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4949 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4950 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4951 : /* This command never recurses */
4952 32 : pass = AT_PASS_MISC;
4953 32 : break;
4954 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4955 234 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4956 234 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4957 : /* No command-specific prep needed */
4958 234 : pass = AT_PASS_MISC;
4959 234 : break;
4960 66 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4961 66 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4962 : /* This command never recurses */
4963 : /* No command-specific prep needed */
4964 66 : pass = AT_PASS_MISC;
4965 66 : break;
4966 1624 : case AT_DropColumn: /* DROP COLUMN */
4967 1624 : ATSimplePermissions(cmd->subtype, rel,
4968 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4969 1618 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4970 : lockmode, context);
4971 : /* Recursion occurs during execution phase */
4972 1606 : pass = AT_PASS_DROP;
4973 1606 : break;
4974 0 : case AT_AddIndex: /* ADD INDEX */
4975 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4976 : /* This command never recurses */
4977 : /* No command-specific prep needed */
4978 0 : pass = AT_PASS_ADD_INDEX;
4979 0 : break;
4980 12876 : case AT_AddConstraint: /* ADD CONSTRAINT */
4981 12876 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4982 : /* Recursion occurs during execution phase */
4983 : /* No command-specific prep needed except saving recurse flag */
4984 12876 : if (recurse)
4985 12526 : cmd->recurse = true;
4986 12876 : pass = AT_PASS_ADD_CONSTR;
4987 12876 : break;
4988 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4989 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4990 : /* This command never recurses */
4991 : /* No command-specific prep needed */
4992 0 : pass = AT_PASS_ADD_INDEXCONSTR;
4993 0 : break;
4994 968 : case AT_DropConstraint: /* DROP CONSTRAINT */
4995 968 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4996 968 : ATCheckPartitionsNotInUse(rel, lockmode);
4997 : /* Other recursion occurs during execution phase */
4998 : /* No command-specific prep needed except saving recurse flag */
4999 962 : if (recurse)
5000 732 : cmd->recurse = true;
5001 962 : pass = AT_PASS_DROP;
5002 962 : break;
5003 1128 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5004 1128 : ATSimplePermissions(cmd->subtype, rel,
5005 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5006 : /* See comments for ATPrepAlterColumnType */
5007 1128 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5008 : AT_PASS_UNSET, context);
5009 : Assert(cmd != NULL);
5010 : /* Performs own recursion */
5011 1122 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5012 : lockmode, context);
5013 978 : pass = AT_PASS_ALTER_TYPE;
5014 978 : break;
5015 164 : case AT_AlterColumnGenericOptions:
5016 164 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5017 : /* This command never recurses */
5018 : /* No command-specific prep needed */
5019 164 : pass = AT_PASS_MISC;
5020 164 : break;
5021 1796 : case AT_ChangeOwner: /* ALTER OWNER */
5022 : /* This command never recurses */
5023 : /* No command-specific prep needed */
5024 1796 : pass = AT_PASS_MISC;
5025 1796 : break;
5026 64 : case AT_ClusterOn: /* CLUSTER ON */
5027 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5028 64 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5029 : /* These commands never recurse */
5030 : /* No command-specific prep needed */
5031 64 : pass = AT_PASS_MISC;
5032 64 : break;
5033 38 : case AT_SetLogged: /* SET LOGGED */
5034 38 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5035 38 : if (tab->chgPersistence)
5036 0 : ereport(ERROR,
5037 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5038 : errmsg("cannot change persistence setting twice")));
5039 38 : tab->chgPersistence = ATPrepChangePersistence(rel, true);
5040 : /* force rewrite if necessary; see comment in ATRewriteTables */
5041 32 : if (tab->chgPersistence)
5042 : {
5043 26 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5044 26 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5045 : }
5046 32 : pass = AT_PASS_MISC;
5047 32 : break;
5048 50 : case AT_SetUnLogged: /* SET UNLOGGED */
5049 50 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5050 50 : if (tab->chgPersistence)
5051 0 : ereport(ERROR,
5052 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5053 : errmsg("cannot change persistence setting twice")));
5054 50 : tab->chgPersistence = ATPrepChangePersistence(rel, false);
5055 : /* force rewrite if necessary; see comment in ATRewriteTables */
5056 44 : if (tab->chgPersistence)
5057 : {
5058 38 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5059 38 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
5060 : }
5061 44 : pass = AT_PASS_MISC;
5062 44 : break;
5063 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5064 6 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5065 6 : pass = AT_PASS_DROP;
5066 6 : break;
5067 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5068 128 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5069 :
5070 : /* check if another access method change was already requested */
5071 128 : if (tab->chgAccessMethod)
5072 18 : ereport(ERROR,
5073 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5074 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5075 :
5076 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5077 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5078 110 : break;
5079 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5080 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5081 : ATT_PARTITIONED_INDEX);
5082 : /* This command never recurses */
5083 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5084 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5085 158 : break;
5086 934 : case AT_SetRelOptions: /* SET (...) */
5087 : case AT_ResetRelOptions: /* RESET (...) */
5088 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5089 934 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5090 : /* This command never recurses */
5091 : /* No command-specific prep needed */
5092 934 : pass = AT_PASS_MISC;
5093 934 : break;
5094 338 : case AT_AddInherit: /* INHERIT */
5095 338 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5096 : /* This command never recurses */
5097 338 : ATPrepAddInherit(rel);
5098 320 : pass = AT_PASS_MISC;
5099 320 : break;
5100 44 : case AT_DropInherit: /* NO INHERIT */
5101 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5102 : /* This command never recurses */
5103 : /* No command-specific prep needed */
5104 44 : pass = AT_PASS_MISC;
5105 44 : break;
5106 132 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5107 132 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5108 : /* Recursion occurs during execution phase */
5109 126 : pass = AT_PASS_MISC;
5110 126 : break;
5111 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5112 388 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5113 : /* Recursion occurs during execution phase */
5114 : /* No command-specific prep needed except saving recurse flag */
5115 388 : if (recurse)
5116 388 : cmd->recurse = true;
5117 388 : pass = AT_PASS_MISC;
5118 388 : break;
5119 450 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5120 450 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5121 450 : pass = AT_PASS_MISC;
5122 : /* This command never recurses */
5123 : /* No command-specific prep needed */
5124 450 : break;
5125 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5126 : case AT_EnableAlwaysTrig:
5127 : case AT_EnableReplicaTrig:
5128 : case AT_EnableTrigAll:
5129 : case AT_EnableTrigUser:
5130 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5131 : case AT_DisableTrigAll:
5132 : case AT_DisableTrigUser:
5133 340 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5134 : /* Set up recursion for phase 2; no other prep needed */
5135 340 : if (recurse)
5136 312 : cmd->recurse = true;
5137 340 : pass = AT_PASS_MISC;
5138 340 : break;
5139 520 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5140 : case AT_EnableAlwaysRule:
5141 : case AT_EnableReplicaRule:
5142 : case AT_DisableRule:
5143 : case AT_AddOf: /* OF */
5144 : case AT_DropOf: /* NOT OF */
5145 : case AT_EnableRowSecurity:
5146 : case AT_DisableRowSecurity:
5147 : case AT_ForceRowSecurity:
5148 : case AT_NoForceRowSecurity:
5149 520 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5150 : /* These commands never recurse */
5151 : /* No command-specific prep needed */
5152 520 : pass = AT_PASS_MISC;
5153 520 : break;
5154 50 : case AT_GenericOptions:
5155 50 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5156 : /* No command-specific prep needed */
5157 50 : pass = AT_PASS_MISC;
5158 50 : break;
5159 2596 : case AT_AttachPartition:
5160 2596 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5161 : /* No command-specific prep needed */
5162 2596 : pass = AT_PASS_MISC;
5163 2596 : break;
5164 528 : case AT_DetachPartition:
5165 528 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5166 : /* No command-specific prep needed */
5167 522 : pass = AT_PASS_MISC;
5168 522 : break;
5169 14 : case AT_DetachPartitionFinalize:
5170 14 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5171 : /* No command-specific prep needed */
5172 14 : pass = AT_PASS_MISC;
5173 14 : break;
5174 252 : case AT_SplitPartition:
5175 252 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5176 : /* No command-specific prep needed */
5177 252 : pass = AT_PASS_MISC;
5178 252 : break;
5179 120 : case AT_MergePartitions:
5180 120 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5181 : /* No command-specific prep needed */
5182 120 : pass = AT_PASS_MISC;
5183 120 : break;
5184 0 : default: /* oops */
5185 0 : elog(ERROR, "unrecognized alter table type: %d",
5186 : (int) cmd->subtype);
5187 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5188 : break;
5189 : }
5190 : Assert(pass > AT_PASS_UNSET);
5191 :
5192 : /* Add the subcommand to the appropriate list for phase 2 */
5193 37010 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5194 37010 : }
5195 :
5196 : /*
5197 : * ATRewriteCatalogs
5198 : *
5199 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5200 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5201 : * conflicts).
5202 : */
5203 : static void
5204 34384 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5205 : AlterTableUtilityContext *context)
5206 : {
5207 : ListCell *ltab;
5208 :
5209 : /*
5210 : * We process all the tables "in parallel", one pass at a time. This is
5211 : * needed because we may have to propagate work from one table to another
5212 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5213 : * re-adding of the foreign key constraint to the other table). Work can
5214 : * only be propagated into later passes, however.
5215 : */
5216 469428 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5217 : {
5218 : /* Go through each table that needs to be processed */
5219 887580 : foreach(ltab, *wqueue)
5220 : {
5221 452536 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5222 452536 : List *subcmds = tab->subcmds[pass];
5223 : ListCell *lcmd;
5224 :
5225 452536 : if (subcmds == NIL)
5226 398108 : continue;
5227 :
5228 : /*
5229 : * Open the relation and store it in tab. This allows subroutines
5230 : * close and reopen, if necessary. Appropriate lock was obtained
5231 : * by phase 1, needn't get it again.
5232 : */
5233 54428 : tab->rel = relation_open(tab->relid, NoLock);
5234 :
5235 109892 : foreach(lcmd, subcmds)
5236 58100 : ATExecCmd(wqueue, tab,
5237 58100 : lfirst_node(AlterTableCmd, lcmd),
5238 : lockmode, pass, context);
5239 :
5240 : /*
5241 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5242 : * (this is not done in ATExecAlterColumnType since it should be
5243 : * done only once if multiple columns of a table are altered).
5244 : */
5245 51792 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5246 960 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5247 :
5248 51792 : if (tab->rel)
5249 : {
5250 51792 : relation_close(tab->rel, NoLock);
5251 51792 : tab->rel = NULL;
5252 : }
5253 : }
5254 : }
5255 :
5256 : /* Check to see if a toast table must be added. */
5257 67260 : foreach(ltab, *wqueue)
5258 : {
5259 35512 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5260 :
5261 : /*
5262 : * If the table is source table of ATTACH PARTITION command, we did
5263 : * not modify anything about it that will change its toasting
5264 : * requirement, so no need to check.
5265 : */
5266 35512 : if (((tab->relkind == RELKIND_RELATION ||
5267 6440 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5268 33674 : tab->partition_constraint == NULL) ||
5269 3770 : tab->relkind == RELKIND_MATVIEW)
5270 31792 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5271 : }
5272 31748 : }
5273 :
5274 : /*
5275 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5276 : */
5277 : static void
5278 58100 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5279 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5280 : AlterTableUtilityContext *context)
5281 : {
5282 58100 : ObjectAddress address = InvalidObjectAddress;
5283 58100 : Relation rel = tab->rel;
5284 :
5285 58100 : switch (cmd->subtype)
5286 : {
5287 1968 : case AT_AddColumn: /* ADD COLUMN */
5288 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5289 1968 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5290 1968 : cmd->recurse, false,
5291 : lockmode, cur_pass, context);
5292 1854 : break;
5293 566 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5294 566 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5295 506 : break;
5296 110 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5297 110 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5298 110 : break;
5299 150 : case AT_AddIdentity:
5300 150 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5301 : cur_pass, context);
5302 : Assert(cmd != NULL);
5303 144 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5304 102 : break;
5305 62 : case AT_SetIdentity:
5306 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5307 : cur_pass, context);
5308 : Assert(cmd != NULL);
5309 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5310 38 : break;
5311 62 : case AT_DropIdentity:
5312 62 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5313 44 : break;
5314 256 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5315 256 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5316 142 : break;
5317 366 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5318 366 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5319 366 : cmd->recurse, false, NULL, lockmode);
5320 336 : break;
5321 14656 : case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5322 14656 : address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5323 14638 : break;
5324 84 : case AT_SetExpression:
5325 84 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5326 78 : break;
5327 32 : case AT_DropExpression:
5328 32 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5329 26 : break;
5330 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5331 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5332 116 : break;
5333 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5334 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5335 26 : break;
5336 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5337 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5338 6 : break;
5339 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5340 234 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5341 222 : break;
5342 66 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5343 66 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5344 : lockmode);
5345 60 : break;
5346 1606 : case AT_DropColumn: /* DROP COLUMN */
5347 1606 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5348 1606 : cmd->behavior, cmd->recurse, false,
5349 1606 : cmd->missing_ok, lockmode,
5350 : NULL);
5351 1432 : break;
5352 1024 : case AT_AddIndex: /* ADD INDEX */
5353 1024 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5354 : lockmode);
5355 896 : break;
5356 434 : case AT_ReAddIndex: /* ADD INDEX */
5357 434 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5358 : lockmode);
5359 434 : break;
5360 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5361 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5362 : true, lockmode);
5363 14 : break;
5364 16546 : case AT_AddConstraint: /* ADD CONSTRAINT */
5365 : /* Transform the command only during initial examination */
5366 16546 : if (cur_pass == AT_PASS_ADD_CONSTR)
5367 12846 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5368 12876 : cmd->recurse, lockmode,
5369 : cur_pass, context);
5370 : /* Depending on constraint type, might be no more work to do now */
5371 16516 : if (cmd != NULL)
5372 : address =
5373 3670 : ATExecAddConstraint(wqueue, tab, rel,
5374 3670 : (Constraint *) cmd->def,
5375 3670 : cmd->recurse, false, lockmode);
5376 15962 : break;
5377 188 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5378 : address =
5379 188 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5380 : true, true, lockmode);
5381 176 : break;
5382 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5383 : * constraint */
5384 : address =
5385 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5386 14 : ((AlterDomainStmt *) cmd->def)->def,
5387 : NULL);
5388 8 : break;
5389 54 : case AT_ReAddComment: /* Re-add existing comment */
5390 54 : address = CommentObject((CommentStmt *) cmd->def);
5391 54 : break;
5392 8434 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5393 8434 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5394 : lockmode);
5395 8422 : break;
5396 126 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5397 126 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5398 114 : break;
5399 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5400 388 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5401 : false, lockmode);
5402 388 : break;
5403 962 : case AT_DropConstraint: /* DROP CONSTRAINT */
5404 962 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5405 962 : cmd->recurse,
5406 962 : cmd->missing_ok, lockmode);
5407 764 : break;
5408 948 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5409 : /* parse transformation was done earlier */
5410 948 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5411 912 : break;
5412 164 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5413 : address =
5414 164 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5415 164 : (List *) cmd->def, lockmode);
5416 158 : break;
5417 1796 : case AT_ChangeOwner: /* ALTER OWNER */
5418 1790 : ATExecChangeOwner(RelationGetRelid(rel),
5419 1796 : get_rolespec_oid(cmd->newowner, false),
5420 : false, lockmode);
5421 1778 : break;
5422 64 : case AT_ClusterOn: /* CLUSTER ON */
5423 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5424 58 : break;
5425 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5426 18 : ATExecDropCluster(rel, lockmode);
5427 12 : break;
5428 76 : case AT_SetLogged: /* SET LOGGED */
5429 : case AT_SetUnLogged: /* SET UNLOGGED */
5430 76 : break;
5431 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5432 : /* nothing to do here, oid columns don't exist anymore */
5433 6 : break;
5434 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5435 :
5436 : /*
5437 : * Only do this for partitioned tables, for which this is just a
5438 : * catalog change. Tables with storage are handled by Phase 3.
5439 : */
5440 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5441 50 : tab->chgAccessMethod)
5442 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5443 92 : break;
5444 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5445 :
5446 : /*
5447 : * Only do this for partitioned tables and indexes, for which this
5448 : * is just a catalog change. Other relation types which have
5449 : * storage are handled by Phase 3.
5450 : */
5451 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5452 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5453 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5454 :
5455 152 : break;
5456 934 : case AT_SetRelOptions: /* SET (...) */
5457 : case AT_ResetRelOptions: /* RESET (...) */
5458 : case AT_ReplaceRelOptions: /* replace entire option list */
5459 934 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5460 882 : break;
5461 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5462 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5463 : TRIGGER_FIRES_ON_ORIGIN, false,
5464 122 : cmd->recurse,
5465 : lockmode);
5466 122 : break;
5467 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5468 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5469 : TRIGGER_FIRES_ALWAYS, false,
5470 40 : cmd->recurse,
5471 : lockmode);
5472 40 : break;
5473 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5474 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5475 : TRIGGER_FIRES_ON_REPLICA, false,
5476 16 : cmd->recurse,
5477 : lockmode);
5478 16 : break;
5479 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5480 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5481 : TRIGGER_DISABLED, false,
5482 138 : cmd->recurse,
5483 : lockmode);
5484 138 : break;
5485 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5486 0 : ATExecEnableDisableTrigger(rel, NULL,
5487 : TRIGGER_FIRES_ON_ORIGIN, false,
5488 0 : cmd->recurse,
5489 : lockmode);
5490 0 : break;
5491 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5492 12 : ATExecEnableDisableTrigger(rel, NULL,
5493 : TRIGGER_DISABLED, false,
5494 12 : cmd->recurse,
5495 : lockmode);
5496 12 : break;
5497 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5498 0 : ATExecEnableDisableTrigger(rel, NULL,
5499 : TRIGGER_FIRES_ON_ORIGIN, true,
5500 0 : cmd->recurse,
5501 : lockmode);
5502 0 : break;
5503 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5504 12 : ATExecEnableDisableTrigger(rel, NULL,
5505 : TRIGGER_DISABLED, true,
5506 12 : cmd->recurse,
5507 : lockmode);
5508 12 : break;
5509 :
5510 8 : case AT_EnableRule: /* ENABLE RULE name */
5511 8 : ATExecEnableDisableRule(rel, cmd->name,
5512 : RULE_FIRES_ON_ORIGIN, lockmode);
5513 8 : break;
5514 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5515 0 : ATExecEnableDisableRule(rel, cmd->name,
5516 : RULE_FIRES_ALWAYS, lockmode);
5517 0 : break;
5518 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5519 6 : ATExecEnableDisableRule(rel, cmd->name,
5520 : RULE_FIRES_ON_REPLICA, lockmode);
5521 6 : break;
5522 32 : case AT_DisableRule: /* DISABLE RULE name */
5523 32 : ATExecEnableDisableRule(rel, cmd->name,
5524 : RULE_DISABLED, lockmode);
5525 32 : break;
5526 :
5527 320 : case AT_AddInherit:
5528 320 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5529 230 : break;
5530 44 : case AT_DropInherit:
5531 44 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5532 38 : break;
5533 66 : case AT_AddOf:
5534 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5535 30 : break;
5536 6 : case AT_DropOf:
5537 6 : ATExecDropOf(rel, lockmode);
5538 6 : break;
5539 468 : case AT_ReplicaIdentity:
5540 468 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5541 420 : break;
5542 278 : case AT_EnableRowSecurity:
5543 278 : ATExecSetRowSecurity(rel, true);
5544 278 : break;
5545 10 : case AT_DisableRowSecurity:
5546 10 : ATExecSetRowSecurity(rel, false);
5547 10 : break;
5548 82 : case AT_ForceRowSecurity:
5549 82 : ATExecForceNoForceRowSecurity(rel, true);
5550 82 : break;
5551 32 : case AT_NoForceRowSecurity:
5552 32 : ATExecForceNoForceRowSecurity(rel, false);
5553 32 : break;
5554 50 : case AT_GenericOptions:
5555 50 : ATExecGenericOptions(rel, (List *) cmd->def);
5556 48 : break;
5557 2596 : case AT_AttachPartition:
5558 2596 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5559 : cur_pass, context);
5560 : Assert(cmd != NULL);
5561 2566 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5562 2182 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5563 : context);
5564 : else
5565 384 : address = ATExecAttachPartitionIdx(wqueue, rel,
5566 384 : ((PartitionCmd *) cmd->def)->name);
5567 2230 : break;
5568 522 : case AT_DetachPartition:
5569 522 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5570 : cur_pass, context);
5571 : Assert(cmd != NULL);
5572 : /* ATPrepCmd ensures it must be a table */
5573 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5574 516 : address = ATExecDetachPartition(wqueue, tab, rel,
5575 516 : ((PartitionCmd *) cmd->def)->name,
5576 516 : ((PartitionCmd *) cmd->def)->concurrent);
5577 386 : break;
5578 14 : case AT_DetachPartitionFinalize:
5579 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5580 14 : break;
5581 252 : case AT_SplitPartition:
5582 252 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5583 : cur_pass, context);
5584 : Assert(cmd != NULL);
5585 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5586 120 : ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5587 : context);
5588 114 : break;
5589 120 : case AT_MergePartitions:
5590 120 : 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 66 : ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5595 : context);
5596 66 : break;
5597 0 : default: /* oops */
5598 0 : elog(ERROR, "unrecognized alter table type: %d",
5599 : (int) cmd->subtype);
5600 : break;
5601 : }
5602 :
5603 : /*
5604 : * Report the subcommand to interested event triggers.
5605 : */
5606 55464 : if (cmd)
5607 42618 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5608 :
5609 : /*
5610 : * Bump the command counter to ensure the next subcommand in the sequence
5611 : * can see the changes so far
5612 : */
5613 55464 : CommandCounterIncrement();
5614 55464 : }
5615 :
5616 : /*
5617 : * ATParseTransformCmd: perform parse transformation for one subcommand
5618 : *
5619 : * Returns the transformed subcommand tree, if there is one, else NULL.
5620 : *
5621 : * The parser may hand back additional AlterTableCmd(s) and/or other
5622 : * utility statements, either before or after the original subcommand.
5623 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5624 : * AlteredTableInfo (they had better be for later passes than the current one).
5625 : * Utility statements that are supposed to happen before the AlterTableCmd
5626 : * are executed immediately. Those that are supposed to happen afterwards
5627 : * are added to the tab->afterStmts list to be done at the very end.
5628 : */
5629 : static AlterTableCmd *
5630 19554 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5631 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5632 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5633 : {
5634 19554 : AlterTableCmd *newcmd = NULL;
5635 19554 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5636 : List *beforeStmts;
5637 : List *afterStmts;
5638 : ListCell *lc;
5639 :
5640 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5641 19554 : atstmt->relation =
5642 19554 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5643 19554 : pstrdup(RelationGetRelationName(rel)),
5644 : -1);
5645 19554 : atstmt->relation->inh = recurse;
5646 19554 : atstmt->cmds = list_make1(cmd);
5647 19554 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5648 19554 : atstmt->missing_ok = false;
5649 :
5650 : /* Transform the AlterTableStmt */
5651 19554 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5652 : atstmt,
5653 : context->queryString,
5654 : &beforeStmts,
5655 : &afterStmts);
5656 :
5657 : /* Execute any statements that should happen before these subcommand(s) */
5658 19738 : foreach(lc, beforeStmts)
5659 : {
5660 448 : Node *stmt = (Node *) lfirst(lc);
5661 :
5662 448 : ProcessUtilityForAlterTable(stmt, context);
5663 436 : CommandCounterIncrement();
5664 : }
5665 :
5666 : /* Examine the transformed subcommands and schedule them appropriately */
5667 46048 : foreach(lc, atstmt->cmds)
5668 : {
5669 26758 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5670 : AlterTablePass pass;
5671 :
5672 : /*
5673 : * This switch need only cover the subcommand types that can be added
5674 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5675 : * executing the subcommand immediately, as a substitute for the
5676 : * original subcommand. (Note, however, that this does cause
5677 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5678 : * which is important for index and foreign key constraints.)
5679 : *
5680 : * We assume we needn't do any phase-1 checks for added subcommands.
5681 : */
5682 26758 : switch (cmd2->subtype)
5683 : {
5684 7150 : case AT_SetAttNotNull:
5685 7150 : ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
5686 7150 : pass = AT_PASS_COL_ATTRS;
5687 7150 : break;
5688 1042 : case AT_AddIndex:
5689 :
5690 : /*
5691 : * A primary key on an inheritance parent needs supporting NOT
5692 : * NULL constraint on its children; enqueue commands to create
5693 : * those or mark them inherited if they already exist.
5694 : */
5695 1042 : ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5696 1042 : pass = AT_PASS_ADD_INDEX;
5697 1042 : break;
5698 8434 : case AT_AddIndexConstraint:
5699 : /* as above */
5700 8434 : ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5701 8434 : pass = AT_PASS_ADD_INDEXCONSTR;
5702 8434 : break;
5703 3688 : case AT_AddConstraint:
5704 : /* Recursion occurs during execution phase */
5705 3688 : if (recurse)
5706 3638 : cmd2->recurse = true;
5707 3688 : switch (castNode(Constraint, cmd2->def)->contype)
5708 : {
5709 0 : case CONSTR_PRIMARY:
5710 : case CONSTR_UNIQUE:
5711 : case CONSTR_EXCLUSION:
5712 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5713 0 : break;
5714 3688 : default:
5715 3688 : pass = AT_PASS_ADD_OTHERCONSTR;
5716 3688 : break;
5717 : }
5718 3688 : break;
5719 0 : case AT_AlterColumnGenericOptions:
5720 : /* This command never recurses */
5721 : /* No command-specific prep needed */
5722 0 : pass = AT_PASS_MISC;
5723 0 : break;
5724 6444 : default:
5725 6444 : pass = cur_pass;
5726 6444 : break;
5727 : }
5728 :
5729 26758 : if (pass < cur_pass)
5730 : {
5731 : /* Cannot schedule into a pass we already finished */
5732 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5733 : pass);
5734 : }
5735 26758 : else if (pass > cur_pass)
5736 : {
5737 : /* OK, queue it up for later */
5738 20314 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5739 : }
5740 : else
5741 : {
5742 : /*
5743 : * We should see at most one subcommand for the current pass,
5744 : * which is the transformed version of the original subcommand.
5745 : */
5746 6444 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5747 : {
5748 : /* Found the transformed version of our subcommand */
5749 6444 : newcmd = cmd2;
5750 : }
5751 : else
5752 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5753 : pass);
5754 : }
5755 : }
5756 :
5757 : /* Queue up any after-statements to happen at the end */
5758 19290 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5759 :
5760 19290 : return newcmd;
5761 : }
5762 :
5763 : /*
5764 : * ATRewriteTables: ALTER TABLE phase 3
5765 : */
5766 : static void
5767 31748 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5768 : AlterTableUtilityContext *context)
5769 : {
5770 : ListCell *ltab;
5771 :
5772 : /* Go through each table that needs to be checked or rewritten */
5773 66986 : foreach(ltab, *wqueue)
5774 : {
5775 35494 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5776 :
5777 : /* Relations without storage may be ignored here */
5778 35494 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5779 6154 : continue;
5780 :
5781 : /*
5782 : * If we change column data types, the operation has to be propagated
5783 : * to tables that use this table's rowtype as a column type.
5784 : * tab->newvals will also be non-NULL in the case where we're adding a
5785 : * column with a default. We choose to forbid that case as well,
5786 : * since composite types might eventually support defaults.
5787 : *
5788 : * (Eventually we'll probably need to check for composite type
5789 : * dependencies even when we're just scanning the table without a
5790 : * rewrite, but at the moment a composite type does not enforce any
5791 : * constraints, so it's not necessary/appropriate to enforce them just
5792 : * during ALTER.)
5793 : */
5794 29340 : if (tab->newvals != NIL || tab->rewrite > 0)
5795 : {
5796 : Relation rel;
5797 :
5798 1456 : rel = table_open(tab->relid, NoLock);
5799 1456 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5800 1444 : table_close(rel, NoLock);
5801 : }
5802 :
5803 : /*
5804 : * We only need to rewrite the table if at least one column needs to
5805 : * be recomputed, or we are changing its persistence or access method.
5806 : *
5807 : * There are two reasons for requiring a rewrite when changing
5808 : * persistence: on one hand, we need to ensure that the buffers
5809 : * belonging to each of the two relations are marked with or without
5810 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5811 : * and assigns a new relfilenumber, we automatically create or drop an
5812 : * init fork for the relation as appropriate.
5813 : */
5814 29328 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5815 848 : {
5816 : /* Build a temporary relation and copy data */
5817 : Relation OldHeap;
5818 : Oid OIDNewHeap;
5819 : Oid NewAccessMethod;
5820 : Oid NewTableSpace;
5821 : char persistence;
5822 :
5823 886 : OldHeap = table_open(tab->relid, NoLock);
5824 :
5825 : /*
5826 : * We don't support rewriting of system catalogs; there are too
5827 : * many corner cases and too little benefit. In particular this
5828 : * is certainly not going to work for mapped catalogs.
5829 : */
5830 886 : if (IsSystemRelation(OldHeap))
5831 0 : ereport(ERROR,
5832 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5833 : errmsg("cannot rewrite system relation \"%s\"",
5834 : RelationGetRelationName(OldHeap))));
5835 :
5836 886 : if (RelationIsUsedAsCatalogTable(OldHeap))
5837 2 : ereport(ERROR,
5838 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5839 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5840 : RelationGetRelationName(OldHeap))));
5841 :
5842 : /*
5843 : * Don't allow rewrite on temp tables of other backends ... their
5844 : * local buffer manager is not going to cope.
5845 : */
5846 884 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5847 0 : ereport(ERROR,
5848 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5849 : errmsg("cannot rewrite temporary tables of other sessions")));
5850 :
5851 : /*
5852 : * Select destination tablespace (same as original unless user
5853 : * requested a change)
5854 : */
5855 884 : if (tab->newTableSpace)
5856 0 : NewTableSpace = tab->newTableSpace;
5857 : else
5858 884 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5859 :
5860 : /*
5861 : * Select destination access method (same as original unless user
5862 : * requested a change)
5863 : */
5864 884 : if (tab->chgAccessMethod)
5865 36 : NewAccessMethod = tab->newAccessMethod;
5866 : else
5867 848 : NewAccessMethod = OldHeap->rd_rel->relam;
5868 :
5869 : /*
5870 : * Select persistence of transient table (same as original unless
5871 : * user requested a change)
5872 : */
5873 884 : persistence = tab->chgPersistence ?
5874 832 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5875 :
5876 884 : table_close(OldHeap, NoLock);
5877 :
5878 : /*
5879 : * Fire off an Event Trigger now, before actually rewriting the
5880 : * table.
5881 : *
5882 : * We don't support Event Trigger for nested commands anywhere,
5883 : * here included, and parsetree is given NULL when coming from
5884 : * AlterTableInternal.
5885 : *
5886 : * And fire it only once.
5887 : */
5888 884 : if (parsetree)
5889 884 : EventTriggerTableRewrite((Node *) parsetree,
5890 : tab->relid,
5891 : tab->rewrite);
5892 :
5893 : /*
5894 : * Create transient table that will receive the modified data.
5895 : *
5896 : * Ensure it is marked correctly as logged or unlogged. We have
5897 : * to do this here so that buffers for the new relfilenumber will
5898 : * have the right persistence set, and at the same time ensure
5899 : * that the original filenumbers's buffers will get read in with
5900 : * the correct setting (i.e. the original one). Otherwise a
5901 : * rollback after the rewrite would possibly result with buffers
5902 : * for the original filenumbers having the wrong persistence
5903 : * setting.
5904 : *
5905 : * NB: This relies on swap_relation_files() also swapping the
5906 : * persistence. That wouldn't work for pg_class, but that can't be
5907 : * unlogged anyway.
5908 : */
5909 878 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5910 : persistence, lockmode);
5911 :
5912 : /*
5913 : * Copy the heap data into the new table with the desired
5914 : * modifications, and test the current data within the table
5915 : * against new constraints generated by ALTER TABLE commands.
5916 : */
5917 878 : ATRewriteTable(tab, OIDNewHeap, lockmode);
5918 :
5919 : /*
5920 : * Swap the physical files of the old and new heaps, then rebuild
5921 : * indexes and discard the old heap. We can use RecentXmin for
5922 : * the table's new relfrozenxid because we rewrote all the tuples
5923 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5924 : * we never try to swap toast tables by content, since we have no
5925 : * interest in letting this code work on system catalogs.
5926 : */
5927 854 : finish_heap_swap(tab->relid, OIDNewHeap,
5928 : false, false, true,
5929 854 : !OidIsValid(tab->newTableSpace),
5930 : RecentXmin,
5931 : ReadNextMultiXactId(),
5932 : persistence);
5933 :
5934 848 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5935 : }
5936 28442 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5937 : {
5938 12 : if (tab->chgPersistence)
5939 12 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5940 : }
5941 : else
5942 : {
5943 : /*
5944 : * If required, test the current data within the table against new
5945 : * constraints generated by ALTER TABLE commands, but don't
5946 : * rebuild data.
5947 : */
5948 28430 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5949 25674 : tab->partition_constraint != NULL)
5950 4540 : ATRewriteTable(tab, InvalidOid, lockmode);
5951 :
5952 : /*
5953 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5954 : * just do a block-by-block copy.
5955 : */
5956 28224 : if (tab->newTableSpace)
5957 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5958 : }
5959 :
5960 : /*
5961 : * Also change persistence of owned sequences, so that it matches the
5962 : * table persistence.
5963 : */
5964 29084 : if (tab->chgPersistence)
5965 : {
5966 64 : List *seqlist = getOwnedSequences(tab->relid);
5967 : ListCell *lc;
5968 :
5969 112 : foreach(lc, seqlist)
5970 : {
5971 48 : Oid seq_relid = lfirst_oid(lc);
5972 :
5973 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5974 : }
5975 : }
5976 : }
5977 :
5978 : /*
5979 : * Foreign key constraints are checked in a final pass, since (a) it's
5980 : * generally best to examine each one separately, and (b) it's at least
5981 : * theoretically possible that we have changed both relations of the
5982 : * foreign key, and we'd better have finished both rewrites before we try
5983 : * to read the tables.
5984 : */
5985 66514 : foreach(ltab, *wqueue)
5986 : {
5987 35102 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5988 35102 : Relation rel = NULL;
5989 : ListCell *lcon;
5990 :
5991 : /* Relations without storage may be ignored here too */
5992 35102 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5993 6074 : continue;
5994 :
5995 30684 : foreach(lcon, tab->constraints)
5996 : {
5997 1736 : NewConstraint *con = lfirst(lcon);
5998 :
5999 1736 : if (con->contype == CONSTR_FOREIGN)
6000 : {
6001 1084 : Constraint *fkconstraint = (Constraint *) con->qual;
6002 : Relation refrel;
6003 :
6004 1084 : if (rel == NULL)
6005 : {
6006 : /* Long since locked, no need for another */
6007 1072 : rel = table_open(tab->relid, NoLock);
6008 : }
6009 :
6010 1084 : refrel = table_open(con->refrelid, RowShareLock);
6011 :
6012 1084 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6013 : con->refindid,
6014 : con->conid,
6015 1084 : con->conwithperiod);
6016 :
6017 : /*
6018 : * No need to mark the constraint row as validated, we did
6019 : * that when we inserted the row earlier.
6020 : */
6021 :
6022 1004 : table_close(refrel, NoLock);
6023 : }
6024 : }
6025 :
6026 28948 : if (rel)
6027 992 : table_close(rel, NoLock);
6028 : }
6029 :
6030 : /* Finally, run any afterStmts that were queued up */
6031 66396 : foreach(ltab, *wqueue)
6032 : {
6033 34984 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6034 : ListCell *lc;
6035 :
6036 35070 : foreach(lc, tab->afterStmts)
6037 : {
6038 86 : Node *stmt = (Node *) lfirst(lc);
6039 :
6040 86 : ProcessUtilityForAlterTable(stmt, context);
6041 86 : CommandCounterIncrement();
6042 : }
6043 : }
6044 31412 : }
6045 :
6046 : /*
6047 : * ATRewriteTable: scan or rewrite one table
6048 : *
6049 : * OIDNewHeap is InvalidOid if we don't need to rewrite
6050 : */
6051 : static void
6052 5418 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
6053 : {
6054 : Relation oldrel;
6055 : Relation newrel;
6056 : TupleDesc oldTupDesc;
6057 : TupleDesc newTupDesc;
6058 5418 : bool needscan = false;
6059 : List *notnull_attrs;
6060 : int i;
6061 : ListCell *l;
6062 : EState *estate;
6063 : CommandId mycid;
6064 : BulkInsertState bistate;
6065 : int ti_options;
6066 5418 : ExprState *partqualstate = NULL;
6067 :
6068 : /*
6069 : * Open the relation(s). We have surely already locked the existing
6070 : * table.
6071 : */
6072 5418 : oldrel = table_open(tab->relid, NoLock);
6073 5418 : oldTupDesc = tab->oldDesc;
6074 5418 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6075 :
6076 5418 : if (OidIsValid(OIDNewHeap))
6077 878 : newrel = table_open(OIDNewHeap, lockmode);
6078 : else
6079 4540 : newrel = NULL;
6080 :
6081 : /*
6082 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6083 : * is empty, so don't bother using it.
6084 : */
6085 5418 : if (newrel)
6086 : {
6087 878 : mycid = GetCurrentCommandId(true);
6088 878 : bistate = GetBulkInsertState();
6089 878 : ti_options = TABLE_INSERT_SKIP_FSM;
6090 : }
6091 : else
6092 : {
6093 : /* keep compiler quiet about using these uninitialized */
6094 4540 : mycid = 0;
6095 4540 : bistate = NULL;
6096 4540 : ti_options = 0;
6097 : }
6098 :
6099 : /*
6100 : * Generate the constraint and default execution states
6101 : */
6102 :
6103 5418 : estate = CreateExecutorState();
6104 :
6105 : /* Build the needed expression execution states */
6106 7250 : foreach(l, tab->constraints)
6107 : {
6108 1832 : NewConstraint *con = lfirst(l);
6109 :
6110 1832 : switch (con->contype)
6111 : {
6112 742 : case CONSTR_CHECK:
6113 742 : needscan = true;
6114 742 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6115 742 : break;
6116 1090 : case CONSTR_FOREIGN:
6117 : /* Nothing to do here */
6118 1090 : break;
6119 0 : default:
6120 0 : elog(ERROR, "unrecognized constraint type: %d",
6121 : (int) con->contype);
6122 : }
6123 : }
6124 :
6125 : /* Build expression execution states for partition check quals */
6126 5418 : if (tab->partition_constraint)
6127 : {
6128 1926 : needscan = true;
6129 1926 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6130 : }
6131 :
6132 6322 : foreach(l, tab->newvals)
6133 : {
6134 904 : NewColumnValue *ex = lfirst(l);
6135 :
6136 : /* expr already planned */
6137 904 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6138 : }
6139 :
6140 5418 : notnull_attrs = NIL;
6141 5418 : if (newrel || tab->verify_new_notnull)
6142 : {
6143 : /*
6144 : * If we are rebuilding the tuples OR if we added any new but not
6145 : * verified not-null constraints, check all not-null constraints. This
6146 : * is a bit of overkill but it minimizes risk of bugs, and
6147 : * heap_attisnull is a pretty cheap test anyway.
6148 : */
6149 7174 : for (i = 0; i < newTupDesc->natts; i++)
6150 : {
6151 5226 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6152 :
6153 5226 : if (attr->attnotnull && !attr->attisdropped)
6154 2100 : notnull_attrs = lappend_int(notnull_attrs, i);
6155 : }
6156 1948 : if (notnull_attrs)
6157 1540 : needscan = true;
6158 : }
6159 :
6160 5418 : if (newrel || needscan)
6161 : {
6162 : ExprContext *econtext;
6163 : TupleTableSlot *oldslot;
6164 : TupleTableSlot *newslot;
6165 : TableScanDesc scan;
6166 : MemoryContext oldCxt;
6167 4512 : List *dropped_attrs = NIL;
6168 : ListCell *lc;
6169 : Snapshot snapshot;
6170 :
6171 4512 : if (newrel)
6172 878 : ereport(DEBUG1,
6173 : (errmsg_internal("rewriting table \"%s\"",
6174 : RelationGetRelationName(oldrel))));
6175 : else
6176 3634 : ereport(DEBUG1,
6177 : (errmsg_internal("verifying table \"%s\"",
6178 : RelationGetRelationName(oldrel))));
6179 :
6180 4512 : if (newrel)
6181 : {
6182 : /*
6183 : * All predicate locks on the tuples or pages are about to be made
6184 : * invalid, because we move tuples around. Promote them to
6185 : * relation locks.
6186 : */
6187 878 : TransferPredicateLocksToHeapRelation(oldrel);
6188 : }
6189 :
6190 4512 : econtext = GetPerTupleExprContext(estate);
6191 :
6192 : /*
6193 : * Create necessary tuple slots. When rewriting, two slots are needed,
6194 : * otherwise one suffices. In the case where one slot suffices, we
6195 : * need to use the new tuple descriptor, otherwise some constraints
6196 : * can't be evaluated. Note that even when the tuple layout is the
6197 : * same and no rewrite is required, the tupDescs might not be
6198 : * (consider ADD COLUMN without a default).
6199 : */
6200 4512 : if (tab->rewrite)
6201 : {
6202 : Assert(newrel != NULL);
6203 878 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6204 : table_slot_callbacks(oldrel));
6205 878 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6206 : table_slot_callbacks(newrel));
6207 :
6208 : /*
6209 : * Set all columns in the new slot to NULL initially, to ensure
6210 : * columns added as part of the rewrite are initialized to NULL.
6211 : * That is necessary as tab->newvals will not contain an
6212 : * expression for columns with a NULL default, e.g. when adding a
6213 : * column without a default together with a column with a default
6214 : * requiring an actual rewrite.
6215 : */
6216 878 : ExecStoreAllNullTuple(newslot);
6217 : }
6218 : else
6219 : {
6220 3634 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6221 : table_slot_callbacks(oldrel));
6222 3634 : newslot = NULL;
6223 : }
6224 :
6225 : /*
6226 : * Any attributes that are dropped according to the new tuple
6227 : * descriptor can be set to NULL. We precompute the list of dropped
6228 : * attributes to avoid needing to do so in the per-tuple loop.
6229 : */
6230 16008 : for (i = 0; i < newTupDesc->natts; i++)
6231 : {
6232 11496 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6233 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6234 : }
6235 :
6236 : /*
6237 : * Scan through the rows, generating a new row if needed and then
6238 : * checking all the constraints.
6239 : */
6240 4512 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6241 4512 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6242 :
6243 : /*
6244 : * Switch to per-tuple memory context and reset it for each tuple
6245 : * produced, so we don't leak memory.
6246 : */
6247 4512 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6248 :
6249 767092 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6250 : {
6251 : TupleTableSlot *insertslot;
6252 :
6253 762810 : if (tab->rewrite > 0)
6254 : {
6255 : /* Extract data from old tuple */
6256 97564 : slot_getallattrs(oldslot);
6257 97564 : ExecClearTuple(newslot);
6258 :
6259 : /* copy attributes */
6260 97564 : memcpy(newslot->tts_values, oldslot->tts_values,
6261 97564 : sizeof(Datum) * oldslot->tts_nvalid);
6262 97564 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6263 97564 : sizeof(bool) * oldslot->tts_nvalid);
6264 :
6265 : /* Set dropped attributes to null in new tuple */
6266 97650 : foreach(lc, dropped_attrs)
6267 86 : newslot->tts_isnull[lfirst_int(lc)] = true;
6268 :
6269 : /*
6270 : * Constraints and GENERATED expressions might reference the
6271 : * tableoid column, so fill tts_tableOid with the desired
6272 : * value. (We must do this each time, because it gets
6273 : * overwritten with newrel's OID during storing.)
6274 : */
6275 97564 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6276 :
6277 : /*
6278 : * Process supplied expressions to replace selected columns.
6279 : *
6280 : * First, evaluate expressions whose inputs come from the old
6281 : * tuple.
6282 : */
6283 97564 : econtext->ecxt_scantuple = oldslot;
6284 :
6285 200948 : foreach(l, tab->newvals)
6286 : {
6287 103396 : NewColumnValue *ex = lfirst(l);
6288 :
6289 103396 : if (ex->is_generated)
6290 150 : continue;
6291 :
6292 103246 : newslot->tts_values[ex->attnum - 1]
6293 103234 : = ExecEvalExpr(ex->exprstate,
6294 : econtext,
6295 103246 : &newslot->tts_isnull[ex->attnum - 1]);
6296 : }
6297 :
6298 97552 : ExecStoreVirtualTuple(newslot);
6299 :
6300 : /*
6301 : * Now, evaluate any expressions whose inputs come from the
6302 : * new tuple. We assume these columns won't reference each
6303 : * other, so that there's no ordering dependency.
6304 : */
6305 97552 : econtext->ecxt_scantuple = newslot;
6306 :
6307 200936 : foreach(l, tab->newvals)
6308 : {
6309 103384 : NewColumnValue *ex = lfirst(l);
6310 :
6311 103384 : if (!ex->is_generated)
6312 103234 : continue;
6313 :
6314 150 : newslot->tts_values[ex->attnum - 1]
6315 150 : = ExecEvalExpr(ex->exprstate,
6316 : econtext,
6317 150 : &newslot->tts_isnull[ex->attnum - 1]);
6318 : }
6319 :
6320 97552 : insertslot = newslot;
6321 : }
6322 : else
6323 : {
6324 : /*
6325 : * If there's no rewrite, old and new table are guaranteed to
6326 : * have the same AM, so we can just use the old slot to verify
6327 : * new constraints etc.
6328 : */
6329 665246 : insertslot = oldslot;
6330 : }
6331 :
6332 : /* Now check any constraints on the possibly-changed tuple */
6333 762798 : econtext->ecxt_scantuple = insertslot;
6334 :
6335 3339112 : foreach(l, notnull_attrs)
6336 : {
6337 2576386 : int attn = lfirst_int(l);
6338 :
6339 2576386 : if (slot_attisnull(insertslot, attn + 1))
6340 : {
6341 72 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6342 :
6343 72 : ereport(ERROR,
6344 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6345 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6346 : NameStr(attr->attname),
6347 : RelationGetRelationName(oldrel)),
6348 : errtablecol(oldrel, attn + 1)));
6349 : }
6350 : }
6351 :
6352 770826 : foreach(l, tab->constraints)
6353 : {
6354 8172 : NewConstraint *con = lfirst(l);
6355 :
6356 8172 : switch (con->contype)
6357 : {
6358 8072 : case CONSTR_CHECK:
6359 8072 : if (!ExecCheck(con->qualstate, econtext))
6360 72 : ereport(ERROR,
6361 : (errcode(ERRCODE_CHECK_VIOLATION),
6362 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6363 : con->name,
6364 : RelationGetRelationName(oldrel)),
6365 : errtableconstraint(oldrel, con->name)));
6366 8000 : break;
6367 100 : case CONSTR_NOTNULL:
6368 : case CONSTR_FOREIGN:
6369 : /* Nothing to do here */
6370 100 : break;
6371 0 : default:
6372 0 : elog(ERROR, "unrecognized constraint type: %d",
6373 : (int) con->contype);
6374 : }
6375 : }
6376 :
6377 762654 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6378 : {
6379 74 : if (tab->validate_default)
6380 26 : ereport(ERROR,
6381 : (errcode(ERRCODE_CHECK_VIOLATION),
6382 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6383 : RelationGetRelationName(oldrel)),
6384 : errtable(oldrel)));
6385 : else
6386 48 : ereport(ERROR,
6387 : (errcode(ERRCODE_CHECK_VIOLATION),
6388 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6389 : RelationGetRelationName(oldrel)),
6390 : errtable(oldrel)));
6391 : }
6392 :
6393 : /* Write the tuple out to the new relation */
6394 762580 : if (newrel)
6395 97540 : table_tuple_insert(newrel, insertslot, mycid,
6396 : ti_options, bistate);
6397 :
6398 762580 : ResetExprContext(econtext);
6399 :
6400 762580 : CHECK_FOR_INTERRUPTS();
6401 : }
6402 :
6403 4282 : MemoryContextSwitchTo(oldCxt);
6404 4282 : table_endscan(scan);
6405 4282 : UnregisterSnapshot(snapshot);
6406 :
6407 4282 : ExecDropSingleTupleTableSlot(oldslot);
6408 4282 : if (newslot)
6409 854 : ExecDropSingleTupleTableSlot(newslot);
6410 : }
6411 :
6412 5188 : FreeExecutorState(estate);
6413 :
6414 5188 : table_close(oldrel, NoLock);
6415 5188 : if (newrel)
6416 : {
6417 854 : FreeBulkInsertState(bistate);
6418 :
6419 854 : table_finish_bulk_insert(newrel, ti_options);
6420 :
6421 854 : table_close(newrel, NoLock);
6422 : }
6423 5188 : }
6424 :
6425 : /*
6426 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6427 : */
6428 : static AlteredTableInfo *
6429 43882 : ATGetQueueEntry(List **wqueue, Relation rel)
6430 : {
6431 43882 : Oid relid = RelationGetRelid(rel);
6432 : AlteredTableInfo *tab;
6433 : ListCell *ltab;
6434 :
6435 53076 : foreach(ltab, *wqueue)
6436 : {
6437 14484 : tab = (AlteredTableInfo *) lfirst(ltab);
6438 14484 : if (tab->relid == relid)
6439 5290 : return tab;
6440 : }
6441 :
6442 : /*
6443 : * Not there, so add it. Note that we make a copy of the relation's
6444 : * existing descriptor before anything interesting can happen to it.
6445 : */
6446 38592 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6447 38592 : tab->relid = relid;
6448 38592 : tab->rel = NULL; /* set later */
6449 38592 : tab->relkind = rel->rd_rel->relkind;
6450 38592 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6451 38592 : tab->newAccessMethod = InvalidOid;
6452 38592 : tab->chgAccessMethod = false;
6453 38592 : tab->newTableSpace = InvalidOid;
6454 38592 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6455 38592 : tab->chgPersistence = false;
6456 :
6457 38592 : *wqueue = lappend(*wqueue, tab);
6458 :
6459 38592 : return tab;
6460 : }
6461 :
6462 : static const char *
6463 42 : alter_table_type_to_string(AlterTableType cmdtype)
6464 : {
6465 42 : switch (cmdtype)
6466 : {
6467 0 : case AT_AddColumn:
6468 : case AT_AddColumnToView:
6469 0 : return "ADD COLUMN";
6470 0 : case AT_ColumnDefault:
6471 : case AT_CookedColumnDefault:
6472 0 : return "ALTER COLUMN ... SET DEFAULT";
6473 6 : case AT_DropNotNull:
6474 6 : return "ALTER COLUMN ... DROP NOT NULL";
6475 6 : case AT_SetNotNull:
6476 6 : return "ALTER COLUMN ... SET NOT NULL";
6477 0 : case AT_SetAttNotNull:
6478 0 : return NULL; /* not real grammar */
6479 0 : case AT_SetExpression:
6480 0 : return "ALTER COLUMN ... SET EXPRESSION";
6481 0 : case AT_DropExpression:
6482 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6483 0 : case AT_SetStatistics:
6484 0 : return "ALTER COLUMN ... SET STATISTICS";
6485 12 : case AT_SetOptions:
6486 12 : return "ALTER COLUMN ... SET";
6487 0 : case AT_ResetOptions:
6488 0 : return "ALTER COLUMN ... RESET";
6489 0 : case AT_SetStorage:
6490 0 : return "ALTER COLUMN ... SET STORAGE";
6491 0 : case AT_SetCompression:
6492 0 : return "ALTER COLUMN ... SET COMPRESSION";
6493 6 : case AT_DropColumn:
6494 6 : return "DROP COLUMN";
6495 0 : case AT_AddIndex:
6496 : case AT_ReAddIndex:
6497 0 : return NULL; /* not real grammar */
6498 0 : case AT_AddConstraint:
6499 : case AT_ReAddConstraint:
6500 : case AT_ReAddDomainConstraint:
6501 : case AT_AddIndexConstraint:
6502 0 : return "ADD CONSTRAINT";
6503 6 : case AT_AlterConstraint:
6504 6 : return "ALTER CONSTRAINT";
6505 0 : case AT_ValidateConstraint:
6506 0 : return "VALIDATE CONSTRAINT";
6507 0 : case AT_DropConstraint:
6508 0 : return "DROP CONSTRAINT";
6509 0 : case AT_ReAddComment:
6510 0 : return NULL; /* not real grammar */
6511 0 : case AT_AlterColumnType:
6512 0 : return "ALTER COLUMN ... SET DATA TYPE";
6513 0 : case AT_AlterColumnGenericOptions:
6514 0 : return "ALTER COLUMN ... OPTIONS";
6515 0 : case AT_ChangeOwner:
6516 0 : return "OWNER TO";
6517 0 : case AT_ClusterOn:
6518 0 : return "CLUSTER ON";
6519 0 : case AT_DropCluster:
6520 0 : return "SET WITHOUT CLUSTER";
6521 0 : case AT_SetAccessMethod:
6522 0 : return "SET ACCESS METHOD";
6523 0 : case AT_SetLogged:
6524 0 : return "SET LOGGED";
6525 0 : case AT_SetUnLogged:
6526 0 : return "SET UNLOGGED";
6527 0 : case AT_DropOids:
6528 0 : return "SET WITHOUT OIDS";
6529 0 : case AT_SetTableSpace:
6530 0 : return "SET TABLESPACE";
6531 0 : case AT_SetRelOptions:
6532 0 : return "SET";
6533 0 : case AT_ResetRelOptions:
6534 0 : return "RESET";
6535 0 : case AT_ReplaceRelOptions:
6536 0 : return NULL; /* not real grammar */
6537 0 : case AT_EnableTrig:
6538 0 : return "ENABLE TRIGGER";
6539 0 : case AT_EnableAlwaysTrig:
6540 0 : return "ENABLE ALWAYS TRIGGER";
6541 0 : case AT_EnableReplicaTrig:
6542 0 : return "ENABLE REPLICA TRIGGER";
6543 0 : case AT_DisableTrig:
6544 0 : return "DISABLE TRIGGER";
6545 0 : case AT_EnableTrigAll:
6546 0 : return "ENABLE TRIGGER ALL";
6547 0 : case AT_DisableTrigAll:
6548 0 : return "DISABLE TRIGGER ALL";
6549 0 : case AT_EnableTrigUser:
6550 0 : return "ENABLE TRIGGER USER";
6551 0 : case AT_DisableTrigUser:
6552 0 : return "DISABLE TRIGGER USER";
6553 0 : case AT_EnableRule:
6554 0 : return "ENABLE RULE";
6555 0 : case AT_EnableAlwaysRule:
6556 0 : return "ENABLE ALWAYS RULE";
6557 0 : case AT_EnableReplicaRule:
6558 0 : return "ENABLE REPLICA RULE";
6559 0 : case AT_DisableRule:
6560 0 : return "DISABLE RULE";
6561 0 : case AT_AddInherit:
6562 0 : return "INHERIT";
6563 0 : case AT_DropInherit:
6564 0 : return "NO INHERIT";
6565 0 : case AT_AddOf:
6566 0 : return "OF";
6567 0 : case AT_DropOf:
6568 0 : return "NOT OF";
6569 0 : case AT_ReplicaIdentity:
6570 0 : return "REPLICA IDENTITY";
6571 0 : case AT_EnableRowSecurity:
6572 0 : return "ENABLE ROW SECURITY";
6573 0 : case AT_DisableRowSecurity:
6574 0 : return "DISABLE ROW SECURITY";
6575 0 : case AT_ForceRowSecurity:
6576 0 : return "FORCE ROW SECURITY";
6577 0 : case AT_NoForceRowSecurity:
6578 0 : return "NO FORCE ROW SECURITY";
6579 0 : case AT_GenericOptions:
6580 0 : return "OPTIONS";
6581 0 : case AT_AttachPartition:
6582 0 : return "ATTACH PARTITION";
6583 6 : case AT_DetachPartition:
6584 6 : return "DETACH PARTITION";
6585 0 : case AT_DetachPartitionFinalize:
6586 0 : return "DETACH PARTITION ... FINALIZE";
6587 0 : case AT_SplitPartition:
6588 0 : return "SPLIT PARTITION";
6589 0 : case AT_MergePartitions:
6590 0 : return "MERGE PARTITIONS";
6591 0 : case AT_AddIdentity:
6592 0 : return "ALTER COLUMN ... ADD IDENTITY";
6593 0 : case AT_SetIdentity:
6594 0 : return "ALTER COLUMN ... SET";
6595 0 : case AT_DropIdentity:
6596 0 : return "ALTER COLUMN ... DROP IDENTITY";
6597 0 : case AT_ReAddStatistics:
6598 0 : return NULL; /* not real grammar */
6599 : }
6600 :
6601 0 : return NULL;
6602 : }
6603 :
6604 : /*
6605 : * ATSimplePermissions
6606 : *
6607 : * - Ensure that it is a relation (or possibly a view)
6608 : * - Ensure this user is the owner
6609 : * - Ensure that it is not a system table
6610 : */
6611 : static void
6612 40046 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6613 : {
6614 : int actual_target;
6615 :
6616 40046 : switch (rel->rd_rel->relkind)
6617 : {
6618 37840 : case RELKIND_RELATION:
6619 : case RELKIND_PARTITIONED_TABLE:
6620 37840 : actual_target = ATT_TABLE;
6621 37840 : break;
6622 396 : case RELKIND_VIEW:
6623 396 : actual_target = ATT_VIEW;
6624 396 : break;
6625 46 : case RELKIND_MATVIEW:
6626 46 : actual_target = ATT_MATVIEW;
6627 46 : break;
6628 226 : case RELKIND_INDEX:
6629 226 : actual_target = ATT_INDEX;
6630 226 : break;
6631 426 : case RELKIND_PARTITIONED_INDEX:
6632 426 : actual_target = ATT_PARTITIONED_INDEX;
6633 426 : break;
6634 214 : case RELKIND_COMPOSITE_TYPE:
6635 214 : actual_target = ATT_COMPOSITE_TYPE;
6636 214 : break;
6637 886 : case RELKIND_FOREIGN_TABLE:
6638 886 : actual_target = ATT_FOREIGN_TABLE;
6639 886 : break;
6640 12 : case RELKIND_SEQUENCE:
6641 12 : actual_target = ATT_SEQUENCE;
6642 12 : break;
6643 0 : default:
6644 0 : actual_target = 0;
6645 0 : break;
6646 : }
6647 :
6648 : /* Wrong target type? */
6649 40046 : if ((actual_target & allowed_targets) == 0)
6650 : {
6651 42 : const char *action_str = alter_table_type_to_string(cmdtype);
6652 :
6653 42 : if (action_str)
6654 42 : ereport(ERROR,
6655 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6656 : /* translator: %s is a group of some SQL keywords */
6657 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6658 : action_str, RelationGetRelationName(rel)),
6659 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6660 : else
6661 : /* internal error? */
6662 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6663 : RelationGetRelationName(rel));
6664 : }
6665 :
6666 : /* Permissions checks */
6667 40004 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6668 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6669 12 : RelationGetRelationName(rel));
6670 :
6671 39992 : if (!allowSystemTableMods && IsSystemRelation(rel))
6672 0 : ereport(ERROR,
6673 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6674 : errmsg("permission denied: \"%s\" is a system catalog",
6675 : RelationGetRelationName(rel))));
6676 39992 : }
6677 :
6678 : /*
6679 : * ATSimpleRecursion
6680 : *
6681 : * Simple table recursion sufficient for most ALTER TABLE operations.
6682 : * All direct and indirect children are processed in an unspecified order.
6683 : * Note that if a child inherits from the original table via multiple
6684 : * inheritance paths, it will be visited just once.
6685 : */
6686 : static void
6687 15616 : ATSimpleRecursion(List **wqueue, Relation rel,
6688 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6689 : AlterTableUtilityContext *context)
6690 : {
6691 : /*
6692 : * Propagate to children, if desired and if there are (or might be) any
6693 : * children.
6694 : */
6695 15616 : if (recurse && rel->rd_rel->relhassubclass)
6696 : {
6697 148 : Oid relid = RelationGetRelid(rel);
6698 : ListCell *child;
6699 : List *children;
6700 :
6701 148 : children = find_all_inheritors(relid, lockmode, NULL);
6702 :
6703 : /*
6704 : * find_all_inheritors does the recursive search of the inheritance
6705 : * hierarchy, so all we have to do is process all of the relids in the
6706 : * list that it returns.
6707 : */
6708 624 : foreach(child, children)
6709 : {
6710 476 : Oid childrelid = lfirst_oid(child);
6711 : Relation childrel;
6712 :
6713 476 : if (childrelid == relid)
6714 148 : continue;
6715 : /* find_all_inheritors already got lock */
6716 328 : childrel = relation_open(childrelid, NoLock);
6717 328 : CheckTableNotInUse(childrel, "ALTER TABLE");
6718 328 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6719 328 : relation_close(childrel, NoLock);
6720 : }
6721 : }
6722 15616 : }
6723 :
6724 : /*
6725 : * Obtain list of partitions of the given table, locking them all at the given
6726 : * lockmode and ensuring that they all pass CheckTableNotInUse.
6727 : *
6728 : * This function is a no-op if the given relation is not a partitioned table;
6729 : * in particular, nothing is done if it's a legacy inheritance parent.
6730 : */
6731 : static void
6732 968 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6733 : {
6734 968 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6735 : {
6736 : List *inh;
6737 : ListCell *cell;
6738 :
6739 172 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6740 : /* first element is the parent rel; must ignore it */
6741 588 : for_each_from(cell, inh, 1)
6742 : {
6743 : Relation childrel;
6744 :
6745 : /* find_all_inheritors already got lock */
6746 422 : childrel = table_open(lfirst_oid(cell), NoLock);
6747 422 : CheckTableNotInUse(childrel, "ALTER TABLE");
6748 416 : table_close(childrel, NoLock);
6749 : }
6750 166 : list_free(inh);
6751 : }
6752 962 : }
6753 :
6754 : /*
6755 : * ATTypedTableRecursion
6756 : *
6757 : * Propagate ALTER TYPE operations to the typed tables of that type.
6758 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6759 : * recursion to inheritance children of the typed tables.
6760 : */
6761 : static void
6762 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6763 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6764 : {
6765 : ListCell *child;
6766 : List *children;
6767 :
6768 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6769 :
6770 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6771 190 : RelationGetRelationName(rel),
6772 : cmd->behavior);
6773 :
6774 202 : foreach(child, children)
6775 : {
6776 30 : Oid childrelid = lfirst_oid(child);
6777 : Relation childrel;
6778 :
6779 30 : childrel = relation_open(childrelid, lockmode);
6780 30 : CheckTableNotInUse(childrel, "ALTER TABLE");
6781 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6782 30 : relation_close(childrel, NoLock);
6783 : }
6784 172 : }
6785 :
6786 :
6787 : /*
6788 : * find_composite_type_dependencies
6789 : *
6790 : * Check to see if the type "typeOid" is being used as a column in some table
6791 : * (possibly nested several levels deep in composite types, arrays, etc!).
6792 : * Eventually, we'd like to propagate the check or rewrite operation
6793 : * into such tables, but for now, just error out if we find any.
6794 : *
6795 : * Caller should provide either the associated relation of a rowtype,
6796 : * or a type name (not both) for use in the error message, if any.
6797 : *
6798 : * Note that "typeOid" is not necessarily a composite type; it could also be
6799 : * another container type such as an array or range, or a domain over one of
6800 : * these things. The name of this function is therefore somewhat historical,
6801 : * but it's not worth changing.
6802 : *
6803 : * We assume that functions and views depending on the type are not reasons
6804 : * to reject the ALTER. (How safe is this really?)
6805 : */
6806 : void
6807 3866 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6808 : const char *origTypeName)
6809 : {
6810 : Relation depRel;
6811 : ScanKeyData key[2];
6812 : SysScanDesc depScan;
6813 : HeapTuple depTup;
6814 :
6815 : /* since this function recurses, it could be driven to stack overflow */
6816 3866 : check_stack_depth();
6817 :
6818 : /*
6819 : * We scan pg_depend to find those things that depend on the given type.
6820 : * (We assume we can ignore refobjsubid for a type.)
6821 : */
6822 3866 : depRel = table_open(DependRelationId, AccessShareLock);
6823 :
6824 3866 : ScanKeyInit(&key[0],
6825 : Anum_pg_depend_refclassid,
6826 : BTEqualStrategyNumber, F_OIDEQ,
6827 : ObjectIdGetDatum(TypeRelationId));
6828 3866 : ScanKeyInit(&key[1],
6829 : Anum_pg_depend_refobjid,
6830 : BTEqualStrategyNumber, F_OIDEQ,
6831 : ObjectIdGetDatum(typeOid));
6832 :
6833 3866 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6834 : NULL, 2, key);
6835 :
6836 5976 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6837 : {
6838 2206 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6839 : Relation rel;
6840 : TupleDesc tupleDesc;
6841 : Form_pg_attribute att;
6842 :
6843 : /* Check for directly dependent types */
6844 2206 : if (pg_depend->classid == TypeRelationId)
6845 : {
6846 : /*
6847 : * This must be an array, domain, or range containing the given
6848 : * type, so recursively check for uses of this type. Note that
6849 : * any error message will mention the original type not the
6850 : * container; this is intentional.
6851 : */
6852 1860 : find_composite_type_dependencies(pg_depend->objid,
6853 : origRelation, origTypeName);
6854 1836 : continue;
6855 : }
6856 :
6857 : /* Else, ignore dependees that aren't relations */
6858 346 : if (pg_depend->classid != RelationRelationId)
6859 122 : continue;
6860 :
6861 224 : rel = relation_open(pg_depend->objid, AccessShareLock);
6862 224 : tupleDesc = RelationGetDescr(rel);
6863 :
6864 : /*
6865 : * If objsubid identifies a specific column, refer to that in error
6866 : * messages. Otherwise, search to see if there's a user column of the
6867 : * type. (We assume system columns are never of interesting types.)
6868 : * The search is needed because an index containing an expression
6869 : * column of the target type will just be recorded as a whole-relation
6870 : * dependency. If we do not find a column of the type, the dependency
6871 : * must indicate that the type is transiently referenced in an index
6872 : * expression but not stored on disk, which we assume is OK, just as
6873 : * we do for references in views. (It could also be that the target
6874 : * type is embedded in some container type that is stored in an index
6875 : * column, but the previous recursion should catch such cases.)
6876 : */
6877 224 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6878 66 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6879 : else
6880 : {
6881 158 : att = NULL;
6882 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6883 : {
6884 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6885 254 : if (att->atttypid == typeOid && !att->attisdropped)
6886 6 : break;
6887 248 : att = NULL;
6888 : }
6889 158 : if (att == NULL)
6890 : {
6891 : /* No such column, so assume OK */
6892 152 : relation_close(rel, AccessShareLock);
6893 152 : continue;
6894 : }
6895 : }
6896 :
6897 : /*
6898 : * We definitely should reject if the relation has storage. If it's
6899 : * partitioned, then perhaps we don't have to reject: if there are
6900 : * partitions then we'll fail when we find one, else there is no
6901 : * stored data to worry about. However, it's possible that the type
6902 : * change would affect conclusions about whether the type is sortable
6903 : * or hashable and thus (if it's a partitioning column) break the
6904 : * partitioning rule. For now, reject for partitioned rels too.
6905 : */
6906 72 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6907 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6908 : {
6909 72 : if (origTypeName)
6910 30 : ereport(ERROR,
6911 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6912 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6913 : origTypeName,
6914 : RelationGetRelationName(rel),
6915 : NameStr(att->attname))));
6916 42 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6917 18 : ereport(ERROR,
6918 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6919 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6920 : RelationGetRelationName(origRelation),
6921 : RelationGetRelationName(rel),
6922 : NameStr(att->attname))));
6923 24 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6924 6 : ereport(ERROR,
6925 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6926 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6927 : RelationGetRelationName(origRelation),
6928 : RelationGetRelationName(rel),
6929 : NameStr(att->attname))));
6930 : else
6931 18 : ereport(ERROR,
6932 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6933 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6934 : RelationGetRelationName(origRelation),
6935 : RelationGetRelationName(rel),
6936 : NameStr(att->attname))));
6937 : }
6938 0 : else if (OidIsValid(rel->rd_rel->reltype))
6939 : {
6940 : /*
6941 : * A view or composite type itself isn't a problem, but we must
6942 : * recursively check for indirect dependencies via its rowtype.
6943 : */
6944 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6945 : origRelation, origTypeName);
6946 : }
6947 :
6948 0 : relation_close(rel, AccessShareLock);
6949 : }
6950 :
6951 3770 : systable_endscan(depScan);
6952 :
6953 3770 : relation_close(depRel, AccessShareLock);
6954 3770 : }
6955 :
6956 :
6957 : /*
6958 : * find_typed_table_dependencies
6959 : *
6960 : * Check to see if a composite type is being used as the type of a
6961 : * typed table. Abort if any are found and behavior is RESTRICT.
6962 : * Else return the list of tables.
6963 : */
6964 : static List *
6965 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6966 : {
6967 : Relation classRel;
6968 : ScanKeyData key[1];
6969 : TableScanDesc scan;
6970 : HeapTuple tuple;
6971 214 : List *result = NIL;
6972 :
6973 214 : classRel = table_open(RelationRelationId, AccessShareLock);
6974 :
6975 214 : ScanKeyInit(&key[0],
6976 : Anum_pg_class_reloftype,
6977 : BTEqualStrategyNumber, F_OIDEQ,
6978 : ObjectIdGetDatum(typeOid));
6979 :
6980 214 : scan = table_beginscan_catalog(classRel, 1, key);
6981 :
6982 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6983 : {
6984 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6985 :
6986 60 : if (behavior == DROP_RESTRICT)
6987 24 : ereport(ERROR,
6988 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6989 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6990 : typeName),
6991 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6992 : else
6993 36 : result = lappend_oid(result, classform->oid);
6994 : }
6995 :
6996 190 : table_endscan(scan);
6997 190 : table_close(classRel, AccessShareLock);
6998 :
6999 190 : return result;
7000 : }
7001 :
7002 :
7003 : /*
7004 : * check_of_type
7005 : *
7006 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7007 : * isn't suitable, throw an error. Currently, we require that the type
7008 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7009 : * would require handling a number of extra corner cases in the DDL commands.
7010 : * (Also, allowing domain-over-composite would open up a can of worms about
7011 : * whether and how the domain's constraints should apply to derived tables.)
7012 : */
7013 : void
7014 170 : check_of_type(HeapTuple typetuple)
7015 : {
7016 170 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7017 170 : bool typeOk = false;
7018 :
7019 170 : if (typ->typtype == TYPTYPE_COMPOSITE)
7020 : {
7021 : Relation typeRelation;
7022 :
7023 : Assert(OidIsValid(typ->typrelid));
7024 170 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7025 170 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7026 :
7027 : /*
7028 : * Close the parent rel, but keep our AccessShareLock on it until xact
7029 : * commit. That will prevent someone else from deleting or ALTERing
7030 : * the type before the typed table creation/conversion commits.
7031 : */
7032 170 : relation_close(typeRelation, NoLock);
7033 : }
7034 170 : if (!typeOk)
7035 6 : ereport(ERROR,
7036 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7037 : errmsg("type %s is not a composite type",
7038 : format_type_be(typ->oid))));
7039 164 : }
7040 :
7041 :
7042 : /*
7043 : * ALTER TABLE ADD COLUMN
7044 : *
7045 : * Adds an additional attribute to a relation making the assumption that
7046 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7047 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7048 : * AlterTableCmd's.
7049 : *
7050 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7051 : * have to decide at runtime whether to recurse or not depending on whether we
7052 : * actually add a column or merely merge with an existing column. (We can't
7053 : * check this in a static pre-pass because it won't handle multiple inheritance
7054 : * situations correctly.)
7055 : */
7056 : static void
7057 1986 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7058 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7059 : AlterTableUtilityContext *context)
7060 : {
7061 1986 : if (rel->rd_rel->reloftype && !recursing)
7062 6 : ereport(ERROR,
7063 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7064 : errmsg("cannot add column to typed table")));
7065 :
7066 1980 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7067 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7068 :
7069 1974 : if (recurse && !is_view)
7070 1874 : cmd->recurse = true;
7071 1974 : }
7072 :
7073 : /*
7074 : * Add a column to a table. The return value is the address of the
7075 : * new column in the parent relation.
7076 : *
7077 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7078 : * copy (but that happens only after we check for IF NOT EXISTS).
7079 : */
7080 : static ObjectAddress
7081 2604 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7082 : AlterTableCmd **cmd, bool recurse, bool recursing,
7083 : LOCKMODE lockmode, AlterTablePass cur_pass,
7084 : AlterTableUtilityContext *context)
7085 : {
7086 2604 : Oid myrelid = RelationGetRelid(rel);
7087 2604 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7088 2604 : bool if_not_exists = (*cmd)->missing_ok;
7089 : Relation pgclass,
7090 : attrdesc;
7091 : HeapTuple reltup;
7092 : Form_pg_attribute attribute;
7093 : int newattnum;
7094 : char relkind;
7095 : Expr *defval;
7096 : List *children;
7097 : ListCell *child;
7098 : AlterTableCmd *childcmd;
7099 : ObjectAddress address;
7100 : TupleDesc tupdesc;
7101 :
7102 : /* since this function recurses, it could be driven to stack overflow */
7103 2604 : check_stack_depth();
7104 :
7105 : /* At top level, permission check was done in ATPrepCmd, else do it */
7106 2604 : if (recursing)
7107 636 : ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7108 :
7109 2604 : if (rel->rd_rel->relispartition && !recursing)
7110 12 : ereport(ERROR,
7111 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7112 : errmsg("cannot add column to a partition")));
7113 :
7114 2592 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7115 :
7116 : /*
7117 : * Are we adding the column to a recursion child? If so, check whether to
7118 : * merge with an existing definition for the column. If we do merge, we
7119 : * must not recurse. Children will already have the column, and recursing
7120 : * into them would mess up attinhcount.
7121 : */
7122 2592 : if (colDef->inhcount > 0)
7123 : {
7124 : HeapTuple tuple;
7125 :
7126 : /* Does child already have a column by this name? */
7127 636 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7128 636 : if (HeapTupleIsValid(tuple))
7129 : {
7130 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7131 : Oid ctypeId;
7132 : int32 ctypmod;
7133 : Oid ccollid;
7134 :
7135 : /* Child column must match on type, typmod, and collation */
7136 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7137 48 : if (ctypeId != childatt->atttypid ||
7138 48 : ctypmod != childatt->atttypmod)
7139 0 : ereport(ERROR,
7140 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7141 : errmsg("child table \"%s\" has different type for column \"%s\"",
7142 : RelationGetRelationName(rel), colDef->colname)));
7143 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7144 48 : if (ccollid != childatt->attcollation)
7145 0 : ereport(ERROR,
7146 : (errcode(ERRCODE_COLLATION_MISMATCH),
7147 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7148 : RelationGetRelationName(rel), colDef->colname),
7149 : errdetail("\"%s\" versus \"%s\"",
7150 : get_collation_name(ccollid),
7151 : get_collation_name(childatt->attcollation))));
7152 :
7153 : /* Bump the existing child att's inhcount */
7154 48 : childatt->attinhcount++;
7155 48 : if (childatt->attinhcount < 0)
7156 0 : ereport(ERROR,
7157 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7158 : errmsg("too many inheritance parents"));
7159 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7160 :
7161 48 : heap_freetuple(tuple);
7162 :
7163 : /* Inform the user about the merge */
7164 48 : ereport(NOTICE,
7165 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7166 : colDef->colname, RelationGetRelationName(rel))));
7167 :
7168 48 : table_close(attrdesc, RowExclusiveLock);
7169 :
7170 : /* Make the child column change visible */
7171 48 : CommandCounterIncrement();
7172 :
7173 48 : return InvalidObjectAddress;
7174 : }
7175 : }
7176 :
7177 : /* skip if the name already exists and if_not_exists is true */
7178 2544 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7179 : {
7180 54 : table_close(attrdesc, RowExclusiveLock);
7181 54 : return InvalidObjectAddress;
7182 : }
7183 :
7184 : /*
7185 : * Okay, we need to add the column, so go ahead and do parse
7186 : * transformation. This can result in queueing up, or even immediately
7187 : * executing, subsidiary operations (such as creation of unique indexes);
7188 : * so we mustn't do it until we have made the if_not_exists check.
7189 : *
7190 : * When recursing, the command was already transformed and we needn't do
7191 : * so again. Also, if context isn't given we can't transform. (That
7192 : * currently happens only for AT_AddColumnToView; we expect that view.c
7193 : * passed us a ColumnDef that doesn't need work.)
7194 : */
7195 2460 : if (context != NULL && !recursing)
7196 : {
7197 1848 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7198 : cur_pass, context);
7199 : Assert(*cmd != NULL);
7200 1848 : colDef = castNode(ColumnDef, (*cmd)->def);
7201 : }
7202 :
7203 : /*
7204 : * Regular inheritance children are independent enough not to inherit the
7205 : * identity column from parent hence cannot recursively add identity
7206 : * column if the table has inheritance children.
7207 : *
7208 : * Partitions, on the other hand, are integral part of a partitioned table
7209 : * and inherit identity column. Hence propagate identity column down the
7210 : * partition hierarchy.
7211 : */
7212 2460 : if (colDef->identity &&
7213 54 : recurse &&
7214 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7215 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7216 6 : ereport(ERROR,
7217 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7218 : errmsg("cannot recursively add identity column to table that has child tables")));
7219 :
7220 2454 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7221 :
7222 2454 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7223 2454 : if (!HeapTupleIsValid(reltup))
7224 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7225 2454 : relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7226 :
7227 : /* Determine the new attribute's number */
7228 2454 : newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7229 2454 : if (newattnum > MaxHeapAttributeNumber)
7230 0 : ereport(ERROR,
7231 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7232 : errmsg("tables can have at most %d columns",
7233 : MaxHeapAttributeNumber)));
7234 :
7235 : /*
7236 : * Construct new attribute's pg_attribute entry.
7237 : */
7238 2454 : tupdesc = BuildDescForRelation(list_make1(colDef));
7239 :
7240 2442 : attribute = TupleDescAttr(tupdesc, 0);
7241 :
7242 : /* Fix up attribute number */
7243 2442 : attribute->attnum = newattnum;
7244 :
7245 : /* make sure datatype is legal for a column */
7246 2442 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7247 2442 : list_make1_oid(rel->rd_rel->reltype),
7248 : 0);
7249 :
7250 2412 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7251 :
7252 2412 : table_close(attrdesc, RowExclusiveLock);
7253 :
7254 : /*
7255 : * Update pg_class tuple as appropriate
7256 : */
7257 2412 : ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7258 :
7259 2412 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7260 :
7261 2412 : heap_freetuple(reltup);
7262 :
7263 : /* Post creation hook for new attribute */
7264 2412 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7265 :
7266 2412 : table_close(pgclass, RowExclusiveLock);
7267 :
7268 : /* Make the attribute's catalog entry visible */
7269 2412 : CommandCounterIncrement();
7270 :
7271 : /*
7272 : * Store the DEFAULT, if any, in the catalogs
7273 : */
7274 2412 : if (colDef->raw_default)
7275 : {
7276 : RawColumnDefault *rawEnt;
7277 :
7278 700 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7279 700 : rawEnt->attnum = attribute->attnum;
7280 700 : rawEnt->raw_default = copyObject(colDef->raw_default);
7281 :
7282 : /*
7283 : * Attempt to skip a complete table rewrite by storing the specified
7284 : * DEFAULT value outside of the heap. This may be disabled inside
7285 : * AddRelationNewConstraints if the optimization cannot be applied.
7286 : */
7287 700 : rawEnt->missingMode = (!colDef->generated);
7288 :
7289 700 : rawEnt->generated = colDef->generated;
7290 :
7291 : /*
7292 : * This function is intended for CREATE TABLE, so it processes a
7293 : * _list_ of defaults, but we just do one.
7294 : */
7295 700 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7296 : false, true, false, NULL);
7297 :
7298 : /* Make the additional catalog changes visible */
7299 688 : CommandCounterIncrement();
7300 :
7301 : /*
7302 : * Did the request for a missing value work? If not we'll have to do a
7303 : * rewrite
7304 : */
7305 688 : if (!rawEnt->missingMode)
7306 108 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7307 : }
7308 :
7309 : /*
7310 : * Tell Phase 3 to fill in the default expression, if there is one.
7311 : *
7312 : * If there is no default, Phase 3 doesn't have to do anything, because
7313 : * that effectively means that the default is NULL. The heap tuple access
7314 : * routines always check for attnum > # of attributes in tuple, and return
7315 : * NULL if so, so without any modification of the tuple data we will get
7316 : * the effect of NULL values in the new column.
7317 : *
7318 : * An exception occurs when the new column is of a domain type: the domain
7319 : * might have a not-null constraint, or a check constraint that indirectly
7320 : * rejects nulls. If there are any domain constraints then we construct
7321 : * an explicit NULL default value that will be passed through
7322 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7323 : * rewriting the table which we really don't have to do, but the present
7324 : * design of domain processing doesn't offer any simple way of checking
7325 : * the constraints more directly.)
7326 : *
7327 : * Note: we use build_column_default, and not just the cooked default
7328 : * returned by AddRelationNewConstraints, so that the right thing happens
7329 : * when a datatype's default applies.
7330 : *
7331 : * Note: it might seem that this should happen at the end of Phase 2, so
7332 : * that the effects of subsequent subcommands can be taken into account.
7333 : * It's intentional that we do it now, though. The new column should be
7334 : * filled according to what is said in the ADD COLUMN subcommand, so that
7335 : * the effects are the same as if this subcommand had been run by itself
7336 : * and the later subcommands had been issued in new ALTER TABLE commands.
7337 : *
7338 : * We can skip this entirely for relations without storage, since Phase 3
7339 : * is certainly not going to touch them. System attributes don't have
7340 : * interesting defaults, either.
7341 : */
7342 2400 : if (RELKIND_HAS_STORAGE(relkind))
7343 : {
7344 : /*
7345 : * For an identity column, we can't use build_column_default(),
7346 : * because the sequence ownership isn't set yet. So do it manually.
7347 : */
7348 2048 : if (colDef->identity)
7349 : {
7350 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7351 :
7352 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7353 42 : nve->typeId = attribute->atttypid;
7354 :
7355 42 : defval = (Expr *) nve;
7356 :
7357 : /* must do a rewrite for identity columns */
7358 42 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7359 : }
7360 : else
7361 2006 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7362 :
7363 2048 : if (!defval && DomainHasConstraints(attribute->atttypid))
7364 : {
7365 : Oid baseTypeId;
7366 : int32 baseTypeMod;
7367 : Oid baseTypeColl;
7368 :
7369 6 : baseTypeMod = attribute->atttypmod;
7370 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7371 6 : baseTypeColl = get_typcollation(baseTypeId);
7372 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7373 6 : defval = (Expr *) coerce_to_target_type(NULL,
7374 : (Node *) defval,
7375 : baseTypeId,
7376 : attribute->atttypid,
7377 : attribute->atttypmod,
7378 : COERCION_ASSIGNMENT,
7379 : COERCE_IMPLICIT_CAST,
7380 : -1);
7381 6 : if (defval == NULL) /* should not happen */
7382 0 : elog(ERROR, "failed to coerce base type to domain");
7383 : }
7384 :
7385 2048 : if (defval)
7386 : {
7387 : NewColumnValue *newval;
7388 :
7389 606 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7390 606 : newval->attnum = attribute->attnum;
7391 606 : newval->expr = expression_planner(defval);
7392 606 : newval->is_generated = (colDef->generated != '\0');
7393 :
7394 606 : tab->newvals = lappend(tab->newvals, newval);
7395 : }
7396 :
7397 2048 : if (DomainHasConstraints(attribute->atttypid))
7398 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7399 :
7400 2048 : if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7401 : {
7402 : /*
7403 : * If the new column is NOT NULL, and there is no missing value,
7404 : * tell Phase 3 it needs to check for NULLs.
7405 : */
7406 1598 : tab->verify_new_notnull |= colDef->is_not_null;
7407 : }
7408 : }
7409 :
7410 : /*
7411 : * Add needed dependency entries for the new column.
7412 : */
7413 2400 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7414 2400 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7415 :
7416 : /*
7417 : * Propagate to children as appropriate. Unlike most other ALTER
7418 : * routines, we have to do this one level of recursion at a time; we can't
7419 : * use find_all_inheritors to do it in one pass.
7420 : */
7421 : children =
7422 2400 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7423 :
7424 : /*
7425 : * If we are told not to recurse, there had better not be any child
7426 : * tables; else the addition would put them out of step.
7427 : */
7428 2400 : if (children && !recurse)
7429 12 : ereport(ERROR,
7430 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7431 : errmsg("column must be added to child tables too")));
7432 :
7433 : /* Children should see column as singly inherited */
7434 2388 : if (!recursing)
7435 : {
7436 1800 : childcmd = copyObject(*cmd);
7437 1800 : colDef = castNode(ColumnDef, childcmd->def);
7438 1800 : colDef->inhcount = 1;
7439 1800 : colDef->is_local = false;
7440 : }
7441 : else
7442 588 : childcmd = *cmd; /* no need to copy again */
7443 :
7444 3024 : foreach(child, children)
7445 : {
7446 636 : Oid childrelid = lfirst_oid(child);
7447 : Relation childrel;
7448 : AlteredTableInfo *childtab;
7449 :
7450 : /* find_inheritance_children already got lock */
7451 636 : childrel = table_open(childrelid, NoLock);
7452 636 : CheckTableNotInUse(childrel, "ALTER TABLE");
7453 :
7454 : /* Find or create work queue entry for this table */
7455 636 : childtab = ATGetQueueEntry(wqueue, childrel);
7456 :
7457 : /* Recurse to child; return value is ignored */
7458 636 : ATExecAddColumn(wqueue, childtab, childrel,
7459 : &childcmd, recurse, true,
7460 : lockmode, cur_pass, context);
7461 :
7462 636 : table_close(childrel, NoLock);
7463 : }
7464 :
7465 2388 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7466 2388 : return address;
7467 : }
7468 :
7469 : /*
7470 : * If a new or renamed column will collide with the name of an existing
7471 : * column and if_not_exists is false then error out, else do nothing.
7472 : */
7473 : static bool
7474 2982 : check_for_column_name_collision(Relation rel, const char *colname,
7475 : bool if_not_exists)
7476 : {
7477 : HeapTuple attTuple;
7478 : int attnum;
7479 :
7480 : /*
7481 : * this test is deliberately not attisdropped-aware, since if one tries to
7482 : * add a column matching a dropped column name, it's gonna fail anyway.
7483 : */
7484 2982 : attTuple = SearchSysCache2(ATTNAME,
7485 : ObjectIdGetDatum(RelationGetRelid(rel)),
7486 : PointerGetDatum(colname));
7487 2982 : if (!HeapTupleIsValid(attTuple))
7488 2886 : return true;
7489 :
7490 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7491 96 : ReleaseSysCache(attTuple);
7492 :
7493 : /*
7494 : * We throw a different error message for conflicts with system column
7495 : * names, since they are normally not shown and the user might otherwise
7496 : * be confused about the reason for the conflict.
7497 : */
7498 96 : if (attnum <= 0)
7499 12 : ereport(ERROR,
7500 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7501 : errmsg("column name \"%s\" conflicts with a system column name",
7502 : colname)));
7503 : else
7504 : {
7505 84 : if (if_not_exists)
7506 : {
7507 54 : ereport(NOTICE,
7508 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7509 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7510 : colname, RelationGetRelationName(rel))));
7511 54 : return false;
7512 : }
7513 :
7514 30 : ereport(ERROR,
7515 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7516 : errmsg("column \"%s\" of relation \"%s\" already exists",
7517 : colname, RelationGetRelationName(rel))));
7518 : }
7519 :
7520 : return true;
7521 : }
7522 :
7523 : /*
7524 : * Install a column's dependency on its datatype.
7525 : */
7526 : static void
7527 3312 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7528 : {
7529 : ObjectAddress myself,
7530 : referenced;
7531 :
7532 3312 : myself.classId = RelationRelationId;
7533 3312 : myself.objectId = relid;
7534 3312 : myself.objectSubId = attnum;
7535 3312 : referenced.classId = TypeRelationId;
7536 3312 : referenced.objectId = typid;
7537 3312 : referenced.objectSubId = 0;
7538 3312 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7539 3312 : }
7540 :
7541 : /*
7542 : * Install a column's dependency on its collation.
7543 : */
7544 : static void
7545 3312 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7546 : {
7547 : ObjectAddress myself,
7548 : referenced;
7549 :
7550 : /* We know the default collation is pinned, so don't bother recording it */
7551 3312 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7552 : {
7553 18 : myself.classId = RelationRelationId;
7554 18 : myself.objectId = relid;
7555 18 : myself.objectSubId = attnum;
7556 18 : referenced.classId = CollationRelationId;
7557 18 : referenced.objectId = collid;
7558 18 : referenced.objectSubId = 0;
7559 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7560 : }
7561 3312 : }
7562 :
7563 : /*
7564 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7565 : *
7566 : * Return the address of the modified column. If the column was already
7567 : * nullable, InvalidObjectAddress is returned.
7568 : */
7569 : static ObjectAddress
7570 256 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7571 : LOCKMODE lockmode)
7572 : {
7573 : HeapTuple tuple;
7574 : HeapTuple conTup;
7575 : Form_pg_attribute attTup;
7576 : AttrNumber attnum;
7577 : Relation attr_rel;
7578 : ObjectAddress address;
7579 : List *readyRels;
7580 :
7581 : /*
7582 : * lookup the attribute
7583 : */
7584 256 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7585 :
7586 256 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7587 256 : if (!HeapTupleIsValid(tuple))
7588 18 : ereport(ERROR,
7589 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7590 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7591 : colName, RelationGetRelationName(rel))));
7592 238 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7593 238 : attnum = attTup->attnum;
7594 238 : ObjectAddressSubSet(address, RelationRelationId,
7595 : RelationGetRelid(rel), attnum);
7596 :
7597 : /* If the column is already nullable there's nothing to do. */
7598 238 : if (!attTup->attnotnull)
7599 : {
7600 6 : table_close(attr_rel, RowExclusiveLock);
7601 6 : return InvalidObjectAddress;
7602 : }
7603 :
7604 : /* Prevent them from altering a system attribute */
7605 232 : if (attnum <= 0)
7606 0 : ereport(ERROR,
7607 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7608 : errmsg("cannot alter system column \"%s\"",
7609 : colName)));
7610 :
7611 232 : if (attTup->attidentity)
7612 18 : ereport(ERROR,
7613 : (errcode(ERRCODE_SYNTAX_ERROR),
7614 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7615 : colName, RelationGetRelationName(rel))));
7616 :
7617 : /*
7618 : * It's not OK to remove a constraint only for the parent and leave it in
7619 : * the children, so disallow that.
7620 : */
7621 214 : if (!recurse)
7622 : {
7623 12 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7624 : {
7625 : PartitionDesc partdesc;
7626 :
7627 12 : partdesc = RelationGetPartitionDesc(rel, true);
7628 :
7629 12 : if (partdesc->nparts > 0)
7630 6 : ereport(ERROR,
7631 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7632 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7633 : errhint("Do not specify the ONLY keyword."));
7634 : }
7635 0 : else if (rel->rd_rel->relhassubclass &&
7636 0 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
7637 : {
7638 0 : ereport(ERROR,
7639 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7640 : errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7641 : colName),
7642 : errhint("Do not specify the ONLY keyword."));
7643 : }
7644 : }
7645 :
7646 : /*
7647 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7648 : */
7649 208 : if (rel->rd_rel->relispartition)
7650 : {
7651 18 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7652 18 : Relation parent = table_open(parentId, AccessShareLock);
7653 18 : TupleDesc tupDesc = RelationGetDescr(parent);
7654 : AttrNumber parent_attnum;
7655 :
7656 18 : parent_attnum = get_attnum(parentId, colName);
7657 18 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7658 18 : ereport(ERROR,
7659 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7660 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7661 : colName)));
7662 0 : table_close(parent, AccessShareLock);
7663 : }
7664 :
7665 : /*
7666 : * Find the constraint that makes this column NOT NULL, and drop it if we
7667 : * see one. dropconstraint_internal() will do necessary consistency
7668 : * checking. If there isn't one, there are two possibilities: either the
7669 : * column is marked attnotnull because it's part of the primary key, and
7670 : * then we just throw an appropriate error; or it's a leftover marking
7671 : * that we can remove. However, before doing the latter, to avoid
7672 : * breaking consistency any further, prevent this if the column is part of
7673 : * the replica identity.
7674 : */
7675 190 : conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
7676 190 : if (conTup == NULL)
7677 : {
7678 : Bitmapset *pkcols;
7679 : Bitmapset *ircols;
7680 :
7681 : /*
7682 : * If the column is in a primary key, throw a specific error message.
7683 : */
7684 30 : pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
7685 30 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
7686 : pkcols))
7687 12 : ereport(ERROR,
7688 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7689 : errmsg("column \"%s\" is in a primary key", colName));
7690 :
7691 : /* Also throw an error if the column is in the replica identity */
7692 18 : ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
7693 18 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
7694 6 : ereport(ERROR,
7695 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7696 : errmsg("column \"%s\" is in index used as replica identity",
7697 : get_attname(RelationGetRelid(rel), attnum, false)));
7698 :
7699 : /* Otherwise, just remove the attnotnull marking and do nothing else. */
7700 12 : attTup->attnotnull = false;
7701 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7702 : }
7703 : else
7704 : {
7705 : /* The normal case: we have a pg_constraint row, remove it */
7706 160 : readyRels = NIL;
7707 160 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7708 : false, &readyRels, lockmode);
7709 :
7710 124 : heap_freetuple(conTup);
7711 : }
7712 :
7713 136 : InvokeObjectPostAlterHook(RelationRelationId,
7714 : RelationGetRelid(rel), attnum);
7715 :
7716 136 : table_close(attr_rel, RowExclusiveLock);
7717 :
7718 136 : return address;
7719 : }
7720 :
7721 : /*
7722 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7723 : * to verify it; recurses to apply the same to children.
7724 : *
7725 : * When called to alter an existing table, 'wqueue' must be given so that we can
7726 : * queue a check that existing tuples pass the constraint. When called from
7727 : * table creation, 'wqueue' should be passed as NULL.
7728 : *
7729 : * Returns true if the flag was set in any table, otherwise false.
7730 : */
7731 : static bool
7732 23660 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
7733 : LOCKMODE lockmode)
7734 : {
7735 : HeapTuple tuple;
7736 : Form_pg_attribute attForm;
7737 23660 : bool retval = false;
7738 :
7739 : /* Guard against stack overflow due to overly deep inheritance tree. */
7740 23660 : check_stack_depth();
7741 :
7742 23660 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7743 23660 : if (!HeapTupleIsValid(tuple))
7744 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7745 : attnum, RelationGetRelid(rel));
7746 23660 : attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7747 23660 : if (!attForm->attnotnull)
7748 : {
7749 : Relation attr_rel;
7750 :
7751 1786 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7752 :
7753 1786 : attForm->attnotnull = true;
7754 1786 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7755 :
7756 1786 : table_close(attr_rel, RowExclusiveLock);
7757 :
7758 : /*
7759 : * And set up for existing values to be checked, unless another
7760 : * constraint already proves this.
7761 : */
7762 1786 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7763 : {
7764 : AlteredTableInfo *tab;
7765 :
7766 1516 : tab = ATGetQueueEntry(wqueue, rel);
7767 1516 : tab->verify_new_notnull = true;
7768 : }
7769 :
7770 1786 : retval = true;
7771 : }
7772 :
7773 23660 : if (recurse)
7774 : {
7775 : List *children;
7776 : ListCell *lc;
7777 :
7778 : /* Make above update visible, for multiple inheritance cases */
7779 1038 : if (retval)
7780 284 : CommandCounterIncrement();
7781 :
7782 1038 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7783 1230 : foreach(lc, children)
7784 : {
7785 192 : Oid childrelid = lfirst_oid(lc);
7786 : Relation childrel;
7787 : AttrNumber childattno;
7788 :
7789 : /* find_inheritance_children already got lock */
7790 192 : childrel = table_open(childrelid, NoLock);
7791 192 : CheckTableNotInUse(childrel, "ALTER TABLE");
7792 :
7793 192 : childattno = get_attnum(RelationGetRelid(childrel),
7794 192 : get_attname(RelationGetRelid(rel), attnum,
7795 : false));
7796 192 : retval |= set_attnotnull(wqueue, childrel, childattno,
7797 : recurse, lockmode);
7798 192 : table_close(childrel, NoLock);
7799 : }
7800 : }
7801 :
7802 23660 : return retval;
7803 : }
7804 :
7805 : /*
7806 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7807 : *
7808 : * Add a not-null constraint to a single table and its children. Returns
7809 : * the address of the constraint added to the parent relation, if one gets
7810 : * added, or InvalidObjectAddress otherwise.
7811 : *
7812 : * We must recurse to child tables during execution, rather than using
7813 : * ALTER TABLE's normal prep-time recursion.
7814 : */
7815 : static ObjectAddress
7816 496 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7817 : bool recurse, bool recursing, List **readyRels,
7818 : LOCKMODE lockmode)
7819 : {
7820 : HeapTuple tuple;
7821 : Relation constr_rel;
7822 : ScanKeyData skey;
7823 : SysScanDesc conscan;
7824 : AttrNumber attnum;
7825 : ObjectAddress address;
7826 : Constraint *constraint;
7827 : CookedConstraint *ccon;
7828 : List *cooked;
7829 496 : bool is_no_inherit = false;
7830 496 : List *ready = NIL;
7831 :
7832 : /* Guard against stack overflow due to overly deep inheritance tree. */
7833 496 : check_stack_depth();
7834 :
7835 : /*
7836 : * In cases of multiple inheritance, we might visit the same child more
7837 : * than once. In the topmost call, set up a list that we fill with all
7838 : * visited relations, to skip those.
7839 : */
7840 496 : if (readyRels == NULL)
7841 : {
7842 : Assert(!recursing);
7843 366 : readyRels = &ready;
7844 : }
7845 496 : if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7846 6 : return InvalidObjectAddress;
7847 490 : *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7848 :
7849 : /* At top level, permission check was done in ATPrepCmd, else do it */
7850 490 : if (recursing)
7851 : {
7852 124 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7853 : Assert(conName != NULL);
7854 : }
7855 :
7856 490 : attnum = get_attnum(RelationGetRelid(rel), colName);
7857 490 : if (attnum == InvalidAttrNumber)
7858 18 : ereport(ERROR,
7859 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7860 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7861 : colName, RelationGetRelationName(rel))));
7862 :
7863 : /* Prevent them from altering a system attribute */
7864 472 : if (attnum <= 0)
7865 0 : ereport(ERROR,
7866 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7867 : errmsg("cannot alter system column \"%s\"",
7868 : colName)));
7869 :
7870 : /* See if there's already a constraint */
7871 472 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7872 472 : ScanKeyInit(&skey,
7873 : Anum_pg_constraint_conrelid,
7874 : BTEqualStrategyNumber, F_OIDEQ,
7875 : ObjectIdGetDatum(RelationGetRelid(rel)));
7876 472 : conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7877 : NULL, 1, &skey);
7878 :
7879 898 : while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7880 : {
7881 446 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7882 446 : bool changed = false;
7883 : HeapTuple copytup;
7884 :
7885 446 : if (conForm->contype != CONSTRAINT_NOTNULL)
7886 206 : continue;
7887 :
7888 240 : if (extractNotNullColumn(tuple) != attnum)
7889 220 : continue;
7890 :
7891 20 : copytup = heap_copytuple(tuple);
7892 20 : conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7893 :
7894 : /*
7895 : * If we find an appropriate constraint, we're almost done, but just
7896 : * need to change some properties on it: if we're recursing, increment
7897 : * coninhcount; if not, set conislocal if not already set.
7898 : */
7899 20 : if (recursing)
7900 : {
7901 6 : conForm->coninhcount++;
7902 6 : changed = true;
7903 : }
7904 14 : else if (!conForm->conislocal)
7905 : {
7906 0 : conForm->conislocal = true;
7907 0 : changed = true;
7908 : }
7909 :
7910 20 : if (changed)
7911 : {
7912 6 : CatalogTupleUpdate(constr_rel, ©tup->t_self, copytup);
7913 6 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7914 : }
7915 :
7916 20 : systable_endscan(conscan);
7917 20 : table_close(constr_rel, RowExclusiveLock);
7918 :
7919 20 : if (changed)
7920 6 : return address;
7921 : else
7922 14 : return InvalidObjectAddress;
7923 : }
7924 :
7925 452 : systable_endscan(conscan);
7926 452 : table_close(constr_rel, RowExclusiveLock);
7927 :
7928 : /*
7929 : * If we're asked not to recurse, and children exist, raise an error for
7930 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7931 : * specified.
7932 : */
7933 482 : if (!recurse &&
7934 30 : find_inheritance_children(RelationGetRelid(rel),
7935 : NoLock) != NIL)
7936 : {
7937 24 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7938 12 : ereport(ERROR,
7939 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7940 : errmsg("constraint must be added to child tables too"),
7941 : errhint("Do not specify the ONLY keyword."));
7942 : else
7943 12 : is_no_inherit = true;
7944 : }
7945 :
7946 : /*
7947 : * No constraint exists; we must add one. First determine a name to use,
7948 : * if we haven't already.
7949 : */
7950 440 : if (!recursing)
7951 : {
7952 : Assert(conName == NULL);
7953 322 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7954 : colName, "not_null",
7955 322 : RelationGetNamespace(rel),
7956 : NIL);
7957 : }
7958 440 : constraint = makeNode(Constraint);
7959 440 : constraint->contype = CONSTR_NOTNULL;
7960 440 : constraint->conname = conName;
7961 440 : constraint->deferrable = false;
7962 440 : constraint->initdeferred = false;
7963 440 : constraint->location = -1;
7964 440 : constraint->keys = list_make1(makeString(colName));
7965 440 : constraint->is_no_inherit = is_no_inherit;
7966 440 : constraint->inhcount = recursing ? 1 : 0;
7967 440 : constraint->skip_validation = false;
7968 440 : constraint->initially_valid = true;
7969 :
7970 : /* and do it */
7971 440 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7972 440 : false, !recursing, false, NULL);
7973 440 : ccon = linitial(cooked);
7974 440 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7975 :
7976 440 : InvokeObjectPostAlterHook(RelationRelationId,
7977 : RelationGetRelid(rel), attnum);
7978 :
7979 : /*
7980 : * Mark pg_attribute.attnotnull for the column. Tell that function not to
7981 : * recurse, because we're going to do it here.
7982 : */
7983 440 : set_attnotnull(wqueue, rel, attnum, false, lockmode);
7984 :
7985 : /*
7986 : * Recurse to propagate the constraint to children that don't have one.
7987 : */
7988 440 : if (recurse)
7989 : {
7990 : List *children;
7991 : ListCell *lc;
7992 :
7993 422 : children = find_inheritance_children(RelationGetRelid(rel),
7994 : lockmode);
7995 :
7996 552 : foreach(lc, children)
7997 : {
7998 : Relation childrel;
7999 :
8000 130 : childrel = table_open(lfirst_oid(lc), NoLock);
8001 :
8002 130 : ATExecSetNotNull(wqueue, childrel,
8003 : conName, colName, recurse, true,
8004 : readyRels, lockmode);
8005 :
8006 130 : table_close(childrel, NoLock);
8007 : }
8008 : }
8009 :
8010 440 : return address;
8011 : }
8012 :
8013 : /*
8014 : * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
8015 : *
8016 : * This doesn't exist in the grammar; it's used when creating a
8017 : * primary key and the column is not already marked attnotnull.
8018 : */
8019 : static ObjectAddress
8020 14656 : ATExecSetAttNotNull(List **wqueue, Relation rel,
8021 : const char *colName, LOCKMODE lockmode)
8022 : {
8023 : AttrNumber attnum;
8024 14656 : ObjectAddress address = InvalidObjectAddress;
8025 :
8026 14656 : attnum = get_attnum(RelationGetRelid(rel), colName);
8027 14656 : if (attnum == InvalidAttrNumber)
8028 18 : ereport(ERROR,
8029 : errcode(ERRCODE_UNDEFINED_COLUMN),
8030 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8031 : colName, RelationGetRelationName(rel)));
8032 :
8033 : /*
8034 : * Make the change, if necessary, and only if so report the column as
8035 : * changed
8036 : */
8037 14638 : if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
8038 844 : ObjectAddressSubSet(address, RelationRelationId,
8039 : RelationGetRelid(rel), attnum);
8040 :
8041 14638 : return address;
8042 : }
8043 :
8044 : /*
8045 : * NotNullImpliedByRelConstraints
8046 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8047 : */
8048 : static bool
8049 1566 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8050 : {
8051 1566 : NullTest *nnulltest = makeNode(NullTest);
8052 :
8053 3132 : nnulltest->arg = (Expr *) makeVar(1,
8054 1566 : attr->attnum,
8055 : attr->atttypid,
8056 : attr->atttypmod,
8057 : attr->attcollation,
8058 : 0);
8059 1566 : nnulltest->nulltesttype = IS_NOT_NULL;
8060 :
8061 : /*
8062 : * argisrow = false is correct even for a composite column, because
8063 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8064 : * case, just IS DISTINCT FROM NULL.
8065 : */
8066 1566 : nnulltest->argisrow = false;
8067 1566 : nnulltest->location = -1;
8068 :
8069 1566 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8070 : {
8071 50 : ereport(DEBUG1,
8072 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8073 : RelationGetRelationName(rel), NameStr(attr->attname))));
8074 50 : return true;
8075 : }
8076 :
8077 1516 : return false;
8078 : }
8079 :
8080 : /*
8081 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8082 : *
8083 : * Return the address of the affected column.
8084 : */
8085 : static ObjectAddress
8086 566 : ATExecColumnDefault(Relation rel, const char *colName,
8087 : Node *newDefault, LOCKMODE lockmode)
8088 : {
8089 566 : TupleDesc tupdesc = RelationGetDescr(rel);
8090 : AttrNumber attnum;
8091 : ObjectAddress address;
8092 :
8093 : /*
8094 : * get the number of the attribute
8095 : */
8096 566 : attnum = get_attnum(RelationGetRelid(rel), colName);
8097 566 : if (attnum == InvalidAttrNumber)
8098 30 : ereport(ERROR,
8099 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8100 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8101 : colName, RelationGetRelationName(rel))));
8102 :
8103 : /* Prevent them from altering a system attribute */
8104 536 : if (attnum <= 0)
8105 0 : ereport(ERROR,
8106 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8107 : errmsg("cannot alter system column \"%s\"",
8108 : colName)));
8109 :
8110 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8111 18 : ereport(ERROR,
8112 : (errcode(ERRCODE_SYNTAX_ERROR),
8113 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8114 : colName, RelationGetRelationName(rel)),
8115 : /* translator: %s is an SQL ALTER command */
8116 : newDefault ? 0 : errhint("Use %s instead.",
8117 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8118 :
8119 518 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8120 6 : ereport(ERROR,
8121 : (errcode(ERRCODE_SYNTAX_ERROR),
8122 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8123 : colName, RelationGetRelationName(rel)),
8124 : newDefault ?
8125 : /* translator: %s is an SQL ALTER command */
8126 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8127 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8128 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8129 :
8130 : /*
8131 : * Remove any old default for the column. We use RESTRICT here for
8132 : * safety, but at present we do not expect anything to depend on the
8133 : * default.
8134 : *
8135 : * We treat removing the existing default as an internal operation when it
8136 : * is preparatory to adding a new default, but as a user-initiated
8137 : * operation when the user asked for a drop.
8138 : */
8139 512 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8140 : newDefault != NULL);
8141 :
8142 512 : if (newDefault)
8143 : {
8144 : /* SET DEFAULT */
8145 : RawColumnDefault *rawEnt;
8146 :
8147 338 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8148 338 : rawEnt->attnum = attnum;
8149 338 : rawEnt->raw_default = newDefault;
8150 338 : rawEnt->missingMode = false;
8151 338 : rawEnt->generated = '\0';
8152 :
8153 : /*
8154 : * This function is intended for CREATE TABLE, so it processes a
8155 : * _list_ of defaults, but we just do one.
8156 : */
8157 338 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8158 : false, true, false, NULL);
8159 : }
8160 :
8161 506 : ObjectAddressSubSet(address, RelationRelationId,
8162 : RelationGetRelid(rel), attnum);
8163 506 : return address;
8164 : }
8165 :
8166 : /*
8167 : * Add a pre-cooked default expression.
8168 : *
8169 : * Return the address of the affected column.
8170 : */
8171 : static ObjectAddress
8172 110 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8173 : Node *newDefault)
8174 : {
8175 : ObjectAddress address;
8176 :
8177 : /* We assume no checking is required */
8178 :
8179 : /*
8180 : * Remove any old default for the column. We use RESTRICT here for
8181 : * safety, but at present we do not expect anything to depend on the
8182 : * default. (In ordinary cases, there could not be a default in place
8183 : * anyway, but it's possible when combining LIKE with inheritance.)
8184 : */
8185 110 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8186 : true);
8187 :
8188 110 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8189 :
8190 110 : ObjectAddressSubSet(address, RelationRelationId,
8191 : RelationGetRelid(rel), attnum);
8192 110 : return address;
8193 : }
8194 :
8195 : /*
8196 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8197 : *
8198 : * Return the address of the affected column.
8199 : */
8200 : static ObjectAddress
8201 150 : ATExecAddIdentity(Relation rel, const char *colName,
8202 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8203 : {
8204 : Relation attrelation;
8205 : HeapTuple tuple;
8206 : Form_pg_attribute attTup;
8207 : AttrNumber attnum;
8208 : ObjectAddress address;
8209 150 : ColumnDef *cdef = castNode(ColumnDef, def);
8210 : bool ispartitioned;
8211 :
8212 150 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8213 150 : if (ispartitioned && !recurse)
8214 0 : ereport(ERROR,
8215 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8216 : errmsg("cannot add identity to a column of only the partitioned table"),
8217 : errhint("Do not specify the ONLY keyword.")));
8218 :
8219 150 : if (rel->rd_rel->relispartition && !recursing)
8220 12 : ereport(ERROR,
8221 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8222 : errmsg("cannot add identity to a column of a partition"));
8223 :
8224 138 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8225 :
8226 138 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8227 138 : if (!HeapTupleIsValid(tuple))
8228 0 : ereport(ERROR,
8229 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8230 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8231 : colName, RelationGetRelationName(rel))));
8232 138 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8233 138 : attnum = attTup->attnum;
8234 :
8235 : /* Can't alter a system attribute */
8236 138 : if (attnum <= 0)
8237 0 : ereport(ERROR,
8238 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8239 : errmsg("cannot alter system column \"%s\"",
8240 : colName)));
8241 :
8242 : /*
8243 : * Creating a column as identity implies NOT NULL, so adding the identity
8244 : * to an existing column that is not NOT NULL would create a state that
8245 : * cannot be reproduced without contortions.
8246 : */
8247 138 : if (!attTup->attnotnull)
8248 6 : ereport(ERROR,
8249 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8250 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8251 : colName, RelationGetRelationName(rel))));
8252 :
8253 132 : if (attTup->attidentity)
8254 18 : ereport(ERROR,
8255 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8256 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8257 : colName, RelationGetRelationName(rel))));
8258 :
8259 114 : if (attTup->atthasdef)
8260 6 : ereport(ERROR,
8261 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8262 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8263 : colName, RelationGetRelationName(rel))));
8264 :
8265 108 : attTup->attidentity = cdef->identity;
8266 108 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8267 :
8268 108 : InvokeObjectPostAlterHook(RelationRelationId,
8269 : RelationGetRelid(rel),
8270 : attTup->attnum);
8271 108 : ObjectAddressSubSet(address, RelationRelationId,
8272 : RelationGetRelid(rel), attnum);
8273 108 : heap_freetuple(tuple);
8274 :
8275 108 : table_close(attrelation, RowExclusiveLock);
8276 :
8277 : /*
8278 : * Recurse to propagate the identity column to partitions. Identity is
8279 : * not inherited in regular inheritance children.
8280 : */
8281 108 : if (recurse && ispartitioned)
8282 : {
8283 : List *children;
8284 : ListCell *lc;
8285 :
8286 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8287 :
8288 16 : foreach(lc, children)
8289 : {
8290 : Relation childrel;
8291 :
8292 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8293 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8294 6 : table_close(childrel, NoLock);
8295 : }
8296 : }
8297 :
8298 108 : return address;
8299 : }
8300 :
8301 : /*
8302 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8303 : *
8304 : * Return the address of the affected column.
8305 : */
8306 : static ObjectAddress
8307 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8308 : LOCKMODE lockmode, bool recurse, bool recursing)
8309 : {
8310 : ListCell *option;
8311 74 : DefElem *generatedEl = NULL;
8312 : HeapTuple tuple;
8313 : Form_pg_attribute attTup;
8314 : AttrNumber attnum;
8315 : Relation attrelation;
8316 : ObjectAddress address;
8317 : bool ispartitioned;
8318 :
8319 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8320 74 : if (ispartitioned && !recurse)
8321 6 : ereport(ERROR,
8322 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8323 : errmsg("cannot change identity column of only the partitioned table"),
8324 : errhint("Do not specify the ONLY keyword.")));
8325 :
8326 68 : if (rel->rd_rel->relispartition && !recursing)
8327 12 : ereport(ERROR,
8328 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8329 : errmsg("cannot change identity column of a partition"));
8330 :
8331 100 : foreach(option, castNode(List, def))
8332 : {
8333 44 : DefElem *defel = lfirst_node(DefElem, option);
8334 :
8335 44 : if (strcmp(defel->defname, "generated") == 0)
8336 : {
8337 44 : if (generatedEl)
8338 0 : ereport(ERROR,
8339 : (errcode(ERRCODE_SYNTAX_ERROR),
8340 : errmsg("conflicting or redundant options")));
8341 44 : generatedEl = defel;
8342 : }
8343 : else
8344 0 : elog(ERROR, "option \"%s\" not recognized",
8345 : defel->defname);
8346 : }
8347 :
8348 : /*
8349 : * Even if there is nothing to change here, we run all the checks. There
8350 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8351 : * there.
8352 : */
8353 :
8354 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8355 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8356 56 : if (!HeapTupleIsValid(tuple))
8357 0 : ereport(ERROR,
8358 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8359 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8360 : colName, RelationGetRelationName(rel))));
8361 :
8362 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8363 56 : attnum = attTup->attnum;
8364 :
8365 56 : if (attnum <= 0)
8366 0 : ereport(ERROR,
8367 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8368 : errmsg("cannot alter system column \"%s\"",
8369 : colName)));
8370 :
8371 56 : if (!attTup->attidentity)
8372 6 : ereport(ERROR,
8373 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8374 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8375 : colName, RelationGetRelationName(rel))));
8376 :
8377 50 : if (generatedEl)
8378 : {
8379 44 : attTup->attidentity = defGetInt32(generatedEl);
8380 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8381 :
8382 44 : InvokeObjectPostAlterHook(RelationRelationId,
8383 : RelationGetRelid(rel),
8384 : attTup->attnum);
8385 44 : ObjectAddressSubSet(address, RelationRelationId,
8386 : RelationGetRelid(rel), attnum);
8387 : }
8388 : else
8389 6 : address = InvalidObjectAddress;
8390 :
8391 50 : heap_freetuple(tuple);
8392 50 : table_close(attrelation, RowExclusiveLock);
8393 :
8394 : /*
8395 : * Recurse to propagate the identity change to partitions. Identity is not
8396 : * inherited in regular inheritance children.
8397 : */
8398 50 : if (generatedEl && recurse && ispartitioned)
8399 : {
8400 : List *children;
8401 : ListCell *lc;
8402 :
8403 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8404 :
8405 18 : foreach(lc, children)
8406 : {
8407 : Relation childrel;
8408 :
8409 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8410 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8411 12 : table_close(childrel, NoLock);
8412 : }
8413 : }
8414 :
8415 50 : return address;
8416 : }
8417 :
8418 : /*
8419 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8420 : *
8421 : * Return the address of the affected column.
8422 : */
8423 : static ObjectAddress
8424 98 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8425 : bool recurse, bool recursing)
8426 : {
8427 : HeapTuple tuple;
8428 : Form_pg_attribute attTup;
8429 : AttrNumber attnum;
8430 : Relation attrelation;
8431 : ObjectAddress address;
8432 : Oid seqid;
8433 : ObjectAddress seqaddress;
8434 : bool ispartitioned;
8435 :
8436 98 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8437 98 : if (ispartitioned && !recurse)
8438 6 : ereport(ERROR,
8439 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8440 : errmsg("cannot drop identity from a column of only the partitioned table"),
8441 : errhint("Do not specify the ONLY keyword.")));
8442 :
8443 92 : if (rel->rd_rel->relispartition && !recursing)
8444 6 : ereport(ERROR,
8445 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8446 : errmsg("cannot drop identity from a column of a partition"));
8447 :
8448 86 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8449 86 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8450 86 : if (!HeapTupleIsValid(tuple))
8451 0 : ereport(ERROR,
8452 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8453 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8454 : colName, RelationGetRelationName(rel))));
8455 :
8456 86 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8457 86 : attnum = attTup->attnum;
8458 :
8459 86 : if (attnum <= 0)
8460 0 : ereport(ERROR,
8461 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8462 : errmsg("cannot alter system column \"%s\"",
8463 : colName)));
8464 :
8465 86 : if (!attTup->attidentity)
8466 : {
8467 12 : if (!missing_ok)
8468 6 : ereport(ERROR,
8469 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8470 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8471 : colName, RelationGetRelationName(rel))));
8472 : else
8473 : {
8474 6 : ereport(NOTICE,
8475 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8476 : colName, RelationGetRelationName(rel))));
8477 6 : heap_freetuple(tuple);
8478 6 : table_close(attrelation, RowExclusiveLock);
8479 6 : return InvalidObjectAddress;
8480 : }
8481 : }
8482 :
8483 74 : attTup->attidentity = '\0';
8484 74 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8485 :
8486 74 : InvokeObjectPostAlterHook(RelationRelationId,
8487 : RelationGetRelid(rel),
8488 : attTup->attnum);
8489 74 : ObjectAddressSubSet(address, RelationRelationId,
8490 : RelationGetRelid(rel), attnum);
8491 74 : heap_freetuple(tuple);
8492 :
8493 74 : table_close(attrelation, RowExclusiveLock);
8494 :
8495 : /*
8496 : * Recurse to drop the identity from column in partitions. Identity is
8497 : * not inherited in regular inheritance children so ignore them.
8498 : */
8499 74 : if (recurse && ispartitioned)
8500 : {
8501 : List *children;
8502 : ListCell *lc;
8503 :
8504 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8505 :
8506 12 : foreach(lc, children)
8507 : {
8508 : Relation childrel;
8509 :
8510 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8511 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8512 6 : table_close(childrel, NoLock);
8513 : }
8514 : }
8515 :
8516 74 : if (!recursing)
8517 : {
8518 : /* drop the internal sequence */
8519 38 : seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
8520 38 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8521 : RelationRelationId, DEPENDENCY_INTERNAL);
8522 38 : CommandCounterIncrement();
8523 38 : seqaddress.classId = RelationRelationId;
8524 38 : seqaddress.objectId = seqid;
8525 38 : seqaddress.objectSubId = 0;
8526 38 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8527 : }
8528 :
8529 74 : return address;
8530 : }
8531 :
8532 : /*
8533 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8534 : *
8535 : * Return the address of the affected column.
8536 : */
8537 : static ObjectAddress
8538 84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8539 : Node *newExpr, LOCKMODE lockmode)
8540 : {
8541 : HeapTuple tuple;
8542 : Form_pg_attribute attTup;
8543 : AttrNumber attnum;
8544 : Oid attrdefoid;
8545 : ObjectAddress address;
8546 : Expr *defval;
8547 : NewColumnValue *newval;
8548 : RawColumnDefault *rawEnt;
8549 :
8550 84 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8551 84 : if (!HeapTupleIsValid(tuple))
8552 0 : ereport(ERROR,
8553 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8554 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8555 : colName, RelationGetRelationName(rel))));
8556 :
8557 84 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8558 84 : attnum = attTup->attnum;
8559 :
8560 84 : if (attnum <= 0)
8561 0 : ereport(ERROR,
8562 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8563 : errmsg("cannot alter system column \"%s\"",
8564 : colName)));
8565 :
8566 84 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8567 6 : ereport(ERROR,
8568 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8569 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8570 : colName, RelationGetRelationName(rel))));
8571 78 : ReleaseSysCache(tuple);
8572 :
8573 : /*
8574 : * Clear all the missing values if we're rewriting the table, since this
8575 : * renders them pointless.
8576 : */
8577 78 : RelationClearMissing(rel);
8578 :
8579 : /* make sure we don't conflict with later attribute modifications */
8580 78 : CommandCounterIncrement();
8581 :
8582 : /*
8583 : * Find everything that depends on the column (constraints, indexes, etc),
8584 : * and record enough information to let us recreate the objects after
8585 : * rewrite.
8586 : */
8587 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8588 :
8589 : /*
8590 : * Drop the dependency records of the GENERATED expression, in particular
8591 : * its INTERNAL dependency on the column, which would otherwise cause
8592 : * dependency.c to refuse to perform the deletion.
8593 : */
8594 78 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8595 78 : if (!OidIsValid(attrdefoid))
8596 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8597 : RelationGetRelid(rel), attnum);
8598 78 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8599 :
8600 : /* Make above changes visible */
8601 78 : CommandCounterIncrement();
8602 :
8603 : /*
8604 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8605 : * safety, but at present we do not expect anything to depend on the
8606 : * expression.
8607 : */
8608 78 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8609 : false, false);
8610 :
8611 : /* Prepare to store the new expression, in the catalogs */
8612 78 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8613 78 : rawEnt->attnum = attnum;
8614 78 : rawEnt->raw_default = newExpr;
8615 78 : rawEnt->missingMode = false;
8616 78 : rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8617 :
8618 : /* Store the generated expression */
8619 78 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8620 : false, true, false, NULL);
8621 :
8622 : /* Make above new expression visible */
8623 78 : CommandCounterIncrement();
8624 :
8625 : /* Prepare for table rewrite */
8626 78 : defval = (Expr *) build_column_default(rel, attnum);
8627 :
8628 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8629 78 : newval->attnum = attnum;
8630 78 : newval->expr = expression_planner(defval);
8631 78 : newval->is_generated = true;
8632 :
8633 78 : tab->newvals = lappend(tab->newvals, newval);
8634 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8635 :
8636 : /* Drop any pg_statistic entry for the column */
8637 78 : RemoveStatistics(RelationGetRelid(rel), attnum);
8638 :
8639 78 : InvokeObjectPostAlterHook(RelationRelationId,
8640 : RelationGetRelid(rel), attnum);
8641 :
8642 78 : ObjectAddressSubSet(address, RelationRelationId,
8643 : RelationGetRelid(rel), attnum);
8644 78 : return address;
8645 : }
8646 :
8647 : /*
8648 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8649 : */
8650 : static void
8651 44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8652 : {
8653 : /*
8654 : * Reject ONLY if there are child tables. We could implement this, but it
8655 : * is a bit complicated. GENERATED clauses must be attached to the column
8656 : * definition and cannot be added later like DEFAULT, so if a child table
8657 : * has a generation expression that the parent does not have, the child
8658 : * column will necessarily be an attislocal column. So to implement ONLY
8659 : * here, we'd need extra code to update attislocal of the direct child
8660 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8661 : * resulting state can be properly dumped and restored.
8662 : */
8663 56 : if (!recurse &&
8664 12 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8665 6 : ereport(ERROR,
8666 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8667 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8668 :
8669 : /*
8670 : * Cannot drop generation expression from inherited columns.
8671 : */
8672 38 : if (!recursing)
8673 : {
8674 : HeapTuple tuple;
8675 : Form_pg_attribute attTup;
8676 :
8677 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8678 32 : if (!HeapTupleIsValid(tuple))
8679 0 : ereport(ERROR,
8680 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8681 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8682 : cmd->name, RelationGetRelationName(rel))));
8683 :
8684 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8685 :
8686 32 : if (attTup->attinhcount > 0)
8687 6 : ereport(ERROR,
8688 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8689 : errmsg("cannot drop generation expression from inherited column")));
8690 : }
8691 32 : }
8692 :
8693 : /*
8694 : * Return the address of the affected column.
8695 : */
8696 : static ObjectAddress
8697 32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8698 : {
8699 : HeapTuple tuple;
8700 : Form_pg_attribute attTup;
8701 : AttrNumber attnum;
8702 : Relation attrelation;
8703 : Oid attrdefoid;
8704 : ObjectAddress address;
8705 :
8706 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8707 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8708 32 : if (!HeapTupleIsValid(tuple))
8709 0 : ereport(ERROR,
8710 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8711 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8712 : colName, RelationGetRelationName(rel))));
8713 :
8714 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8715 32 : attnum = attTup->attnum;
8716 :
8717 32 : if (attnum <= 0)
8718 0 : ereport(ERROR,
8719 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8720 : errmsg("cannot alter system column \"%s\"",
8721 : colName)));
8722 :
8723 32 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8724 : {
8725 12 : if (!missing_ok)
8726 6 : ereport(ERROR,
8727 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8728 : errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8729 : colName, RelationGetRelationName(rel))));
8730 : else
8731 : {
8732 6 : ereport(NOTICE,
8733 : (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8734 : colName, RelationGetRelationName(rel))));
8735 6 : heap_freetuple(tuple);
8736 6 : table_close(attrelation, RowExclusiveLock);
8737 6 : return InvalidObjectAddress;
8738 : }
8739 : }
8740 :
8741 : /*
8742 : * Mark the column as no longer generated. (The atthasdef flag needs to
8743 : * get cleared too, but RemoveAttrDefault will handle that.)
8744 : */
8745 20 : attTup->attgenerated = '\0';
8746 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8747 :
8748 20 : InvokeObjectPostAlterHook(RelationRelationId,
8749 : RelationGetRelid(rel),
8750 : attnum);
8751 20 : heap_freetuple(tuple);
8752 :
8753 20 : table_close(attrelation, RowExclusiveLock);
8754 :
8755 : /*
8756 : * Drop the dependency records of the GENERATED expression, in particular
8757 : * its INTERNAL dependency on the column, which would otherwise cause
8758 : * dependency.c to refuse to perform the deletion.
8759 : */
8760 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8761 20 : if (!OidIsValid(attrdefoid))
8762 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8763 : RelationGetRelid(rel), attnum);
8764 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8765 :
8766 : /* Make above changes visible */
8767 20 : CommandCounterIncrement();
8768 :
8769 : /*
8770 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8771 : * safety, but at present we do not expect anything to depend on the
8772 : * default.
8773 : */
8774 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8775 : false, false);
8776 :
8777 20 : ObjectAddressSubSet(address, RelationRelationId,
8778 : RelationGetRelid(rel), attnum);
8779 20 : return address;
8780 : }
8781 :
8782 : /*
8783 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8784 : *
8785 : * Return value is the address of the modified column
8786 : */
8787 : static ObjectAddress
8788 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8789 : {
8790 164 : int newtarget = 0;
8791 : bool newtarget_default;
8792 : Relation attrelation;
8793 : HeapTuple tuple,
8794 : newtuple;
8795 : Form_pg_attribute attrtuple;
8796 : AttrNumber attnum;
8797 : ObjectAddress address;
8798 : Datum repl_val[Natts_pg_attribute];
8799 : bool repl_null[Natts_pg_attribute];
8800 : bool repl_repl[Natts_pg_attribute];
8801 :
8802 : /*
8803 : * We allow referencing columns by numbers only for indexes, since table
8804 : * column numbers could contain gaps if columns are later dropped.
8805 : */
8806 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8807 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8808 : !colName)
8809 0 : ereport(ERROR,
8810 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8811 : errmsg("cannot refer to non-index column by number")));
8812 :
8813 : /* -1 was used in previous versions for the default setting */
8814 164 : if (newValue && intVal(newValue) != -1)
8815 : {
8816 120 : newtarget = intVal(newValue);
8817 120 : newtarget_default = false;
8818 : }
8819 : else
8820 44 : newtarget_default = true;
8821 :
8822 164 : if (!newtarget_default)
8823 : {
8824 : /*
8825 : * Limit target to a sane range
8826 : */
8827 120 : if (newtarget < 0)
8828 : {
8829 0 : ereport(ERROR,
8830 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8831 : errmsg("statistics target %d is too low",
8832 : newtarget)));
8833 : }
8834 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8835 : {
8836 0 : newtarget = MAX_STATISTICS_TARGET;
8837 0 : ereport(WARNING,
8838 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8839 : errmsg("lowering statistics target to %d",
8840 : newtarget)));
8841 : }
8842 : }
8843 :
8844 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8845 :
8846 164 : if (colName)
8847 : {
8848 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8849 :
8850 100 : if (!HeapTupleIsValid(tuple))
8851 12 : ereport(ERROR,
8852 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8853 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8854 : colName, RelationGetRelationName(rel))));
8855 : }
8856 : else
8857 : {
8858 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8859 :
8860 64 : if (!HeapTupleIsValid(tuple))
8861 12 : ereport(ERROR,
8862 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8863 : errmsg("column number %d of relation \"%s\" does not exist",
8864 : colNum, RelationGetRelationName(rel))));
8865 : }
8866 :
8867 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8868 :
8869 140 : attnum = attrtuple->attnum;
8870 140 : if (attnum <= 0)
8871 0 : ereport(ERROR,
8872 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8873 : errmsg("cannot alter system column \"%s\"",
8874 : colName)));
8875 :
8876 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8877 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8878 : {
8879 52 : if (attnum > rel->rd_index->indnkeyatts)
8880 6 : ereport(ERROR,
8881 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8882 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8883 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8884 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8885 18 : ereport(ERROR,
8886 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8887 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8888 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8889 : errhint("Alter statistics on table column instead.")));
8890 : }
8891 :
8892 : /* Build new tuple. */
8893 116 : memset(repl_null, false, sizeof(repl_null));
8894 116 : memset(repl_repl, false, sizeof(repl_repl));
8895 116 : if (!newtarget_default)
8896 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8897 : else
8898 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8899 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8900 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8901 : repl_val, repl_null, repl_repl);
8902 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8903 :
8904 116 : InvokeObjectPostAlterHook(RelationRelationId,
8905 : RelationGetRelid(rel),
8906 : attrtuple->attnum);
8907 116 : ObjectAddressSubSet(address, RelationRelationId,
8908 : RelationGetRelid(rel), attnum);
8909 :
8910 116 : heap_freetuple(newtuple);
8911 :
8912 116 : ReleaseSysCache(tuple);
8913 :
8914 116 : table_close(attrelation, RowExclusiveLock);
8915 :
8916 116 : return address;
8917 : }
8918 :
8919 : /*
8920 : * Return value is the address of the modified column
8921 : */
8922 : static ObjectAddress
8923 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8924 : bool isReset, LOCKMODE lockmode)
8925 : {
8926 : Relation attrelation;
8927 : HeapTuple tuple,
8928 : newtuple;
8929 : Form_pg_attribute attrtuple;
8930 : AttrNumber attnum;
8931 : Datum datum,
8932 : newOptions;
8933 : bool isnull;
8934 : ObjectAddress address;
8935 : Datum repl_val[Natts_pg_attribute];
8936 : bool repl_null[Natts_pg_attribute];
8937 : bool repl_repl[Natts_pg_attribute];
8938 :
8939 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8940 :
8941 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8942 :
8943 32 : if (!HeapTupleIsValid(tuple))
8944 0 : ereport(ERROR,
8945 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8946 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8947 : colName, RelationGetRelationName(rel))));
8948 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8949 :
8950 32 : attnum = attrtuple->attnum;
8951 32 : if (attnum <= 0)
8952 0 : ereport(ERROR,
8953 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8954 : errmsg("cannot alter system column \"%s\"",
8955 : colName)));
8956 :
8957 : /* Generate new proposed attoptions (text array) */
8958 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8959 : &isnull);
8960 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8961 : castNode(List, options), NULL, NULL,
8962 : false, isReset);
8963 : /* Validate new options */
8964 32 : (void) attribute_reloptions(newOptions, true);
8965 :
8966 : /* Build new tuple. */
8967 32 : memset(repl_null, false, sizeof(repl_null));
8968 32 : memset(repl_repl, false, sizeof(repl_repl));
8969 32 : if (newOptions != (Datum) 0)
8970 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8971 : else
8972 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8973 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8974 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8975 : repl_val, repl_null, repl_repl);
8976 :
8977 : /* Update system catalog. */
8978 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8979 :
8980 32 : InvokeObjectPostAlterHook(RelationRelationId,
8981 : RelationGetRelid(rel),
8982 : attrtuple->attnum);
8983 32 : ObjectAddressSubSet(address, RelationRelationId,
8984 : RelationGetRelid(rel), attnum);
8985 :
8986 32 : heap_freetuple(newtuple);
8987 :
8988 32 : ReleaseSysCache(tuple);
8989 :
8990 32 : table_close(attrelation, RowExclusiveLock);
8991 :
8992 32 : return address;
8993 : }
8994 :
8995 : /*
8996 : * Helper function for ATExecSetStorage and ATExecSetCompression
8997 : *
8998 : * Set the attstorage and/or attcompression fields for index columns
8999 : * associated with the specified table column.
9000 : */
9001 : static void
9002 282 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9003 : AttrNumber attnum,
9004 : bool setstorage, char newstorage,
9005 : bool setcompression, char newcompression,
9006 : LOCKMODE lockmode)
9007 : {
9008 : ListCell *lc;
9009 :
9010 354 : foreach(lc, RelationGetIndexList(rel))
9011 : {
9012 72 : Oid indexoid = lfirst_oid(lc);
9013 : Relation indrel;
9014 72 : AttrNumber indattnum = 0;
9015 : HeapTuple tuple;
9016 :
9017 72 : indrel = index_open(indexoid, lockmode);
9018 :
9019 120 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9020 : {
9021 78 : if (indrel->rd_index->indkey.values[i] == attnum)
9022 : {
9023 30 : indattnum = i + 1;
9024 30 : break;
9025 : }
9026 : }
9027 :
9028 72 : if (indattnum == 0)
9029 : {
9030 42 : index_close(indrel, lockmode);
9031 42 : continue;
9032 : }
9033 :
9034 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9035 :
9036 30 : if (HeapTupleIsValid(tuple))
9037 : {
9038 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9039 :
9040 30 : if (setstorage)
9041 24 : attrtuple->attstorage = newstorage;
9042 :
9043 30 : if (setcompression)
9044 6 : attrtuple->attcompression = newcompression;
9045 :
9046 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9047 :
9048 30 : InvokeObjectPostAlterHook(RelationRelationId,
9049 : RelationGetRelid(rel),
9050 : attrtuple->attnum);
9051 :
9052 30 : heap_freetuple(tuple);
9053 : }
9054 :
9055 30 : index_close(indrel, lockmode);
9056 : }
9057 282 : }
9058 :
9059 : /*
9060 : * ALTER TABLE ALTER COLUMN SET STORAGE
9061 : *
9062 : * Return value is the address of the modified column
9063 : */
9064 : static ObjectAddress
9065 234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9066 : {
9067 : Relation attrelation;
9068 : HeapTuple tuple;
9069 : Form_pg_attribute attrtuple;
9070 : AttrNumber attnum;
9071 : ObjectAddress address;
9072 :
9073 234 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9074 :
9075 234 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9076 :
9077 234 : if (!HeapTupleIsValid(tuple))
9078 12 : ereport(ERROR,
9079 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9080 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9081 : colName, RelationGetRelationName(rel))));
9082 222 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9083 :
9084 222 : attnum = attrtuple->attnum;
9085 222 : if (attnum <= 0)
9086 0 : ereport(ERROR,
9087 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9088 : errmsg("cannot alter system column \"%s\"",
9089 : colName)));
9090 :
9091 222 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9092 :
9093 222 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9094 :
9095 222 : InvokeObjectPostAlterHook(RelationRelationId,
9096 : RelationGetRelid(rel),
9097 : attrtuple->attnum);
9098 :
9099 : /*
9100 : * Apply the change to indexes as well (only for simple index columns,
9101 : * matching behavior of index.c ConstructTupleDescriptor()).
9102 : */
9103 222 : SetIndexStorageProperties(rel, attrelation, attnum,
9104 222 : true, attrtuple->attstorage,
9105 : false, 0,
9106 : lockmode);
9107 :
9108 222 : heap_freetuple(tuple);
9109 :
9110 222 : table_close(attrelation, RowExclusiveLock);
9111 :
9112 222 : ObjectAddressSubSet(address, RelationRelationId,
9113 : RelationGetRelid(rel), attnum);
9114 222 : return address;
9115 : }
9116 :
9117 :
9118 : /*
9119 : * ALTER TABLE DROP COLUMN
9120 : *
9121 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9122 : * because we have to decide at runtime whether to recurse or not depending
9123 : * on whether attinhcount goes to zero or not. (We can't check this in a
9124 : * static pre-pass because it won't handle multiple inheritance situations
9125 : * correctly.)
9126 : */
9127 : static void
9128 1618 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9129 : AlterTableCmd *cmd, LOCKMODE lockmode,
9130 : AlterTableUtilityContext *context)
9131 : {
9132 1618 : if (rel->rd_rel->reloftype && !recursing)
9133 6 : ereport(ERROR,
9134 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9135 : errmsg("cannot drop column from typed table")));
9136 :
9137 1612 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9138 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9139 :
9140 1606 : if (recurse)
9141 1336 : cmd->recurse = true;
9142 1606 : }
9143 :
9144 : /*
9145 : * Drops column 'colName' from relation 'rel' and returns the address of the
9146 : * dropped column. The column is also dropped (or marked as no longer
9147 : * inherited from relation) from the relation's inheritance children, if any.
9148 : *
9149 : * In the recursive invocations for inheritance child relations, instead of
9150 : * dropping the column directly (if to be dropped at all), its object address
9151 : * is added to 'addrs', which must be non-NULL in such invocations. All
9152 : * columns are dropped at the same time after all the children have been
9153 : * checked recursively.
9154 : */
9155 : static ObjectAddress
9156 2162 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9157 : DropBehavior behavior,
9158 : bool recurse, bool recursing,
9159 : bool missing_ok, LOCKMODE lockmode,
9160 : ObjectAddresses *addrs)
9161 : {
9162 : HeapTuple tuple;
9163 : Form_pg_attribute targetatt;
9164 : AttrNumber attnum;
9165 : List *children;
9166 : ObjectAddress object;
9167 : bool is_expr;
9168 :
9169 : /* At top level, permission check was done in ATPrepCmd, else do it */
9170 2162 : if (recursing)
9171 556 : ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9172 :
9173 : /* Initialize addrs on the first invocation */
9174 : Assert(!recursing || addrs != NULL);
9175 :
9176 : /* since this function recurses, it could be driven to stack overflow */
9177 2162 : check_stack_depth();
9178 :
9179 2162 : if (!recursing)
9180 1606 : addrs = new_object_addresses();
9181 :
9182 : /*
9183 : * get the number of the attribute
9184 : */
9185 2162 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9186 2162 : if (!HeapTupleIsValid(tuple))
9187 : {
9188 54 : if (!missing_ok)
9189 : {
9190 36 : ereport(ERROR,
9191 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9192 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9193 : colName, RelationGetRelationName(rel))));
9194 : }
9195 : else
9196 : {
9197 18 : ereport(NOTICE,
9198 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9199 : colName, RelationGetRelationName(rel))));
9200 18 : return InvalidObjectAddress;
9201 : }
9202 : }
9203 2108 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9204 :
9205 2108 : attnum = targetatt->attnum;
9206 :
9207 : /* Can't drop a system attribute */
9208 2108 : if (attnum <= 0)
9209 6 : ereport(ERROR,
9210 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9211 : errmsg("cannot drop system column \"%s\"",
9212 : colName)));
9213 :
9214 : /*
9215 : * Don't drop inherited columns, unless recursing (presumably from a drop
9216 : * of the parent column)
9217 : */
9218 2102 : if (targetatt->attinhcount > 0 && !recursing)
9219 48 : ereport(ERROR,
9220 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9221 : errmsg("cannot drop inherited column \"%s\"",
9222 : colName)));
9223 :
9224 : /*
9225 : * Don't drop columns used in the partition key, either. (If we let this
9226 : * go through, the key column's dependencies would cause a cascaded drop
9227 : * of the whole table, which is surely not what the user expected.)
9228 : */
9229 2054 : if (has_partition_attrs(rel,
9230 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9231 : &is_expr))
9232 30 : ereport(ERROR,
9233 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9234 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9235 : colName, RelationGetRelationName(rel))));
9236 :
9237 2024 : ReleaseSysCache(tuple);
9238 :
9239 : /*
9240 : * Propagate to children as appropriate. Unlike most other ALTER
9241 : * routines, we have to do this one level of recursion at a time; we can't
9242 : * use find_all_inheritors to do it in one pass.
9243 : */
9244 : children =
9245 2024 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9246 :
9247 2024 : if (children)
9248 : {
9249 : Relation attr_rel;
9250 : ListCell *child;
9251 :
9252 : /*
9253 : * In case of a partitioned table, the column must be dropped from the
9254 : * partitions as well.
9255 : */
9256 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9257 6 : ereport(ERROR,
9258 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9259 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9260 : errhint("Do not specify the ONLY keyword.")));
9261 :
9262 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9263 882 : foreach(child, children)
9264 : {
9265 592 : Oid childrelid = lfirst_oid(child);
9266 : Relation childrel;
9267 : Form_pg_attribute childatt;
9268 :
9269 : /* find_inheritance_children already got lock */
9270 592 : childrel = table_open(childrelid, NoLock);
9271 592 : CheckTableNotInUse(childrel, "ALTER TABLE");
9272 :
9273 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9274 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9275 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9276 : colName, childrelid);
9277 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9278 :
9279 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9280 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9281 : childrelid, colName);
9282 :
9283 592 : if (recurse)
9284 : {
9285 : /*
9286 : * If the child column has other definition sources, just
9287 : * decrement its inheritance count; if not, recurse to delete
9288 : * it.
9289 : */
9290 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9291 : {
9292 : /* Time to delete this child column, too */
9293 556 : ATExecDropColumn(wqueue, childrel, colName,
9294 : behavior, true, true,
9295 : false, lockmode, addrs);
9296 : }
9297 : else
9298 : {
9299 : /* Child column must survive my deletion */
9300 12 : childatt->attinhcount--;
9301 :
9302 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9303 :
9304 : /* Make update visible */
9305 12 : CommandCounterIncrement();
9306 : }
9307 : }
9308 : else
9309 : {
9310 : /*
9311 : * If we were told to drop ONLY in this table (no recursion),
9312 : * we need to mark the inheritors' attributes as locally
9313 : * defined rather than inherited.
9314 : */
9315 24 : childatt->attinhcount--;
9316 24 : childatt->attislocal = true;
9317 :
9318 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9319 :
9320 : /* Make update visible */
9321 24 : CommandCounterIncrement();
9322 : }
9323 :
9324 586 : heap_freetuple(tuple);
9325 :
9326 586 : table_close(childrel, NoLock);
9327 : }
9328 290 : table_close(attr_rel, RowExclusiveLock);
9329 : }
9330 :
9331 : /* Add object to delete */
9332 2012 : object.classId = RelationRelationId;
9333 2012 : object.objectId = RelationGetRelid(rel);
9334 2012 : object.objectSubId = attnum;
9335 2012 : add_exact_object_address(&object, addrs);
9336 :
9337 2012 : if (!recursing)
9338 : {
9339 : /* Recursion has ended, drop everything that was collected */
9340 1462 : performMultipleDeletions(addrs, behavior, 0);
9341 1414 : free_object_addresses(addrs);
9342 : }
9343 :
9344 1964 : return object;
9345 : }
9346 :
9347 : /*
9348 : * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
9349 : * constraint on its children.
9350 : */
9351 : static void
9352 9476 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9353 : LOCKMODE lockmode, AlterTableUtilityContext *context)
9354 : {
9355 : List *children;
9356 9476 : List *newconstrs = NIL;
9357 : IndexStmt *indexstmt;
9358 :
9359 : /* No work if not creating a primary key */
9360 9476 : if (!IsA(cmd->def, IndexStmt))
9361 0 : return;
9362 9476 : indexstmt = castNode(IndexStmt, cmd->def);
9363 9476 : if (!indexstmt->primary)
9364 4004 : return;
9365 :
9366 : /* No work if no legacy inheritance children are present */
9367 5472 : if (rel->rd_rel->relkind != RELKIND_RELATION ||
9368 5334 : !rel->rd_rel->relhassubclass)
9369 5432 : return;
9370 :
9371 : /*
9372 : * Acquire locks all the way down the hierarchy. The recursion to lower
9373 : * levels occurs at execution time as necessary, so we don't need to do it
9374 : * here, and we don't need the returned list either.
9375 : */
9376 40 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
9377 :
9378 : /*
9379 : * Construct the list of constraints that we need to add to each child
9380 : * relation.
9381 : */
9382 120 : foreach_node(IndexElem, elem, indexstmt->indexParams)
9383 : {
9384 : Constraint *nnconstr;
9385 :
9386 : Assert(elem->expr == NULL);
9387 :
9388 40 : nnconstr = makeNode(Constraint);
9389 40 : nnconstr->contype = CONSTR_NOTNULL;
9390 40 : nnconstr->conname = NULL; /* XXX use PK name? */
9391 40 : nnconstr->inhcount = 1;
9392 40 : nnconstr->deferrable = false;
9393 40 : nnconstr->initdeferred = false;
9394 40 : nnconstr->location = -1;
9395 40 : nnconstr->keys = list_make1(makeString(elem->name));
9396 40 : nnconstr->skip_validation = false;
9397 40 : nnconstr->initially_valid = true;
9398 :
9399 40 : newconstrs = lappend(newconstrs, nnconstr);
9400 : }
9401 :
9402 : /* Finally, add AT subcommands to add each constraint to each child. */
9403 40 : children = find_inheritance_children(RelationGetRelid(rel), NoLock);
9404 160 : foreach_oid(childrelid, children)
9405 : {
9406 80 : Relation childrel = table_open(childrelid, NoLock);
9407 80 : AlterTableCmd *newcmd = makeNode(AlterTableCmd);
9408 : ListCell *lc2;
9409 :
9410 80 : newcmd->subtype = AT_AddConstraint;
9411 80 : newcmd->recurse = true;
9412 :
9413 160 : foreach(lc2, newconstrs)
9414 : {
9415 : /* ATPrepCmd copies newcmd, so we can scribble on it here */
9416 80 : newcmd->def = lfirst(lc2);
9417 :
9418 80 : ATPrepCmd(wqueue, childrel, newcmd,
9419 : true, false, lockmode, context);
9420 : }
9421 :
9422 80 : table_close(childrel, NoLock);
9423 : }
9424 : }
9425 :
9426 : /*
9427 : * ALTER TABLE ADD INDEX
9428 : *
9429 : * There is no such command in the grammar, but parse_utilcmd.c converts
9430 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9431 : * us schedule creation of the index at the appropriate time during ALTER.
9432 : *
9433 : * Return value is the address of the new index.
9434 : */
9435 : static ObjectAddress
9436 1458 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9437 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9438 : {
9439 : bool check_rights;
9440 : bool skip_build;
9441 : bool quiet;
9442 : ObjectAddress address;
9443 :
9444 : Assert(IsA(stmt, IndexStmt));
9445 : Assert(!stmt->concurrent);
9446 :
9447 : /* The IndexStmt has already been through transformIndexStmt */
9448 : Assert(stmt->transformed);
9449 :
9450 : /* suppress schema rights check when rebuilding existing index */
9451 1458 : check_rights = !is_rebuild;
9452 : /* skip index build if phase 3 will do it or we're reusing an old one */
9453 1458 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9454 : /* suppress notices when rebuilding existing index */
9455 1458 : quiet = is_rebuild;
9456 :
9457 1458 : address = DefineIndex(RelationGetRelid(rel),
9458 : stmt,
9459 : InvalidOid, /* no predefined OID */
9460 : InvalidOid, /* no parent index */
9461 : InvalidOid, /* no parent constraint */
9462 : -1, /* total_parts unknown */
9463 : true, /* is_alter_table */
9464 : check_rights,
9465 : false, /* check_not_in_use - we did it already */
9466 : skip_build,
9467 : quiet);
9468 :
9469 : /*
9470 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9471 : * new index instead of building from scratch. Restore associated fields.
9472 : * This may store InvalidSubTransactionId in both fields, in which case
9473 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9474 : * this after the CCI that made catalog rows visible to any rebuild. The
9475 : * DROP of the old edition of this index will have scheduled the storage
9476 : * for deletion at commit, so cancel that pending deletion.
9477 : */
9478 1330 : if (RelFileNumberIsValid(stmt->oldNumber))
9479 : {
9480 72 : Relation irel = index_open(address.objectId, NoLock);
9481 :
9482 72 : irel->rd_createSubid = stmt->oldCreateSubid;
9483 72 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9484 72 : RelationPreserveStorage(irel->rd_locator, true);
9485 72 : index_close(irel, NoLock);
9486 : }
9487 :
9488 1330 : return address;
9489 : }
9490 :
9491 : /*
9492 : * ALTER TABLE ADD STATISTICS
9493 : *
9494 : * This is no such command in the grammar, but we use this internally to add
9495 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9496 : * column type change.
9497 : */
9498 : static ObjectAddress
9499 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9500 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9501 : {
9502 : ObjectAddress address;
9503 :
9504 : Assert(IsA(stmt, CreateStatsStmt));
9505 :
9506 : /* The CreateStatsStmt has already been through transformStatsStmt */
9507 : Assert(stmt->transformed);
9508 :
9509 14 : address = CreateStatistics(stmt);
9510 :
9511 14 : return address;
9512 : }
9513 :
9514 : /*
9515 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9516 : *
9517 : * Returns the address of the new constraint.
9518 : */
9519 : static ObjectAddress
9520 8434 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9521 : IndexStmt *stmt, LOCKMODE lockmode)
9522 : {
9523 8434 : Oid index_oid = stmt->indexOid;
9524 : Relation indexRel;
9525 : char *indexName;
9526 : IndexInfo *indexInfo;
9527 : char *constraintName;
9528 : char constraintType;
9529 : ObjectAddress address;
9530 : bits16 flags;
9531 :
9532 : Assert(IsA(stmt, IndexStmt));
9533 : Assert(OidIsValid(index_oid));
9534 : Assert(stmt->isconstraint);
9535 :
9536 : /*
9537 : * Doing this on partitioned tables is not a simple feature to implement,
9538 : * so let's punt for now.
9539 : */
9540 8434 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9541 6 : ereport(ERROR,
9542 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9543 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9544 :
9545 8428 : indexRel = index_open(index_oid, AccessShareLock);
9546 :
9547 8428 : indexName = pstrdup(RelationGetRelationName(indexRel));
9548 :
9549 8428 : indexInfo = BuildIndexInfo(indexRel);
9550 :
9551 : /* this should have been checked at parse time */
9552 8428 : if (!indexInfo->ii_Unique)
9553 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9554 :
9555 : /*
9556 : * Determine name to assign to constraint. We require a constraint to
9557 : * have the same name as the underlying index; therefore, use the index's
9558 : * existing name as the default constraint name, and if the user
9559 : * explicitly gives some other name for the constraint, rename the index
9560 : * to match.
9561 : */
9562 8428 : constraintName = stmt->idxname;
9563 8428 : if (constraintName == NULL)
9564 8408 : constraintName = indexName;
9565 20 : else if (strcmp(constraintName, indexName) != 0)
9566 : {
9567 14 : ereport(NOTICE,
9568 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9569 : indexName, constraintName)));
9570 14 : RenameRelationInternal(index_oid, constraintName, false, true);
9571 : }
9572 :
9573 : /* Extra checks needed if making primary key */
9574 8428 : if (stmt->primary)
9575 4766 : index_check_primary_key(rel, indexInfo, true, stmt);
9576 :
9577 : /* Note we currently don't support EXCLUSION constraints here */
9578 8422 : if (stmt->primary)
9579 4760 : constraintType = CONSTRAINT_PRIMARY;
9580 : else
9581 3662 : constraintType = CONSTRAINT_UNIQUE;
9582 :
9583 : /* Create the catalog entries for the constraint */
9584 8422 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9585 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9586 16844 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9587 8422 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9588 8422 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9589 :
9590 8422 : address = index_constraint_create(rel,
9591 : index_oid,
9592 : InvalidOid,
9593 : indexInfo,
9594 : constraintName,
9595 : constraintType,
9596 : flags,
9597 : allowSystemTableMods,
9598 : false); /* is_internal */
9599 :
9600 8422 : index_close(indexRel, NoLock);
9601 :
9602 8422 : return address;
9603 : }
9604 :
9605 : /*
9606 : * ALTER TABLE ADD CONSTRAINT
9607 : *
9608 : * Return value is the address of the new constraint; if no constraint was
9609 : * added, InvalidObjectAddress is returned.
9610 : */
9611 : static ObjectAddress
9612 3858 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9613 : Constraint *newConstraint, bool recurse, bool is_readd,
9614 : LOCKMODE lockmode)
9615 : {
9616 3858 : ObjectAddress address = InvalidObjectAddress;
9617 :
9618 : Assert(IsA(newConstraint, Constraint));
9619 :
9620 : /*
9621 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9622 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9623 : * parse_utilcmd.c).
9624 : */
9625 3858 : switch (newConstraint->contype)
9626 : {
9627 1394 : case CONSTR_CHECK:
9628 : case CONSTR_NOTNULL:
9629 : address =
9630 1394 : ATAddCheckNNConstraint(wqueue, tab, rel,
9631 : newConstraint, recurse, false, is_readd,
9632 : lockmode);
9633 1316 : break;
9634 :
9635 2464 : case CONSTR_FOREIGN:
9636 :
9637 : /*
9638 : * Assign or validate constraint name
9639 : */
9640 2464 : if (newConstraint->conname)
9641 : {
9642 1148 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9643 : RelationGetRelid(rel),
9644 1148 : newConstraint->conname))
9645 0 : ereport(ERROR,
9646 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9647 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9648 : newConstraint->conname,
9649 : RelationGetRelationName(rel))));
9650 : }
9651 : else
9652 1316 : newConstraint->conname =
9653 1316 : ChooseConstraintName(RelationGetRelationName(rel),
9654 1316 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9655 : "fkey",
9656 1316 : RelationGetNamespace(rel),
9657 : NIL);
9658 :
9659 2464 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9660 : newConstraint,
9661 : recurse, false,
9662 : lockmode);
9663 1976 : break;
9664 :
9665 0 : default:
9666 0 : elog(ERROR, "unrecognized constraint type: %d",
9667 : (int) newConstraint->contype);
9668 : }
9669 :
9670 3292 : return address;
9671 : }
9672 :
9673 : /*
9674 : * Generate the column-name portion of the constraint name for a new foreign
9675 : * key given the list of column names that reference the referenced
9676 : * table. This will be passed to ChooseConstraintName along with the parent
9677 : * table name and the "fkey" suffix.
9678 : *
9679 : * We know that less than NAMEDATALEN characters will actually be used, so we
9680 : * can truncate the result once we've generated that many.
9681 : *
9682 : * XXX see also ChooseExtendedStatisticNameAddition and
9683 : * ChooseIndexNameAddition.
9684 : */
9685 : static char *
9686 2042 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9687 : {
9688 : char buf[NAMEDATALEN * 2];
9689 2042 : int buflen = 0;
9690 : ListCell *lc;
9691 :
9692 2042 : buf[0] = '\0';
9693 4506 : foreach(lc, colnames)
9694 : {
9695 2464 : const char *name = strVal(lfirst(lc));
9696 :
9697 2464 : if (buflen > 0)
9698 422 : buf[buflen++] = '_'; /* insert _ between names */
9699 :
9700 : /*
9701 : * At this point we have buflen <= NAMEDATALEN. name should be less
9702 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9703 : */
9704 2464 : strlcpy(buf + buflen, name, NAMEDATALEN);
9705 2464 : buflen += strlen(buf + buflen);
9706 2464 : if (buflen >= NAMEDATALEN)
9707 0 : break;
9708 : }
9709 2042 : return pstrdup(buf);
9710 : }
9711 :
9712 : /*
9713 : * Add a check or not-null constraint to a single table and its children.
9714 : * Returns the address of the constraint added to the parent relation,
9715 : * if one gets added, or InvalidObjectAddress otherwise.
9716 : *
9717 : * Subroutine for ATExecAddConstraint.
9718 : *
9719 : * We must recurse to child tables during execution, rather than using
9720 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9721 : * constraints *must* be given the same name, else they won't be seen as
9722 : * related later. If the user didn't explicitly specify a name, then
9723 : * AddRelationNewConstraints would normally assign different names to the
9724 : * child constraints. To fix that, we must capture the name assigned at
9725 : * the parent table and pass that down.
9726 : */
9727 : static ObjectAddress
9728 2032 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9729 : Constraint *constr, bool recurse, bool recursing,
9730 : bool is_readd, LOCKMODE lockmode)
9731 : {
9732 : List *newcons;
9733 : ListCell *lcon;
9734 : List *children;
9735 : ListCell *child;
9736 2032 : ObjectAddress address = InvalidObjectAddress;
9737 :
9738 : /* Guard against stack overflow due to overly deep inheritance tree. */
9739 2032 : check_stack_depth();
9740 :
9741 : /* At top level, permission check was done in ATPrepCmd, else do it */
9742 2032 : if (recursing)
9743 504 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9744 :
9745 : /*
9746 : * Call AddRelationNewConstraints to do the work, making sure it works on
9747 : * a copy of the Constraint so transformExpr can't modify the original. It
9748 : * returns a list of cooked constraints.
9749 : *
9750 : * If the constraint ends up getting merged with a pre-existing one, it's
9751 : * omitted from the returned list, which is what we want: we do not need
9752 : * to do any validation work. That can only happen at child tables,
9753 : * though, since we disallow merging at the top level.
9754 : */
9755 2032 : newcons = AddRelationNewConstraints(rel, NIL,
9756 2032 : list_make1(copyObject(constr)),
9757 2032 : recursing || is_readd, /* allow_merge */
9758 2032 : !recursing, /* is_local */
9759 : is_readd, /* is_internal */
9760 2032 : NULL); /* queryString not available
9761 : * here */
9762 :
9763 : /* we don't expect more than one constraint here */
9764 : Assert(list_length(newcons) <= 1);
9765 :
9766 : /* Add each to-be-validated constraint to Phase 3's queue */
9767 3782 : foreach(lcon, newcons)
9768 : {
9769 1822 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9770 :
9771 1822 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9772 : {
9773 : NewConstraint *newcon;
9774 :
9775 796 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9776 796 : newcon->name = ccon->name;
9777 796 : newcon->contype = ccon->contype;
9778 796 : newcon->qual = ccon->expr;
9779 :
9780 796 : tab->constraints = lappend(tab->constraints, newcon);
9781 : }
9782 :
9783 : /* Save the actually assigned name if it was defaulted */
9784 1822 : if (constr->conname == NULL)
9785 554 : constr->conname = ccon->name;
9786 :
9787 : /*
9788 : * If adding a not-null constraint, set the pg_attribute flag and tell
9789 : * phase 3 to verify existing rows, if needed.
9790 : */
9791 1822 : if (constr->contype == CONSTR_NOTNULL)
9792 632 : set_attnotnull(wqueue, rel, ccon->attnum,
9793 632 : !ccon->is_no_inherit, lockmode);
9794 :
9795 1822 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9796 : }
9797 :
9798 : /* At this point we must have a locked-down name to use */
9799 : Assert(newcons == NIL || constr->conname != NULL);
9800 :
9801 : /* Advance command counter in case same table is visited multiple times */
9802 1960 : CommandCounterIncrement();
9803 :
9804 : /*
9805 : * If the constraint got merged with an existing constraint, we're done.
9806 : * We mustn't recurse to child tables in this case, because they've
9807 : * already got the constraint, and visiting them again would lead to an
9808 : * incorrect value for coninhcount.
9809 : */
9810 1960 : if (newcons == NIL)
9811 138 : return address;
9812 :
9813 : /*
9814 : * If adding a NO INHERIT constraint, no need to find our children.
9815 : */
9816 1822 : if (constr->is_no_inherit)
9817 72 : return address;
9818 :
9819 : /*
9820 : * Propagate to children as appropriate. Unlike most other ALTER
9821 : * routines, we have to do this one level of recursion at a time; we can't
9822 : * use find_all_inheritors to do it in one pass.
9823 : */
9824 : children =
9825 1750 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9826 :
9827 : /*
9828 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9829 : * constraint creation only if there are no children currently. Error out
9830 : * otherwise.
9831 : */
9832 1750 : if (!recurse && children != NIL)
9833 6 : ereport(ERROR,
9834 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9835 : errmsg("constraint must be added to child tables too")));
9836 :
9837 : /*
9838 : * The constraint must appear as inherited in children, so create a
9839 : * modified constraint object to use.
9840 : */
9841 1744 : constr = copyObject(constr);
9842 1744 : constr->inhcount = 1;
9843 2242 : foreach(child, children)
9844 : {
9845 504 : Oid childrelid = lfirst_oid(child);
9846 : Relation childrel;
9847 : AlteredTableInfo *childtab;
9848 :
9849 : /* find_inheritance_children already got lock */
9850 504 : childrel = table_open(childrelid, NoLock);
9851 504 : CheckTableNotInUse(childrel, "ALTER TABLE");
9852 :
9853 : /* Find or create work queue entry for this table */
9854 504 : childtab = ATGetQueueEntry(wqueue, childrel);
9855 :
9856 : /*
9857 : * Recurse to child. XXX if we didn't create a constraint on the
9858 : * parent because it already existed, and we do create one on a child,
9859 : * should we return that child's constraint ObjectAddress here?
9860 : */
9861 504 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9862 : constr, recurse, true, is_readd, lockmode);
9863 :
9864 498 : table_close(childrel, NoLock);
9865 : }
9866 :
9867 1738 : return address;
9868 : }
9869 :
9870 : /*
9871 : * Add a foreign-key constraint to a single table; return the new constraint's
9872 : * address.
9873 : *
9874 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9875 : * lock on the rel, and have done appropriate validity checks for it.
9876 : * We do permissions checks here, however.
9877 : *
9878 : * When the referenced or referencing tables (or both) are partitioned,
9879 : * multiple pg_constraint rows are required -- one for each partitioned table
9880 : * and each partition on each side (fortunately, not one for every combination
9881 : * thereof). We also need action triggers on each leaf partition on the
9882 : * referenced side, and check triggers on each leaf partition on the
9883 : * referencing side.
9884 : */
9885 : static ObjectAddress
9886 2464 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9887 : Constraint *fkconstraint,
9888 : bool recurse, bool recursing, LOCKMODE lockmode)
9889 : {
9890 : Relation pkrel;
9891 2464 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9892 2464 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9893 2464 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9894 2464 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9895 2464 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9896 2464 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9897 2464 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9898 2464 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9899 2464 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9900 : bool with_period;
9901 : bool pk_has_without_overlaps;
9902 : int i;
9903 : int numfks,
9904 : numpks,
9905 : numfkdelsetcols;
9906 : Oid indexOid;
9907 : bool old_check_ok;
9908 : ObjectAddress address;
9909 2464 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9910 :
9911 : /*
9912 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9913 : * delete rows out from under us.
9914 : */
9915 2464 : if (OidIsValid(fkconstraint->old_pktable_oid))
9916 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9917 : else
9918 2392 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9919 :
9920 : /*
9921 : * Validity checks (permission checks wait till we have the column
9922 : * numbers)
9923 : */
9924 2464 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9925 : {
9926 330 : if (!recurse)
9927 6 : ereport(ERROR,
9928 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9929 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9930 : RelationGetRelationName(rel),
9931 : RelationGetRelationName(pkrel))));
9932 324 : if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9933 6 : ereport(ERROR,
9934 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9935 : errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9936 : RelationGetRelationName(rel),
9937 : RelationGetRelationName(pkrel)),
9938 : errdetail("This feature is not yet supported on partitioned tables.")));
9939 : }
9940 :
9941 2452 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9942 296 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9943 0 : ereport(ERROR,
9944 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9945 : errmsg("referenced relation \"%s\" is not a table",
9946 : RelationGetRelationName(pkrel))));
9947 :
9948 2452 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9949 2 : ereport(ERROR,
9950 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9951 : errmsg("permission denied: \"%s\" is a system catalog",
9952 : RelationGetRelationName(pkrel))));
9953 :
9954 : /*
9955 : * References from permanent or unlogged tables to temp tables, and from
9956 : * permanent tables to unlogged tables, are disallowed because the
9957 : * referenced data can vanish out from under us. References from temp
9958 : * tables to any other table type are also disallowed, because other
9959 : * backends might need to run the RI triggers on the perm table, but they
9960 : * can't reliably see tuples in the local buffers of other backends.
9961 : */
9962 2450 : switch (rel->rd_rel->relpersistence)
9963 : {
9964 2140 : case RELPERSISTENCE_PERMANENT:
9965 2140 : if (!RelationIsPermanent(pkrel))
9966 0 : ereport(ERROR,
9967 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9968 : errmsg("constraints on permanent tables may reference only permanent tables")));
9969 2140 : break;
9970 32 : case RELPERSISTENCE_UNLOGGED:
9971 32 : if (!RelationIsPermanent(pkrel)
9972 32 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9973 0 : ereport(ERROR,
9974 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9975 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9976 32 : break;
9977 278 : case RELPERSISTENCE_TEMP:
9978 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9979 0 : ereport(ERROR,
9980 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9981 : errmsg("constraints on temporary tables may reference only temporary tables")));
9982 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9983 0 : ereport(ERROR,
9984 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9985 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
9986 278 : break;
9987 : }
9988 :
9989 : /*
9990 : * Look up the referencing attributes to make sure they exist, and record
9991 : * their attnums and type OIDs.
9992 : */
9993 2450 : numfks = transformColumnNameList(RelationGetRelid(rel),
9994 : fkconstraint->fk_attrs,
9995 : fkattnum, fktypoid);
9996 2420 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9997 2420 : if (with_period && !fkconstraint->fk_with_period)
9998 24 : ereport(ERROR,
9999 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10000 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10001 :
10002 2396 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10003 : fkconstraint->fk_del_set_cols,
10004 : fkdelsetcols, NULL);
10005 2390 : validateFkOnDeleteSetColumns(numfks, fkattnum,
10006 : numfkdelsetcols, fkdelsetcols,
10007 : fkconstraint->fk_del_set_cols);
10008 :
10009 : /*
10010 : * If the attribute list for the referenced table was omitted, lookup the
10011 : * definition of the primary key and use it. Otherwise, validate the
10012 : * supplied attribute list. In either case, discover the index OID and
10013 : * index opclasses, and the attnums and type OIDs of the attributes.
10014 : */
10015 2384 : if (fkconstraint->pk_attrs == NIL)
10016 : {
10017 1070 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10018 : &fkconstraint->pk_attrs,
10019 : pkattnum, pktypoid,
10020 : opclasses, &pk_has_without_overlaps);
10021 :
10022 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10023 1070 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10024 24 : ereport(ERROR,
10025 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10026 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10027 : }
10028 : else
10029 : {
10030 1314 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10031 : fkconstraint->pk_attrs,
10032 : pkattnum, pktypoid);
10033 :
10034 : /* Since we got pk_attrs, one should be a period. */
10035 1284 : if (with_period && !fkconstraint->pk_with_period)
10036 24 : ereport(ERROR,
10037 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10038 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10039 :
10040 : /* Look for an index matching the column list */
10041 1260 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10042 : with_period, opclasses, &pk_has_without_overlaps);
10043 : }
10044 :
10045 : /*
10046 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10047 : * must use PERIOD.
10048 : */
10049 2270 : if (pk_has_without_overlaps && !with_period)
10050 12 : ereport(ERROR,
10051 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10052 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10053 :
10054 : /*
10055 : * Now we can check permissions.
10056 : */
10057 2258 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10058 :
10059 : /*
10060 : * Check some things for generated columns.
10061 : */
10062 5286 : for (i = 0; i < numfks; i++)
10063 : {
10064 3040 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10065 :
10066 3040 : if (attgenerated)
10067 : {
10068 : /*
10069 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10070 : */
10071 30 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10072 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10073 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10074 6 : ereport(ERROR,
10075 : (errcode(ERRCODE_SYNTAX_ERROR),
10076 : errmsg("invalid %s action for foreign key constraint containing generated column",
10077 : "ON UPDATE")));
10078 24 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10079 18 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10080 6 : ereport(ERROR,
10081 : (errcode(ERRCODE_SYNTAX_ERROR),
10082 : errmsg("invalid %s action for foreign key constraint containing generated column",
10083 : "ON DELETE")));
10084 : }
10085 : }
10086 :
10087 : /*
10088 : * Some actions are currently unsupported for foreign keys using PERIOD.
10089 : */
10090 2246 : if (fkconstraint->fk_with_period)
10091 : {
10092 238 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10093 226 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10094 214 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10095 36 : ereport(ERROR,
10096 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10097 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10098 : "ON UPDATE"));
10099 :
10100 202 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10101 202 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10102 202 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10103 0 : ereport(ERROR,
10104 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10105 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10106 : "ON DELETE"));
10107 : }
10108 :
10109 : /*
10110 : * Look up the equality operators to use in the constraint.
10111 : *
10112 : * Note that we have to be careful about the difference between the actual
10113 : * PK column type and the opclass' declared input type, which might be
10114 : * only binary-compatible with it. The declared opcintype is the right
10115 : * thing to probe pg_amop with.
10116 : */
10117 2210 : if (numfks != numpks)
10118 0 : ereport(ERROR,
10119 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10120 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10121 :
10122 : /*
10123 : * On the strength of a previous constraint, we might avoid scanning
10124 : * tables to validate this one. See below.
10125 : */
10126 2210 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10127 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10128 :
10129 4794 : for (i = 0; i < numpks; i++)
10130 : {
10131 2812 : Oid pktype = pktypoid[i];
10132 2812 : Oid fktype = fktypoid[i];
10133 : Oid fktyped;
10134 : HeapTuple cla_ht;
10135 : Form_pg_opclass cla_tup;
10136 : Oid amid;
10137 : Oid opfamily;
10138 : Oid opcintype;
10139 : Oid pfeqop;
10140 : Oid ppeqop;
10141 : Oid ffeqop;
10142 : int16 eqstrategy;
10143 : Oid pfeqop_right;
10144 :
10145 : /* We need several fields out of the pg_opclass entry */
10146 2812 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10147 2812 : if (!HeapTupleIsValid(cla_ht))
10148 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10149 2812 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10150 2812 : amid = cla_tup->opcmethod;
10151 2812 : opfamily = cla_tup->opcfamily;
10152 2812 : opcintype = cla_tup->opcintype;
10153 2812 : ReleaseSysCache(cla_ht);
10154 :
10155 2812 : if (with_period)
10156 : {
10157 : StrategyNumber rtstrategy;
10158 432 : bool for_overlaps = with_period && i == numpks - 1;
10159 :
10160 : /*
10161 : * GiST indexes are required to support temporal foreign keys
10162 : * because they combine equals and overlaps.
10163 : */
10164 432 : if (amid != GIST_AM_OID)
10165 0 : elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10166 :
10167 432 : rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
10168 :
10169 : /*
10170 : * An opclass can use whatever strategy numbers it wants, so we
10171 : * ask the opclass what number it actually uses instead of our RT*
10172 : * constants.
10173 : */
10174 432 : eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
10175 432 : if (eqstrategy == InvalidStrategy)
10176 : {
10177 : HeapTuple tuple;
10178 :
10179 0 : tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10180 0 : if (!HeapTupleIsValid(tuple))
10181 0 : elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10182 :
10183 0 : ereport(ERROR,
10184 : errcode(ERRCODE_UNDEFINED_OBJECT),
10185 : for_overlaps
10186 : ? errmsg("could not identify an overlaps operator for foreign key")
10187 : : errmsg("could not identify an equality operator for foreign key"),
10188 : errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10189 : rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10190 : }
10191 : }
10192 : else
10193 : {
10194 : /*
10195 : * Check it's a btree; currently this can never fail since no
10196 : * other index AMs support unique indexes. If we ever did have
10197 : * other types of unique indexes, we'd need a way to determine
10198 : * which operator strategy number is equality. (We could use
10199 : * something like GistTranslateStratnum.)
10200 : */
10201 2380 : if (amid != BTREE_AM_OID)
10202 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
10203 2380 : eqstrategy = BTEqualStrategyNumber;
10204 : }
10205 :
10206 : /*
10207 : * There had better be a primary equality operator for the index.
10208 : * We'll use it for PK = PK comparisons.
10209 : */
10210 2812 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10211 : eqstrategy);
10212 :
10213 2812 : if (!OidIsValid(ppeqop))
10214 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10215 : eqstrategy, opcintype, opcintype, opfamily);
10216 :
10217 : /*
10218 : * Are there equality operators that take exactly the FK type? Assume
10219 : * we should look through any domain here.
10220 : */
10221 2812 : fktyped = getBaseType(fktype);
10222 :
10223 2812 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10224 : eqstrategy);
10225 2812 : if (OidIsValid(pfeqop))
10226 : {
10227 2130 : pfeqop_right = fktyped;
10228 2130 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10229 : eqstrategy);
10230 : }
10231 : else
10232 : {
10233 : /* keep compiler quiet */
10234 682 : pfeqop_right = InvalidOid;
10235 682 : ffeqop = InvalidOid;
10236 : }
10237 :
10238 2812 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10239 : {
10240 : /*
10241 : * Otherwise, look for an implicit cast from the FK type to the
10242 : * opcintype, and if found, use the primary equality operator.
10243 : * This is a bit tricky because opcintype might be a polymorphic
10244 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10245 : * whether the two actual column types can be concurrently cast to
10246 : * that type. (Otherwise, we'd fail to reject combinations such
10247 : * as int[] and point[].)
10248 : */
10249 : Oid input_typeids[2];
10250 : Oid target_typeids[2];
10251 :
10252 682 : input_typeids[0] = pktype;
10253 682 : input_typeids[1] = fktype;
10254 682 : target_typeids[0] = opcintype;
10255 682 : target_typeids[1] = opcintype;
10256 682 : if (can_coerce_type(2, input_typeids, target_typeids,
10257 : COERCION_IMPLICIT))
10258 : {
10259 454 : pfeqop = ffeqop = ppeqop;
10260 454 : pfeqop_right = opcintype;
10261 : }
10262 : }
10263 :
10264 2812 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10265 228 : ereport(ERROR,
10266 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10267 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10268 : fkconstraint->conname),
10269 : errdetail("Key columns \"%s\" and \"%s\" "
10270 : "are of incompatible types: %s and %s.",
10271 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10272 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10273 : format_type_be(fktype),
10274 : format_type_be(pktype))));
10275 :
10276 2584 : if (old_check_ok)
10277 : {
10278 : /*
10279 : * When a pfeqop changes, revalidate the constraint. We could
10280 : * permit intra-opfamily changes, but that adds subtle complexity
10281 : * without any concrete benefit for core types. We need not
10282 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10283 : */
10284 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10285 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10286 : old_pfeqop_item);
10287 : }
10288 2584 : if (old_check_ok)
10289 : {
10290 : Oid old_fktype;
10291 : Oid new_fktype;
10292 : CoercionPathType old_pathtype;
10293 : CoercionPathType new_pathtype;
10294 : Oid old_castfunc;
10295 : Oid new_castfunc;
10296 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10297 : fkattnum[i] - 1);
10298 :
10299 : /*
10300 : * Identify coercion pathways from each of the old and new FK-side
10301 : * column types to the right (foreign) operand type of the pfeqop.
10302 : * We may assume that pg_constraint.conkey is not changing.
10303 : */
10304 6 : old_fktype = attr->atttypid;
10305 6 : new_fktype = fktype;
10306 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10307 : &old_castfunc);
10308 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10309 : &new_castfunc);
10310 :
10311 : /*
10312 : * Upon a change to the cast from the FK column to its pfeqop
10313 : * operand, revalidate the constraint. For this evaluation, a
10314 : * binary coercion cast is equivalent to no cast at all. While
10315 : * type implementors should design implicit casts with an eye
10316 : * toward consistency of operations like equality, we cannot
10317 : * assume here that they have done so.
10318 : *
10319 : * A function with a polymorphic argument could change behavior
10320 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10321 : * when the cast destination is polymorphic, we only avoid
10322 : * revalidation if the input type has not changed at all. Given
10323 : * just the core data types and operator classes, this requirement
10324 : * prevents no would-be optimizations.
10325 : *
10326 : * If the cast converts from a base type to a domain thereon, then
10327 : * that domain type must be the opcintype of the unique index.
10328 : * Necessarily, the primary key column must then be of the domain
10329 : * type. Since the constraint was previously valid, all values on
10330 : * the foreign side necessarily exist on the primary side and in
10331 : * turn conform to the domain. Consequently, we need not treat
10332 : * domains specially here.
10333 : *
10334 : * Since we require that all collations share the same notion of
10335 : * equality (which they do, because texteq reduces to bitwise
10336 : * equality), we don't compare collation here.
10337 : *
10338 : * We need not directly consider the PK type. It's necessarily
10339 : * binary coercible to the opcintype of the unique index column,
10340 : * and ri_triggers.c will only deal with PK datums in terms of
10341 : * that opcintype. Changing the opcintype also changes pfeqop.
10342 : */
10343 6 : old_check_ok = (new_pathtype == old_pathtype &&
10344 12 : new_castfunc == old_castfunc &&
10345 6 : (!IsPolymorphicType(pfeqop_right) ||
10346 : new_fktype == old_fktype));
10347 : }
10348 :
10349 2584 : pfeqoperators[i] = pfeqop;
10350 2584 : ppeqoperators[i] = ppeqop;
10351 2584 : ffeqoperators[i] = ffeqop;
10352 : }
10353 :
10354 : /*
10355 : * For FKs with PERIOD we need additional operators to check whether the
10356 : * referencing row's range is contained by the aggregated ranges of the
10357 : * referenced row(s). For rangetypes and multirangetypes this is
10358 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10359 : * support for now. FKs will look these up at "runtime", but we should
10360 : * make sure the lookup works here, even if we don't use the values.
10361 : */
10362 1982 : if (with_period)
10363 : {
10364 : Oid periodoperoid;
10365 : Oid aggedperiodoperoid;
10366 :
10367 184 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10368 : }
10369 :
10370 : /*
10371 : * Create all the constraint and trigger objects, recursing to partitions
10372 : * as necessary. First handle the referenced side.
10373 : */
10374 1976 : address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
10375 : indexOid,
10376 : InvalidOid, /* no parent constraint */
10377 : numfks,
10378 : pkattnum,
10379 : fkattnum,
10380 : pfeqoperators,
10381 : ppeqoperators,
10382 : ffeqoperators,
10383 : numfkdelsetcols,
10384 : fkdelsetcols,
10385 : old_check_ok,
10386 : InvalidOid, InvalidOid,
10387 : with_period);
10388 :
10389 : /* Now handle the referencing side. */
10390 1976 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10391 : indexOid,
10392 : address.objectId,
10393 : numfks,
10394 : pkattnum,
10395 : fkattnum,
10396 : pfeqoperators,
10397 : ppeqoperators,
10398 : ffeqoperators,
10399 : numfkdelsetcols,
10400 : fkdelsetcols,
10401 : old_check_ok,
10402 : lockmode,
10403 : InvalidOid, InvalidOid,
10404 : with_period);
10405 :
10406 : /*
10407 : * Done. Close pk table, but keep lock until we've committed.
10408 : */
10409 1976 : table_close(pkrel, NoLock);
10410 :
10411 1976 : return address;
10412 : }
10413 :
10414 : /*
10415 : * validateFkOnDeleteSetColumns
10416 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10417 : * column lists are valid.
10418 : */
10419 : void
10420 2390 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10421 : int numfksetcols, const int16 *fksetcolsattnums,
10422 : List *fksetcols)
10423 : {
10424 2414 : for (int i = 0; i < numfksetcols; i++)
10425 : {
10426 30 : int16 setcol_attnum = fksetcolsattnums[i];
10427 30 : bool seen = false;
10428 :
10429 54 : for (int j = 0; j < numfks; j++)
10430 : {
10431 48 : if (fkattnums[j] == setcol_attnum)
10432 : {
10433 24 : seen = true;
10434 24 : break;
10435 : }
10436 : }
10437 :
10438 30 : if (!seen)
10439 : {
10440 6 : char *col = strVal(list_nth(fksetcols, i));
10441 :
10442 6 : ereport(ERROR,
10443 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10444 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10445 : }
10446 : }
10447 2384 : }
10448 :
10449 : /*
10450 : * addFkRecurseReferenced
10451 : * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10452 : * side of the constraint
10453 : *
10454 : * Create pg_constraint rows for the referenced side of the constraint,
10455 : * referencing the parent of the referencing side; also create action triggers
10456 : * on leaf partitions. If the table is partitioned, recurse to handle each
10457 : * partition.
10458 : *
10459 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10460 : * of an ALTER TABLE sequence.
10461 : * fkconstraint is the constraint being added.
10462 : * rel is the root referencing relation.
10463 : * pkrel is the referenced relation; might be a partition, if recursing.
10464 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10465 : * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10466 : * top-level constraint.
10467 : * numfks is the number of columns in the foreign key
10468 : * pkattnum is the attnum array of referenced attributes.
10469 : * fkattnum is the attnum array of referencing attributes.
10470 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10471 : * (...) clause
10472 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10473 : * NULL/DEFAULT clause
10474 : * pf/pp/ffeqoperators are OID array of operators between columns.
10475 : * old_check_ok signals that this constraint replaces an existing one that
10476 : * was already validated (thus this one doesn't need validation).
10477 : * parentDelTrigger and parentUpdTrigger, when being recursively called on
10478 : * a partition, are the OIDs of the parent action triggers for DELETE and
10479 : * UPDATE respectively.
10480 : */
10481 : static ObjectAddress
10482 2696 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
10483 : Relation pkrel, Oid indexOid, Oid parentConstr,
10484 : int numfks,
10485 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10486 : Oid *ppeqoperators, Oid *ffeqoperators,
10487 : int numfkdelsetcols, int16 *fkdelsetcols,
10488 : bool old_check_ok,
10489 : Oid parentDelTrigger, Oid parentUpdTrigger,
10490 : bool with_period)
10491 : {
10492 : ObjectAddress address;
10493 : Oid constrOid;
10494 : char *conname;
10495 : bool conislocal;
10496 : int coninhcount;
10497 : bool connoinherit;
10498 : Oid deleteTriggerOid,
10499 : updateTriggerOid;
10500 :
10501 : /*
10502 : * Verify relkind for each referenced partition. At the top level, this
10503 : * is redundant with a previous check, but we need it when recursing.
10504 : */
10505 2696 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10506 378 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10507 0 : ereport(ERROR,
10508 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10509 : errmsg("referenced relation \"%s\" is not a table",
10510 : RelationGetRelationName(pkrel))));
10511 :
10512 : /*
10513 : * Caller supplies us with a constraint name; however, it may be used in
10514 : * this partition, so come up with a different one in that case.
10515 : */
10516 2696 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10517 : RelationGetRelid(rel),
10518 2696 : fkconstraint->conname))
10519 720 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10520 720 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10521 : "fkey",
10522 720 : RelationGetNamespace(rel), NIL);
10523 : else
10524 1976 : conname = fkconstraint->conname;
10525 :
10526 2696 : if (OidIsValid(parentConstr))
10527 : {
10528 720 : conislocal = false;
10529 720 : coninhcount = 1;
10530 720 : connoinherit = false;
10531 : }
10532 : else
10533 : {
10534 1976 : conislocal = true;
10535 1976 : coninhcount = 0;
10536 :
10537 : /*
10538 : * always inherit for partitioned tables, never for legacy inheritance
10539 : */
10540 1976 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10541 : }
10542 :
10543 : /*
10544 : * Record the FK constraint in pg_constraint.
10545 : */
10546 2696 : constrOid = CreateConstraintEntry(conname,
10547 2696 : RelationGetNamespace(rel),
10548 : CONSTRAINT_FOREIGN,
10549 2696 : fkconstraint->deferrable,
10550 2696 : fkconstraint->initdeferred,
10551 2696 : fkconstraint->initially_valid,
10552 : parentConstr,
10553 : RelationGetRelid(rel),
10554 : fkattnum,
10555 : numfks,
10556 : numfks,
10557 : InvalidOid, /* not a domain constraint */
10558 : indexOid,
10559 : RelationGetRelid(pkrel),
10560 : pkattnum,
10561 : pfeqoperators,
10562 : ppeqoperators,
10563 : ffeqoperators,
10564 : numfks,
10565 2696 : fkconstraint->fk_upd_action,
10566 2696 : fkconstraint->fk_del_action,
10567 : fkdelsetcols,
10568 : numfkdelsetcols,
10569 2696 : fkconstraint->fk_matchtype,
10570 : NULL, /* no exclusion constraint */
10571 : NULL, /* no check constraint */
10572 : NULL,
10573 : conislocal, /* islocal */
10574 : coninhcount, /* inhcount */
10575 : connoinherit, /* conNoInherit */
10576 : with_period, /* conPeriod */
10577 : false); /* is_internal */
10578 :
10579 2696 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10580 :
10581 : /*
10582 : * Mark the child constraint as part of the parent constraint; it must not
10583 : * be dropped on its own. (This constraint is deleted when the partition
10584 : * is detached, but a special check needs to occur that the partition
10585 : * contains no referenced values.)
10586 : */
10587 2696 : if (OidIsValid(parentConstr))
10588 : {
10589 : ObjectAddress referenced;
10590 :
10591 720 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10592 720 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10593 : }
10594 :
10595 : /* make new constraint visible, in case we add more */
10596 2696 : CommandCounterIncrement();
10597 :
10598 : /*
10599 : * Create the action triggers that enforce the constraint.
10600 : */
10601 2696 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10602 : fkconstraint,
10603 : constrOid, indexOid,
10604 : parentDelTrigger, parentUpdTrigger,
10605 : &deleteTriggerOid, &updateTriggerOid);
10606 :
10607 : /*
10608 : * If the referenced table is partitioned, recurse on ourselves to handle
10609 : * each partition. We need one pg_constraint row created for each
10610 : * partition in addition to the pg_constraint row for the parent table.
10611 : */
10612 2696 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10613 : {
10614 378 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10615 :
10616 948 : for (int i = 0; i < pd->nparts; i++)
10617 : {
10618 : Relation partRel;
10619 : AttrMap *map;
10620 : AttrNumber *mapped_pkattnum;
10621 : Oid partIndexId;
10622 :
10623 570 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10624 :
10625 : /*
10626 : * Map the attribute numbers in the referenced side of the FK
10627 : * definition to match the partition's column layout.
10628 : */
10629 570 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10630 : RelationGetDescr(pkrel),
10631 : false);
10632 570 : if (map)
10633 : {
10634 46 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10635 92 : for (int j = 0; j < numfks; j++)
10636 46 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10637 : }
10638 : else
10639 524 : mapped_pkattnum = pkattnum;
10640 :
10641 : /* do the deed */
10642 570 : partIndexId = index_get_partition(partRel, indexOid);
10643 570 : if (!OidIsValid(partIndexId))
10644 0 : elog(ERROR, "index for %u not found in partition %s",
10645 : indexOid, RelationGetRelationName(partRel));
10646 570 : addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10647 : partIndexId, constrOid, numfks,
10648 : mapped_pkattnum, fkattnum,
10649 : pfeqoperators, ppeqoperators, ffeqoperators,
10650 : numfkdelsetcols, fkdelsetcols,
10651 : old_check_ok,
10652 : deleteTriggerOid, updateTriggerOid,
10653 : with_period);
10654 :
10655 : /* Done -- clean up (but keep the lock) */
10656 570 : table_close(partRel, NoLock);
10657 570 : if (map)
10658 : {
10659 46 : pfree(mapped_pkattnum);
10660 46 : free_attrmap(map);
10661 : }
10662 : }
10663 : }
10664 :
10665 2696 : return address;
10666 : }
10667 :
10668 : /*
10669 : * addFkRecurseReferencing
10670 : * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10671 : *
10672 : * If the referencing relation is a plain relation, create the necessary check
10673 : * triggers that implement the constraint, and set up for Phase 3 constraint
10674 : * verification. If the referencing relation is a partitioned table, then
10675 : * we create a pg_constraint row for it and recurse on this routine for each
10676 : * partition.
10677 : *
10678 : * We assume that the referenced relation is locked against concurrent
10679 : * deletions. If it's a partitioned relation, every partition must be so
10680 : * locked.
10681 : *
10682 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10683 : * of an ALTER TABLE sequence.
10684 : * fkconstraint is the constraint being added.
10685 : * rel is the referencing relation; might be a partition, if recursing.
10686 : * pkrel is the root referenced relation.
10687 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10688 : * parentConstr is the OID of the parent constraint (there is always one).
10689 : * numfks is the number of columns in the foreign key
10690 : * pkattnum is the attnum array of referenced attributes.
10691 : * fkattnum is the attnum array of referencing attributes.
10692 : * pf/pp/ffeqoperators are OID array of operators between columns.
10693 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10694 : * (...) clause
10695 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10696 : * NULL/DEFAULT clause
10697 : * old_check_ok signals that this constraint replaces an existing one that
10698 : * was already validated (thus this one doesn't need validation).
10699 : * lockmode is the lockmode to acquire on partitions when recursing.
10700 : * parentInsTrigger and parentUpdTrigger, when being recursively called on
10701 : * a partition, are the OIDs of the parent check triggers for INSERT and
10702 : * UPDATE respectively.
10703 : */
10704 : static void
10705 2746 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10706 : Relation pkrel, Oid indexOid, Oid parentConstr,
10707 : int numfks, int16 *pkattnum, int16 *fkattnum,
10708 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10709 : int numfkdelsetcols, int16 *fkdelsetcols,
10710 : bool old_check_ok, LOCKMODE lockmode,
10711 : Oid parentInsTrigger, Oid parentUpdTrigger,
10712 : bool with_period)
10713 : {
10714 : Oid insertTriggerOid,
10715 : updateTriggerOid;
10716 :
10717 : Assert(OidIsValid(parentConstr));
10718 :
10719 2746 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10720 0 : ereport(ERROR,
10721 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10722 : errmsg("foreign key constraints are not supported on foreign tables")));
10723 :
10724 : /*
10725 : * Add the check triggers to it and, if necessary, schedule it to be
10726 : * checked in Phase 3.
10727 : *
10728 : * If the relation is partitioned, drill down to do it to its partitions.
10729 : */
10730 2746 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10731 : RelationGetRelid(pkrel),
10732 : fkconstraint,
10733 : parentConstr,
10734 : indexOid,
10735 : parentInsTrigger, parentUpdTrigger,
10736 : &insertTriggerOid, &updateTriggerOid);
10737 :
10738 2746 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10739 : {
10740 : /*
10741 : * Tell Phase 3 to check that the constraint is satisfied by existing
10742 : * rows. We can skip this during table creation, when requested
10743 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10744 : * and when we're recreating a constraint following a SET DATA TYPE
10745 : * operation that did not impugn its validity.
10746 : */
10747 2334 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10748 : {
10749 : NewConstraint *newcon;
10750 : AlteredTableInfo *tab;
10751 :
10752 792 : tab = ATGetQueueEntry(wqueue, rel);
10753 :
10754 792 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10755 792 : newcon->name = get_constraint_name(parentConstr);
10756 792 : newcon->contype = CONSTR_FOREIGN;
10757 792 : newcon->refrelid = RelationGetRelid(pkrel);
10758 792 : newcon->refindid = indexOid;
10759 792 : newcon->conid = parentConstr;
10760 792 : newcon->conwithperiod = fkconstraint->fk_with_period;
10761 792 : newcon->qual = (Node *) fkconstraint;
10762 :
10763 792 : tab->constraints = lappend(tab->constraints, newcon);
10764 : }
10765 : }
10766 412 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10767 : {
10768 412 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10769 : Relation trigrel;
10770 :
10771 : /*
10772 : * Triggers of the foreign keys will be manipulated a bunch of times
10773 : * in the loop below. To avoid repeatedly opening/closing the trigger
10774 : * catalog relation, we open it here and pass it to the subroutines
10775 : * called below.
10776 : */
10777 412 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10778 :
10779 : /*
10780 : * Recurse to take appropriate action on each partition; either we
10781 : * find an existing constraint to reparent to ours, or we create a new
10782 : * one.
10783 : */
10784 788 : for (int i = 0; i < pd->nparts; i++)
10785 : {
10786 376 : Oid partitionId = pd->oids[i];
10787 376 : Relation partition = table_open(partitionId, lockmode);
10788 : List *partFKs;
10789 : AttrMap *attmap;
10790 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10791 : bool attached;
10792 : char *conname;
10793 : Oid constrOid;
10794 : ObjectAddress address,
10795 : referenced;
10796 : ListCell *cell;
10797 :
10798 376 : CheckTableNotInUse(partition, "ALTER TABLE");
10799 :
10800 376 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10801 : RelationGetDescr(rel),
10802 : false);
10803 968 : for (int j = 0; j < numfks; j++)
10804 592 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10805 :
10806 : /* Check whether an existing constraint can be repurposed */
10807 376 : partFKs = copyObject(RelationGetFKeyList(partition));
10808 376 : attached = false;
10809 394 : foreach(cell, partFKs)
10810 : {
10811 : ForeignKeyCacheInfo *fk;
10812 :
10813 30 : fk = lfirst_node(ForeignKeyCacheInfo, cell);
10814 30 : if (tryAttachPartitionForeignKey(fk,
10815 : partitionId,
10816 : parentConstr,
10817 : numfks,
10818 : mapped_fkattnum,
10819 : pkattnum,
10820 : pfeqoperators,
10821 : insertTriggerOid,
10822 : updateTriggerOid,
10823 : trigrel))
10824 : {
10825 12 : attached = true;
10826 12 : break;
10827 : }
10828 : }
10829 376 : if (attached)
10830 : {
10831 12 : table_close(partition, NoLock);
10832 12 : continue;
10833 : }
10834 :
10835 : /*
10836 : * No luck finding a good constraint to reuse; create our own.
10837 : */
10838 364 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10839 : RelationGetRelid(partition),
10840 364 : fkconstraint->conname))
10841 0 : conname = ChooseConstraintName(RelationGetRelationName(partition),
10842 0 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10843 : "fkey",
10844 0 : RelationGetNamespace(partition), NIL);
10845 : else
10846 364 : conname = fkconstraint->conname;
10847 : constrOid =
10848 364 : CreateConstraintEntry(conname,
10849 364 : RelationGetNamespace(partition),
10850 : CONSTRAINT_FOREIGN,
10851 364 : fkconstraint->deferrable,
10852 364 : fkconstraint->initdeferred,
10853 364 : fkconstraint->initially_valid,
10854 : parentConstr,
10855 : partitionId,
10856 : mapped_fkattnum,
10857 : numfks,
10858 : numfks,
10859 : InvalidOid,
10860 : indexOid,
10861 : RelationGetRelid(pkrel),
10862 : pkattnum,
10863 : pfeqoperators,
10864 : ppeqoperators,
10865 : ffeqoperators,
10866 : numfks,
10867 364 : fkconstraint->fk_upd_action,
10868 364 : fkconstraint->fk_del_action,
10869 : fkdelsetcols,
10870 : numfkdelsetcols,
10871 364 : fkconstraint->fk_matchtype,
10872 : NULL,
10873 : NULL,
10874 : NULL,
10875 : false,
10876 : 1,
10877 : false,
10878 : with_period, /* conPeriod */
10879 : false);
10880 :
10881 : /*
10882 : * Give this constraint partition-type dependencies on the parent
10883 : * constraint as well as the table.
10884 : */
10885 364 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10886 364 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10887 364 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10888 364 : ObjectAddressSet(referenced, RelationRelationId, partitionId);
10889 364 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10890 :
10891 : /* Make all this visible before recursing */
10892 364 : CommandCounterIncrement();
10893 :
10894 : /* call ourselves to finalize the creation and we're done */
10895 364 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10896 : indexOid,
10897 : constrOid,
10898 : numfks,
10899 : pkattnum,
10900 : mapped_fkattnum,
10901 : pfeqoperators,
10902 : ppeqoperators,
10903 : ffeqoperators,
10904 : numfkdelsetcols,
10905 : fkdelsetcols,
10906 : old_check_ok,
10907 : lockmode,
10908 : insertTriggerOid,
10909 : updateTriggerOid,
10910 : with_period);
10911 :
10912 364 : table_close(partition, NoLock);
10913 : }
10914 :
10915 412 : table_close(trigrel, RowExclusiveLock);
10916 : }
10917 2746 : }
10918 :
10919 : /*
10920 : * CloneForeignKeyConstraints
10921 : * Clone foreign keys from a partitioned table to a newly acquired
10922 : * partition.
10923 : *
10924 : * partitionRel is a partition of parentRel, so we can be certain that it has
10925 : * the same columns with the same datatypes. The columns may be in different
10926 : * order, though.
10927 : *
10928 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10929 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10930 : * PARTITION OF).
10931 : */
10932 : static void
10933 10180 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10934 : Relation partitionRel)
10935 : {
10936 : /* This only works for declarative partitioning */
10937 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10938 :
10939 : /*
10940 : * Clone constraints for which the parent is on the referenced side.
10941 : */
10942 10180 : CloneFkReferenced(parentRel, partitionRel);
10943 :
10944 : /*
10945 : * Now clone constraints where the parent is on the referencing side.
10946 : */
10947 10180 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10948 10180 : }
10949 :
10950 : /*
10951 : * CloneFkReferenced
10952 : * Subroutine for CloneForeignKeyConstraints
10953 : *
10954 : * Find all the FKs that have the parent relation on the referenced side;
10955 : * clone those constraints to the given partition. This is to be called
10956 : * when the partition is being created or attached.
10957 : *
10958 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10959 : *
10960 : * This recurses to partitions, if the relation being attached is partitioned.
10961 : * Recursion is done by calling addFkRecurseReferenced.
10962 : */
10963 : static void
10964 10180 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10965 : {
10966 : Relation pg_constraint;
10967 : AttrMap *attmap;
10968 : ListCell *cell;
10969 : SysScanDesc scan;
10970 : ScanKeyData key[2];
10971 : HeapTuple tuple;
10972 10180 : List *clone = NIL;
10973 : Relation trigrel;
10974 :
10975 : /*
10976 : * Search for any constraints where this partition's parent is in the
10977 : * referenced side. However, we must not clone any constraint whose
10978 : * parent constraint is also going to be cloned, to avoid duplicates. So
10979 : * do it in two steps: first construct the list of constraints to clone,
10980 : * then go over that list cloning those whose parents are not in the list.
10981 : * (We must not rely on the parent being seen first, since the catalog
10982 : * scan could return children first.)
10983 : */
10984 10180 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10985 10180 : ScanKeyInit(&key[0],
10986 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10987 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10988 10180 : ScanKeyInit(&key[1],
10989 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
10990 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10991 : /* This is a seqscan, as we don't have a usable index ... */
10992 10180 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
10993 : NULL, 2, key);
10994 10510 : while ((tuple = systable_getnext(scan)) != NULL)
10995 : {
10996 330 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10997 :
10998 330 : clone = lappend_oid(clone, constrForm->oid);
10999 : }
11000 10180 : systable_endscan(scan);
11001 10180 : table_close(pg_constraint, RowShareLock);
11002 :
11003 : /*
11004 : * Triggers of the foreign keys will be manipulated a bunch of times in
11005 : * the loop below. To avoid repeatedly opening/closing the trigger
11006 : * catalog relation, we open it here and pass it to the subroutines called
11007 : * below.
11008 : */
11009 10180 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11010 :
11011 10180 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11012 : RelationGetDescr(parentRel),
11013 : false);
11014 10510 : foreach(cell, clone)
11015 : {
11016 330 : Oid constrOid = lfirst_oid(cell);
11017 : Form_pg_constraint constrForm;
11018 : Relation fkRel;
11019 : Oid indexOid;
11020 : Oid partIndexId;
11021 : int numfks;
11022 : AttrNumber conkey[INDEX_MAX_KEYS];
11023 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11024 : AttrNumber confkey[INDEX_MAX_KEYS];
11025 : Oid conpfeqop[INDEX_MAX_KEYS];
11026 : Oid conppeqop[INDEX_MAX_KEYS];
11027 : Oid conffeqop[INDEX_MAX_KEYS];
11028 : int numfkdelsetcols;
11029 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11030 : Constraint *fkconstraint;
11031 : Oid deleteTriggerOid,
11032 : updateTriggerOid;
11033 :
11034 330 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11035 330 : if (!HeapTupleIsValid(tuple))
11036 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11037 330 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11038 :
11039 : /*
11040 : * As explained above: don't try to clone a constraint for which we're
11041 : * going to clone the parent.
11042 : */
11043 330 : if (list_member_oid(clone, constrForm->conparentid))
11044 : {
11045 126 : ReleaseSysCache(tuple);
11046 180 : continue;
11047 : }
11048 :
11049 : /*
11050 : * Don't clone self-referencing foreign keys, which can be in the
11051 : * partitioned table or in the partition-to-be.
11052 : */
11053 204 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11054 162 : constrForm->conrelid == RelationGetRelid(partitionRel))
11055 : {
11056 54 : ReleaseSysCache(tuple);
11057 54 : continue;
11058 : }
11059 :
11060 : /*
11061 : * Because we're only expanding the key space at the referenced side,
11062 : * we don't need to prevent any operation in the referencing table, so
11063 : * AccessShareLock suffices (assumes that dropping the constraint
11064 : * acquires AEL).
11065 : */
11066 150 : fkRel = table_open(constrForm->conrelid, AccessShareLock);
11067 :
11068 150 : indexOid = constrForm->conindid;
11069 150 : DeconstructFkConstraintRow(tuple,
11070 : &numfks,
11071 : conkey,
11072 : confkey,
11073 : conpfeqop,
11074 : conppeqop,
11075 : conffeqop,
11076 : &numfkdelsetcols,
11077 : confdelsetcols);
11078 :
11079 300 : for (int i = 0; i < numfks; i++)
11080 150 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11081 :
11082 150 : fkconstraint = makeNode(Constraint);
11083 150 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11084 150 : fkconstraint->conname = NameStr(constrForm->conname);
11085 150 : fkconstraint->deferrable = constrForm->condeferrable;
11086 150 : fkconstraint->initdeferred = constrForm->condeferred;
11087 150 : fkconstraint->location = -1;
11088 150 : fkconstraint->pktable = NULL;
11089 : /* ->fk_attrs determined below */
11090 150 : fkconstraint->pk_attrs = NIL;
11091 150 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11092 150 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11093 150 : fkconstraint->fk_del_action = constrForm->confdeltype;
11094 150 : fkconstraint->fk_del_set_cols = NIL;
11095 150 : fkconstraint->old_conpfeqop = NIL;
11096 150 : fkconstraint->old_pktable_oid = InvalidOid;
11097 150 : fkconstraint->skip_validation = false;
11098 150 : fkconstraint->initially_valid = true;
11099 :
11100 : /* set up colnames that are used to generate the constraint name */
11101 300 : for (int i = 0; i < numfks; i++)
11102 : {
11103 : Form_pg_attribute att;
11104 :
11105 150 : att = TupleDescAttr(RelationGetDescr(fkRel),
11106 : conkey[i] - 1);
11107 150 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11108 150 : makeString(NameStr(att->attname)));
11109 : }
11110 :
11111 : /*
11112 : * Add the new foreign key constraint pointing to the new partition.
11113 : * Because this new partition appears in the referenced side of the
11114 : * constraint, we don't need to set up for Phase 3 check.
11115 : */
11116 150 : partIndexId = index_get_partition(partitionRel, indexOid);
11117 150 : if (!OidIsValid(partIndexId))
11118 0 : elog(ERROR, "index for %u not found in partition %s",
11119 : indexOid, RelationGetRelationName(partitionRel));
11120 :
11121 : /*
11122 : * Get the "action" triggers belonging to the constraint to pass as
11123 : * parent OIDs for similar triggers that will be created on the
11124 : * partition in addFkRecurseReferenced().
11125 : */
11126 150 : GetForeignKeyActionTriggers(trigrel, constrOid,
11127 : constrForm->confrelid, constrForm->conrelid,
11128 : &deleteTriggerOid, &updateTriggerOid);
11129 :
11130 150 : addFkRecurseReferenced(NULL,
11131 : fkconstraint,
11132 : fkRel,
11133 : partitionRel,
11134 : partIndexId,
11135 : constrOid,
11136 : numfks,
11137 : mapped_confkey,
11138 : conkey,
11139 : conpfeqop,
11140 : conppeqop,
11141 : conffeqop,
11142 : numfkdelsetcols,
11143 : confdelsetcols,
11144 : true,
11145 : deleteTriggerOid,
11146 : updateTriggerOid,
11147 150 : constrForm->conperiod);
11148 :
11149 150 : table_close(fkRel, NoLock);
11150 150 : ReleaseSysCache(tuple);
11151 : }
11152 :
11153 10180 : table_close(trigrel, RowExclusiveLock);
11154 10180 : }
11155 :
11156 : /*
11157 : * CloneFkReferencing
11158 : * Subroutine for CloneForeignKeyConstraints
11159 : *
11160 : * For each FK constraint of the parent relation in the given list, find an
11161 : * equivalent constraint in its partition relation that can be reparented;
11162 : * if one cannot be found, create a new constraint in the partition as its
11163 : * child.
11164 : *
11165 : * If wqueue is given, it is used to set up phase-3 verification for each
11166 : * cloned constraint; if omitted, we assume that such verification is not
11167 : * needed (example: the partition is being created anew).
11168 : */
11169 : static void
11170 10180 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11171 : {
11172 : AttrMap *attmap;
11173 : List *partFKs;
11174 10180 : List *clone = NIL;
11175 : ListCell *cell;
11176 : Relation trigrel;
11177 :
11178 : /* obtain a list of constraints that we need to clone */
11179 11076 : foreach(cell, RelationGetFKeyList(parentRel))
11180 : {
11181 896 : ForeignKeyCacheInfo *fk = lfirst(cell);
11182 :
11183 896 : clone = lappend_oid(clone, fk->conoid);
11184 : }
11185 :
11186 : /*
11187 : * Silently do nothing if there's nothing to do. In particular, this
11188 : * avoids throwing a spurious error for foreign tables.
11189 : */
11190 10180 : if (clone == NIL)
11191 9732 : return;
11192 :
11193 448 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11194 0 : ereport(ERROR,
11195 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11196 : errmsg("foreign key constraints are not supported on foreign tables")));
11197 :
11198 : /*
11199 : * Triggers of the foreign keys will be manipulated a bunch of times in
11200 : * the loop below. To avoid repeatedly opening/closing the trigger
11201 : * catalog relation, we open it here and pass it to the subroutines called
11202 : * below.
11203 : */
11204 448 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11205 :
11206 : /*
11207 : * The constraint key may differ, if the columns in the partition are
11208 : * different. This map is used to convert them.
11209 : */
11210 448 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11211 : RelationGetDescr(parentRel),
11212 : false);
11213 :
11214 448 : partFKs = copyObject(RelationGetFKeyList(partRel));
11215 :
11216 1344 : foreach(cell, clone)
11217 : {
11218 896 : Oid parentConstrOid = lfirst_oid(cell);
11219 : Form_pg_constraint constrForm;
11220 : Relation pkrel;
11221 : HeapTuple tuple;
11222 : int numfks;
11223 : AttrNumber conkey[INDEX_MAX_KEYS];
11224 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11225 : AttrNumber confkey[INDEX_MAX_KEYS];
11226 : Oid conpfeqop[INDEX_MAX_KEYS];
11227 : Oid conppeqop[INDEX_MAX_KEYS];
11228 : Oid conffeqop[INDEX_MAX_KEYS];
11229 : int numfkdelsetcols;
11230 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11231 : Constraint *fkconstraint;
11232 : bool attached;
11233 : Oid indexOid;
11234 : Oid constrOid;
11235 : ObjectAddress address,
11236 : referenced;
11237 : ListCell *lc;
11238 : Oid insertTriggerOid,
11239 : updateTriggerOid;
11240 : bool with_period;
11241 :
11242 896 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11243 896 : if (!HeapTupleIsValid(tuple))
11244 0 : elog(ERROR, "cache lookup failed for constraint %u",
11245 : parentConstrOid);
11246 896 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11247 :
11248 : /* Don't clone constraints whose parents are being cloned */
11249 896 : if (list_member_oid(clone, constrForm->conparentid))
11250 : {
11251 424 : ReleaseSysCache(tuple);
11252 490 : continue;
11253 : }
11254 :
11255 : /*
11256 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11257 : * relation, that means to lock all partitions.
11258 : */
11259 472 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11260 472 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11261 184 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11262 : ShareRowExclusiveLock, NULL);
11263 :
11264 472 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11265 : conpfeqop, conppeqop, conffeqop,
11266 : &numfkdelsetcols, confdelsetcols);
11267 1082 : for (int i = 0; i < numfks; i++)
11268 610 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11269 :
11270 : /*
11271 : * Get the "check" triggers belonging to the constraint to pass as
11272 : * parent OIDs for similar triggers that will be created on the
11273 : * partition in addFkRecurseReferencing(). They are also passed to
11274 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11275 : * the partition's existing "check" triggers, that is, if the
11276 : * corresponding constraints is deemed attachable to the parent
11277 : * constraint.
11278 : */
11279 472 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11280 : constrForm->confrelid, constrForm->conrelid,
11281 : &insertTriggerOid, &updateTriggerOid);
11282 :
11283 : /*
11284 : * Before creating a new constraint, see whether any existing FKs are
11285 : * fit for the purpose. If one is, attach the parent constraint to
11286 : * it, and don't clone anything. This way we avoid the expensive
11287 : * verification step and don't end up with a duplicate FK, and we
11288 : * don't need to recurse to partitions for this constraint.
11289 : */
11290 472 : attached = false;
11291 556 : foreach(lc, partFKs)
11292 : {
11293 150 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11294 :
11295 150 : if (tryAttachPartitionForeignKey(fk,
11296 : RelationGetRelid(partRel),
11297 : parentConstrOid,
11298 : numfks,
11299 : mapped_conkey,
11300 : confkey,
11301 : conpfeqop,
11302 : insertTriggerOid,
11303 : updateTriggerOid,
11304 : trigrel))
11305 : {
11306 66 : attached = true;
11307 66 : table_close(pkrel, NoLock);
11308 66 : break;
11309 : }
11310 : }
11311 472 : if (attached)
11312 : {
11313 66 : ReleaseSysCache(tuple);
11314 66 : continue;
11315 : }
11316 :
11317 : /* No dice. Set up to create our own constraint */
11318 406 : fkconstraint = makeNode(Constraint);
11319 406 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11320 : /* ->conname determined below */
11321 406 : fkconstraint->deferrable = constrForm->condeferrable;
11322 406 : fkconstraint->initdeferred = constrForm->condeferred;
11323 406 : fkconstraint->location = -1;
11324 406 : fkconstraint->pktable = NULL;
11325 : /* ->fk_attrs determined below */
11326 406 : fkconstraint->pk_attrs = NIL;
11327 406 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11328 406 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11329 406 : fkconstraint->fk_del_action = constrForm->confdeltype;
11330 406 : fkconstraint->fk_del_set_cols = NIL;
11331 406 : fkconstraint->old_conpfeqop = NIL;
11332 406 : fkconstraint->old_pktable_oid = InvalidOid;
11333 406 : fkconstraint->skip_validation = false;
11334 406 : fkconstraint->initially_valid = true;
11335 902 : for (int i = 0; i < numfks; i++)
11336 : {
11337 : Form_pg_attribute att;
11338 :
11339 496 : att = TupleDescAttr(RelationGetDescr(partRel),
11340 : mapped_conkey[i] - 1);
11341 496 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11342 496 : makeString(NameStr(att->attname)));
11343 : }
11344 406 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
11345 : RelationGetRelid(partRel),
11346 406 : NameStr(constrForm->conname)))
11347 6 : fkconstraint->conname =
11348 6 : ChooseConstraintName(RelationGetRelationName(partRel),
11349 6 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
11350 : "fkey",
11351 6 : RelationGetNamespace(partRel), NIL);
11352 : else
11353 400 : fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
11354 :
11355 406 : indexOid = constrForm->conindid;
11356 406 : with_period = constrForm->conperiod;
11357 : constrOid =
11358 406 : CreateConstraintEntry(fkconstraint->conname,
11359 : constrForm->connamespace,
11360 : CONSTRAINT_FOREIGN,
11361 406 : fkconstraint->deferrable,
11362 406 : fkconstraint->initdeferred,
11363 406 : constrForm->convalidated,
11364 : parentConstrOid,
11365 : RelationGetRelid(partRel),
11366 : mapped_conkey,
11367 : numfks,
11368 : numfks,
11369 : InvalidOid, /* not a domain constraint */
11370 : indexOid,
11371 : constrForm->confrelid, /* same foreign rel */
11372 : confkey,
11373 : conpfeqop,
11374 : conppeqop,
11375 : conffeqop,
11376 : numfks,
11377 406 : fkconstraint->fk_upd_action,
11378 406 : fkconstraint->fk_del_action,
11379 : confdelsetcols,
11380 : numfkdelsetcols,
11381 406 : fkconstraint->fk_matchtype,
11382 : NULL,
11383 : NULL,
11384 : NULL,
11385 : false, /* islocal */
11386 : 1, /* inhcount */
11387 : false, /* conNoInherit */
11388 : with_period, /* conPeriod */
11389 : true);
11390 :
11391 : /* Set up partition dependencies for the new constraint */
11392 406 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
11393 406 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
11394 406 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
11395 406 : ObjectAddressSet(referenced, RelationRelationId,
11396 : RelationGetRelid(partRel));
11397 406 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
11398 :
11399 : /* Done with the cloned constraint's tuple */
11400 406 : ReleaseSysCache(tuple);
11401 :
11402 : /* Make all this visible before recursing */
11403 406 : CommandCounterIncrement();
11404 :
11405 406 : addFkRecurseReferencing(wqueue,
11406 : fkconstraint,
11407 : partRel,
11408 : pkrel,
11409 : indexOid,
11410 : constrOid,
11411 : numfks,
11412 : confkey,
11413 : mapped_conkey,
11414 : conpfeqop,
11415 : conppeqop,
11416 : conffeqop,
11417 : numfkdelsetcols,
11418 : confdelsetcols,
11419 : false, /* no old check exists */
11420 : AccessExclusiveLock,
11421 : insertTriggerOid,
11422 : updateTriggerOid,
11423 : with_period);
11424 406 : table_close(pkrel, NoLock);
11425 : }
11426 :
11427 448 : table_close(trigrel, RowExclusiveLock);
11428 : }
11429 :
11430 : /*
11431 : * When the parent of a partition receives [the referencing side of] a foreign
11432 : * key, we must propagate that foreign key to the partition. However, the
11433 : * partition might already have an equivalent foreign key; this routine
11434 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11435 : * by the other parameters. If they are equivalent, create the link between
11436 : * the two constraints and return true.
11437 : *
11438 : * If the given FK does not match the one defined by rest of the params,
11439 : * return false.
11440 : */
11441 : static bool
11442 180 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11443 : Oid partRelid,
11444 : Oid parentConstrOid,
11445 : int numfks,
11446 : AttrNumber *mapped_conkey,
11447 : AttrNumber *confkey,
11448 : Oid *conpfeqop,
11449 : Oid parentInsTrigger,
11450 : Oid parentUpdTrigger,
11451 : Relation trigrel)
11452 : {
11453 : HeapTuple parentConstrTup;
11454 : Form_pg_constraint parentConstr;
11455 : HeapTuple partcontup;
11456 : Form_pg_constraint partConstr;
11457 : ScanKeyData key;
11458 : SysScanDesc scan;
11459 : HeapTuple trigtup;
11460 : Oid insertTriggerOid,
11461 : updateTriggerOid;
11462 :
11463 180 : parentConstrTup = SearchSysCache1(CONSTROID,
11464 : ObjectIdGetDatum(parentConstrOid));
11465 180 : if (!HeapTupleIsValid(parentConstrTup))
11466 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11467 180 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11468 :
11469 : /*
11470 : * Do some quick & easy initial checks. If any of these fail, we cannot
11471 : * use this constraint.
11472 : */
11473 180 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11474 : {
11475 0 : ReleaseSysCache(parentConstrTup);
11476 0 : return false;
11477 : }
11478 510 : for (int i = 0; i < numfks; i++)
11479 : {
11480 330 : if (fk->conkey[i] != mapped_conkey[i] ||
11481 330 : fk->confkey[i] != confkey[i] ||
11482 330 : fk->conpfeqop[i] != conpfeqop[i])
11483 : {
11484 0 : ReleaseSysCache(parentConstrTup);
11485 0 : return false;
11486 : }
11487 : }
11488 :
11489 : /*
11490 : * Looks good so far; do some more extensive checks. Presumably the check
11491 : * for 'convalidated' could be dropped, since we don't really care about
11492 : * that, but let's be careful for now.
11493 : */
11494 180 : partcontup = SearchSysCache1(CONSTROID,
11495 : ObjectIdGetDatum(fk->conoid));
11496 180 : if (!HeapTupleIsValid(partcontup))
11497 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11498 180 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11499 180 : if (OidIsValid(partConstr->conparentid) ||
11500 156 : !partConstr->convalidated ||
11501 156 : partConstr->condeferrable != parentConstr->condeferrable ||
11502 128 : partConstr->condeferred != parentConstr->condeferred ||
11503 128 : partConstr->confupdtype != parentConstr->confupdtype ||
11504 92 : partConstr->confdeltype != parentConstr->confdeltype ||
11505 92 : partConstr->confmatchtype != parentConstr->confmatchtype)
11506 : {
11507 102 : ReleaseSysCache(parentConstrTup);
11508 102 : ReleaseSysCache(partcontup);
11509 102 : return false;
11510 : }
11511 :
11512 78 : ReleaseSysCache(partcontup);
11513 78 : ReleaseSysCache(parentConstrTup);
11514 :
11515 : /*
11516 : * Looks good! Attach this constraint. The action triggers in the new
11517 : * partition become redundant -- the parent table already has equivalent
11518 : * ones, and those will be able to reach the partition. Remove the ones
11519 : * in the partition. We identify them because they have our constraint
11520 : * OID, as well as being on the referenced rel.
11521 : */
11522 78 : ScanKeyInit(&key,
11523 : Anum_pg_trigger_tgconstraint,
11524 : BTEqualStrategyNumber, F_OIDEQ,
11525 : ObjectIdGetDatum(fk->conoid));
11526 78 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11527 : NULL, 1, &key);
11528 390 : while ((trigtup = systable_getnext(scan)) != NULL)
11529 : {
11530 312 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11531 : ObjectAddress trigger;
11532 :
11533 312 : if (trgform->tgconstrrelid != fk->conrelid)
11534 156 : continue;
11535 156 : if (trgform->tgrelid != fk->confrelid)
11536 0 : continue;
11537 :
11538 : /*
11539 : * The constraint is originally set up to contain this trigger as an
11540 : * implementation object, so there's a dependency record that links
11541 : * the two; however, since the trigger is no longer needed, we remove
11542 : * the dependency link in order to be able to drop the trigger while
11543 : * keeping the constraint intact.
11544 : */
11545 156 : deleteDependencyRecordsFor(TriggerRelationId,
11546 : trgform->oid,
11547 : false);
11548 : /* make dependency deletion visible to performDeletion */
11549 156 : CommandCounterIncrement();
11550 156 : ObjectAddressSet(trigger, TriggerRelationId,
11551 : trgform->oid);
11552 156 : performDeletion(&trigger, DROP_RESTRICT, 0);
11553 : /* make trigger drop visible, in case the loop iterates */
11554 156 : CommandCounterIncrement();
11555 : }
11556 :
11557 78 : systable_endscan(scan);
11558 :
11559 78 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11560 :
11561 : /*
11562 : * Like the constraint, attach partition's "check" triggers to the
11563 : * corresponding parent triggers.
11564 : */
11565 78 : GetForeignKeyCheckTriggers(trigrel,
11566 : fk->conoid, fk->confrelid, fk->conrelid,
11567 : &insertTriggerOid, &updateTriggerOid);
11568 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11569 78 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11570 : partRelid);
11571 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11572 78 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11573 : partRelid);
11574 :
11575 78 : CommandCounterIncrement();
11576 78 : return true;
11577 : }
11578 :
11579 : /*
11580 : * GetForeignKeyActionTriggers
11581 : * Returns delete and update "action" triggers of the given relation
11582 : * belonging to the given constraint
11583 : */
11584 : static void
11585 150 : GetForeignKeyActionTriggers(Relation trigrel,
11586 : Oid conoid, Oid confrelid, Oid conrelid,
11587 : Oid *deleteTriggerOid,
11588 : Oid *updateTriggerOid)
11589 : {
11590 : ScanKeyData key;
11591 : SysScanDesc scan;
11592 : HeapTuple trigtup;
11593 :
11594 150 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11595 150 : ScanKeyInit(&key,
11596 : Anum_pg_trigger_tgconstraint,
11597 : BTEqualStrategyNumber, F_OIDEQ,
11598 : ObjectIdGetDatum(conoid));
11599 :
11600 150 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11601 : NULL, 1, &key);
11602 312 : while ((trigtup = systable_getnext(scan)) != NULL)
11603 : {
11604 312 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11605 :
11606 312 : if (trgform->tgconstrrelid != conrelid)
11607 12 : continue;
11608 300 : if (trgform->tgrelid != confrelid)
11609 0 : continue;
11610 : /* Only ever look at "action" triggers on the PK side. */
11611 300 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11612 0 : continue;
11613 300 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11614 : {
11615 : Assert(*deleteTriggerOid == InvalidOid);
11616 150 : *deleteTriggerOid = trgform->oid;
11617 : }
11618 150 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11619 : {
11620 : Assert(*updateTriggerOid == InvalidOid);
11621 150 : *updateTriggerOid = trgform->oid;
11622 : }
11623 : #ifndef USE_ASSERT_CHECKING
11624 : /* In an assert-enabled build, continue looking to find duplicates */
11625 300 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11626 150 : break;
11627 : #endif
11628 : }
11629 :
11630 150 : if (!OidIsValid(*deleteTriggerOid))
11631 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11632 : conoid);
11633 150 : if (!OidIsValid(*updateTriggerOid))
11634 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11635 : conoid);
11636 :
11637 150 : systable_endscan(scan);
11638 150 : }
11639 :
11640 : /*
11641 : * GetForeignKeyCheckTriggers
11642 : * Returns insert and update "check" triggers of the given relation
11643 : * belonging to the given constraint
11644 : */
11645 : static void
11646 616 : GetForeignKeyCheckTriggers(Relation trigrel,
11647 : Oid conoid, Oid confrelid, Oid conrelid,
11648 : Oid *insertTriggerOid,
11649 : Oid *updateTriggerOid)
11650 : {
11651 : ScanKeyData key;
11652 : SysScanDesc scan;
11653 : HeapTuple trigtup;
11654 :
11655 616 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11656 616 : ScanKeyInit(&key,
11657 : Anum_pg_trigger_tgconstraint,
11658 : BTEqualStrategyNumber, F_OIDEQ,
11659 : ObjectIdGetDatum(conoid));
11660 :
11661 616 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11662 : NULL, 1, &key);
11663 2036 : while ((trigtup = systable_getnext(scan)) != NULL)
11664 : {
11665 2036 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11666 :
11667 2036 : if (trgform->tgconstrrelid != confrelid)
11668 720 : continue;
11669 1316 : if (trgform->tgrelid != conrelid)
11670 0 : continue;
11671 : /* Only ever look at "check" triggers on the FK side. */
11672 1316 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11673 84 : continue;
11674 1232 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11675 : {
11676 : Assert(*insertTriggerOid == InvalidOid);
11677 616 : *insertTriggerOid = trgform->oid;
11678 : }
11679 616 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11680 : {
11681 : Assert(*updateTriggerOid == InvalidOid);
11682 616 : *updateTriggerOid = trgform->oid;
11683 : }
11684 : #ifndef USE_ASSERT_CHECKING
11685 : /* In an assert-enabled build, continue looking to find duplicates. */
11686 1232 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11687 616 : break;
11688 : #endif
11689 : }
11690 :
11691 616 : if (!OidIsValid(*insertTriggerOid))
11692 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11693 : conoid);
11694 616 : if (!OidIsValid(*updateTriggerOid))
11695 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11696 : conoid);
11697 :
11698 616 : systable_endscan(scan);
11699 616 : }
11700 :
11701 : /*
11702 : * ALTER TABLE ALTER CONSTRAINT
11703 : *
11704 : * Update the attributes of a constraint.
11705 : *
11706 : * Currently only works for Foreign Key constraints.
11707 : *
11708 : * If the constraint is modified, returns its address; otherwise, return
11709 : * InvalidObjectAddress.
11710 : */
11711 : static ObjectAddress
11712 126 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11713 : bool recursing, LOCKMODE lockmode)
11714 : {
11715 : Constraint *cmdcon;
11716 : Relation conrel;
11717 : Relation tgrel;
11718 : SysScanDesc scan;
11719 : ScanKeyData skey[3];
11720 : HeapTuple contuple;
11721 : Form_pg_constraint currcon;
11722 : ObjectAddress address;
11723 126 : List *otherrelids = NIL;
11724 : ListCell *lc;
11725 :
11726 126 : cmdcon = castNode(Constraint, cmd->def);
11727 :
11728 126 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11729 126 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11730 :
11731 : /*
11732 : * Find and check the target constraint
11733 : */
11734 126 : ScanKeyInit(&skey[0],
11735 : Anum_pg_constraint_conrelid,
11736 : BTEqualStrategyNumber, F_OIDEQ,
11737 : ObjectIdGetDatum(RelationGetRelid(rel)));
11738 126 : ScanKeyInit(&skey[1],
11739 : Anum_pg_constraint_contypid,
11740 : BTEqualStrategyNumber, F_OIDEQ,
11741 : ObjectIdGetDatum(InvalidOid));
11742 126 : ScanKeyInit(&skey[2],
11743 : Anum_pg_constraint_conname,
11744 : BTEqualStrategyNumber, F_NAMEEQ,
11745 126 : CStringGetDatum(cmdcon->conname));
11746 126 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11747 : true, NULL, 3, skey);
11748 :
11749 : /* There can be at most one matching row */
11750 126 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11751 0 : ereport(ERROR,
11752 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11753 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11754 : cmdcon->conname, RelationGetRelationName(rel))));
11755 :
11756 126 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11757 126 : if (currcon->contype != CONSTRAINT_FOREIGN)
11758 0 : ereport(ERROR,
11759 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11760 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11761 : cmdcon->conname, RelationGetRelationName(rel))));
11762 :
11763 : /*
11764 : * If it's not the topmost constraint, raise an error.
11765 : *
11766 : * Altering a non-topmost constraint leaves some triggers untouched, since
11767 : * they are not directly connected to this constraint; also, pg_dump would
11768 : * ignore the deferrability status of the individual constraint, since it
11769 : * only dumps topmost constraints. Avoid these problems by refusing this
11770 : * operation and telling the user to alter the parent constraint instead.
11771 : */
11772 126 : if (OidIsValid(currcon->conparentid))
11773 : {
11774 : HeapTuple tp;
11775 12 : Oid parent = currcon->conparentid;
11776 12 : char *ancestorname = NULL;
11777 12 : char *ancestortable = NULL;
11778 :
11779 : /* Loop to find the topmost constraint */
11780 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11781 : {
11782 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11783 :
11784 : /* If no parent, this is the constraint we want */
11785 24 : if (!OidIsValid(contup->conparentid))
11786 : {
11787 12 : ancestorname = pstrdup(NameStr(contup->conname));
11788 12 : ancestortable = get_rel_name(contup->conrelid);
11789 12 : ReleaseSysCache(tp);
11790 12 : break;
11791 : }
11792 :
11793 12 : parent = contup->conparentid;
11794 12 : ReleaseSysCache(tp);
11795 : }
11796 :
11797 12 : ereport(ERROR,
11798 : (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11799 : cmdcon->conname, RelationGetRelationName(rel)),
11800 : ancestorname && ancestortable ?
11801 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11802 : cmdcon->conname, ancestorname, ancestortable) : 0,
11803 : errhint("You may alter the constraint it derives from instead.")));
11804 : }
11805 :
11806 : /*
11807 : * Do the actual catalog work. We can skip changing if already in the
11808 : * desired state, but not if a partitioned table: partitions need to be
11809 : * processed regardless, in case they had the constraint locally changed.
11810 : */
11811 114 : address = InvalidObjectAddress;
11812 114 : if (currcon->condeferrable != cmdcon->deferrable ||
11813 6 : currcon->condeferred != cmdcon->initdeferred ||
11814 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11815 : {
11816 114 : if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11817 : &otherrelids, lockmode))
11818 114 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11819 : }
11820 :
11821 : /*
11822 : * ATExecAlterConstrRecurse already invalidated relcache for the relations
11823 : * having the constraint itself; here we also invalidate for relations
11824 : * that have any triggers that are part of the constraint.
11825 : */
11826 258 : foreach(lc, otherrelids)
11827 144 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11828 :
11829 114 : systable_endscan(scan);
11830 :
11831 114 : table_close(tgrel, RowExclusiveLock);
11832 114 : table_close(conrel, RowExclusiveLock);
11833 :
11834 114 : return address;
11835 : }
11836 :
11837 : /*
11838 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11839 : * constraint is altered.
11840 : *
11841 : * *otherrelids is appended OIDs of relations containing affected triggers.
11842 : *
11843 : * Note that we must recurse even when the values are correct, in case
11844 : * indirect descendants have had their constraints altered locally.
11845 : * (This could be avoided if we forbade altering constraints in partitions
11846 : * but existing releases don't do that.)
11847 : */
11848 : static bool
11849 180 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11850 : Relation rel, HeapTuple contuple, List **otherrelids,
11851 : LOCKMODE lockmode)
11852 : {
11853 : Form_pg_constraint currcon;
11854 : Oid conoid;
11855 : Oid refrelid;
11856 180 : bool changed = false;
11857 :
11858 : /* since this function recurses, it could be driven to stack overflow */
11859 180 : check_stack_depth();
11860 :
11861 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11862 180 : conoid = currcon->oid;
11863 180 : refrelid = currcon->confrelid;
11864 :
11865 : /*
11866 : * Update pg_constraint with the flags from cmdcon.
11867 : *
11868 : * If called to modify a constraint that's already in the desired state,
11869 : * silently do nothing.
11870 : */
11871 180 : if (currcon->condeferrable != cmdcon->deferrable ||
11872 6 : currcon->condeferred != cmdcon->initdeferred)
11873 : {
11874 : HeapTuple copyTuple;
11875 : Form_pg_constraint copy_con;
11876 : HeapTuple tgtuple;
11877 : ScanKeyData tgkey;
11878 : SysScanDesc tgscan;
11879 :
11880 180 : copyTuple = heap_copytuple(contuple);
11881 180 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11882 180 : copy_con->condeferrable = cmdcon->deferrable;
11883 180 : copy_con->condeferred = cmdcon->initdeferred;
11884 180 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11885 :
11886 180 : InvokeObjectPostAlterHook(ConstraintRelationId,
11887 : conoid, 0);
11888 :
11889 180 : heap_freetuple(copyTuple);
11890 180 : changed = true;
11891 :
11892 : /* Make new constraint flags visible to others */
11893 180 : CacheInvalidateRelcache(rel);
11894 :
11895 : /*
11896 : * Now we need to update the multiple entries in pg_trigger that
11897 : * implement the constraint.
11898 : */
11899 180 : ScanKeyInit(&tgkey,
11900 : Anum_pg_trigger_tgconstraint,
11901 : BTEqualStrategyNumber, F_OIDEQ,
11902 : ObjectIdGetDatum(conoid));
11903 180 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11904 : NULL, 1, &tgkey);
11905 768 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11906 : {
11907 588 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11908 : Form_pg_trigger copy_tg;
11909 : HeapTuple tgCopyTuple;
11910 :
11911 : /*
11912 : * Remember OIDs of other relation(s) involved in FK constraint.
11913 : * (Note: it's likely that we could skip forcing a relcache inval
11914 : * for other rels that don't have a trigger whose properties
11915 : * change, but let's be conservative.)
11916 : */
11917 588 : if (tgform->tgrelid != RelationGetRelid(rel))
11918 288 : *otherrelids = list_append_unique_oid(*otherrelids,
11919 : tgform->tgrelid);
11920 :
11921 : /*
11922 : * Update deferrability of RI_FKey_noaction_del,
11923 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11924 : * triggers, but not others; see createForeignKeyActionTriggers
11925 : * and CreateFKCheckTrigger.
11926 : */
11927 588 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11928 474 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11929 342 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11930 192 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11931 42 : continue;
11932 :
11933 546 : tgCopyTuple = heap_copytuple(tgtuple);
11934 546 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11935 :
11936 546 : copy_tg->tgdeferrable = cmdcon->deferrable;
11937 546 : copy_tg->tginitdeferred = cmdcon->initdeferred;
11938 546 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11939 :
11940 546 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11941 :
11942 546 : heap_freetuple(tgCopyTuple);
11943 : }
11944 :
11945 180 : systable_endscan(tgscan);
11946 : }
11947 :
11948 : /*
11949 : * If the table at either end of the constraint is partitioned, we need to
11950 : * recurse and handle every constraint that is a child of this one.
11951 : *
11952 : * (This assumes that the recurse flag is forcibly set for partitioned
11953 : * tables, and not set for legacy inheritance, though we don't check for
11954 : * that here.)
11955 : */
11956 336 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11957 156 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11958 : {
11959 : ScanKeyData pkey;
11960 : SysScanDesc pscan;
11961 : HeapTuple childtup;
11962 :
11963 42 : ScanKeyInit(&pkey,
11964 : Anum_pg_constraint_conparentid,
11965 : BTEqualStrategyNumber, F_OIDEQ,
11966 : ObjectIdGetDatum(conoid));
11967 :
11968 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11969 : true, NULL, 1, &pkey);
11970 :
11971 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11972 : {
11973 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11974 : Relation childrel;
11975 :
11976 66 : childrel = table_open(childcon->conrelid, lockmode);
11977 66 : ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11978 : otherrelids, lockmode);
11979 66 : table_close(childrel, NoLock);
11980 : }
11981 :
11982 42 : systable_endscan(pscan);
11983 : }
11984 :
11985 180 : return changed;
11986 : }
11987 :
11988 : /*
11989 : * ALTER TABLE VALIDATE CONSTRAINT
11990 : *
11991 : * XXX The reason we handle recursion here rather than at Phase 1 is because
11992 : * there's no good way to skip recursing when handling foreign keys: there is
11993 : * no need to lock children in that case, yet we wouldn't be able to avoid
11994 : * doing so at that level.
11995 : *
11996 : * Return value is the address of the validated constraint. If the constraint
11997 : * was already validated, InvalidObjectAddress is returned.
11998 : */
11999 : static ObjectAddress
12000 436 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12001 : bool recurse, bool recursing, LOCKMODE lockmode)
12002 : {
12003 : Relation conrel;
12004 : SysScanDesc scan;
12005 : ScanKeyData skey[3];
12006 : HeapTuple tuple;
12007 : Form_pg_constraint con;
12008 : ObjectAddress address;
12009 :
12010 436 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12011 :
12012 : /*
12013 : * Find and check the target constraint
12014 : */
12015 436 : ScanKeyInit(&skey[0],
12016 : Anum_pg_constraint_conrelid,
12017 : BTEqualStrategyNumber, F_OIDEQ,
12018 : ObjectIdGetDatum(RelationGetRelid(rel)));
12019 436 : ScanKeyInit(&skey[1],
12020 : Anum_pg_constraint_contypid,
12021 : BTEqualStrategyNumber, F_OIDEQ,
12022 : ObjectIdGetDatum(InvalidOid));
12023 436 : ScanKeyInit(&skey[2],
12024 : Anum_pg_constraint_conname,
12025 : BTEqualStrategyNumber, F_NAMEEQ,
12026 : CStringGetDatum(constrName));
12027 436 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12028 : true, NULL, 3, skey);
12029 :
12030 : /* There can be at most one matching row */
12031 436 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12032 0 : ereport(ERROR,
12033 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12034 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12035 : constrName, RelationGetRelationName(rel))));
12036 :
12037 436 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12038 436 : if (con->contype != CONSTRAINT_FOREIGN &&
12039 132 : con->contype != CONSTRAINT_CHECK)
12040 0 : ereport(ERROR,
12041 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12042 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12043 : constrName, RelationGetRelationName(rel))));
12044 :
12045 436 : if (!con->convalidated)
12046 : {
12047 : AlteredTableInfo *tab;
12048 : HeapTuple copyTuple;
12049 : Form_pg_constraint copy_con;
12050 :
12051 418 : if (con->contype == CONSTRAINT_FOREIGN)
12052 : {
12053 : NewConstraint *newcon;
12054 : Constraint *fkconstraint;
12055 :
12056 : /* Queue validation for phase 3 */
12057 298 : fkconstraint = makeNode(Constraint);
12058 : /* for now this is all we need */
12059 298 : fkconstraint->conname = constrName;
12060 :
12061 298 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12062 298 : newcon->name = constrName;
12063 298 : newcon->contype = CONSTR_FOREIGN;
12064 298 : newcon->refrelid = con->confrelid;
12065 298 : newcon->refindid = con->conindid;
12066 298 : newcon->conid = con->oid;
12067 298 : newcon->qual = (Node *) fkconstraint;
12068 :
12069 : /* Find or create work queue entry for this table */
12070 298 : tab = ATGetQueueEntry(wqueue, rel);
12071 298 : tab->constraints = lappend(tab->constraints, newcon);
12072 :
12073 : /*
12074 : * We disallow creating invalid foreign keys to or from
12075 : * partitioned tables, so ignoring the recursion bit is okay.
12076 : */
12077 : }
12078 120 : else if (con->contype == CONSTRAINT_CHECK)
12079 : {
12080 120 : List *children = NIL;
12081 : ListCell *child;
12082 : NewConstraint *newcon;
12083 : Datum val;
12084 : char *conbin;
12085 :
12086 : /*
12087 : * If we're recursing, the parent has already done this, so skip
12088 : * it. Also, if the constraint is a NO INHERIT constraint, we
12089 : * shouldn't try to look for it in the children.
12090 : */
12091 120 : if (!recursing && !con->connoinherit)
12092 66 : children = find_all_inheritors(RelationGetRelid(rel),
12093 : lockmode, NULL);
12094 :
12095 : /*
12096 : * For CHECK constraints, we must ensure that we only mark the
12097 : * constraint as validated on the parent if it's already validated
12098 : * on the children.
12099 : *
12100 : * We recurse before validating on the parent, to reduce risk of
12101 : * deadlocks.
12102 : */
12103 234 : foreach(child, children)
12104 : {
12105 114 : Oid childoid = lfirst_oid(child);
12106 : Relation childrel;
12107 :
12108 114 : if (childoid == RelationGetRelid(rel))
12109 66 : continue;
12110 :
12111 : /*
12112 : * If we are told not to recurse, there had better not be any
12113 : * child tables, because we can't mark the constraint on the
12114 : * parent valid unless it is valid for all child tables.
12115 : */
12116 48 : if (!recurse)
12117 0 : ereport(ERROR,
12118 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12119 : errmsg("constraint must be validated on child tables too")));
12120 :
12121 : /* find_all_inheritors already got lock */
12122 48 : childrel = table_open(childoid, NoLock);
12123 :
12124 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12125 : true, lockmode);
12126 48 : table_close(childrel, NoLock);
12127 : }
12128 :
12129 : /* Queue validation for phase 3 */
12130 120 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12131 120 : newcon->name = constrName;
12132 120 : newcon->contype = CONSTR_CHECK;
12133 120 : newcon->refrelid = InvalidOid;
12134 120 : newcon->refindid = InvalidOid;
12135 120 : newcon->conid = con->oid;
12136 :
12137 120 : val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12138 : Anum_pg_constraint_conbin);
12139 120 : conbin = TextDatumGetCString(val);
12140 120 : newcon->qual = (Node *) stringToNode(conbin);
12141 :
12142 : /* Find or create work queue entry for this table */
12143 120 : tab = ATGetQueueEntry(wqueue, rel);
12144 120 : tab->constraints = lappend(tab->constraints, newcon);
12145 :
12146 : /*
12147 : * Invalidate relcache so that others see the new validated
12148 : * constraint.
12149 : */
12150 120 : CacheInvalidateRelcache(rel);
12151 : }
12152 :
12153 : /*
12154 : * Now update the catalog, while we have the door open.
12155 : */
12156 418 : copyTuple = heap_copytuple(tuple);
12157 418 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12158 418 : copy_con->convalidated = true;
12159 418 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12160 :
12161 418 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12162 :
12163 418 : heap_freetuple(copyTuple);
12164 :
12165 418 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12166 : }
12167 : else
12168 18 : address = InvalidObjectAddress; /* already validated */
12169 :
12170 436 : systable_endscan(scan);
12171 :
12172 436 : table_close(conrel, RowExclusiveLock);
12173 :
12174 436 : return address;
12175 : }
12176 :
12177 :
12178 : /*
12179 : * transformColumnNameList - transform list of column names
12180 : *
12181 : * Lookup each name and return its attnum and, optionally, type OID
12182 : *
12183 : * Note: the name of this function suggests that it's general-purpose,
12184 : * but actually it's only used to look up names appearing in foreign-key
12185 : * clauses. The error messages would need work to use it in other cases,
12186 : * and perhaps the validity checks as well.
12187 : */
12188 : static int
12189 6160 : transformColumnNameList(Oid relId, List *colList,
12190 : int16 *attnums, Oid *atttypids)
12191 : {
12192 : ListCell *l;
12193 : int attnum;
12194 :
12195 6160 : attnum = 0;
12196 11262 : foreach(l, colList)
12197 : {
12198 5168 : char *attname = strVal(lfirst(l));
12199 : HeapTuple atttuple;
12200 : Form_pg_attribute attform;
12201 :
12202 5168 : atttuple = SearchSysCacheAttName(relId, attname);
12203 5168 : if (!HeapTupleIsValid(atttuple))
12204 54 : ereport(ERROR,
12205 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12206 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12207 : attname)));
12208 5114 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12209 5114 : if (attform->attnum < 0)
12210 12 : ereport(ERROR,
12211 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12212 : errmsg("system columns cannot be used in foreign keys")));
12213 5102 : if (attnum >= INDEX_MAX_KEYS)
12214 0 : ereport(ERROR,
12215 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12216 : errmsg("cannot have more than %d keys in a foreign key",
12217 : INDEX_MAX_KEYS)));
12218 5102 : attnums[attnum] = attform->attnum;
12219 5102 : if (atttypids != NULL)
12220 5072 : atttypids[attnum] = attform->atttypid;
12221 5102 : ReleaseSysCache(atttuple);
12222 5102 : attnum++;
12223 : }
12224 :
12225 6094 : return attnum;
12226 : }
12227 :
12228 : /*
12229 : * transformFkeyGetPrimaryKey -
12230 : *
12231 : * Look up the names, attnums, and types of the primary key attributes
12232 : * for the pkrel. Also return the index OID and index opclasses of the
12233 : * index supporting the primary key. Also return whether the index has
12234 : * WITHOUT OVERLAPS.
12235 : *
12236 : * All parameters except pkrel are output parameters. Also, the function
12237 : * return value is the number of attributes in the primary key.
12238 : *
12239 : * Used when the column list in the REFERENCES specification is omitted.
12240 : */
12241 : static int
12242 1070 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12243 : List **attnamelist,
12244 : int16 *attnums, Oid *atttypids,
12245 : Oid *opclasses, bool *pk_has_without_overlaps)
12246 : {
12247 : List *indexoidlist;
12248 : ListCell *indexoidscan;
12249 1070 : HeapTuple indexTuple = NULL;
12250 1070 : Form_pg_index indexStruct = NULL;
12251 : Datum indclassDatum;
12252 : oidvector *indclass;
12253 : int i;
12254 :
12255 : /*
12256 : * Get the list of index OIDs for the table from the relcache, and look up
12257 : * each one in the pg_index syscache until we find one marked primary key
12258 : * (hopefully there isn't more than one such). Insist it's valid, too.
12259 : */
12260 1070 : *indexOid = InvalidOid;
12261 :
12262 1070 : indexoidlist = RelationGetIndexList(pkrel);
12263 :
12264 1076 : foreach(indexoidscan, indexoidlist)
12265 : {
12266 1076 : Oid indexoid = lfirst_oid(indexoidscan);
12267 :
12268 1076 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12269 1076 : if (!HeapTupleIsValid(indexTuple))
12270 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12271 1076 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12272 1076 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12273 : {
12274 : /*
12275 : * Refuse to use a deferrable primary key. This is per SQL spec,
12276 : * and there would be a lot of interesting semantic problems if we
12277 : * tried to allow it.
12278 : */
12279 1070 : if (!indexStruct->indimmediate)
12280 0 : ereport(ERROR,
12281 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12282 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12283 : RelationGetRelationName(pkrel))));
12284 :
12285 1070 : *indexOid = indexoid;
12286 1070 : break;
12287 : }
12288 6 : ReleaseSysCache(indexTuple);
12289 : }
12290 :
12291 1070 : list_free(indexoidlist);
12292 :
12293 : /*
12294 : * Check that we found it
12295 : */
12296 1070 : if (!OidIsValid(*indexOid))
12297 0 : ereport(ERROR,
12298 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12299 : errmsg("there is no primary key for referenced table \"%s\"",
12300 : RelationGetRelationName(pkrel))));
12301 :
12302 : /* Must get indclass the hard way */
12303 1070 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12304 : Anum_pg_index_indclass);
12305 1070 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12306 :
12307 : /*
12308 : * Now build the list of PK attributes from the indkey definition (we
12309 : * assume a primary key cannot have expressional elements)
12310 : */
12311 1070 : *attnamelist = NIL;
12312 2504 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12313 : {
12314 1434 : int pkattno = indexStruct->indkey.values[i];
12315 :
12316 1434 : attnums[i] = pkattno;
12317 1434 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12318 1434 : opclasses[i] = indclass->values[i];
12319 1434 : *attnamelist = lappend(*attnamelist,
12320 1434 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12321 : }
12322 :
12323 1070 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12324 :
12325 1070 : ReleaseSysCache(indexTuple);
12326 :
12327 1070 : return i;
12328 : }
12329 :
12330 : /*
12331 : * transformFkeyCheckAttrs -
12332 : *
12333 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12334 : * reference as part of a foreign key constraint.
12335 : *
12336 : * Returns the OID of the unique index supporting the constraint and
12337 : * populates the caller-provided 'opclasses' array with the opclasses
12338 : * associated with the index columns. Also sets whether the index
12339 : * uses WITHOUT OVERLAPS.
12340 : *
12341 : * Raises an ERROR on validation failure.
12342 : */
12343 : static Oid
12344 1260 : transformFkeyCheckAttrs(Relation pkrel,
12345 : int numattrs, int16 *attnums,
12346 : bool with_period, Oid *opclasses,
12347 : bool *pk_has_without_overlaps)
12348 : {
12349 1260 : Oid indexoid = InvalidOid;
12350 1260 : bool found = false;
12351 1260 : bool found_deferrable = false;
12352 : List *indexoidlist;
12353 : ListCell *indexoidscan;
12354 : int i,
12355 : j;
12356 :
12357 : /*
12358 : * Reject duplicate appearances of columns in the referenced-columns list.
12359 : * Such a case is forbidden by the SQL standard, and even if we thought it
12360 : * useful to allow it, there would be ambiguity about how to match the
12361 : * list to unique indexes (in particular, it'd be unclear which index
12362 : * opclass goes with which FK column).
12363 : */
12364 2950 : for (i = 0; i < numattrs; i++)
12365 : {
12366 2238 : for (j = i + 1; j < numattrs; j++)
12367 : {
12368 548 : if (attnums[i] == attnums[j])
12369 24 : ereport(ERROR,
12370 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12371 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12372 : }
12373 : }
12374 :
12375 : /*
12376 : * Get the list of index OIDs for the table from the relcache, and look up
12377 : * each one in the pg_index syscache, and match unique indexes to the list
12378 : * of attnums we are given.
12379 : */
12380 1236 : indexoidlist = RelationGetIndexList(pkrel);
12381 :
12382 1416 : foreach(indexoidscan, indexoidlist)
12383 : {
12384 : HeapTuple indexTuple;
12385 : Form_pg_index indexStruct;
12386 :
12387 1404 : indexoid = lfirst_oid(indexoidscan);
12388 1404 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12389 1404 : if (!HeapTupleIsValid(indexTuple))
12390 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12391 1404 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12392 :
12393 : /*
12394 : * Must have the right number of columns; must be unique (or if
12395 : * temporal then exclusion instead) and not a partial index; forget it
12396 : * if there are any expressions, too. Invalid indexes are out as well.
12397 : */
12398 2700 : if (indexStruct->indnkeyatts == numattrs &&
12399 1296 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12400 2564 : indexStruct->indisvalid &&
12401 2564 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12402 1282 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12403 : {
12404 : Datum indclassDatum;
12405 : oidvector *indclass;
12406 :
12407 : /* Must get indclass the hard way */
12408 1282 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12409 : Anum_pg_index_indclass);
12410 1282 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12411 :
12412 : /*
12413 : * The given attnum list may match the index columns in any order.
12414 : * Check for a match, and extract the appropriate opclasses while
12415 : * we're at it.
12416 : *
12417 : * We know that attnums[] is duplicate-free per the test at the
12418 : * start of this function, and we checked above that the number of
12419 : * index columns agrees, so if we find a match for each attnums[]
12420 : * entry then we must have a one-to-one match in some order.
12421 : */
12422 2960 : for (i = 0; i < numattrs; i++)
12423 : {
12424 1736 : found = false;
12425 2318 : for (j = 0; j < numattrs; j++)
12426 : {
12427 2260 : if (attnums[i] == indexStruct->indkey.values[j])
12428 : {
12429 1678 : opclasses[i] = indclass->values[j];
12430 1678 : found = true;
12431 1678 : break;
12432 : }
12433 : }
12434 1736 : if (!found)
12435 58 : break;
12436 : }
12437 : /* The last attribute in the index must be the PERIOD FK part */
12438 1282 : if (found && with_period)
12439 : {
12440 130 : int16 periodattnum = attnums[numattrs - 1];
12441 :
12442 130 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12443 : }
12444 :
12445 : /*
12446 : * Refuse to use a deferrable unique/primary key. This is per SQL
12447 : * spec, and there would be a lot of interesting semantic problems
12448 : * if we tried to allow it.
12449 : */
12450 1282 : if (found && !indexStruct->indimmediate)
12451 : {
12452 : /*
12453 : * Remember that we found an otherwise matching index, so that
12454 : * we can generate a more appropriate error message.
12455 : */
12456 0 : found_deferrable = true;
12457 0 : found = false;
12458 : }
12459 :
12460 : /* We need to know whether the index has WITHOUT OVERLAPS */
12461 1282 : if (found)
12462 1224 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12463 : }
12464 1404 : ReleaseSysCache(indexTuple);
12465 1404 : if (found)
12466 1224 : break;
12467 : }
12468 :
12469 1236 : if (!found)
12470 : {
12471 12 : if (found_deferrable)
12472 0 : ereport(ERROR,
12473 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12474 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12475 : RelationGetRelationName(pkrel))));
12476 : else
12477 12 : ereport(ERROR,
12478 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12479 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12480 : RelationGetRelationName(pkrel))));
12481 : }
12482 :
12483 1224 : list_free(indexoidlist);
12484 :
12485 1224 : return indexoid;
12486 : }
12487 :
12488 : /*
12489 : * findFkeyCast -
12490 : *
12491 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12492 : * Caller has equal regard for binary coercibility and for an exact match.
12493 : */
12494 : static CoercionPathType
12495 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12496 : {
12497 : CoercionPathType ret;
12498 :
12499 12 : if (targetTypeId == sourceTypeId)
12500 : {
12501 12 : ret = COERCION_PATH_RELABELTYPE;
12502 12 : *funcid = InvalidOid;
12503 : }
12504 : else
12505 : {
12506 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12507 : COERCION_IMPLICIT, funcid);
12508 0 : if (ret == COERCION_PATH_NONE)
12509 : /* A previously-relied-upon cast is now gone. */
12510 0 : elog(ERROR, "could not find cast from %u to %u",
12511 : sourceTypeId, targetTypeId);
12512 : }
12513 :
12514 12 : return ret;
12515 : }
12516 :
12517 : /*
12518 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12519 : *
12520 : * Note: we have already checked that the user owns the referencing table,
12521 : * else we'd have failed much earlier; no additional checks are needed for it.
12522 : */
12523 : static void
12524 2258 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12525 : {
12526 2258 : Oid roleid = GetUserId();
12527 : AclResult aclresult;
12528 : int i;
12529 :
12530 : /* Okay if we have relation-level REFERENCES permission */
12531 2258 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12532 : ACL_REFERENCES);
12533 2258 : if (aclresult == ACLCHECK_OK)
12534 2258 : return;
12535 : /* Else we must have REFERENCES on each column */
12536 0 : for (i = 0; i < natts; i++)
12537 : {
12538 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12539 : roleid, ACL_REFERENCES);
12540 0 : if (aclresult != ACLCHECK_OK)
12541 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12542 0 : RelationGetRelationName(rel));
12543 : }
12544 : }
12545 :
12546 : /*
12547 : * Scan the existing rows in a table to verify they meet a proposed FK
12548 : * constraint.
12549 : *
12550 : * Caller must have opened and locked both relations appropriately.
12551 : */
12552 : static void
12553 1084 : validateForeignKeyConstraint(char *conname,
12554 : Relation rel,
12555 : Relation pkrel,
12556 : Oid pkindOid,
12557 : Oid constraintOid,
12558 : bool hasperiod)
12559 : {
12560 : TupleTableSlot *slot;
12561 : TableScanDesc scan;
12562 1084 : Trigger trig = {0};
12563 : Snapshot snapshot;
12564 : MemoryContext oldcxt;
12565 : MemoryContext perTupCxt;
12566 :
12567 1084 : ereport(DEBUG1,
12568 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12569 :
12570 : /*
12571 : * Build a trigger call structure; we'll need it either way.
12572 : */
12573 1084 : trig.tgoid = InvalidOid;
12574 1084 : trig.tgname = conname;
12575 1084 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12576 1084 : trig.tgisinternal = true;
12577 1084 : trig.tgconstrrelid = RelationGetRelid(pkrel);
12578 1084 : trig.tgconstrindid = pkindOid;
12579 1084 : trig.tgconstraint = constraintOid;
12580 1084 : trig.tgdeferrable = false;
12581 1084 : trig.tginitdeferred = false;
12582 : /* we needn't fill in remaining fields */
12583 :
12584 : /*
12585 : * See if we can do it with a single LEFT JOIN query. A false result
12586 : * indicates we must proceed with the fire-the-trigger method. We can't do
12587 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12588 : * left joins.
12589 : */
12590 1084 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12591 876 : return;
12592 :
12593 : /*
12594 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12595 : * if that tuple had just been inserted. If any of those fail, it should
12596 : * ereport(ERROR) and that's that.
12597 : */
12598 146 : snapshot = RegisterSnapshot(GetLatestSnapshot());
12599 146 : slot = table_slot_create(rel, NULL);
12600 146 : scan = table_beginscan(rel, snapshot, 0, NULL);
12601 :
12602 146 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12603 : "validateForeignKeyConstraint",
12604 : ALLOCSET_SMALL_SIZES);
12605 146 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12606 :
12607 230 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12608 : {
12609 102 : LOCAL_FCINFO(fcinfo, 0);
12610 102 : TriggerData trigdata = {0};
12611 :
12612 102 : CHECK_FOR_INTERRUPTS();
12613 :
12614 : /*
12615 : * Make a call to the trigger function
12616 : *
12617 : * No parameters are passed, but we do set a context
12618 : */
12619 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12620 :
12621 : /*
12622 : * We assume RI_FKey_check_ins won't look at flinfo...
12623 : */
12624 102 : trigdata.type = T_TriggerData;
12625 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12626 102 : trigdata.tg_relation = rel;
12627 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12628 102 : trigdata.tg_trigslot = slot;
12629 102 : trigdata.tg_trigger = &trig;
12630 :
12631 102 : fcinfo->context = (Node *) &trigdata;
12632 :
12633 102 : RI_FKey_check_ins(fcinfo);
12634 :
12635 84 : MemoryContextReset(perTupCxt);
12636 : }
12637 :
12638 128 : MemoryContextSwitchTo(oldcxt);
12639 128 : MemoryContextDelete(perTupCxt);
12640 128 : table_endscan(scan);
12641 128 : UnregisterSnapshot(snapshot);
12642 128 : ExecDropSingleTupleTableSlot(slot);
12643 : }
12644 :
12645 : /*
12646 : * CreateFKCheckTrigger
12647 : * Creates the insert (on_insert=true) or update "check" trigger that
12648 : * implements a given foreign key
12649 : *
12650 : * Returns the OID of the so created trigger.
12651 : */
12652 : static Oid
12653 5492 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12654 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12655 : bool on_insert)
12656 : {
12657 : ObjectAddress trigAddress;
12658 : CreateTrigStmt *fk_trigger;
12659 :
12660 : /*
12661 : * Note: for a self-referential FK (referencing and referenced tables are
12662 : * the same), it is important that the ON UPDATE action fires before the
12663 : * CHECK action, since both triggers will fire on the same row during an
12664 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12665 : * state of the row. Triggers fire in name order, so we ensure this by
12666 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12667 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12668 : */
12669 5492 : fk_trigger = makeNode(CreateTrigStmt);
12670 5492 : fk_trigger->replace = false;
12671 5492 : fk_trigger->isconstraint = true;
12672 5492 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
12673 5492 : fk_trigger->relation = NULL;
12674 :
12675 : /* Either ON INSERT or ON UPDATE */
12676 5492 : if (on_insert)
12677 : {
12678 2746 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12679 2746 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12680 : }
12681 : else
12682 : {
12683 2746 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12684 2746 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12685 : }
12686 :
12687 5492 : fk_trigger->args = NIL;
12688 5492 : fk_trigger->row = true;
12689 5492 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12690 5492 : fk_trigger->columns = NIL;
12691 5492 : fk_trigger->whenClause = NULL;
12692 5492 : fk_trigger->transitionRels = NIL;
12693 5492 : fk_trigger->deferrable = fkconstraint->deferrable;
12694 5492 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12695 5492 : fk_trigger->constrrel = NULL;
12696 :
12697 5492 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12698 : constraintOid, indexOid, InvalidOid,
12699 : parentTrigOid, NULL, true, false);
12700 :
12701 : /* Make changes-so-far visible */
12702 5492 : CommandCounterIncrement();
12703 :
12704 5492 : return trigAddress.objectId;
12705 : }
12706 :
12707 : /*
12708 : * createForeignKeyActionTriggers
12709 : * Create the referenced-side "action" triggers that implement a foreign
12710 : * key.
12711 : *
12712 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
12713 : * *updateTrigOid.
12714 : */
12715 : static void
12716 2762 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12717 : Oid constraintOid, Oid indexOid,
12718 : Oid parentDelTrigger, Oid parentUpdTrigger,
12719 : Oid *deleteTrigOid, Oid *updateTrigOid)
12720 : {
12721 : CreateTrigStmt *fk_trigger;
12722 : ObjectAddress trigAddress;
12723 :
12724 : /*
12725 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12726 : * DELETE action on the referenced table.
12727 : */
12728 2762 : fk_trigger = makeNode(CreateTrigStmt);
12729 2762 : fk_trigger->replace = false;
12730 2762 : fk_trigger->isconstraint = true;
12731 2762 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12732 2762 : fk_trigger->relation = NULL;
12733 2762 : fk_trigger->args = NIL;
12734 2762 : fk_trigger->row = true;
12735 2762 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12736 2762 : fk_trigger->events = TRIGGER_TYPE_DELETE;
12737 2762 : fk_trigger->columns = NIL;
12738 2762 : fk_trigger->whenClause = NULL;
12739 2762 : fk_trigger->transitionRels = NIL;
12740 2762 : fk_trigger->constrrel = NULL;
12741 :
12742 2762 : switch (fkconstraint->fk_del_action)
12743 : {
12744 2100 : case FKCONSTR_ACTION_NOACTION:
12745 2100 : fk_trigger->deferrable = fkconstraint->deferrable;
12746 2100 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12747 2100 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12748 2100 : break;
12749 64 : case FKCONSTR_ACTION_RESTRICT:
12750 64 : fk_trigger->deferrable = false;
12751 64 : fk_trigger->initdeferred = false;
12752 64 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12753 64 : break;
12754 440 : case FKCONSTR_ACTION_CASCADE:
12755 440 : fk_trigger->deferrable = false;
12756 440 : fk_trigger->initdeferred = false;
12757 440 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12758 440 : break;
12759 98 : case FKCONSTR_ACTION_SETNULL:
12760 98 : fk_trigger->deferrable = false;
12761 98 : fk_trigger->initdeferred = false;
12762 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12763 98 : break;
12764 60 : case FKCONSTR_ACTION_SETDEFAULT:
12765 60 : fk_trigger->deferrable = false;
12766 60 : fk_trigger->initdeferred = false;
12767 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12768 60 : break;
12769 0 : default:
12770 0 : elog(ERROR, "unrecognized FK action type: %d",
12771 : (int) fkconstraint->fk_del_action);
12772 : break;
12773 : }
12774 :
12775 2762 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12776 : RelationGetRelid(rel),
12777 : constraintOid, indexOid, InvalidOid,
12778 : parentDelTrigger, NULL, true, false);
12779 2762 : if (deleteTrigOid)
12780 2696 : *deleteTrigOid = trigAddress.objectId;
12781 :
12782 : /* Make changes-so-far visible */
12783 2762 : CommandCounterIncrement();
12784 :
12785 : /*
12786 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12787 : * UPDATE action on the referenced table.
12788 : */
12789 2762 : fk_trigger = makeNode(CreateTrigStmt);
12790 2762 : fk_trigger->replace = false;
12791 2762 : fk_trigger->isconstraint = true;
12792 2762 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12793 2762 : fk_trigger->relation = NULL;
12794 2762 : fk_trigger->args = NIL;
12795 2762 : fk_trigger->row = true;
12796 2762 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12797 2762 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12798 2762 : fk_trigger->columns = NIL;
12799 2762 : fk_trigger->whenClause = NULL;
12800 2762 : fk_trigger->transitionRels = NIL;
12801 2762 : fk_trigger->constrrel = NULL;
12802 :
12803 2762 : switch (fkconstraint->fk_upd_action)
12804 : {
12805 2328 : case FKCONSTR_ACTION_NOACTION:
12806 2328 : fk_trigger->deferrable = fkconstraint->deferrable;
12807 2328 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12808 2328 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12809 2328 : break;
12810 42 : case FKCONSTR_ACTION_RESTRICT:
12811 42 : fk_trigger->deferrable = false;
12812 42 : fk_trigger->initdeferred = false;
12813 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12814 42 : break;
12815 288 : case FKCONSTR_ACTION_CASCADE:
12816 288 : fk_trigger->deferrable = false;
12817 288 : fk_trigger->initdeferred = false;
12818 288 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12819 288 : break;
12820 62 : case FKCONSTR_ACTION_SETNULL:
12821 62 : fk_trigger->deferrable = false;
12822 62 : fk_trigger->initdeferred = false;
12823 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12824 62 : break;
12825 42 : case FKCONSTR_ACTION_SETDEFAULT:
12826 42 : fk_trigger->deferrable = false;
12827 42 : fk_trigger->initdeferred = false;
12828 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12829 42 : break;
12830 0 : default:
12831 0 : elog(ERROR, "unrecognized FK action type: %d",
12832 : (int) fkconstraint->fk_upd_action);
12833 : break;
12834 : }
12835 :
12836 2762 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12837 : RelationGetRelid(rel),
12838 : constraintOid, indexOid, InvalidOid,
12839 : parentUpdTrigger, NULL, true, false);
12840 2762 : if (updateTrigOid)
12841 2696 : *updateTrigOid = trigAddress.objectId;
12842 2762 : }
12843 :
12844 : /*
12845 : * createForeignKeyCheckTriggers
12846 : * Create the referencing-side "check" triggers that implement a foreign
12847 : * key.
12848 : *
12849 : * Returns the OIDs of the so created triggers in *insertTrigOid and
12850 : * *updateTrigOid.
12851 : */
12852 : static void
12853 2746 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12854 : Constraint *fkconstraint, Oid constraintOid,
12855 : Oid indexOid,
12856 : Oid parentInsTrigger, Oid parentUpdTrigger,
12857 : Oid *insertTrigOid, Oid *updateTrigOid)
12858 : {
12859 2746 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12860 : constraintOid, indexOid,
12861 : parentInsTrigger, true);
12862 2746 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12863 : constraintOid, indexOid,
12864 : parentUpdTrigger, false);
12865 2746 : }
12866 :
12867 : /*
12868 : * ALTER TABLE DROP CONSTRAINT
12869 : *
12870 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12871 : */
12872 : static void
12873 962 : ATExecDropConstraint(Relation rel, const char *constrName,
12874 : DropBehavior behavior, bool recurse,
12875 : bool missing_ok, LOCKMODE lockmode)
12876 : {
12877 : Relation conrel;
12878 : SysScanDesc scan;
12879 : ScanKeyData skey[3];
12880 : HeapTuple tuple;
12881 962 : bool found = false;
12882 :
12883 962 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12884 :
12885 : /*
12886 : * Find and drop the target constraint
12887 : */
12888 962 : ScanKeyInit(&skey[0],
12889 : Anum_pg_constraint_conrelid,
12890 : BTEqualStrategyNumber, F_OIDEQ,
12891 : ObjectIdGetDatum(RelationGetRelid(rel)));
12892 962 : ScanKeyInit(&skey[1],
12893 : Anum_pg_constraint_contypid,
12894 : BTEqualStrategyNumber, F_OIDEQ,
12895 : ObjectIdGetDatum(InvalidOid));
12896 962 : ScanKeyInit(&skey[2],
12897 : Anum_pg_constraint_conname,
12898 : BTEqualStrategyNumber, F_NAMEEQ,
12899 : CStringGetDatum(constrName));
12900 962 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12901 : true, NULL, 3, skey);
12902 :
12903 : /* There can be at most one matching row */
12904 962 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12905 : {
12906 926 : List *readyRels = NIL;
12907 :
12908 926 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
12909 : missing_ok, &readyRels, lockmode);
12910 752 : found = true;
12911 : }
12912 :
12913 788 : systable_endscan(scan);
12914 :
12915 788 : if (!found)
12916 : {
12917 36 : if (!missing_ok)
12918 24 : ereport(ERROR,
12919 : errcode(ERRCODE_UNDEFINED_OBJECT),
12920 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12921 : constrName, RelationGetRelationName(rel)));
12922 : else
12923 12 : ereport(NOTICE,
12924 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12925 : constrName, RelationGetRelationName(rel)));
12926 : }
12927 :
12928 764 : table_close(conrel, RowExclusiveLock);
12929 764 : }
12930 :
12931 : /*
12932 : * Remove a constraint, using its pg_constraint tuple
12933 : *
12934 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12935 : * DROP NOT NULL.
12936 : *
12937 : * Returns the address of the constraint being removed.
12938 : */
12939 : static ObjectAddress
12940 1320 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12941 : bool recurse, bool recursing, bool missing_ok, List **readyRels,
12942 : LOCKMODE lockmode)
12943 : {
12944 : Relation conrel;
12945 : Form_pg_constraint con;
12946 : ObjectAddress conobj;
12947 : List *children;
12948 1320 : bool is_no_inherit_constraint = false;
12949 : char *constrName;
12950 1320 : List *unconstrained_cols = NIL;
12951 1320 : char *colname = NULL;
12952 1320 : bool dropping_pk = false;
12953 :
12954 1320 : if (list_member_oid(*readyRels, RelationGetRelid(rel)))
12955 0 : return InvalidObjectAddress;
12956 1320 : *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12957 :
12958 : /* Guard against stack overflow due to overly deep inheritance tree. */
12959 1320 : check_stack_depth();
12960 :
12961 : /* At top level, permission check was done in ATPrepCmd, else do it */
12962 1320 : if (recursing)
12963 234 : ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12964 :
12965 1314 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12966 :
12967 1314 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12968 1314 : constrName = NameStr(con->conname);
12969 :
12970 : /*
12971 : * If we're asked to drop a constraint which is both defined locally and
12972 : * inherited, we can simply mark it as no longer having a local
12973 : * definition, and no further changes are required.
12974 : *
12975 : * XXX We do this for not-null constraints only, not CHECK, because the
12976 : * latter have historically not behaved this way and it might be confusing
12977 : * to change the behavior now.
12978 : */
12979 1314 : if (con->contype == CONSTRAINT_NOTNULL &&
12980 484 : con->conislocal && con->coninhcount > 0)
12981 : {
12982 : HeapTuple copytup;
12983 :
12984 6 : copytup = heap_copytuple(constraintTup);
12985 6 : con = (Form_pg_constraint) GETSTRUCT(copytup);
12986 6 : con->conislocal = false;
12987 6 : CatalogTupleUpdate(conrel, ©tup->t_self, copytup);
12988 6 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
12989 :
12990 6 : CommandCounterIncrement();
12991 6 : table_close(conrel, RowExclusiveLock);
12992 6 : return conobj;
12993 : }
12994 :
12995 : /* Don't allow drop of inherited constraints */
12996 1308 : if (con->coninhcount > 0 && !recursing)
12997 138 : ereport(ERROR,
12998 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12999 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13000 : constrName, RelationGetRelationName(rel))));
13001 :
13002 : /*
13003 : * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
13004 : * more checks and actions below, so obtain the list of columns that are
13005 : * constrained by the constraint being dropped.
13006 : */
13007 1170 : if (con->contype == CONSTRAINT_NOTNULL)
13008 : {
13009 : AttrNumber colnum;
13010 :
13011 460 : colnum = extractNotNullColumn(constraintTup);
13012 460 : unconstrained_cols = list_make1_int(colnum);
13013 460 : colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
13014 : colnum - 1)->attname);
13015 : }
13016 710 : else if (con->contype == CONSTRAINT_PRIMARY)
13017 : {
13018 : Datum adatum;
13019 : ArrayType *arr;
13020 : int numkeys;
13021 : bool isNull;
13022 : int16 *attnums;
13023 :
13024 140 : dropping_pk = true;
13025 :
13026 140 : adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
13027 : RelationGetDescr(conrel), &isNull);
13028 140 : if (isNull)
13029 0 : elog(ERROR, "null conkey for constraint %u", con->oid);
13030 140 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
13031 140 : numkeys = ARR_DIMS(arr)[0];
13032 140 : if (ARR_NDIM(arr) != 1 ||
13033 140 : numkeys < 0 ||
13034 140 : ARR_HASNULL(arr) ||
13035 140 : ARR_ELEMTYPE(arr) != INT2OID)
13036 0 : elog(ERROR, "conkey is not a 1-D smallint array");
13037 140 : attnums = (int16 *) ARR_DATA_PTR(arr);
13038 :
13039 318 : for (int i = 0; i < numkeys; i++)
13040 178 : unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
13041 : }
13042 :
13043 1170 : is_no_inherit_constraint = con->connoinherit;
13044 :
13045 : /*
13046 : * If it's a foreign-key constraint, we'd better lock the referenced table
13047 : * and check that that's not in use, just as we've already done for the
13048 : * constrained table (else we might, eg, be dropping a trigger that has
13049 : * unfired events). But we can/must skip that in the self-referential
13050 : * case.
13051 : */
13052 1170 : if (con->contype == CONSTRAINT_FOREIGN &&
13053 192 : con->confrelid != RelationGetRelid(rel))
13054 : {
13055 : Relation frel;
13056 :
13057 : /* Must match lock taken by RemoveTriggerById: */
13058 192 : frel = table_open(con->confrelid, AccessExclusiveLock);
13059 192 : CheckTableNotInUse(frel, "ALTER TABLE");
13060 186 : table_close(frel, NoLock);
13061 : }
13062 :
13063 : /*
13064 : * Perform the actual constraint deletion
13065 : */
13066 1164 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13067 1164 : performDeletion(&conobj, behavior, 0);
13068 :
13069 : /*
13070 : * If this was a NOT NULL or the primary key, verify that we still have
13071 : * constraints to support GENERATED AS IDENTITY or the replica identity.
13072 : */
13073 1128 : if (unconstrained_cols != NIL)
13074 : {
13075 : Relation attrel;
13076 : Bitmapset *pkcols;
13077 : Bitmapset *ircols;
13078 :
13079 : /* Make implicit attnotnull changes visible */
13080 564 : CommandCounterIncrement();
13081 :
13082 564 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
13083 :
13084 : /*
13085 : * We want to test columns for their presence in the primary key, but
13086 : * only if we're not dropping it.
13087 : */
13088 564 : pkcols = dropping_pk ? NULL :
13089 460 : RelationGetIndexAttrBitmap(rel,
13090 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
13091 564 : ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13092 :
13093 1682 : foreach_int(attnum, unconstrained_cols)
13094 : {
13095 : HeapTuple atttup;
13096 : HeapTuple contup;
13097 : Form_pg_attribute attForm;
13098 : char attidentity;
13099 :
13100 : /*
13101 : * Obtain pg_attribute tuple and verify conditions on it.
13102 : */
13103 590 : atttup = SearchSysCacheAttNum(RelationGetRelid(rel), attnum);
13104 590 : if (!HeapTupleIsValid(atttup))
13105 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13106 : attnum, RelationGetRelid(rel));
13107 590 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13108 590 : attidentity = attForm->attidentity;
13109 590 : ReleaseSysCache(atttup);
13110 :
13111 : /*
13112 : * Since the above deletion has been made visible, we can now
13113 : * search for any remaining constraints on this column (or these
13114 : * columns, in the case we're dropping a multicol primary key.)
13115 : * Then, verify whether any further NOT NULL or primary key
13116 : * exists: if none and this is a generated identity column or the
13117 : * replica identity, abort the whole thing.
13118 : */
13119 590 : contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
13120 1162 : if (contup ||
13121 572 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
13122 : pkcols))
13123 248 : continue;
13124 :
13125 : /*
13126 : * It's not valid to drop the not-null constraint for a GENERATED
13127 : * AS IDENTITY column.
13128 : */
13129 342 : if (attidentity != '\0')
13130 0 : ereport(ERROR,
13131 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13132 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13133 : get_attname(RelationGetRelid(rel), attnum,
13134 : false),
13135 : RelationGetRelationName(rel)));
13136 :
13137 : /*
13138 : * It's not valid to drop the not-null constraint for a column in
13139 : * the replica identity index, either. (FULL is not affected.)
13140 : */
13141 342 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
13142 18 : ereport(ERROR,
13143 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13144 : errmsg("column \"%s\" is in index used as replica identity",
13145 : get_attname(RelationGetRelid(rel), attnum, false)));
13146 : }
13147 546 : table_close(attrel, RowExclusiveLock);
13148 : }
13149 :
13150 : /*
13151 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13152 : * are dropped via the dependency mechanism, so we're done here.
13153 : */
13154 1110 : if (con->contype != CONSTRAINT_CHECK &&
13155 786 : con->contype != CONSTRAINT_NOTNULL &&
13156 338 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13157 : {
13158 66 : table_close(conrel, RowExclusiveLock);
13159 66 : return conobj;
13160 : }
13161 :
13162 : /*
13163 : * Propagate to children as appropriate. Unlike most other ALTER
13164 : * routines, we have to do this one level of recursion at a time; we can't
13165 : * use find_all_inheritors to do it in one pass.
13166 : */
13167 1044 : if (!is_no_inherit_constraint)
13168 566 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13169 : else
13170 478 : children = NIL;
13171 :
13172 : /*
13173 : * For a partitioned table, if partitions exist and we are told not to
13174 : * recurse, it's a user error. It doesn't make sense to have a constraint
13175 : * be defined only on the parent, especially if it's a partitioned table.
13176 : */
13177 1044 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
13178 62 : children != NIL && !recurse)
13179 6 : ereport(ERROR,
13180 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13181 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13182 : errhint("Do not specify the ONLY keyword.")));
13183 :
13184 2364 : foreach_oid(childrelid, children)
13185 : {
13186 : Relation childrel;
13187 : HeapTuple tuple;
13188 : Form_pg_constraint childcon;
13189 :
13190 300 : if (list_member_oid(*readyRels, childrelid))
13191 6 : continue; /* child already processed */
13192 :
13193 : /* find_inheritance_children already got lock */
13194 294 : childrel = table_open(childrelid, NoLock);
13195 294 : CheckTableNotInUse(childrel, "ALTER TABLE");
13196 :
13197 : /*
13198 : * We search for not-null constraints by column name, and others by
13199 : * constraint name.
13200 : */
13201 294 : if (con->contype == CONSTRAINT_NOTNULL)
13202 : {
13203 94 : tuple = findNotNullConstraint(childrelid, colname);
13204 94 : if (!HeapTupleIsValid(tuple))
13205 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13206 : colname, RelationGetRelid(childrel));
13207 : }
13208 : else
13209 : {
13210 : SysScanDesc scan;
13211 : ScanKeyData skey[3];
13212 :
13213 200 : ScanKeyInit(&skey[0],
13214 : Anum_pg_constraint_conrelid,
13215 : BTEqualStrategyNumber, F_OIDEQ,
13216 : ObjectIdGetDatum(childrelid));
13217 200 : ScanKeyInit(&skey[1],
13218 : Anum_pg_constraint_contypid,
13219 : BTEqualStrategyNumber, F_OIDEQ,
13220 : ObjectIdGetDatum(InvalidOid));
13221 200 : ScanKeyInit(&skey[2],
13222 : Anum_pg_constraint_conname,
13223 : BTEqualStrategyNumber, F_NAMEEQ,
13224 : CStringGetDatum(constrName));
13225 200 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13226 : true, NULL, 3, skey);
13227 : /* There can only be one, so no need to loop */
13228 200 : tuple = systable_getnext(scan);
13229 200 : if (!HeapTupleIsValid(tuple))
13230 0 : ereport(ERROR,
13231 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13232 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13233 : constrName,
13234 : RelationGetRelationName(childrel))));
13235 200 : tuple = heap_copytuple(tuple);
13236 200 : systable_endscan(scan);
13237 : }
13238 :
13239 294 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13240 :
13241 : /* Right now only CHECK and not-null constraints can be inherited */
13242 294 : if (childcon->contype != CONSTRAINT_CHECK &&
13243 94 : childcon->contype != CONSTRAINT_NOTNULL)
13244 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13245 :
13246 294 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13247 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13248 : childrelid, NameStr(childcon->conname));
13249 :
13250 294 : if (recurse)
13251 : {
13252 : /*
13253 : * If the child constraint has other definition sources, just
13254 : * decrement its inheritance count; if not, recurse to delete it.
13255 : */
13256 288 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13257 : {
13258 : /* Time to delete this child constraint, too */
13259 210 : dropconstraint_internal(childrel, tuple, behavior,
13260 : recurse, true, missing_ok, readyRels,
13261 : lockmode);
13262 : }
13263 : else
13264 : {
13265 : /* Child constraint must survive my deletion */
13266 78 : childcon->coninhcount--;
13267 78 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13268 :
13269 : /* Make update visible */
13270 78 : CommandCounterIncrement();
13271 : }
13272 : }
13273 : else
13274 : {
13275 : /*
13276 : * If we were told to drop ONLY in this table (no recursion) and
13277 : * there are no further parents for this constraint, we need to
13278 : * mark the inheritors' constraints as locally defined rather than
13279 : * inherited.
13280 : */
13281 6 : childcon->coninhcount--;
13282 6 : if (childcon->coninhcount == 0)
13283 6 : childcon->conislocal = true;
13284 :
13285 6 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13286 :
13287 : /* Make update visible */
13288 6 : CommandCounterIncrement();
13289 : }
13290 :
13291 288 : heap_freetuple(tuple);
13292 :
13293 288 : table_close(childrel, NoLock);
13294 : }
13295 :
13296 : /*
13297 : * In addition, when dropping a primary key from a legacy-inheritance
13298 : * parent table, we must recurse to children to mark the corresponding NOT
13299 : * NULL constraint as no longer inherited, or drop it if this its last
13300 : * reference.
13301 : */
13302 1032 : if (con->contype == CONSTRAINT_PRIMARY &&
13303 92 : rel->rd_rel->relkind == RELKIND_RELATION &&
13304 92 : rel->rd_rel->relhassubclass)
13305 : {
13306 12 : List *colnames = NIL;
13307 12 : List *pkready = NIL;
13308 :
13309 : /*
13310 : * Because primary keys are always marked as NO INHERIT, we don't have
13311 : * a list of children yet, so obtain one now.
13312 : */
13313 12 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13314 :
13315 : /*
13316 : * Find out the list of column names to process. Fortunately, we
13317 : * already have the list of column numbers.
13318 : */
13319 36 : foreach_int(attnum, unconstrained_cols)
13320 : {
13321 12 : colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
13322 : attnum, false));
13323 : }
13324 :
13325 48 : foreach_oid(childrelid, children)
13326 : {
13327 : Relation childrel;
13328 :
13329 24 : if (list_member_oid(pkready, childrelid))
13330 0 : continue; /* child already processed */
13331 :
13332 : /* find_inheritance_children already got lock */
13333 24 : childrel = table_open(childrelid, NoLock);
13334 24 : CheckTableNotInUse(childrel, "ALTER TABLE");
13335 :
13336 72 : foreach_ptr(char, colName, colnames)
13337 : {
13338 : HeapTuple contup;
13339 :
13340 24 : contup = findNotNullConstraint(childrelid, colName);
13341 24 : if (contup == NULL)
13342 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13343 : colName, RelationGetRelationName(childrel));
13344 :
13345 24 : dropconstraint_internal(childrel, contup,
13346 : DROP_RESTRICT, true, true,
13347 : false, &pkready,
13348 : lockmode);
13349 24 : pkready = NIL;
13350 : }
13351 :
13352 24 : table_close(childrel, NoLock);
13353 :
13354 24 : pkready = lappend_oid(pkready, childrelid);
13355 : }
13356 : }
13357 :
13358 1032 : table_close(conrel, RowExclusiveLock);
13359 :
13360 1032 : return conobj;
13361 : }
13362 :
13363 : /*
13364 : * ALTER COLUMN TYPE
13365 : *
13366 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13367 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13368 : * transformed (and must be, because we rely on some transformed fields).
13369 : *
13370 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13371 : * table will be done "in parallel" during phase 3, so all the USING
13372 : * expressions should be parsed assuming the original column types. Also,
13373 : * this allows a USING expression to refer to a field that will be dropped.
13374 : *
13375 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13376 : * the first two execution steps in phase 2; they must not see the effects
13377 : * of any other subcommand types, since the USING expressions are parsed
13378 : * against the unmodified table's state.
13379 : */
13380 : static void
13381 1122 : ATPrepAlterColumnType(List **wqueue,
13382 : AlteredTableInfo *tab, Relation rel,
13383 : bool recurse, bool recursing,
13384 : AlterTableCmd *cmd, LOCKMODE lockmode,
13385 : AlterTableUtilityContext *context)
13386 : {
13387 1122 : char *colName = cmd->name;
13388 1122 : ColumnDef *def = (ColumnDef *) cmd->def;
13389 1122 : TypeName *typeName = def->typeName;
13390 1122 : Node *transform = def->cooked_default;
13391 : HeapTuple tuple;
13392 : Form_pg_attribute attTup;
13393 : AttrNumber attnum;
13394 : Oid targettype;
13395 : int32 targettypmod;
13396 : Oid targetcollid;
13397 : NewColumnValue *newval;
13398 1122 : ParseState *pstate = make_parsestate(NULL);
13399 : AclResult aclresult;
13400 : bool is_expr;
13401 :
13402 1122 : if (rel->rd_rel->reloftype && !recursing)
13403 6 : ereport(ERROR,
13404 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13405 : errmsg("cannot alter column type of typed table")));
13406 :
13407 : /* lookup the attribute so we can check inheritance status */
13408 1116 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13409 1116 : if (!HeapTupleIsValid(tuple))
13410 0 : ereport(ERROR,
13411 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13412 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13413 : colName, RelationGetRelationName(rel))));
13414 1116 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13415 1116 : attnum = attTup->attnum;
13416 :
13417 : /* Can't alter a system attribute */
13418 1116 : if (attnum <= 0)
13419 0 : ereport(ERROR,
13420 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13421 : errmsg("cannot alter system column \"%s\"",
13422 : colName)));
13423 :
13424 : /*
13425 : * Don't alter inherited columns. At outer level, there had better not be
13426 : * any inherited definition; when recursing, we assume this was checked at
13427 : * the parent level (see below).
13428 : */
13429 1116 : if (attTup->attinhcount > 0 && !recursing)
13430 6 : ereport(ERROR,
13431 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13432 : errmsg("cannot alter inherited column \"%s\"",
13433 : colName)));
13434 :
13435 : /* Don't alter columns used in the partition key */
13436 1110 : if (has_partition_attrs(rel,
13437 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13438 : &is_expr))
13439 18 : ereport(ERROR,
13440 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13441 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13442 : colName, RelationGetRelationName(rel))));
13443 :
13444 : /* Look up the target type */
13445 1092 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13446 :
13447 1092 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13448 1092 : if (aclresult != ACLCHECK_OK)
13449 12 : aclcheck_error_type(aclresult, targettype);
13450 :
13451 : /* And the collation */
13452 1080 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13453 :
13454 : /* make sure datatype is legal for a column */
13455 1080 : CheckAttributeType(colName, targettype, targetcollid,
13456 1080 : list_make1_oid(rel->rd_rel->reltype),
13457 : 0);
13458 :
13459 1074 : if (tab->relkind == RELKIND_RELATION ||
13460 190 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13461 : {
13462 : /*
13463 : * Set up an expression to transform the old data value to the new
13464 : * type. If a USING option was given, use the expression as
13465 : * transformed by transformAlterTableStmt, else just take the old
13466 : * value and try to coerce it. We do this first so that type
13467 : * incompatibility can be detected before we waste effort, and because
13468 : * we need the expression to be parsed against the original table row
13469 : * type.
13470 : */
13471 938 : if (!transform)
13472 : {
13473 716 : transform = (Node *) makeVar(1, attnum,
13474 : attTup->atttypid, attTup->atttypmod,
13475 : attTup->attcollation,
13476 : 0);
13477 : }
13478 :
13479 938 : transform = coerce_to_target_type(pstate,
13480 : transform, exprType(transform),
13481 : targettype, targettypmod,
13482 : COERCION_ASSIGNMENT,
13483 : COERCE_IMPLICIT_CAST,
13484 : -1);
13485 938 : if (transform == NULL)
13486 : {
13487 : /* error text depends on whether USING was specified or not */
13488 24 : if (def->cooked_default != NULL)
13489 6 : ereport(ERROR,
13490 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13491 : errmsg("result of USING clause for column \"%s\""
13492 : " cannot be cast automatically to type %s",
13493 : colName, format_type_be(targettype)),
13494 : errhint("You might need to add an explicit cast.")));
13495 : else
13496 18 : ereport(ERROR,
13497 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13498 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13499 : colName, format_type_be(targettype)),
13500 : /* translator: USING is SQL, don't translate it */
13501 : errhint("You might need to specify \"USING %s::%s\".",
13502 : quote_identifier(colName),
13503 : format_type_with_typemod(targettype,
13504 : targettypmod))));
13505 : }
13506 :
13507 : /* Fix collations after all else */
13508 914 : assign_expr_collations(pstate, transform);
13509 :
13510 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13511 914 : transform = (Node *) expression_planner((Expr *) transform);
13512 :
13513 : /*
13514 : * Add a work queue item to make ATRewriteTable update the column
13515 : * contents.
13516 : */
13517 914 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13518 914 : newval->attnum = attnum;
13519 914 : newval->expr = (Expr *) transform;
13520 914 : newval->is_generated = false;
13521 :
13522 914 : tab->newvals = lappend(tab->newvals, newval);
13523 914 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13524 750 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13525 : }
13526 136 : else if (transform)
13527 12 : ereport(ERROR,
13528 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13529 : errmsg("\"%s\" is not a table",
13530 : RelationGetRelationName(rel))));
13531 :
13532 1038 : if (!RELKIND_HAS_STORAGE(tab->relkind))
13533 : {
13534 : /*
13535 : * For relations without storage, do this check now. Regular tables
13536 : * will check it later when the table is being rewritten.
13537 : */
13538 178 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13539 : }
13540 :
13541 1008 : ReleaseSysCache(tuple);
13542 :
13543 : /*
13544 : * Recurse manually by queueing a new command for each child, if
13545 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13546 : * remap attribute numbers in the USING expression, if any.
13547 : *
13548 : * If we are told not to recurse, there had better not be any child
13549 : * tables; else the alter would put them out of step.
13550 : */
13551 1008 : if (recurse)
13552 : {
13553 768 : Oid relid = RelationGetRelid(rel);
13554 : List *child_oids,
13555 : *child_numparents;
13556 : ListCell *lo,
13557 : *li;
13558 :
13559 768 : child_oids = find_all_inheritors(relid, lockmode,
13560 : &child_numparents);
13561 :
13562 : /*
13563 : * find_all_inheritors does the recursive search of the inheritance
13564 : * hierarchy, so all we have to do is process all of the relids in the
13565 : * list that it returns.
13566 : */
13567 1726 : forboth(lo, child_oids, li, child_numparents)
13568 : {
13569 982 : Oid childrelid = lfirst_oid(lo);
13570 982 : int numparents = lfirst_int(li);
13571 : Relation childrel;
13572 : HeapTuple childtuple;
13573 : Form_pg_attribute childattTup;
13574 :
13575 982 : if (childrelid == relid)
13576 768 : continue;
13577 :
13578 : /* find_all_inheritors already got lock */
13579 214 : childrel = relation_open(childrelid, NoLock);
13580 214 : CheckTableNotInUse(childrel, "ALTER TABLE");
13581 :
13582 : /*
13583 : * Verify that the child doesn't have any inherited definitions of
13584 : * this column that came from outside this inheritance hierarchy.
13585 : * (renameatt makes a similar test, though in a different way
13586 : * because of its different recursion mechanism.)
13587 : */
13588 214 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13589 : colName);
13590 214 : if (!HeapTupleIsValid(childtuple))
13591 0 : ereport(ERROR,
13592 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13593 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13594 : colName, RelationGetRelationName(childrel))));
13595 214 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13596 :
13597 214 : if (childattTup->attinhcount > numparents)
13598 6 : ereport(ERROR,
13599 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13600 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13601 : colName, RelationGetRelationName(childrel))));
13602 :
13603 208 : ReleaseSysCache(childtuple);
13604 :
13605 : /*
13606 : * Remap the attribute numbers. If no USING expression was
13607 : * specified, there is no need for this step.
13608 : */
13609 208 : if (def->cooked_default)
13610 : {
13611 : AttrMap *attmap;
13612 : bool found_whole_row;
13613 :
13614 : /* create a copy to scribble on */
13615 72 : cmd = copyObject(cmd);
13616 :
13617 72 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13618 : RelationGetDescr(rel),
13619 : false);
13620 144 : ((ColumnDef *) cmd->def)->cooked_default =
13621 72 : map_variable_attnos(def->cooked_default,
13622 : 1, 0,
13623 : attmap,
13624 : InvalidOid, &found_whole_row);
13625 72 : if (found_whole_row)
13626 6 : ereport(ERROR,
13627 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13628 : errmsg("cannot convert whole-row table reference"),
13629 : errdetail("USING expression contains a whole-row table reference.")));
13630 66 : pfree(attmap);
13631 : }
13632 202 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13633 190 : relation_close(childrel, NoLock);
13634 : }
13635 : }
13636 290 : else if (!recursing &&
13637 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13638 0 : ereport(ERROR,
13639 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13640 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13641 : colName)));
13642 :
13643 984 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13644 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13645 978 : }
13646 :
13647 : /*
13648 : * When the data type of a column is changed, a rewrite might not be required
13649 : * if the new type is sufficiently identical to the old one, and the USING
13650 : * clause isn't trying to insert some other value. It's safe to skip the
13651 : * rewrite in these cases:
13652 : *
13653 : * - the old type is binary coercible to the new type
13654 : * - the new type is an unconstrained domain over the old type
13655 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13656 : *
13657 : * In the case of a constrained domain, we could get by with scanning the
13658 : * table and checking the constraint rather than actually rewriting it, but we
13659 : * don't currently try to do that.
13660 : */
13661 : static bool
13662 1018 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13663 : {
13664 : Assert(expr != NULL);
13665 :
13666 : for (;;)
13667 : {
13668 : /* only one varno, so no need to check that */
13669 1018 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13670 164 : return false;
13671 854 : else if (IsA(expr, RelabelType))
13672 92 : expr = (Node *) ((RelabelType *) expr)->arg;
13673 762 : else if (IsA(expr, CoerceToDomain))
13674 : {
13675 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
13676 :
13677 0 : if (DomainHasConstraints(d->resulttype))
13678 0 : return true;
13679 0 : expr = (Node *) d->arg;
13680 : }
13681 762 : else if (IsA(expr, FuncExpr))
13682 : {
13683 562 : FuncExpr *f = (FuncExpr *) expr;
13684 :
13685 562 : switch (f->funcid)
13686 : {
13687 18 : case F_TIMESTAMPTZ_TIMESTAMP:
13688 : case F_TIMESTAMP_TIMESTAMPTZ:
13689 18 : if (TimestampTimestampTzRequiresRewrite())
13690 6 : return true;
13691 : else
13692 12 : expr = linitial(f->args);
13693 12 : break;
13694 544 : default:
13695 544 : return true;
13696 : }
13697 : }
13698 : else
13699 200 : return true;
13700 : }
13701 : }
13702 :
13703 : /*
13704 : * ALTER COLUMN .. SET DATA TYPE
13705 : *
13706 : * Return the address of the modified column.
13707 : */
13708 : static ObjectAddress
13709 948 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13710 : AlterTableCmd *cmd, LOCKMODE lockmode)
13711 : {
13712 948 : char *colName = cmd->name;
13713 948 : ColumnDef *def = (ColumnDef *) cmd->def;
13714 948 : TypeName *typeName = def->typeName;
13715 : HeapTuple heapTup;
13716 : Form_pg_attribute attTup,
13717 : attOldTup;
13718 : AttrNumber attnum;
13719 : HeapTuple typeTuple;
13720 : Form_pg_type tform;
13721 : Oid targettype;
13722 : int32 targettypmod;
13723 : Oid targetcollid;
13724 : Node *defaultexpr;
13725 : Relation attrelation;
13726 : Relation depRel;
13727 : ScanKeyData key[3];
13728 : SysScanDesc scan;
13729 : HeapTuple depTup;
13730 : ObjectAddress address;
13731 :
13732 : /*
13733 : * Clear all the missing values if we're rewriting the table, since this
13734 : * renders them pointless.
13735 : */
13736 948 : if (tab->rewrite)
13737 : {
13738 : Relation newrel;
13739 :
13740 696 : newrel = table_open(RelationGetRelid(rel), NoLock);
13741 696 : RelationClearMissing(newrel);
13742 696 : relation_close(newrel, NoLock);
13743 : /* make sure we don't conflict with later attribute modifications */
13744 696 : CommandCounterIncrement();
13745 : }
13746 :
13747 948 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13748 :
13749 : /* Look up the target column */
13750 948 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13751 948 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13752 0 : ereport(ERROR,
13753 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13754 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13755 : colName, RelationGetRelationName(rel))));
13756 948 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13757 948 : attnum = attTup->attnum;
13758 948 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13759 :
13760 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13761 948 : if (attTup->atttypid != attOldTup->atttypid ||
13762 948 : attTup->atttypmod != attOldTup->atttypmod)
13763 0 : ereport(ERROR,
13764 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13765 : errmsg("cannot alter type of column \"%s\" twice",
13766 : colName)));
13767 :
13768 : /* Look up the target type (should not fail, since prep found it) */
13769 948 : typeTuple = typenameType(NULL, typeName, &targettypmod);
13770 948 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
13771 948 : targettype = tform->oid;
13772 : /* And the collation */
13773 948 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13774 :
13775 : /*
13776 : * If there is a default expression for the column, get it and ensure we
13777 : * can coerce it to the new datatype. (We must do this before changing
13778 : * the column type, because build_column_default itself will try to
13779 : * coerce, and will not issue the error message we want if it fails.)
13780 : *
13781 : * We remove any implicit coercion steps at the top level of the old
13782 : * default expression; this has been agreed to satisfy the principle of
13783 : * least surprise. (The conversion to the new column type should act like
13784 : * it started from what the user sees as the stored expression, and the
13785 : * implicit coercions aren't going to be shown.)
13786 : */
13787 948 : if (attTup->atthasdef)
13788 : {
13789 62 : defaultexpr = build_column_default(rel, attnum);
13790 : Assert(defaultexpr);
13791 62 : defaultexpr = strip_implicit_coercions(defaultexpr);
13792 62 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13793 : defaultexpr, exprType(defaultexpr),
13794 : targettype, targettypmod,
13795 : COERCION_ASSIGNMENT,
13796 : COERCE_IMPLICIT_CAST,
13797 : -1);
13798 62 : if (defaultexpr == NULL)
13799 : {
13800 12 : if (attTup->attgenerated)
13801 6 : ereport(ERROR,
13802 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13803 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13804 : colName, format_type_be(targettype))));
13805 : else
13806 6 : ereport(ERROR,
13807 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13808 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13809 : colName, format_type_be(targettype))));
13810 : }
13811 : }
13812 : else
13813 886 : defaultexpr = NULL;
13814 :
13815 : /*
13816 : * Find everything that depends on the column (constraints, indexes, etc),
13817 : * and record enough information to let us recreate the objects.
13818 : *
13819 : * The actual recreation does not happen here, but only after we have
13820 : * performed all the individual ALTER TYPE operations. We have to save
13821 : * the info before executing ALTER TYPE, though, else the deparser will
13822 : * get confused.
13823 : */
13824 936 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13825 :
13826 : /*
13827 : * Now scan for dependencies of this column on other things. The only
13828 : * things we should find are the dependency on the column datatype and
13829 : * possibly a collation dependency. Those can be removed.
13830 : */
13831 912 : depRel = table_open(DependRelationId, RowExclusiveLock);
13832 :
13833 912 : ScanKeyInit(&key[0],
13834 : Anum_pg_depend_classid,
13835 : BTEqualStrategyNumber, F_OIDEQ,
13836 : ObjectIdGetDatum(RelationRelationId));
13837 912 : ScanKeyInit(&key[1],
13838 : Anum_pg_depend_objid,
13839 : BTEqualStrategyNumber, F_OIDEQ,
13840 : ObjectIdGetDatum(RelationGetRelid(rel)));
13841 912 : ScanKeyInit(&key[2],
13842 : Anum_pg_depend_objsubid,
13843 : BTEqualStrategyNumber, F_INT4EQ,
13844 : Int32GetDatum((int32) attnum));
13845 :
13846 912 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
13847 : NULL, 3, key);
13848 :
13849 916 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13850 : {
13851 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13852 : ObjectAddress foundObject;
13853 :
13854 4 : foundObject.classId = foundDep->refclassid;
13855 4 : foundObject.objectId = foundDep->refobjid;
13856 4 : foundObject.objectSubId = foundDep->refobjsubid;
13857 :
13858 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
13859 0 : elog(ERROR, "found unexpected dependency type '%c'",
13860 : foundDep->deptype);
13861 4 : if (!(foundDep->refclassid == TypeRelationId &&
13862 4 : foundDep->refobjid == attTup->atttypid) &&
13863 0 : !(foundDep->refclassid == CollationRelationId &&
13864 0 : foundDep->refobjid == attTup->attcollation))
13865 0 : elog(ERROR, "found unexpected dependency for column: %s",
13866 : getObjectDescription(&foundObject, false));
13867 :
13868 4 : CatalogTupleDelete(depRel, &depTup->t_self);
13869 : }
13870 :
13871 912 : systable_endscan(scan);
13872 :
13873 912 : table_close(depRel, RowExclusiveLock);
13874 :
13875 : /*
13876 : * Here we go --- change the recorded column type and collation. (Note
13877 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13878 : * fix up the missing value if any.
13879 : */
13880 912 : if (attTup->atthasmissing)
13881 : {
13882 : Datum missingval;
13883 : bool missingNull;
13884 :
13885 : /* if rewrite is true the missing value should already be cleared */
13886 : Assert(tab->rewrite == 0);
13887 :
13888 : /* Get the missing value datum */
13889 6 : missingval = heap_getattr(heapTup,
13890 : Anum_pg_attribute_attmissingval,
13891 : attrelation->rd_att,
13892 : &missingNull);
13893 :
13894 : /* if it's a null array there is nothing to do */
13895 :
13896 6 : if (!missingNull)
13897 : {
13898 : /*
13899 : * Get the datum out of the array and repack it in a new array
13900 : * built with the new type data. We assume that since the table
13901 : * doesn't need rewriting, the actual Datum doesn't need to be
13902 : * changed, only the array metadata.
13903 : */
13904 :
13905 6 : int one = 1;
13906 : bool isNull;
13907 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
13908 6 : bool nullsAtt[Natts_pg_attribute] = {0};
13909 6 : bool replacesAtt[Natts_pg_attribute] = {0};
13910 : HeapTuple newTup;
13911 :
13912 12 : missingval = array_get_element(missingval,
13913 : 1,
13914 : &one,
13915 : 0,
13916 6 : attTup->attlen,
13917 6 : attTup->attbyval,
13918 6 : attTup->attalign,
13919 : &isNull);
13920 6 : missingval = PointerGetDatum(construct_array(&missingval,
13921 : 1,
13922 : targettype,
13923 6 : tform->typlen,
13924 6 : tform->typbyval,
13925 6 : tform->typalign));
13926 :
13927 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13928 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13929 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13930 :
13931 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13932 : valuesAtt, nullsAtt, replacesAtt);
13933 6 : heap_freetuple(heapTup);
13934 6 : heapTup = newTup;
13935 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13936 : }
13937 : }
13938 :
13939 912 : attTup->atttypid = targettype;
13940 912 : attTup->atttypmod = targettypmod;
13941 912 : attTup->attcollation = targetcollid;
13942 912 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13943 0 : ereport(ERROR,
13944 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13945 : errmsg("too many array dimensions"));
13946 912 : attTup->attndims = list_length(typeName->arrayBounds);
13947 912 : attTup->attlen = tform->typlen;
13948 912 : attTup->attbyval = tform->typbyval;
13949 912 : attTup->attalign = tform->typalign;
13950 912 : attTup->attstorage = tform->typstorage;
13951 912 : attTup->attcompression = InvalidCompressionMethod;
13952 :
13953 912 : ReleaseSysCache(typeTuple);
13954 :
13955 912 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13956 :
13957 912 : table_close(attrelation, RowExclusiveLock);
13958 :
13959 : /* Install dependencies on new datatype and collation */
13960 912 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13961 912 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13962 :
13963 : /*
13964 : * Drop any pg_statistic entry for the column, since it's now wrong type
13965 : */
13966 912 : RemoveStatistics(RelationGetRelid(rel), attnum);
13967 :
13968 912 : InvokeObjectPostAlterHook(RelationRelationId,
13969 : RelationGetRelid(rel), attnum);
13970 :
13971 : /*
13972 : * Update the default, if present, by brute force --- remove and re-add
13973 : * the default. Probably unsafe to take shortcuts, since the new version
13974 : * may well have additional dependencies. (It's okay to do this now,
13975 : * rather than after other ALTER TYPE commands, since the default won't
13976 : * depend on other column types.)
13977 : */
13978 912 : if (defaultexpr)
13979 : {
13980 : /*
13981 : * If it's a GENERATED default, drop its dependency records, in
13982 : * particular its INTERNAL dependency on the column, which would
13983 : * otherwise cause dependency.c to refuse to perform the deletion.
13984 : */
13985 50 : if (attTup->attgenerated)
13986 : {
13987 6 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13988 :
13989 6 : if (!OidIsValid(attrdefoid))
13990 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13991 : RelationGetRelid(rel), attnum);
13992 6 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13993 : }
13994 :
13995 : /*
13996 : * Make updates-so-far visible, particularly the new pg_attribute row
13997 : * which will be updated again.
13998 : */
13999 50 : CommandCounterIncrement();
14000 :
14001 : /*
14002 : * We use RESTRICT here for safety, but at present we do not expect
14003 : * anything to depend on the default.
14004 : */
14005 50 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14006 : true);
14007 :
14008 50 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
14009 : }
14010 :
14011 912 : ObjectAddressSubSet(address, RelationRelationId,
14012 : RelationGetRelid(rel), attnum);
14013 :
14014 : /* Cleanup */
14015 912 : heap_freetuple(heapTup);
14016 :
14017 912 : return address;
14018 : }
14019 :
14020 : /*
14021 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14022 : * that depends on the column (constraints, indexes, etc), and record enough
14023 : * information to let us recreate the objects.
14024 : */
14025 : static void
14026 1014 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14027 : Relation rel, AttrNumber attnum, const char *colName)
14028 : {
14029 : Relation depRel;
14030 : ScanKeyData key[3];
14031 : SysScanDesc scan;
14032 : HeapTuple depTup;
14033 :
14034 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14035 :
14036 1014 : depRel = table_open(DependRelationId, RowExclusiveLock);
14037 :
14038 1014 : ScanKeyInit(&key[0],
14039 : Anum_pg_depend_refclassid,
14040 : BTEqualStrategyNumber, F_OIDEQ,
14041 : ObjectIdGetDatum(RelationRelationId));
14042 1014 : ScanKeyInit(&key[1],
14043 : Anum_pg_depend_refobjid,
14044 : BTEqualStrategyNumber, F_OIDEQ,
14045 : ObjectIdGetDatum(RelationGetRelid(rel)));
14046 1014 : ScanKeyInit(&key[2],
14047 : Anum_pg_depend_refobjsubid,
14048 : BTEqualStrategyNumber, F_INT4EQ,
14049 : Int32GetDatum((int32) attnum));
14050 :
14051 1014 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14052 : NULL, 3, key);
14053 :
14054 1944 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14055 : {
14056 954 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14057 : ObjectAddress foundObject;
14058 :
14059 954 : foundObject.classId = foundDep->classid;
14060 954 : foundObject.objectId = foundDep->objid;
14061 954 : foundObject.objectSubId = foundDep->objsubid;
14062 :
14063 954 : switch (foundObject.classId)
14064 : {
14065 264 : case RelationRelationId:
14066 : {
14067 264 : char relKind = get_rel_relkind(foundObject.objectId);
14068 :
14069 264 : if (relKind == RELKIND_INDEX ||
14070 : relKind == RELKIND_PARTITIONED_INDEX)
14071 : {
14072 : Assert(foundObject.objectSubId == 0);
14073 232 : RememberIndexForRebuilding(foundObject.objectId, tab);
14074 : }
14075 32 : else if (relKind == RELKIND_SEQUENCE)
14076 : {
14077 : /*
14078 : * This must be a SERIAL column's sequence. We need
14079 : * not do anything to it.
14080 : */
14081 : Assert(foundObject.objectSubId == 0);
14082 : }
14083 : else
14084 : {
14085 : /* Not expecting any other direct dependencies... */
14086 0 : elog(ERROR, "unexpected object depending on column: %s",
14087 : getObjectDescription(&foundObject, false));
14088 : }
14089 264 : break;
14090 : }
14091 :
14092 524 : case ConstraintRelationId:
14093 : Assert(foundObject.objectSubId == 0);
14094 524 : RememberConstraintForRebuilding(foundObject.objectId, tab);
14095 524 : break;
14096 :
14097 12 : case RewriteRelationId:
14098 : /* XXX someday see if we can cope with revising views */
14099 12 : if (subtype == AT_AlterColumnType)
14100 12 : ereport(ERROR,
14101 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14102 : errmsg("cannot alter type of a column used by a view or rule"),
14103 : errdetail("%s depends on column \"%s\"",
14104 : getObjectDescription(&foundObject, false),
14105 : colName)));
14106 0 : break;
14107 :
14108 0 : case TriggerRelationId:
14109 :
14110 : /*
14111 : * A trigger can depend on a column because the column is
14112 : * specified as an update target, or because the column is
14113 : * used in the trigger's WHEN condition. The first case would
14114 : * not require any extra work, but the second case would
14115 : * require updating the WHEN expression, which will take a
14116 : * significant amount of new code. Since we can't easily tell
14117 : * which case applies, we punt for both. FIXME someday.
14118 : */
14119 0 : if (subtype == AT_AlterColumnType)
14120 0 : ereport(ERROR,
14121 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14122 : errmsg("cannot alter type of a column used in a trigger definition"),
14123 : errdetail("%s depends on column \"%s\"",
14124 : getObjectDescription(&foundObject, false),
14125 : colName)));
14126 0 : break;
14127 :
14128 0 : case PolicyRelationId:
14129 :
14130 : /*
14131 : * A policy can depend on a column because the column is
14132 : * specified in the policy's USING or WITH CHECK qual
14133 : * expressions. It might be possible to rewrite and recheck
14134 : * the policy expression, but punt for now. It's certainly
14135 : * easy enough to remove and recreate the policy; still, FIXME
14136 : * someday.
14137 : */
14138 0 : if (subtype == AT_AlterColumnType)
14139 0 : ereport(ERROR,
14140 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14141 : errmsg("cannot alter type of a column used in a policy definition"),
14142 : errdetail("%s depends on column \"%s\"",
14143 : getObjectDescription(&foundObject, false),
14144 : colName)));
14145 0 : break;
14146 :
14147 140 : case AttrDefaultRelationId:
14148 : {
14149 140 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14150 :
14151 140 : if (col.objectId == RelationGetRelid(rel) &&
14152 140 : col.objectSubId == attnum)
14153 : {
14154 : /*
14155 : * Ignore the column's own default expression. The
14156 : * caller deals with it.
14157 : */
14158 : }
14159 : else
14160 : {
14161 : /*
14162 : * This must be a reference from the expression of a
14163 : * generated column elsewhere in the same table.
14164 : * Changing the type/generated expression of a column
14165 : * that is used by a generated column is not allowed
14166 : * by SQL standard, so just punt for now. It might be
14167 : * doable with some thinking and effort.
14168 : */
14169 12 : if (subtype == AT_AlterColumnType)
14170 12 : ereport(ERROR,
14171 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14172 : errmsg("cannot alter type of a column used by a generated column"),
14173 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14174 : colName,
14175 : get_attname(col.objectId,
14176 : col.objectSubId,
14177 : false))));
14178 : }
14179 128 : break;
14180 : }
14181 :
14182 14 : case StatisticExtRelationId:
14183 :
14184 : /*
14185 : * Give the extended-stats machinery a chance to fix anything
14186 : * that this column type change would break.
14187 : */
14188 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14189 14 : break;
14190 :
14191 0 : default:
14192 :
14193 : /*
14194 : * We don't expect any other sorts of objects to depend on a
14195 : * column.
14196 : */
14197 0 : elog(ERROR, "unexpected object depending on column: %s",
14198 : getObjectDescription(&foundObject, false));
14199 : break;
14200 : }
14201 : }
14202 :
14203 990 : systable_endscan(scan);
14204 990 : table_close(depRel, NoLock);
14205 990 : }
14206 :
14207 : /*
14208 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14209 : * needs to be reset.
14210 : */
14211 : static void
14212 434 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14213 : {
14214 434 : if (!get_index_isreplident(indoid))
14215 416 : return;
14216 :
14217 18 : if (tab->replicaIdentityIndex)
14218 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14219 :
14220 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14221 : }
14222 :
14223 : /*
14224 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14225 : */
14226 : static void
14227 434 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14228 : {
14229 434 : if (!get_index_isclustered(indoid))
14230 416 : return;
14231 :
14232 18 : if (tab->clusterOnIndex)
14233 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14234 :
14235 18 : tab->clusterOnIndex = get_rel_name(indoid);
14236 : }
14237 :
14238 : /*
14239 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14240 : * to be rebuilt (which we might already know).
14241 : */
14242 : static void
14243 536 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14244 : {
14245 : /*
14246 : * This de-duplication check is critical for two independent reasons: we
14247 : * mustn't try to recreate the same constraint twice, and if a constraint
14248 : * depends on more than one column whose type is to be altered, we must
14249 : * capture its definition string before applying any of the column type
14250 : * changes. ruleutils.c will get confused if we ask again later.
14251 : */
14252 536 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14253 : {
14254 : /* OK, capture the constraint's existing definition string */
14255 446 : char *defstring = pg_get_constraintdef_command(conoid);
14256 : Oid indoid;
14257 :
14258 446 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14259 : conoid);
14260 446 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14261 : defstring);
14262 :
14263 : /*
14264 : * For the index of a constraint, if any, remember if it is used for
14265 : * the table's replica identity or if it is a clustered index, so that
14266 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14267 : * those properties.
14268 : */
14269 446 : indoid = get_constraint_index(conoid);
14270 446 : if (OidIsValid(indoid))
14271 : {
14272 222 : RememberReplicaIdentityForRebuilding(indoid, tab);
14273 222 : RememberClusterOnForRebuilding(indoid, tab);
14274 : }
14275 : }
14276 536 : }
14277 :
14278 : /*
14279 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14280 : * to be rebuilt (which we might already know).
14281 : */
14282 : static void
14283 232 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14284 : {
14285 : /*
14286 : * This de-duplication check is critical for two independent reasons: we
14287 : * mustn't try to recreate the same index twice, and if an index depends
14288 : * on more than one column whose type is to be altered, we must capture
14289 : * its definition string before applying any of the column type changes.
14290 : * ruleutils.c will get confused if we ask again later.
14291 : */
14292 232 : if (!list_member_oid(tab->changedIndexOids, indoid))
14293 : {
14294 : /*
14295 : * Before adding it as an index-to-rebuild, we'd better see if it
14296 : * belongs to a constraint, and if so rebuild the constraint instead.
14297 : * Typically this check fails, because constraint indexes normally
14298 : * have only dependencies on their constraint. But it's possible for
14299 : * such an index to also have direct dependencies on table columns,
14300 : * for example with a partial exclusion constraint.
14301 : */
14302 224 : Oid conoid = get_index_constraint(indoid);
14303 :
14304 224 : if (OidIsValid(conoid))
14305 : {
14306 12 : RememberConstraintForRebuilding(conoid, tab);
14307 : }
14308 : else
14309 : {
14310 : /* OK, capture the index's existing definition string */
14311 212 : char *defstring = pg_get_indexdef_string(indoid);
14312 :
14313 212 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14314 : indoid);
14315 212 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14316 : defstring);
14317 :
14318 : /*
14319 : * Remember if this index is used for the table's replica identity
14320 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14321 : * can queue up commands necessary to restore those properties.
14322 : */
14323 212 : RememberReplicaIdentityForRebuilding(indoid, tab);
14324 212 : RememberClusterOnForRebuilding(indoid, tab);
14325 : }
14326 : }
14327 232 : }
14328 :
14329 : /*
14330 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14331 : * needs to be rebuilt (which we might already know).
14332 : */
14333 : static void
14334 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14335 : {
14336 : /*
14337 : * This de-duplication check is critical for two independent reasons: we
14338 : * mustn't try to recreate the same statistics object twice, and if the
14339 : * statistics object depends on more than one column whose type is to be
14340 : * altered, we must capture its definition string before applying any of
14341 : * the type changes. ruleutils.c will get confused if we ask again later.
14342 : */
14343 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14344 : {
14345 : /* OK, capture the statistics object's existing definition string */
14346 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14347 :
14348 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14349 : stxoid);
14350 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14351 : defstring);
14352 : }
14353 14 : }
14354 :
14355 : /*
14356 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14357 : * operations for a particular relation. We have to drop and recreate all the
14358 : * indexes and constraints that depend on the altered columns. We do the
14359 : * actual dropping here, but re-creation is managed by adding work queue
14360 : * entries to do those steps later.
14361 : */
14362 : static void
14363 960 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14364 : {
14365 : ObjectAddress obj;
14366 : ObjectAddresses *objects;
14367 : ListCell *def_item;
14368 : ListCell *oid_item;
14369 :
14370 : /*
14371 : * Collect all the constraints and indexes to drop so we can process them
14372 : * in a single call. That way we don't have to worry about dependencies
14373 : * among them.
14374 : */
14375 960 : objects = new_object_addresses();
14376 :
14377 : /*
14378 : * Re-parse the index and constraint definitions, and attach them to the
14379 : * appropriate work queue entries. We do this before dropping because in
14380 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14381 : * lock on the table the constraint is attached to, and we need to get
14382 : * that before reparsing/dropping.
14383 : *
14384 : * We can't rely on the output of deparsing to tell us which relation to
14385 : * operate on, because concurrent activity might have made the name
14386 : * resolve differently. Instead, we've got to use the OID of the
14387 : * constraint or index we're processing to figure out which relation to
14388 : * operate on.
14389 : */
14390 1406 : forboth(oid_item, tab->changedConstraintOids,
14391 : def_item, tab->changedConstraintDefs)
14392 : {
14393 446 : Oid oldId = lfirst_oid(oid_item);
14394 : HeapTuple tup;
14395 : Form_pg_constraint con;
14396 : Oid relid;
14397 : Oid confrelid;
14398 : char contype;
14399 : bool conislocal;
14400 :
14401 446 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14402 446 : if (!HeapTupleIsValid(tup)) /* should not happen */
14403 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14404 446 : con = (Form_pg_constraint) GETSTRUCT(tup);
14405 446 : if (OidIsValid(con->conrelid))
14406 432 : relid = con->conrelid;
14407 : else
14408 : {
14409 : /* must be a domain constraint */
14410 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14411 14 : if (!OidIsValid(relid))
14412 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14413 : }
14414 446 : confrelid = con->confrelid;
14415 446 : contype = con->contype;
14416 446 : conislocal = con->conislocal;
14417 446 : ReleaseSysCache(tup);
14418 :
14419 446 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14420 446 : add_exact_object_address(&obj, objects);
14421 :
14422 : /*
14423 : * If the constraint is inherited (only), we don't want to inject a
14424 : * new definition here; it'll get recreated when
14425 : * ATAddCheckNNConstraint recurses from adding the parent table's
14426 : * constraint. But we had to carry the info this far so that we can
14427 : * drop the constraint below.
14428 : */
14429 446 : if (!conislocal)
14430 16 : continue;
14431 :
14432 : /*
14433 : * When rebuilding an FK constraint that references the table we're
14434 : * modifying, we might not yet have any lock on the FK's table, so get
14435 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14436 : * step, so there's no value in asking for anything weaker.
14437 : */
14438 430 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14439 36 : LockRelationOid(relid, AccessExclusiveLock);
14440 :
14441 430 : ATPostAlterTypeParse(oldId, relid, confrelid,
14442 430 : (char *) lfirst(def_item),
14443 430 : wqueue, lockmode, tab->rewrite);
14444 : }
14445 1172 : forboth(oid_item, tab->changedIndexOids,
14446 : def_item, tab->changedIndexDefs)
14447 : {
14448 212 : Oid oldId = lfirst_oid(oid_item);
14449 : Oid relid;
14450 :
14451 212 : relid = IndexGetRelation(oldId, false);
14452 212 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14453 212 : (char *) lfirst(def_item),
14454 212 : wqueue, lockmode, tab->rewrite);
14455 :
14456 212 : ObjectAddressSet(obj, RelationRelationId, oldId);
14457 212 : add_exact_object_address(&obj, objects);
14458 : }
14459 :
14460 : /* add dependencies for new statistics */
14461 974 : forboth(oid_item, tab->changedStatisticsOids,
14462 : def_item, tab->changedStatisticsDefs)
14463 : {
14464 14 : Oid oldId = lfirst_oid(oid_item);
14465 : Oid relid;
14466 :
14467 14 : relid = StatisticsGetRelation(oldId, false);
14468 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14469 14 : (char *) lfirst(def_item),
14470 14 : wqueue, lockmode, tab->rewrite);
14471 :
14472 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14473 14 : add_exact_object_address(&obj, objects);
14474 : }
14475 :
14476 : /*
14477 : * Queue up command to restore replica identity index marking
14478 : */
14479 960 : if (tab->replicaIdentityIndex)
14480 : {
14481 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14482 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14483 :
14484 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14485 18 : subcmd->name = tab->replicaIdentityIndex;
14486 18 : cmd->subtype = AT_ReplicaIdentity;
14487 18 : cmd->def = (Node *) subcmd;
14488 :
14489 : /* do it after indexes and constraints */
14490 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14491 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14492 : }
14493 :
14494 : /*
14495 : * Queue up command to restore marking of index used for cluster.
14496 : */
14497 960 : if (tab->clusterOnIndex)
14498 : {
14499 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14500 :
14501 18 : cmd->subtype = AT_ClusterOn;
14502 18 : cmd->name = tab->clusterOnIndex;
14503 :
14504 : /* do it after indexes and constraints */
14505 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14506 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14507 : }
14508 :
14509 : /*
14510 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14511 : * be depending on these objects.
14512 : */
14513 960 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14514 :
14515 960 : free_object_addresses(objects);
14516 :
14517 : /*
14518 : * The objects will get recreated during subsequent passes over the work
14519 : * queue.
14520 : */
14521 960 : }
14522 :
14523 : /*
14524 : * Parse the previously-saved definition string for a constraint, index or
14525 : * statistics object against the newly-established column data type(s), and
14526 : * queue up the resulting command parsetrees for execution.
14527 : *
14528 : * This might fail if, for example, you have a WHERE clause that uses an
14529 : * operator that's not available for the new column type.
14530 : */
14531 : static void
14532 656 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14533 : List **wqueue, LOCKMODE lockmode, bool rewrite)
14534 : {
14535 : List *raw_parsetree_list;
14536 : List *querytree_list;
14537 : ListCell *list_item;
14538 : Relation rel;
14539 :
14540 : /*
14541 : * We expect that we will get only ALTER TABLE and CREATE INDEX
14542 : * statements. Hence, there is no need to pass them through
14543 : * parse_analyze_*() or the rewriter, but instead we need to pass them
14544 : * through parse_utilcmd.c to make them ready for execution.
14545 : */
14546 656 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14547 656 : querytree_list = NIL;
14548 1312 : foreach(list_item, raw_parsetree_list)
14549 : {
14550 656 : RawStmt *rs = lfirst_node(RawStmt, list_item);
14551 656 : Node *stmt = rs->stmt;
14552 :
14553 656 : if (IsA(stmt, IndexStmt))
14554 212 : querytree_list = lappend(querytree_list,
14555 212 : transformIndexStmt(oldRelId,
14556 : (IndexStmt *) stmt,
14557 : cmd));
14558 444 : else if (IsA(stmt, AlterTableStmt))
14559 : {
14560 : List *beforeStmts;
14561 : List *afterStmts;
14562 :
14563 416 : stmt = (Node *) transformAlterTableStmt(oldRelId,
14564 : (AlterTableStmt *) stmt,
14565 : cmd,
14566 : &beforeStmts,
14567 : &afterStmts);
14568 416 : querytree_list = list_concat(querytree_list, beforeStmts);
14569 416 : querytree_list = lappend(querytree_list, stmt);
14570 416 : querytree_list = list_concat(querytree_list, afterStmts);
14571 : }
14572 28 : else if (IsA(stmt, CreateStatsStmt))
14573 14 : querytree_list = lappend(querytree_list,
14574 14 : transformStatsStmt(oldRelId,
14575 : (CreateStatsStmt *) stmt,
14576 : cmd));
14577 : else
14578 14 : querytree_list = lappend(querytree_list, stmt);
14579 : }
14580 :
14581 : /* Caller should already have acquired whatever lock we need. */
14582 656 : rel = relation_open(oldRelId, NoLock);
14583 :
14584 : /*
14585 : * Attach each generated command to the proper place in the work queue.
14586 : * Note this could result in creation of entirely new work-queue entries.
14587 : *
14588 : * Also note that we have to tweak the command subtypes, because it turns
14589 : * out that re-creation of indexes and constraints has to act a bit
14590 : * differently from initial creation.
14591 : */
14592 1312 : foreach(list_item, querytree_list)
14593 : {
14594 656 : Node *stm = (Node *) lfirst(list_item);
14595 : AlteredTableInfo *tab;
14596 :
14597 656 : tab = ATGetQueueEntry(wqueue, rel);
14598 :
14599 656 : if (IsA(stm, IndexStmt))
14600 : {
14601 212 : IndexStmt *stmt = (IndexStmt *) stm;
14602 : AlterTableCmd *newcmd;
14603 :
14604 212 : if (!rewrite)
14605 54 : TryReuseIndex(oldId, stmt);
14606 212 : stmt->reset_default_tblspc = true;
14607 : /* keep the index's comment */
14608 212 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14609 :
14610 212 : newcmd = makeNode(AlterTableCmd);
14611 212 : newcmd->subtype = AT_ReAddIndex;
14612 212 : newcmd->def = (Node *) stmt;
14613 212 : tab->subcmds[AT_PASS_OLD_INDEX] =
14614 212 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14615 : }
14616 444 : else if (IsA(stm, AlterTableStmt))
14617 : {
14618 416 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14619 : ListCell *lcmd;
14620 :
14621 988 : foreach(lcmd, stmt->cmds)
14622 : {
14623 572 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14624 :
14625 572 : if (cmd->subtype == AT_AddIndex)
14626 : {
14627 : IndexStmt *indstmt;
14628 : Oid indoid;
14629 :
14630 222 : indstmt = castNode(IndexStmt, cmd->def);
14631 222 : indoid = get_constraint_index(oldId);
14632 :
14633 222 : if (!rewrite)
14634 48 : TryReuseIndex(indoid, indstmt);
14635 : /* keep any comment on the index */
14636 222 : indstmt->idxcomment = GetComment(indoid,
14637 : RelationRelationId, 0);
14638 222 : indstmt->reset_default_tblspc = true;
14639 :
14640 222 : cmd->subtype = AT_ReAddIndex;
14641 222 : tab->subcmds[AT_PASS_OLD_INDEX] =
14642 222 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14643 :
14644 : /* recreate any comment on the constraint */
14645 222 : RebuildConstraintComment(tab,
14646 : AT_PASS_OLD_INDEX,
14647 : oldId,
14648 : rel,
14649 : NIL,
14650 222 : indstmt->idxname);
14651 : }
14652 350 : else if (cmd->subtype == AT_AddConstraint)
14653 : {
14654 194 : Constraint *con = castNode(Constraint, cmd->def);
14655 :
14656 194 : con->old_pktable_oid = refRelId;
14657 : /* rewriting neither side of a FK */
14658 194 : if (con->contype == CONSTR_FOREIGN &&
14659 72 : !rewrite && tab->rewrite == 0)
14660 6 : TryReuseForeignKey(oldId, con);
14661 194 : con->reset_default_tblspc = true;
14662 194 : cmd->subtype = AT_ReAddConstraint;
14663 194 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14664 194 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14665 :
14666 : /* recreate any comment on the constraint */
14667 194 : RebuildConstraintComment(tab,
14668 : AT_PASS_OLD_CONSTR,
14669 : oldId,
14670 : rel,
14671 : NIL,
14672 194 : con->conname);
14673 : }
14674 156 : else if (cmd->subtype == AT_SetAttNotNull)
14675 : {
14676 : /*
14677 : * We see this subtype when a primary key is created
14678 : * internally, for example when it is replaced with a new
14679 : * constraint (say because one of the columns changes
14680 : * type); in this case we need to reinstate attnotnull,
14681 : * because it was removed because of the drop of the old
14682 : * PK. Schedule this subcommand to an upcoming AT pass.
14683 : */
14684 156 : cmd->subtype = AT_SetAttNotNull;
14685 156 : tab->subcmds[AT_PASS_OLD_COL_ATTRS] =
14686 156 : lappend(tab->subcmds[AT_PASS_OLD_COL_ATTRS], cmd);
14687 : }
14688 : else
14689 0 : elog(ERROR, "unexpected statement subtype: %d",
14690 : (int) cmd->subtype);
14691 : }
14692 : }
14693 28 : else if (IsA(stm, AlterDomainStmt))
14694 : {
14695 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14696 :
14697 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14698 : {
14699 14 : Constraint *con = castNode(Constraint, stmt->def);
14700 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14701 :
14702 14 : cmd->subtype = AT_ReAddDomainConstraint;
14703 14 : cmd->def = (Node *) stmt;
14704 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14705 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14706 :
14707 : /* recreate any comment on the constraint */
14708 14 : RebuildConstraintComment(tab,
14709 : AT_PASS_OLD_CONSTR,
14710 : oldId,
14711 : NULL,
14712 : stmt->typeName,
14713 14 : con->conname);
14714 : }
14715 : else
14716 0 : elog(ERROR, "unexpected statement subtype: %d",
14717 : (int) stmt->subtype);
14718 : }
14719 14 : else if (IsA(stm, CreateStatsStmt))
14720 : {
14721 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14722 : AlterTableCmd *newcmd;
14723 :
14724 : /* keep the statistics object's comment */
14725 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14726 :
14727 14 : newcmd = makeNode(AlterTableCmd);
14728 14 : newcmd->subtype = AT_ReAddStatistics;
14729 14 : newcmd->def = (Node *) stmt;
14730 14 : tab->subcmds[AT_PASS_MISC] =
14731 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14732 : }
14733 : else
14734 0 : elog(ERROR, "unexpected statement type: %d",
14735 : (int) nodeTag(stm));
14736 : }
14737 :
14738 656 : relation_close(rel, NoLock);
14739 656 : }
14740 :
14741 : /*
14742 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14743 : * for a table or domain constraint that is being rebuilt.
14744 : *
14745 : * objid is the OID of the constraint.
14746 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14747 : * as a string list) for a domain constraint.
14748 : * (We could dig that info, as well as the conname, out of the pg_constraint
14749 : * entry; but callers already have them so might as well pass them.)
14750 : */
14751 : static void
14752 430 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14753 : Relation rel, List *domname,
14754 : const char *conname)
14755 : {
14756 : CommentStmt *cmd;
14757 : char *comment_str;
14758 : AlterTableCmd *newcmd;
14759 :
14760 : /* Look for comment for object wanted, and leave if none */
14761 430 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14762 430 : if (comment_str == NULL)
14763 364 : return;
14764 :
14765 : /* Build CommentStmt node, copying all input data for safety */
14766 66 : cmd = makeNode(CommentStmt);
14767 66 : if (rel)
14768 : {
14769 54 : cmd->objtype = OBJECT_TABCONSTRAINT;
14770 54 : cmd->object = (Node *)
14771 54 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14772 : makeString(pstrdup(RelationGetRelationName(rel))),
14773 : makeString(pstrdup(conname)));
14774 : }
14775 : else
14776 : {
14777 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
14778 12 : cmd->object = (Node *)
14779 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
14780 : makeString(pstrdup(conname)));
14781 : }
14782 66 : cmd->comment = comment_str;
14783 :
14784 : /* Append it to list of commands */
14785 66 : newcmd = makeNode(AlterTableCmd);
14786 66 : newcmd->subtype = AT_ReAddComment;
14787 66 : newcmd->def = (Node *) cmd;
14788 66 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14789 : }
14790 :
14791 : /*
14792 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14793 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14794 : */
14795 : static void
14796 102 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14797 : {
14798 102 : if (CheckIndexCompatible(oldId,
14799 102 : stmt->accessMethod,
14800 102 : stmt->indexParams,
14801 102 : stmt->excludeOpNames,
14802 102 : stmt->iswithoutoverlaps))
14803 : {
14804 102 : Relation irel = index_open(oldId, NoLock);
14805 :
14806 : /* If it's a partitioned index, there is no storage to share. */
14807 102 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14808 : {
14809 72 : stmt->oldNumber = irel->rd_locator.relNumber;
14810 72 : stmt->oldCreateSubid = irel->rd_createSubid;
14811 72 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14812 : }
14813 102 : index_close(irel, NoLock);
14814 : }
14815 102 : }
14816 :
14817 : /*
14818 : * Subroutine for ATPostAlterTypeParse().
14819 : *
14820 : * Stash the old P-F equality operator into the Constraint node, for possible
14821 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14822 : * this constraint can be skipped.
14823 : */
14824 : static void
14825 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
14826 : {
14827 : HeapTuple tup;
14828 : Datum adatum;
14829 : ArrayType *arr;
14830 : Oid *rawarr;
14831 : int numkeys;
14832 : int i;
14833 :
14834 : Assert(con->contype == CONSTR_FOREIGN);
14835 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14836 :
14837 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14838 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
14839 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14840 :
14841 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14842 : Anum_pg_constraint_conpfeqop);
14843 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14844 6 : numkeys = ARR_DIMS(arr)[0];
14845 : /* test follows the one in ri_FetchConstraintInfo() */
14846 6 : if (ARR_NDIM(arr) != 1 ||
14847 6 : ARR_HASNULL(arr) ||
14848 6 : ARR_ELEMTYPE(arr) != OIDOID)
14849 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
14850 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14851 :
14852 : /* stash a List of the operator Oids in our Constraint node */
14853 12 : for (i = 0; i < numkeys; i++)
14854 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14855 :
14856 6 : ReleaseSysCache(tup);
14857 6 : }
14858 :
14859 : /*
14860 : * ALTER COLUMN .. OPTIONS ( ... )
14861 : *
14862 : * Returns the address of the modified column
14863 : */
14864 : static ObjectAddress
14865 164 : ATExecAlterColumnGenericOptions(Relation rel,
14866 : const char *colName,
14867 : List *options,
14868 : LOCKMODE lockmode)
14869 : {
14870 : Relation ftrel;
14871 : Relation attrel;
14872 : ForeignServer *server;
14873 : ForeignDataWrapper *fdw;
14874 : HeapTuple tuple;
14875 : HeapTuple newtuple;
14876 : bool isnull;
14877 : Datum repl_val[Natts_pg_attribute];
14878 : bool repl_null[Natts_pg_attribute];
14879 : bool repl_repl[Natts_pg_attribute];
14880 : Datum datum;
14881 : Form_pg_foreign_table fttableform;
14882 : Form_pg_attribute atttableform;
14883 : AttrNumber attnum;
14884 : ObjectAddress address;
14885 :
14886 164 : if (options == NIL)
14887 0 : return InvalidObjectAddress;
14888 :
14889 : /* First, determine FDW validator associated to the foreign table. */
14890 164 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14891 164 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14892 164 : if (!HeapTupleIsValid(tuple))
14893 0 : ereport(ERROR,
14894 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14895 : errmsg("foreign table \"%s\" does not exist",
14896 : RelationGetRelationName(rel))));
14897 164 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14898 164 : server = GetForeignServer(fttableform->ftserver);
14899 164 : fdw = GetForeignDataWrapper(server->fdwid);
14900 :
14901 164 : table_close(ftrel, AccessShareLock);
14902 164 : ReleaseSysCache(tuple);
14903 :
14904 164 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
14905 164 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14906 164 : if (!HeapTupleIsValid(tuple))
14907 0 : ereport(ERROR,
14908 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14909 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14910 : colName, RelationGetRelationName(rel))));
14911 :
14912 : /* Prevent them from altering a system attribute */
14913 164 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14914 164 : attnum = atttableform->attnum;
14915 164 : if (attnum <= 0)
14916 6 : ereport(ERROR,
14917 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14918 : errmsg("cannot alter system column \"%s\"", colName)));
14919 :
14920 :
14921 : /* Initialize buffers for new tuple values */
14922 158 : memset(repl_val, 0, sizeof(repl_val));
14923 158 : memset(repl_null, false, sizeof(repl_null));
14924 158 : memset(repl_repl, false, sizeof(repl_repl));
14925 :
14926 : /* Extract the current options */
14927 158 : datum = SysCacheGetAttr(ATTNAME,
14928 : tuple,
14929 : Anum_pg_attribute_attfdwoptions,
14930 : &isnull);
14931 158 : if (isnull)
14932 148 : datum = PointerGetDatum(NULL);
14933 :
14934 : /* Transform the options */
14935 158 : datum = transformGenericOptions(AttributeRelationId,
14936 : datum,
14937 : options,
14938 : fdw->fdwvalidator);
14939 :
14940 158 : if (PointerIsValid(DatumGetPointer(datum)))
14941 158 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14942 : else
14943 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14944 :
14945 158 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14946 :
14947 : /* Everything looks good - update the tuple */
14948 :
14949 158 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14950 : repl_val, repl_null, repl_repl);
14951 :
14952 158 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14953 :
14954 158 : InvokeObjectPostAlterHook(RelationRelationId,
14955 : RelationGetRelid(rel),
14956 : atttableform->attnum);
14957 158 : ObjectAddressSubSet(address, RelationRelationId,
14958 : RelationGetRelid(rel), attnum);
14959 :
14960 158 : ReleaseSysCache(tuple);
14961 :
14962 158 : table_close(attrel, RowExclusiveLock);
14963 :
14964 158 : heap_freetuple(newtuple);
14965 :
14966 158 : return address;
14967 : }
14968 :
14969 : /*
14970 : * ALTER TABLE OWNER
14971 : *
14972 : * recursing is true if we are recursing from a table to its indexes,
14973 : * sequences, or toast table. We don't allow the ownership of those things to
14974 : * be changed separately from the parent table. Also, we can skip permission
14975 : * checks (this is necessary not just an optimization, else we'd fail to
14976 : * handle toast tables properly).
14977 : *
14978 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14979 : * free-standing composite type.
14980 : */
14981 : void
14982 1978 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14983 : {
14984 : Relation target_rel;
14985 : Relation class_rel;
14986 : HeapTuple tuple;
14987 : Form_pg_class tuple_class;
14988 :
14989 : /*
14990 : * Get exclusive lock till end of transaction on the target table. Use
14991 : * relation_open so that we can work on indexes and sequences.
14992 : */
14993 1978 : target_rel = relation_open(relationOid, lockmode);
14994 :
14995 : /* Get its pg_class tuple, too */
14996 1978 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
14997 :
14998 1978 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14999 1978 : if (!HeapTupleIsValid(tuple))
15000 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
15001 1978 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15002 :
15003 : /* Can we change the ownership of this tuple? */
15004 1978 : switch (tuple_class->relkind)
15005 : {
15006 1740 : case RELKIND_RELATION:
15007 : case RELKIND_VIEW:
15008 : case RELKIND_MATVIEW:
15009 : case RELKIND_FOREIGN_TABLE:
15010 : case RELKIND_PARTITIONED_TABLE:
15011 : /* ok to change owner */
15012 1740 : break;
15013 84 : case RELKIND_INDEX:
15014 84 : if (!recursing)
15015 : {
15016 : /*
15017 : * Because ALTER INDEX OWNER used to be allowed, and in fact
15018 : * is generated by old versions of pg_dump, we give a warning
15019 : * and do nothing rather than erroring out. Also, to avoid
15020 : * unnecessary chatter while restoring those old dumps, say
15021 : * nothing at all if the command would be a no-op anyway.
15022 : */
15023 0 : if (tuple_class->relowner != newOwnerId)
15024 0 : ereport(WARNING,
15025 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15026 : errmsg("cannot change owner of index \"%s\"",
15027 : NameStr(tuple_class->relname)),
15028 : errhint("Change the ownership of the index's table instead.")));
15029 : /* quick hack to exit via the no-op path */
15030 0 : newOwnerId = tuple_class->relowner;
15031 : }
15032 84 : break;
15033 20 : case RELKIND_PARTITIONED_INDEX:
15034 20 : if (recursing)
15035 20 : break;
15036 0 : ereport(ERROR,
15037 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15038 : errmsg("cannot change owner of index \"%s\"",
15039 : NameStr(tuple_class->relname)),
15040 : errhint("Change the ownership of the index's table instead.")));
15041 : break;
15042 92 : case RELKIND_SEQUENCE:
15043 92 : if (!recursing &&
15044 62 : tuple_class->relowner != newOwnerId)
15045 : {
15046 : /* if it's an owned sequence, disallow changing it by itself */
15047 : Oid tableId;
15048 : int32 colId;
15049 :
15050 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15051 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15052 0 : ereport(ERROR,
15053 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15054 : errmsg("cannot change owner of sequence \"%s\"",
15055 : NameStr(tuple_class->relname)),
15056 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
15057 : NameStr(tuple_class->relname),
15058 : get_rel_name(tableId))));
15059 : }
15060 92 : break;
15061 6 : case RELKIND_COMPOSITE_TYPE:
15062 6 : if (recursing)
15063 6 : break;
15064 0 : ereport(ERROR,
15065 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15066 : errmsg("\"%s\" is a composite type",
15067 : NameStr(tuple_class->relname)),
15068 : /* translator: %s is an SQL ALTER command */
15069 : errhint("Use %s instead.",
15070 : "ALTER TYPE")));
15071 : break;
15072 36 : case RELKIND_TOASTVALUE:
15073 36 : if (recursing)
15074 36 : break;
15075 : /* FALL THRU */
15076 : default:
15077 0 : ereport(ERROR,
15078 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15079 : errmsg("cannot change owner of relation \"%s\"",
15080 : NameStr(tuple_class->relname)),
15081 : errdetail_relkind_not_supported(tuple_class->relkind)));
15082 : }
15083 :
15084 : /*
15085 : * If the new owner is the same as the existing owner, consider the
15086 : * command to have succeeded. This is for dump restoration purposes.
15087 : */
15088 1978 : if (tuple_class->relowner != newOwnerId)
15089 : {
15090 : Datum repl_val[Natts_pg_class];
15091 : bool repl_null[Natts_pg_class];
15092 : bool repl_repl[Natts_pg_class];
15093 : Acl *newAcl;
15094 : Datum aclDatum;
15095 : bool isNull;
15096 : HeapTuple newtuple;
15097 :
15098 : /* skip permission checks when recursing to index or toast table */
15099 450 : if (!recursing)
15100 : {
15101 : /* Superusers can always do it */
15102 274 : if (!superuser())
15103 : {
15104 42 : Oid namespaceOid = tuple_class->relnamespace;
15105 : AclResult aclresult;
15106 :
15107 : /* Otherwise, must be owner of the existing object */
15108 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15109 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15110 0 : RelationGetRelationName(target_rel));
15111 :
15112 : /* Must be able to become new owner */
15113 42 : check_can_set_role(GetUserId(), newOwnerId);
15114 :
15115 : /* New owner must have CREATE privilege on namespace */
15116 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15117 : ACL_CREATE);
15118 30 : if (aclresult != ACLCHECK_OK)
15119 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15120 0 : get_namespace_name(namespaceOid));
15121 : }
15122 : }
15123 :
15124 438 : memset(repl_null, false, sizeof(repl_null));
15125 438 : memset(repl_repl, false, sizeof(repl_repl));
15126 :
15127 438 : repl_repl[Anum_pg_class_relowner - 1] = true;
15128 438 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15129 :
15130 : /*
15131 : * Determine the modified ACL for the new owner. This is only
15132 : * necessary when the ACL is non-null.
15133 : */
15134 438 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15135 : Anum_pg_class_relacl,
15136 : &isNull);
15137 438 : if (!isNull)
15138 : {
15139 32 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15140 : tuple_class->relowner, newOwnerId);
15141 32 : repl_repl[Anum_pg_class_relacl - 1] = true;
15142 32 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15143 : }
15144 :
15145 438 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15146 :
15147 438 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15148 :
15149 438 : heap_freetuple(newtuple);
15150 :
15151 : /*
15152 : * We must similarly update any per-column ACLs to reflect the new
15153 : * owner; for neatness reasons that's split out as a subroutine.
15154 : */
15155 438 : change_owner_fix_column_acls(relationOid,
15156 : tuple_class->relowner,
15157 : newOwnerId);
15158 :
15159 : /*
15160 : * Update owner dependency reference, if any. A composite type has
15161 : * none, because it's tracked for the pg_type entry instead of here;
15162 : * indexes and TOAST tables don't have their own entries either.
15163 : */
15164 438 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15165 432 : tuple_class->relkind != RELKIND_INDEX &&
15166 348 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15167 328 : tuple_class->relkind != RELKIND_TOASTVALUE)
15168 292 : changeDependencyOnOwner(RelationRelationId, relationOid,
15169 : newOwnerId);
15170 :
15171 : /*
15172 : * Also change the ownership of the table's row type, if it has one
15173 : */
15174 438 : if (OidIsValid(tuple_class->reltype))
15175 280 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15176 :
15177 : /*
15178 : * If we are operating on a table or materialized view, also change
15179 : * the ownership of any indexes and sequences that belong to the
15180 : * relation, as well as its toast table (if it has one).
15181 : */
15182 438 : if (tuple_class->relkind == RELKIND_RELATION ||
15183 226 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15184 188 : tuple_class->relkind == RELKIND_MATVIEW ||
15185 188 : tuple_class->relkind == RELKIND_TOASTVALUE)
15186 : {
15187 : List *index_oid_list;
15188 : ListCell *i;
15189 :
15190 : /* Find all the indexes belonging to this relation */
15191 286 : index_oid_list = RelationGetIndexList(target_rel);
15192 :
15193 : /* For each index, recursively change its ownership */
15194 390 : foreach(i, index_oid_list)
15195 104 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15196 :
15197 286 : list_free(index_oid_list);
15198 : }
15199 :
15200 : /* If it has a toast table, recurse to change its ownership */
15201 438 : if (tuple_class->reltoastrelid != InvalidOid)
15202 36 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15203 : true, lockmode);
15204 :
15205 : /* If it has dependent sequences, recurse to change them too */
15206 438 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15207 : }
15208 :
15209 1966 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15210 :
15211 1966 : ReleaseSysCache(tuple);
15212 1966 : table_close(class_rel, RowExclusiveLock);
15213 1966 : relation_close(target_rel, NoLock);
15214 1966 : }
15215 :
15216 : /*
15217 : * change_owner_fix_column_acls
15218 : *
15219 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15220 : * and fix any non-null column ACLs to reflect the new owner.
15221 : */
15222 : static void
15223 438 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15224 : {
15225 : Relation attRelation;
15226 : SysScanDesc scan;
15227 : ScanKeyData key[1];
15228 : HeapTuple attributeTuple;
15229 :
15230 438 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15231 438 : ScanKeyInit(&key[0],
15232 : Anum_pg_attribute_attrelid,
15233 : BTEqualStrategyNumber, F_OIDEQ,
15234 : ObjectIdGetDatum(relationOid));
15235 438 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15236 : true, NULL, 1, key);
15237 3012 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15238 : {
15239 2574 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15240 : Datum repl_val[Natts_pg_attribute];
15241 : bool repl_null[Natts_pg_attribute];
15242 : bool repl_repl[Natts_pg_attribute];
15243 : Acl *newAcl;
15244 : Datum aclDatum;
15245 : bool isNull;
15246 : HeapTuple newtuple;
15247 :
15248 : /* Ignore dropped columns */
15249 2574 : if (att->attisdropped)
15250 2574 : continue;
15251 :
15252 2574 : aclDatum = heap_getattr(attributeTuple,
15253 : Anum_pg_attribute_attacl,
15254 : RelationGetDescr(attRelation),
15255 : &isNull);
15256 : /* Null ACLs do not require changes */
15257 2574 : if (isNull)
15258 2574 : continue;
15259 :
15260 0 : memset(repl_null, false, sizeof(repl_null));
15261 0 : memset(repl_repl, false, sizeof(repl_repl));
15262 :
15263 0 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15264 : oldOwnerId, newOwnerId);
15265 0 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15266 0 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15267 :
15268 0 : newtuple = heap_modify_tuple(attributeTuple,
15269 : RelationGetDescr(attRelation),
15270 : repl_val, repl_null, repl_repl);
15271 :
15272 0 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15273 :
15274 0 : heap_freetuple(newtuple);
15275 : }
15276 438 : systable_endscan(scan);
15277 438 : table_close(attRelation, RowExclusiveLock);
15278 438 : }
15279 :
15280 : /*
15281 : * change_owner_recurse_to_sequences
15282 : *
15283 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15284 : * for sequences that are dependent on serial columns, and changes their
15285 : * ownership.
15286 : */
15287 : static void
15288 438 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15289 : {
15290 : Relation depRel;
15291 : SysScanDesc scan;
15292 : ScanKeyData key[2];
15293 : HeapTuple tup;
15294 :
15295 : /*
15296 : * SERIAL sequences are those having an auto dependency on one of the
15297 : * table's columns (we don't care *which* column, exactly).
15298 : */
15299 438 : depRel = table_open(DependRelationId, AccessShareLock);
15300 :
15301 438 : ScanKeyInit(&key[0],
15302 : Anum_pg_depend_refclassid,
15303 : BTEqualStrategyNumber, F_OIDEQ,
15304 : ObjectIdGetDatum(RelationRelationId));
15305 438 : ScanKeyInit(&key[1],
15306 : Anum_pg_depend_refobjid,
15307 : BTEqualStrategyNumber, F_OIDEQ,
15308 : ObjectIdGetDatum(relationOid));
15309 : /* we leave refobjsubid unspecified */
15310 :
15311 438 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15312 : NULL, 2, key);
15313 :
15314 1216 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15315 : {
15316 778 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15317 : Relation seqRel;
15318 :
15319 : /* skip dependencies other than auto dependencies on columns */
15320 778 : if (depForm->refobjsubid == 0 ||
15321 268 : depForm->classid != RelationRelationId ||
15322 122 : depForm->objsubid != 0 ||
15323 122 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15324 656 : continue;
15325 :
15326 : /* Use relation_open just in case it's an index */
15327 122 : seqRel = relation_open(depForm->objid, lockmode);
15328 :
15329 : /* skip non-sequence relations */
15330 122 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15331 : {
15332 : /* No need to keep the lock */
15333 104 : relation_close(seqRel, lockmode);
15334 104 : continue;
15335 : }
15336 :
15337 : /* We don't need to close the sequence while we alter it. */
15338 18 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15339 :
15340 : /* Now we can close it. Keep the lock till end of transaction. */
15341 18 : relation_close(seqRel, NoLock);
15342 : }
15343 :
15344 438 : systable_endscan(scan);
15345 :
15346 438 : relation_close(depRel, AccessShareLock);
15347 438 : }
15348 :
15349 : /*
15350 : * ALTER TABLE CLUSTER ON
15351 : *
15352 : * The only thing we have to do is to change the indisclustered bits.
15353 : *
15354 : * Return the address of the new clustering index.
15355 : */
15356 : static ObjectAddress
15357 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15358 : {
15359 : Oid indexOid;
15360 : ObjectAddress address;
15361 :
15362 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15363 :
15364 64 : if (!OidIsValid(indexOid))
15365 0 : ereport(ERROR,
15366 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15367 : errmsg("index \"%s\" for table \"%s\" does not exist",
15368 : indexName, RelationGetRelationName(rel))));
15369 :
15370 : /* Check index is valid to cluster on */
15371 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15372 :
15373 : /* And do the work */
15374 64 : mark_index_clustered(rel, indexOid, false);
15375 :
15376 58 : ObjectAddressSet(address,
15377 : RelationRelationId, indexOid);
15378 :
15379 58 : return address;
15380 : }
15381 :
15382 : /*
15383 : * ALTER TABLE SET WITHOUT CLUSTER
15384 : *
15385 : * We have to find any indexes on the table that have indisclustered bit
15386 : * set and turn it off.
15387 : */
15388 : static void
15389 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15390 : {
15391 18 : mark_index_clustered(rel, InvalidOid, false);
15392 12 : }
15393 :
15394 : /*
15395 : * Preparation phase for SET ACCESS METHOD
15396 : *
15397 : * Check that the access method exists and determine whether a change is
15398 : * actually needed.
15399 : */
15400 : static void
15401 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15402 : {
15403 : Oid amoid;
15404 :
15405 : /*
15406 : * Look up the access method name and check that it differs from the
15407 : * table's current AM. If DEFAULT was specified for a partitioned table
15408 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15409 : */
15410 110 : if (amname != NULL)
15411 74 : amoid = get_table_am_oid(amname, false);
15412 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15413 18 : amoid = InvalidOid;
15414 : else
15415 18 : amoid = get_table_am_oid(default_table_access_method, false);
15416 :
15417 : /* if it's a match, phase 3 doesn't need to do anything */
15418 110 : if (rel->rd_rel->relam == amoid)
15419 12 : return;
15420 :
15421 : /* Save info for Phase 3 to do the real work */
15422 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15423 98 : tab->newAccessMethod = amoid;
15424 98 : tab->chgAccessMethod = true;
15425 : }
15426 :
15427 : /*
15428 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15429 : * storage that have an interest in preserving AM.
15430 : *
15431 : * Since these have no storage, setting the access method is a catalog only
15432 : * operation.
15433 : */
15434 : static void
15435 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15436 : {
15437 : Relation pg_class;
15438 : Oid oldAccessMethodId;
15439 : HeapTuple tuple;
15440 : Form_pg_class rd_rel;
15441 44 : Oid reloid = RelationGetRelid(rel);
15442 :
15443 : /*
15444 : * Shouldn't be called on relations having storage; these are processed in
15445 : * phase 3.
15446 : */
15447 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15448 :
15449 : /* Get a modifiable copy of the relation's pg_class row. */
15450 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15451 :
15452 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15453 44 : if (!HeapTupleIsValid(tuple))
15454 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15455 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15456 :
15457 : /* Update the pg_class row. */
15458 44 : oldAccessMethodId = rd_rel->relam;
15459 44 : rd_rel->relam = newAccessMethodId;
15460 :
15461 : /* Leave if no update required */
15462 44 : if (rd_rel->relam == oldAccessMethodId)
15463 : {
15464 0 : heap_freetuple(tuple);
15465 0 : table_close(pg_class, RowExclusiveLock);
15466 0 : return;
15467 : }
15468 :
15469 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15470 :
15471 : /*
15472 : * Update the dependency on the new access method. No dependency is added
15473 : * if the new access method is InvalidOid (default case). Be very careful
15474 : * that this has to compare the previous value stored in pg_class with the
15475 : * new one.
15476 : */
15477 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15478 20 : {
15479 : ObjectAddress relobj,
15480 : referenced;
15481 :
15482 : /*
15483 : * New access method is defined and there was no dependency
15484 : * previously, so record a new one.
15485 : */
15486 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15487 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15488 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15489 : }
15490 24 : else if (OidIsValid(oldAccessMethodId) &&
15491 24 : !OidIsValid(rd_rel->relam))
15492 : {
15493 : /*
15494 : * There was an access method defined, and no new one, so just remove
15495 : * the existing dependency.
15496 : */
15497 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15498 : AccessMethodRelationId,
15499 : DEPENDENCY_NORMAL);
15500 : }
15501 : else
15502 : {
15503 : Assert(OidIsValid(oldAccessMethodId) &&
15504 : OidIsValid(rd_rel->relam));
15505 :
15506 : /* Both are valid, so update the dependency */
15507 12 : changeDependencyFor(RelationRelationId, reloid,
15508 : AccessMethodRelationId,
15509 : oldAccessMethodId, rd_rel->relam);
15510 : }
15511 :
15512 : /* make the relam and dependency changes visible */
15513 44 : CommandCounterIncrement();
15514 :
15515 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15516 :
15517 44 : heap_freetuple(tuple);
15518 44 : table_close(pg_class, RowExclusiveLock);
15519 : }
15520 :
15521 : /*
15522 : * ALTER TABLE SET TABLESPACE
15523 : */
15524 : static void
15525 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15526 : {
15527 : Oid tablespaceId;
15528 :
15529 : /* Check that the tablespace exists */
15530 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
15531 :
15532 : /* Check permissions except when moving to database's default */
15533 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15534 : {
15535 : AclResult aclresult;
15536 :
15537 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15538 66 : if (aclresult != ACLCHECK_OK)
15539 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15540 : }
15541 :
15542 : /* Save info for Phase 3 to do the real work */
15543 158 : if (OidIsValid(tab->newTableSpace))
15544 0 : ereport(ERROR,
15545 : (errcode(ERRCODE_SYNTAX_ERROR),
15546 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
15547 :
15548 158 : tab->newTableSpace = tablespaceId;
15549 158 : }
15550 :
15551 : /*
15552 : * Set, reset, or replace reloptions.
15553 : */
15554 : static void
15555 934 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15556 : LOCKMODE lockmode)
15557 : {
15558 : Oid relid;
15559 : Relation pgclass;
15560 : HeapTuple tuple;
15561 : HeapTuple newtuple;
15562 : Datum datum;
15563 : bool isnull;
15564 : Datum newOptions;
15565 : Datum repl_val[Natts_pg_class];
15566 : bool repl_null[Natts_pg_class];
15567 : bool repl_repl[Natts_pg_class];
15568 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
15569 :
15570 934 : if (defList == NIL && operation != AT_ReplaceRelOptions)
15571 0 : return; /* nothing to do */
15572 :
15573 934 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
15574 :
15575 : /* Fetch heap tuple */
15576 934 : relid = RelationGetRelid(rel);
15577 934 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
15578 934 : if (!HeapTupleIsValid(tuple))
15579 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
15580 :
15581 934 : if (operation == AT_ReplaceRelOptions)
15582 : {
15583 : /*
15584 : * If we're supposed to replace the reloptions list, we just pretend
15585 : * there were none before.
15586 : */
15587 194 : datum = (Datum) 0;
15588 194 : isnull = true;
15589 : }
15590 : else
15591 : {
15592 : /* Get the old reloptions */
15593 740 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15594 : &isnull);
15595 : }
15596 :
15597 : /* Generate new proposed reloptions (text array) */
15598 934 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15599 : defList, NULL, validnsps, false,
15600 : operation == AT_ResetRelOptions);
15601 :
15602 : /* Validate */
15603 928 : switch (rel->rd_rel->relkind)
15604 : {
15605 512 : case RELKIND_RELATION:
15606 : case RELKIND_TOASTVALUE:
15607 : case RELKIND_MATVIEW:
15608 512 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15609 512 : break;
15610 6 : case RELKIND_PARTITIONED_TABLE:
15611 6 : (void) partitioned_table_reloptions(newOptions, true);
15612 0 : break;
15613 296 : case RELKIND_VIEW:
15614 296 : (void) view_reloptions(newOptions, true);
15615 278 : break;
15616 114 : case RELKIND_INDEX:
15617 : case RELKIND_PARTITIONED_INDEX:
15618 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15619 92 : break;
15620 0 : default:
15621 0 : ereport(ERROR,
15622 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15623 : errmsg("cannot set options for relation \"%s\"",
15624 : RelationGetRelationName(rel)),
15625 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15626 : break;
15627 : }
15628 :
15629 : /* Special-case validation of view options */
15630 882 : if (rel->rd_rel->relkind == RELKIND_VIEW)
15631 : {
15632 278 : Query *view_query = get_view_query(rel);
15633 278 : List *view_options = untransformRelOptions(newOptions);
15634 : ListCell *cell;
15635 278 : bool check_option = false;
15636 :
15637 380 : foreach(cell, view_options)
15638 : {
15639 102 : DefElem *defel = (DefElem *) lfirst(cell);
15640 :
15641 102 : if (strcmp(defel->defname, "check_option") == 0)
15642 24 : check_option = true;
15643 : }
15644 :
15645 : /*
15646 : * If the check option is specified, look to see if the view is
15647 : * actually auto-updatable or not.
15648 : */
15649 278 : if (check_option)
15650 : {
15651 : const char *view_updatable_error =
15652 24 : view_query_is_auto_updatable(view_query, true);
15653 :
15654 24 : if (view_updatable_error)
15655 0 : ereport(ERROR,
15656 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15657 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15658 : errhint("%s", _(view_updatable_error))));
15659 : }
15660 : }
15661 :
15662 : /*
15663 : * All we need do here is update the pg_class row; the new options will be
15664 : * propagated into relcaches during post-commit cache inval.
15665 : */
15666 882 : memset(repl_val, 0, sizeof(repl_val));
15667 882 : memset(repl_null, false, sizeof(repl_null));
15668 882 : memset(repl_repl, false, sizeof(repl_repl));
15669 :
15670 882 : if (newOptions != (Datum) 0)
15671 594 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15672 : else
15673 288 : repl_null[Anum_pg_class_reloptions - 1] = true;
15674 :
15675 882 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15676 :
15677 882 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15678 : repl_val, repl_null, repl_repl);
15679 :
15680 882 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15681 :
15682 882 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15683 :
15684 882 : heap_freetuple(newtuple);
15685 :
15686 882 : ReleaseSysCache(tuple);
15687 :
15688 : /* repeat the whole exercise for the toast table, if there's one */
15689 882 : if (OidIsValid(rel->rd_rel->reltoastrelid))
15690 : {
15691 : Relation toastrel;
15692 256 : Oid toastid = rel->rd_rel->reltoastrelid;
15693 :
15694 256 : toastrel = table_open(toastid, lockmode);
15695 :
15696 : /* Fetch heap tuple */
15697 256 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15698 256 : if (!HeapTupleIsValid(tuple))
15699 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15700 :
15701 256 : if (operation == AT_ReplaceRelOptions)
15702 : {
15703 : /*
15704 : * If we're supposed to replace the reloptions list, we just
15705 : * pretend there were none before.
15706 : */
15707 0 : datum = (Datum) 0;
15708 0 : isnull = true;
15709 : }
15710 : else
15711 : {
15712 : /* Get the old reloptions */
15713 256 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15714 : &isnull);
15715 : }
15716 :
15717 256 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15718 : defList, "toast", validnsps, false,
15719 : operation == AT_ResetRelOptions);
15720 :
15721 256 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15722 :
15723 256 : memset(repl_val, 0, sizeof(repl_val));
15724 256 : memset(repl_null, false, sizeof(repl_null));
15725 256 : memset(repl_repl, false, sizeof(repl_repl));
15726 :
15727 256 : if (newOptions != (Datum) 0)
15728 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15729 : else
15730 214 : repl_null[Anum_pg_class_reloptions - 1] = true;
15731 :
15732 256 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15733 :
15734 256 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15735 : repl_val, repl_null, repl_repl);
15736 :
15737 256 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15738 :
15739 256 : InvokeObjectPostAlterHookArg(RelationRelationId,
15740 : RelationGetRelid(toastrel), 0,
15741 : InvalidOid, true);
15742 :
15743 256 : heap_freetuple(newtuple);
15744 :
15745 256 : ReleaseSysCache(tuple);
15746 :
15747 256 : table_close(toastrel, NoLock);
15748 : }
15749 :
15750 882 : table_close(pgclass, RowExclusiveLock);
15751 : }
15752 :
15753 : /*
15754 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15755 : * rewriting to be done, so we just want to copy the data as fast as possible.
15756 : */
15757 : static void
15758 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15759 : {
15760 : Relation rel;
15761 : Oid reltoastrelid;
15762 : RelFileNumber newrelfilenumber;
15763 : RelFileLocator newrlocator;
15764 162 : List *reltoastidxids = NIL;
15765 : ListCell *lc;
15766 :
15767 : /*
15768 : * Need lock here in case we are recursing to toast table or index
15769 : */
15770 162 : rel = relation_open(tableOid, lockmode);
15771 :
15772 : /* Check first if relation can be moved to new tablespace */
15773 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15774 : {
15775 2 : InvokeObjectPostAlterHook(RelationRelationId,
15776 : RelationGetRelid(rel), 0);
15777 2 : relation_close(rel, NoLock);
15778 2 : return;
15779 : }
15780 :
15781 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
15782 : /* Fetch the list of indexes on toast relation if necessary */
15783 160 : if (OidIsValid(reltoastrelid))
15784 : {
15785 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15786 :
15787 20 : reltoastidxids = RelationGetIndexList(toastRel);
15788 20 : relation_close(toastRel, lockmode);
15789 : }
15790 :
15791 : /*
15792 : * Relfilenumbers are not unique in databases across tablespaces, so we
15793 : * need to allocate a new one in the new tablespace.
15794 : */
15795 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15796 160 : rel->rd_rel->relpersistence);
15797 :
15798 : /* Open old and new relation */
15799 160 : newrlocator = rel->rd_locator;
15800 160 : newrlocator.relNumber = newrelfilenumber;
15801 160 : newrlocator.spcOid = newTableSpace;
15802 :
15803 : /* hand off to AM to actually create new rel storage and copy the data */
15804 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
15805 : {
15806 62 : index_copy_data(rel, newrlocator);
15807 : }
15808 : else
15809 : {
15810 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15811 98 : table_relation_copy_data(rel, &newrlocator);
15812 : }
15813 :
15814 : /*
15815 : * Update the pg_class row.
15816 : *
15817 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15818 : * executed on pg_class or its indexes (the above copy wouldn't contain
15819 : * the updated pg_class entry), but that's forbidden with
15820 : * CheckRelationTableSpaceMove().
15821 : */
15822 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15823 :
15824 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15825 :
15826 160 : RelationAssumeNewRelfilelocator(rel);
15827 :
15828 160 : relation_close(rel, NoLock);
15829 :
15830 : /* Make sure the reltablespace change is visible */
15831 160 : CommandCounterIncrement();
15832 :
15833 : /* Move associated toast relation and/or indexes, too */
15834 160 : if (OidIsValid(reltoastrelid))
15835 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15836 180 : foreach(lc, reltoastidxids)
15837 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15838 :
15839 : /* Clean up */
15840 160 : list_free(reltoastidxids);
15841 : }
15842 :
15843 : /*
15844 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15845 : * storage that have an interest in preserving tablespace.
15846 : *
15847 : * Since these have no storage the tablespace can be updated with a simple
15848 : * metadata only operation to update the tablespace.
15849 : */
15850 : static void
15851 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15852 : {
15853 : /*
15854 : * Shouldn't be called on relations having storage; these are processed in
15855 : * phase 3.
15856 : */
15857 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15858 :
15859 : /* check if relation can be moved to its new tablespace */
15860 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15861 : {
15862 0 : InvokeObjectPostAlterHook(RelationRelationId,
15863 : RelationGetRelid(rel),
15864 : 0);
15865 0 : return;
15866 : }
15867 :
15868 : /* Update can be done, so change reltablespace */
15869 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15870 :
15871 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15872 :
15873 : /* Make sure the reltablespace change is visible */
15874 30 : CommandCounterIncrement();
15875 : }
15876 :
15877 : /*
15878 : * Alter Table ALL ... SET TABLESPACE
15879 : *
15880 : * Allows a user to move all objects of some type in a given tablespace in the
15881 : * current database to another tablespace. Objects can be chosen based on the
15882 : * owner of the object also, to allow users to move only their objects.
15883 : * The user must have CREATE rights on the new tablespace, as usual. The main
15884 : * permissions handling is done by the lower-level table move function.
15885 : *
15886 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
15887 : * lock can't be acquired then we ereport(ERROR).
15888 : */
15889 : Oid
15890 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15891 : {
15892 30 : List *relations = NIL;
15893 : ListCell *l;
15894 : ScanKeyData key[1];
15895 : Relation rel;
15896 : TableScanDesc scan;
15897 : HeapTuple tuple;
15898 : Oid orig_tablespaceoid;
15899 : Oid new_tablespaceoid;
15900 30 : List *role_oids = roleSpecsToIds(stmt->roles);
15901 :
15902 : /* Ensure we were not asked to move something we can't */
15903 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15904 12 : stmt->objtype != OBJECT_MATVIEW)
15905 0 : ereport(ERROR,
15906 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15907 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15908 :
15909 : /* Get the orig and new tablespace OIDs */
15910 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15911 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15912 :
15913 : /* Can't move shared relations in to or out of pg_global */
15914 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15915 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15916 : new_tablespaceoid == GLOBALTABLESPACE_OID)
15917 0 : ereport(ERROR,
15918 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15919 : errmsg("cannot move relations in to or out of pg_global tablespace")));
15920 :
15921 : /*
15922 : * Must have CREATE rights on the new tablespace, unless it is the
15923 : * database default tablespace (which all users implicitly have CREATE
15924 : * rights on).
15925 : */
15926 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15927 : {
15928 : AclResult aclresult;
15929 :
15930 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15931 : ACL_CREATE);
15932 0 : if (aclresult != ACLCHECK_OK)
15933 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
15934 0 : get_tablespace_name(new_tablespaceoid));
15935 : }
15936 :
15937 : /*
15938 : * Now that the checks are done, check if we should set either to
15939 : * InvalidOid because it is our database's default tablespace.
15940 : */
15941 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
15942 0 : orig_tablespaceoid = InvalidOid;
15943 :
15944 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
15945 30 : new_tablespaceoid = InvalidOid;
15946 :
15947 : /* no-op */
15948 30 : if (orig_tablespaceoid == new_tablespaceoid)
15949 0 : return new_tablespaceoid;
15950 :
15951 : /*
15952 : * Walk the list of objects in the tablespace and move them. This will
15953 : * only find objects in our database, of course.
15954 : */
15955 30 : ScanKeyInit(&key[0],
15956 : Anum_pg_class_reltablespace,
15957 : BTEqualStrategyNumber, F_OIDEQ,
15958 : ObjectIdGetDatum(orig_tablespaceoid));
15959 :
15960 30 : rel = table_open(RelationRelationId, AccessShareLock);
15961 30 : scan = table_beginscan_catalog(rel, 1, key);
15962 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15963 : {
15964 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15965 102 : Oid relOid = relForm->oid;
15966 :
15967 : /*
15968 : * Do not move objects in pg_catalog as part of this, if an admin
15969 : * really wishes to do so, they can issue the individual ALTER
15970 : * commands directly.
15971 : *
15972 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
15973 : * (TOAST will be moved with the main table).
15974 : */
15975 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
15976 204 : relForm->relisshared ||
15977 204 : isAnyTempNamespace(relForm->relnamespace) ||
15978 102 : IsToastNamespace(relForm->relnamespace))
15979 0 : continue;
15980 :
15981 : /* Only move the object type requested */
15982 102 : if ((stmt->objtype == OBJECT_TABLE &&
15983 60 : relForm->relkind != RELKIND_RELATION &&
15984 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15985 66 : (stmt->objtype == OBJECT_INDEX &&
15986 36 : relForm->relkind != RELKIND_INDEX &&
15987 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15988 60 : (stmt->objtype == OBJECT_MATVIEW &&
15989 6 : relForm->relkind != RELKIND_MATVIEW))
15990 42 : continue;
15991 :
15992 : /* Check if we are only moving objects owned by certain roles */
15993 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15994 0 : continue;
15995 :
15996 : /*
15997 : * Handle permissions-checking here since we are locking the tables
15998 : * and also to avoid doing a bunch of work only to fail part-way. Note
15999 : * that permissions will also be checked by AlterTableInternal().
16000 : *
16001 : * Caller must be considered an owner on the table to move it.
16002 : */
16003 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16004 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16005 0 : NameStr(relForm->relname));
16006 :
16007 60 : if (stmt->nowait &&
16008 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16009 0 : ereport(ERROR,
16010 : (errcode(ERRCODE_OBJECT_IN_USE),
16011 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
16012 : get_namespace_name(relForm->relnamespace),
16013 : NameStr(relForm->relname))));
16014 : else
16015 60 : LockRelationOid(relOid, AccessExclusiveLock);
16016 :
16017 : /* Add to our list of objects to move */
16018 60 : relations = lappend_oid(relations, relOid);
16019 : }
16020 :
16021 30 : table_endscan(scan);
16022 30 : table_close(rel, AccessShareLock);
16023 :
16024 30 : if (relations == NIL)
16025 12 : ereport(NOTICE,
16026 : (errcode(ERRCODE_NO_DATA_FOUND),
16027 : errmsg("no matching relations in tablespace \"%s\" found",
16028 : orig_tablespaceoid == InvalidOid ? "(database default)" :
16029 : get_tablespace_name(orig_tablespaceoid))));
16030 :
16031 : /* Everything is locked, loop through and move all of the relations. */
16032 90 : foreach(l, relations)
16033 : {
16034 60 : List *cmds = NIL;
16035 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16036 :
16037 60 : cmd->subtype = AT_SetTableSpace;
16038 60 : cmd->name = stmt->new_tablespacename;
16039 :
16040 60 : cmds = lappend(cmds, cmd);
16041 :
16042 60 : EventTriggerAlterTableStart((Node *) stmt);
16043 : /* OID is set by AlterTableInternal */
16044 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
16045 60 : EventTriggerAlterTableEnd();
16046 : }
16047 :
16048 30 : return new_tablespaceoid;
16049 : }
16050 :
16051 : static void
16052 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
16053 : {
16054 : SMgrRelation dstrel;
16055 :
16056 : /*
16057 : * Since we copy the file directly without looking at the shared buffers,
16058 : * we'd better first flush out any pages of the source relation that are
16059 : * in shared buffers. We assume no new changes will be made while we are
16060 : * holding exclusive lock on the rel.
16061 : */
16062 62 : FlushRelationBuffers(rel);
16063 :
16064 : /*
16065 : * Create and copy all forks of the relation, and schedule unlinking of
16066 : * old physical files.
16067 : *
16068 : * NOTE: any conflict in relfilenumber value will be caught in
16069 : * RelationCreateStorage().
16070 : */
16071 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16072 :
16073 : /* copy main fork */
16074 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16075 62 : rel->rd_rel->relpersistence);
16076 :
16077 : /* copy those extra forks that exist */
16078 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16079 186 : forkNum <= MAX_FORKNUM; forkNum++)
16080 : {
16081 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
16082 : {
16083 0 : smgrcreate(dstrel, forkNum, false);
16084 :
16085 : /*
16086 : * WAL log creation if the relation is persistent, or this is the
16087 : * init fork of an unlogged relation.
16088 : */
16089 0 : if (RelationIsPermanent(rel) ||
16090 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16091 : forkNum == INIT_FORKNUM))
16092 0 : log_smgrcreate(&newrlocator, forkNum);
16093 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16094 0 : rel->rd_rel->relpersistence);
16095 : }
16096 : }
16097 :
16098 : /* drop old relation, and close new one */
16099 62 : RelationDropStorage(rel);
16100 62 : smgrclose(dstrel);
16101 62 : }
16102 :
16103 : /*
16104 : * ALTER TABLE ENABLE/DISABLE TRIGGER
16105 : *
16106 : * We just pass this off to trigger.c.
16107 : */
16108 : static void
16109 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16110 : char fires_when, bool skip_system, bool recurse,
16111 : LOCKMODE lockmode)
16112 : {
16113 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
16114 : fires_when, skip_system, recurse,
16115 : lockmode);
16116 :
16117 340 : InvokeObjectPostAlterHook(RelationRelationId,
16118 : RelationGetRelid(rel), 0);
16119 340 : }
16120 :
16121 : /*
16122 : * ALTER TABLE ENABLE/DISABLE RULE
16123 : *
16124 : * We just pass this off to rewriteDefine.c.
16125 : */
16126 : static void
16127 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16128 : char fires_when, LOCKMODE lockmode)
16129 : {
16130 46 : EnableDisableRule(rel, rulename, fires_when);
16131 :
16132 46 : InvokeObjectPostAlterHook(RelationRelationId,
16133 : RelationGetRelid(rel), 0);
16134 46 : }
16135 :
16136 : /*
16137 : * ALTER TABLE INHERIT
16138 : *
16139 : * Add a parent to the child's parents. This verifies that all the columns and
16140 : * check constraints of the parent appear in the child and that they have the
16141 : * same data types and expressions.
16142 : */
16143 : static void
16144 338 : ATPrepAddInherit(Relation child_rel)
16145 : {
16146 338 : if (child_rel->rd_rel->reloftype)
16147 6 : ereport(ERROR,
16148 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16149 : errmsg("cannot change inheritance of typed table")));
16150 :
16151 332 : if (child_rel->rd_rel->relispartition)
16152 6 : ereport(ERROR,
16153 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16154 : errmsg("cannot change inheritance of a partition")));
16155 :
16156 326 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16157 6 : ereport(ERROR,
16158 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16159 : errmsg("cannot change inheritance of partitioned table")));
16160 320 : }
16161 :
16162 : /*
16163 : * Return the address of the new parent relation.
16164 : */
16165 : static ObjectAddress
16166 320 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16167 : {
16168 : Relation parent_rel;
16169 : List *children;
16170 : ObjectAddress address;
16171 : const char *trigger_name;
16172 :
16173 : /*
16174 : * A self-exclusive lock is needed here. See the similar case in
16175 : * MergeAttributes() for a full explanation.
16176 : */
16177 320 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16178 :
16179 : /*
16180 : * Must be owner of both parent and child -- child was checked by
16181 : * ATSimplePermissions call in ATPrepCmd
16182 : */
16183 320 : ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
16184 :
16185 : /* Permanent rels cannot inherit from temporary ones */
16186 320 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16187 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16188 0 : ereport(ERROR,
16189 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16190 : errmsg("cannot inherit from temporary relation \"%s\"",
16191 : RelationGetRelationName(parent_rel))));
16192 :
16193 : /* If parent rel is temp, it must belong to this session */
16194 320 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16195 6 : !parent_rel->rd_islocaltemp)
16196 0 : ereport(ERROR,
16197 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16198 : errmsg("cannot inherit from temporary relation of another session")));
16199 :
16200 : /* Ditto for the child */
16201 320 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16202 6 : !child_rel->rd_islocaltemp)
16203 0 : ereport(ERROR,
16204 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16205 : errmsg("cannot inherit to temporary relation of another session")));
16206 :
16207 : /* Prevent partitioned tables from becoming inheritance parents */
16208 320 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16209 6 : ereport(ERROR,
16210 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16211 : errmsg("cannot inherit from partitioned table \"%s\"",
16212 : parent->relname)));
16213 :
16214 : /* Likewise for partitions */
16215 314 : if (parent_rel->rd_rel->relispartition)
16216 6 : ereport(ERROR,
16217 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16218 : errmsg("cannot inherit from a partition")));
16219 :
16220 : /*
16221 : * Prevent circularity by seeing if proposed parent inherits from child.
16222 : * (In particular, this disallows making a rel inherit from itself.)
16223 : *
16224 : * This is not completely bulletproof because of race conditions: in
16225 : * multi-level inheritance trees, someone else could concurrently be
16226 : * making another inheritance link that closes the loop but does not join
16227 : * either of the rels we have locked. Preventing that seems to require
16228 : * exclusive locks on the entire inheritance tree, which is a cure worse
16229 : * than the disease. find_all_inheritors() will cope with circularity
16230 : * anyway, so don't sweat it too much.
16231 : *
16232 : * We use weakest lock we can on child's children, namely AccessShareLock.
16233 : */
16234 308 : children = find_all_inheritors(RelationGetRelid(child_rel),
16235 : AccessShareLock, NULL);
16236 :
16237 308 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16238 12 : ereport(ERROR,
16239 : (errcode(ERRCODE_DUPLICATE_TABLE),
16240 : errmsg("circular inheritance not allowed"),
16241 : errdetail("\"%s\" is already a child of \"%s\".",
16242 : parent->relname,
16243 : RelationGetRelationName(child_rel))));
16244 :
16245 : /*
16246 : * If child_rel has row-level triggers with transition tables, we
16247 : * currently don't allow it to become an inheritance child. See also
16248 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16249 : */
16250 296 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16251 296 : if (trigger_name != NULL)
16252 6 : ereport(ERROR,
16253 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16254 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16255 : trigger_name, RelationGetRelationName(child_rel)),
16256 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16257 :
16258 : /* OK to create inheritance */
16259 290 : CreateInheritance(child_rel, parent_rel, false);
16260 :
16261 : /*
16262 : * If parent_rel has a primary key, then child_rel has not-null
16263 : * constraints that make these columns as non nullable. Make those
16264 : * constraints as inherited.
16265 : */
16266 236 : ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
16267 :
16268 230 : ObjectAddressSet(address, RelationRelationId,
16269 : RelationGetRelid(parent_rel));
16270 :
16271 : /* keep our lock on the parent relation until commit */
16272 230 : table_close(parent_rel, NoLock);
16273 :
16274 230 : return address;
16275 : }
16276 :
16277 : /*
16278 : * CreateInheritance
16279 : * Catalog manipulation portion of creating inheritance between a child
16280 : * table and a parent table.
16281 : *
16282 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16283 : */
16284 : static void
16285 2742 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16286 : {
16287 : Relation catalogRelation;
16288 : SysScanDesc scan;
16289 : ScanKeyData key;
16290 : HeapTuple inheritsTuple;
16291 : int32 inhseqno;
16292 :
16293 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16294 2742 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16295 :
16296 : /*
16297 : * Check for duplicates in the list of parents, and determine the highest
16298 : * inhseqno already present; we'll use the next one for the new parent.
16299 : * Also, if proposed child is a partition, it cannot already be
16300 : * inheriting.
16301 : *
16302 : * Note: we do not reject the case where the child already inherits from
16303 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16304 : */
16305 2742 : ScanKeyInit(&key,
16306 : Anum_pg_inherits_inhrelid,
16307 : BTEqualStrategyNumber, F_OIDEQ,
16308 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16309 2742 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16310 : true, NULL, 1, &key);
16311 :
16312 : /* inhseqno sequences start at 1 */
16313 2742 : inhseqno = 0;
16314 2788 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16315 : {
16316 52 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16317 :
16318 52 : if (inh->inhparent == RelationGetRelid(parent_rel))
16319 6 : ereport(ERROR,
16320 : (errcode(ERRCODE_DUPLICATE_TABLE),
16321 : errmsg("relation \"%s\" would be inherited from more than once",
16322 : RelationGetRelationName(parent_rel))));
16323 :
16324 46 : if (inh->inhseqno > inhseqno)
16325 46 : inhseqno = inh->inhseqno;
16326 : }
16327 2736 : systable_endscan(scan);
16328 :
16329 : /* Match up the columns and bump attinhcount as needed */
16330 2736 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16331 :
16332 : /* Match up the constraints and bump coninhcount as needed */
16333 2658 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16334 :
16335 : /*
16336 : * OK, it looks valid. Make the catalog entries that show inheritance.
16337 : */
16338 2616 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16339 : RelationGetRelid(parent_rel),
16340 : inhseqno + 1,
16341 : catalogRelation,
16342 2616 : parent_rel->rd_rel->relkind ==
16343 : RELKIND_PARTITIONED_TABLE);
16344 :
16345 : /* Now we're done with pg_inherits */
16346 2616 : table_close(catalogRelation, RowExclusiveLock);
16347 2616 : }
16348 :
16349 : /*
16350 : * Obtain the source-text form of the constraint expression for a check
16351 : * constraint, given its pg_constraint tuple
16352 : */
16353 : static char *
16354 168 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16355 : {
16356 : Form_pg_constraint con;
16357 : bool isnull;
16358 : Datum attr;
16359 : Datum expr;
16360 :
16361 168 : con = (Form_pg_constraint) GETSTRUCT(contup);
16362 168 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16363 168 : if (isnull)
16364 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16365 :
16366 168 : expr = DirectFunctionCall2(pg_get_expr, attr,
16367 : ObjectIdGetDatum(con->conrelid));
16368 168 : return TextDatumGetCString(expr);
16369 : }
16370 :
16371 : /*
16372 : * Determine whether two check constraints are functionally equivalent
16373 : *
16374 : * The test we apply is to see whether they reverse-compile to the same
16375 : * source string. This insulates us from issues like whether attributes
16376 : * have the same physical column numbers in parent and child relations.
16377 : */
16378 : static bool
16379 84 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16380 : {
16381 84 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16382 84 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16383 :
16384 84 : if (acon->condeferrable != bcon->condeferrable ||
16385 84 : acon->condeferred != bcon->condeferred ||
16386 84 : strcmp(decompile_conbin(a, tupleDesc),
16387 84 : decompile_conbin(b, tupleDesc)) != 0)
16388 6 : return false;
16389 : else
16390 78 : return true;
16391 : }
16392 :
16393 : /*
16394 : * Check columns in child table match up with columns in parent, and increment
16395 : * their attinhcount.
16396 : *
16397 : * Called by CreateInheritance
16398 : *
16399 : * Currently all parent columns must be found in child. Missing columns are an
16400 : * error. One day we might consider creating new columns like CREATE TABLE
16401 : * does. However, that is widely unpopular --- in the common use case of
16402 : * partitioned tables it's a foot-gun.
16403 : *
16404 : * The data type must match exactly. If the parent column is NOT NULL then
16405 : * the child must be as well. Defaults are not compared, however.
16406 : */
16407 : static void
16408 2736 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16409 : {
16410 : Relation attrrel;
16411 : TupleDesc parent_desc;
16412 :
16413 2736 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16414 2736 : parent_desc = RelationGetDescr(parent_rel);
16415 :
16416 9364 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16417 : {
16418 6706 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16419 6706 : char *parent_attname = NameStr(parent_att->attname);
16420 : HeapTuple tuple;
16421 :
16422 : /* Ignore dropped columns in the parent. */
16423 6706 : if (parent_att->attisdropped)
16424 296 : continue;
16425 :
16426 : /* Find same column in child (matching on column name). */
16427 6410 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16428 6410 : if (HeapTupleIsValid(tuple))
16429 : {
16430 6398 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16431 :
16432 6398 : if (parent_att->atttypid != child_att->atttypid ||
16433 6392 : parent_att->atttypmod != child_att->atttypmod)
16434 12 : ereport(ERROR,
16435 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16436 : errmsg("child table \"%s\" has different type for column \"%s\"",
16437 : RelationGetRelationName(child_rel), parent_attname)));
16438 :
16439 6386 : if (parent_att->attcollation != child_att->attcollation)
16440 6 : ereport(ERROR,
16441 : (errcode(ERRCODE_COLLATION_MISMATCH),
16442 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16443 : RelationGetRelationName(child_rel), parent_attname)));
16444 :
16445 : /*
16446 : * If the parent has a not-null constraint that's not NO INHERIT,
16447 : * make sure the child has one too.
16448 : *
16449 : * Other constraints are checked elsewhere.
16450 : */
16451 6380 : if (parent_att->attnotnull && !child_att->attnotnull)
16452 : {
16453 : HeapTuple contup;
16454 :
16455 32 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16456 32 : parent_att->attnum);
16457 32 : if (HeapTupleIsValid(contup) &&
16458 20 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16459 18 : ereport(ERROR,
16460 : errcode(ERRCODE_DATATYPE_MISMATCH),
16461 : errmsg("column \"%s\" in child table must be marked NOT NULL",
16462 : parent_attname));
16463 : }
16464 :
16465 : /*
16466 : * Child column must be generated if and only if parent column is.
16467 : */
16468 6362 : if (parent_att->attgenerated && !child_att->attgenerated)
16469 18 : ereport(ERROR,
16470 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16471 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16472 6344 : if (child_att->attgenerated && !parent_att->attgenerated)
16473 12 : ereport(ERROR,
16474 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16475 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16476 :
16477 : /*
16478 : * Regular inheritance children are independent enough not to
16479 : * inherit identity columns. But partitions are integral part of
16480 : * a partitioned table and inherit identity column.
16481 : */
16482 6332 : if (ispartition)
16483 5796 : child_att->attidentity = parent_att->attidentity;
16484 :
16485 : /*
16486 : * OK, bump the child column's inheritance count. (If we fail
16487 : * later on, this change will just roll back.)
16488 : */
16489 6332 : child_att->attinhcount++;
16490 6332 : if (child_att->attinhcount < 0)
16491 0 : ereport(ERROR,
16492 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16493 : errmsg("too many inheritance parents"));
16494 :
16495 : /*
16496 : * In case of partitions, we must enforce that value of attislocal
16497 : * is same in all partitions. (Note: there are only inherited
16498 : * attributes in partitions)
16499 : */
16500 6332 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16501 : {
16502 : Assert(child_att->attinhcount == 1);
16503 5796 : child_att->attislocal = false;
16504 : }
16505 :
16506 6332 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16507 6332 : heap_freetuple(tuple);
16508 : }
16509 : else
16510 : {
16511 12 : ereport(ERROR,
16512 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16513 : errmsg("child table is missing column \"%s\"", parent_attname)));
16514 : }
16515 : }
16516 :
16517 2658 : table_close(attrrel, RowExclusiveLock);
16518 2658 : }
16519 :
16520 : /*
16521 : * Check constraints in child table match up with constraints in parent,
16522 : * and increment their coninhcount.
16523 : *
16524 : * Constraints that are marked ONLY in the parent are ignored.
16525 : *
16526 : * Called by CreateInheritance
16527 : *
16528 : * Currently all constraints in parent must be present in the child. One day we
16529 : * may consider adding new constraints like CREATE TABLE does.
16530 : *
16531 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
16532 : * constraints. As long as tables have more like 10 constraints it shouldn't be
16533 : * a problem though. Even 100 constraints ought not be the end of the world.
16534 : *
16535 : * XXX See MergeWithExistingConstraint too if you change this code.
16536 : */
16537 : static void
16538 2658 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16539 : {
16540 : Relation constraintrel;
16541 : SysScanDesc parent_scan;
16542 : ScanKeyData parent_key;
16543 : HeapTuple parent_tuple;
16544 2658 : Oid parent_relid = RelationGetRelid(parent_rel);
16545 :
16546 2658 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16547 :
16548 : /* Outer loop scans through the parent's constraint definitions */
16549 2658 : ScanKeyInit(&parent_key,
16550 : Anum_pg_constraint_conrelid,
16551 : BTEqualStrategyNumber, F_OIDEQ,
16552 : ObjectIdGetDatum(parent_relid));
16553 2658 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16554 : true, NULL, 1, &parent_key);
16555 :
16556 4028 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16557 : {
16558 1412 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16559 : SysScanDesc child_scan;
16560 : ScanKeyData child_key;
16561 : HeapTuple child_tuple;
16562 1412 : bool found = false;
16563 :
16564 1412 : if (parent_con->contype != CONSTRAINT_CHECK &&
16565 1284 : parent_con->contype != CONSTRAINT_NOTNULL)
16566 828 : continue;
16567 :
16568 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16569 684 : if (parent_con->connoinherit)
16570 100 : continue;
16571 :
16572 : /* Search for a child constraint matching this one */
16573 584 : ScanKeyInit(&child_key,
16574 : Anum_pg_constraint_conrelid,
16575 : BTEqualStrategyNumber, F_OIDEQ,
16576 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16577 584 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16578 : true, NULL, 1, &child_key);
16579 :
16580 996 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16581 : {
16582 966 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16583 : HeapTuple child_copy;
16584 :
16585 966 : if (child_con->contype != parent_con->contype)
16586 216 : continue;
16587 :
16588 : /*
16589 : * CHECK constraint are matched by name, NOT NULL ones by
16590 : * attribute number
16591 : */
16592 750 : if (child_con->contype == CONSTRAINT_CHECK)
16593 : {
16594 114 : if (strcmp(NameStr(parent_con->conname),
16595 114 : NameStr(child_con->conname)) != 0)
16596 30 : continue;
16597 : }
16598 636 : else if (child_con->contype == CONSTRAINT_NOTNULL)
16599 : {
16600 636 : AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
16601 636 : AttrNumber child_attno = extractNotNullColumn(child_tuple);
16602 :
16603 636 : if (strcmp(get_attname(parent_relid, parent_attno, false),
16604 636 : get_attname(RelationGetRelid(child_rel), child_attno,
16605 : false)) != 0)
16606 166 : continue;
16607 : }
16608 :
16609 554 : if (child_con->contype == CONSTRAINT_CHECK &&
16610 84 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16611 6 : ereport(ERROR,
16612 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16613 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16614 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16615 :
16616 : /*
16617 : * If the CHECK child constraint is "no inherit" then cannot
16618 : * merge.
16619 : *
16620 : * This is not desirable for not-null constraints, mostly because
16621 : * it breaks our pg_upgrade strategy, but it also makes sense on
16622 : * its own: if a child has its own not-null constraint and then
16623 : * acquires a parent with the same constraint, then we start to
16624 : * enforce that constraint for all the descendants of that child
16625 : * too, if any.
16626 : */
16627 548 : if (child_con->contype == CONSTRAINT_CHECK &&
16628 78 : child_con->connoinherit)
16629 0 : ereport(ERROR,
16630 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16631 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16632 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16633 :
16634 : /*
16635 : * If the child constraint is "not valid" then cannot merge with a
16636 : * valid parent constraint
16637 : */
16638 548 : if (parent_con->convalidated && !child_con->convalidated)
16639 0 : ereport(ERROR,
16640 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16641 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16642 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16643 :
16644 : /*
16645 : * OK, bump the child constraint's inheritance count. (If we fail
16646 : * later on, this change will just roll back.)
16647 : */
16648 548 : child_copy = heap_copytuple(child_tuple);
16649 548 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16650 548 : child_con->coninhcount++;
16651 548 : if (child_con->coninhcount < 0)
16652 0 : ereport(ERROR,
16653 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16654 : errmsg("too many inheritance parents"));
16655 548 : if (child_con->contype == CONSTRAINT_NOTNULL &&
16656 470 : child_con->connoinherit)
16657 : {
16658 : /*
16659 : * If the child has children, it's not possible to turn a NO
16660 : * INHERIT constraint into an inheritable one: we would need
16661 : * to recurse to create constraints in those children, but
16662 : * this is not a good place to do that.
16663 : */
16664 6 : if (child_rel->rd_rel->relhassubclass)
16665 6 : ereport(ERROR,
16666 : errmsg("cannot add NOT NULL constraint to column \"%s\" of relation \"%s\" with inheritance children",
16667 : get_attname(RelationGetRelid(child_rel),
16668 : extractNotNullColumn(child_tuple),
16669 : false),
16670 : RelationGetRelationName(child_rel)),
16671 : errdetail("Existing constraint \"%s\" is marked NO INHERIT.",
16672 : NameStr(child_con->conname)));
16673 :
16674 0 : child_con->connoinherit = false;
16675 : }
16676 :
16677 : /*
16678 : * In case of partitions, an inherited constraint must be
16679 : * inherited only once since it cannot have multiple parents and
16680 : * it is never considered local.
16681 : */
16682 542 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16683 : {
16684 : Assert(child_con->coninhcount == 1);
16685 468 : child_con->conislocal = false;
16686 : }
16687 :
16688 542 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16689 542 : heap_freetuple(child_copy);
16690 :
16691 542 : found = true;
16692 542 : break;
16693 : }
16694 :
16695 572 : systable_endscan(child_scan);
16696 :
16697 572 : if (!found)
16698 : {
16699 30 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16700 6 : ereport(ERROR,
16701 : errcode(ERRCODE_DATATYPE_MISMATCH),
16702 : errmsg("column \"%s\" in child table must be marked NOT NULL",
16703 : get_attname(parent_relid,
16704 : extractNotNullColumn(parent_tuple),
16705 : false)));
16706 :
16707 24 : ereport(ERROR,
16708 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16709 : errmsg("child table is missing constraint \"%s\"",
16710 : NameStr(parent_con->conname))));
16711 : }
16712 : }
16713 :
16714 2616 : systable_endscan(parent_scan);
16715 2616 : table_close(constraintrel, RowExclusiveLock);
16716 2616 : }
16717 :
16718 : /*
16719 : * ALTER TABLE NO INHERIT
16720 : *
16721 : * Return value is the address of the relation that is no longer parent.
16722 : */
16723 : static ObjectAddress
16724 44 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16725 : {
16726 : ObjectAddress address;
16727 : Relation parent_rel;
16728 :
16729 44 : if (rel->rd_rel->relispartition)
16730 0 : ereport(ERROR,
16731 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16732 : errmsg("cannot change inheritance of a partition")));
16733 :
16734 : /*
16735 : * AccessShareLock on the parent is probably enough, seeing that DROP
16736 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
16737 : * be inspecting the parent's schema.
16738 : */
16739 44 : parent_rel = table_openrv(parent, AccessShareLock);
16740 :
16741 : /*
16742 : * We don't bother to check ownership of the parent table --- ownership of
16743 : * the child is presumed enough rights.
16744 : */
16745 :
16746 : /* Off to RemoveInheritance() where most of the work happens */
16747 44 : RemoveInheritance(rel, parent_rel, false);
16748 :
16749 : /*
16750 : * If parent_rel has a primary key, then child_rel has not-null
16751 : * constraints that make these columns as non nullable. Mark those
16752 : * constraints as no longer inherited by this parent.
16753 : */
16754 38 : ATInheritAdjustNotNulls(parent_rel, rel, -1);
16755 :
16756 : /*
16757 : * If the parent has a primary key, then we decrement counts for all NOT
16758 : * NULL constraints
16759 : */
16760 :
16761 38 : ObjectAddressSet(address, RelationRelationId,
16762 : RelationGetRelid(parent_rel));
16763 :
16764 : /* keep our lock on the parent relation until commit */
16765 38 : table_close(parent_rel, NoLock);
16766 :
16767 38 : return address;
16768 : }
16769 :
16770 : /*
16771 : * MarkInheritDetached
16772 : *
16773 : * Set inhdetachpending for a partition, for ATExecDetachPartition
16774 : * in concurrent mode. While at it, verify that no other partition is
16775 : * already pending detach.
16776 : */
16777 : static void
16778 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16779 : {
16780 : Relation catalogRelation;
16781 : SysScanDesc scan;
16782 : ScanKeyData key;
16783 : HeapTuple inheritsTuple;
16784 146 : bool found = false;
16785 :
16786 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16787 :
16788 : /*
16789 : * Find pg_inherits entries by inhparent. (We need to scan them all in
16790 : * order to verify that no other partition is pending detach.)
16791 : */
16792 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16793 146 : ScanKeyInit(&key,
16794 : Anum_pg_inherits_inhparent,
16795 : BTEqualStrategyNumber, F_OIDEQ,
16796 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16797 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16798 : true, NULL, 1, &key);
16799 :
16800 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16801 : {
16802 : Form_pg_inherits inhForm;
16803 :
16804 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16805 286 : if (inhForm->inhdetachpending)
16806 2 : ereport(ERROR,
16807 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16808 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16809 : get_rel_name(inhForm->inhrelid),
16810 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16811 : RelationGetRelationName(parent_rel)),
16812 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16813 :
16814 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16815 : {
16816 : HeapTuple newtup;
16817 :
16818 144 : newtup = heap_copytuple(inheritsTuple);
16819 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16820 :
16821 144 : CatalogTupleUpdate(catalogRelation,
16822 : &inheritsTuple->t_self,
16823 : newtup);
16824 144 : found = true;
16825 144 : heap_freetuple(newtup);
16826 : /* keep looking, to ensure we catch others pending detach */
16827 : }
16828 : }
16829 :
16830 : /* Done */
16831 144 : systable_endscan(scan);
16832 144 : table_close(catalogRelation, RowExclusiveLock);
16833 :
16834 144 : if (!found)
16835 0 : ereport(ERROR,
16836 : (errcode(ERRCODE_UNDEFINED_TABLE),
16837 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16838 : RelationGetRelationName(child_rel),
16839 : RelationGetRelationName(parent_rel))));
16840 144 : }
16841 :
16842 : /*
16843 : * RemoveInheritance
16844 : *
16845 : * Drop a parent from the child's parents. This just adjusts the attinhcount
16846 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16847 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16848 : *
16849 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16850 : * up attislocal stays true, which means if a child is ever removed from a
16851 : * parent then its columns will never be automatically dropped which may
16852 : * surprise. But at least we'll never surprise by dropping columns someone
16853 : * isn't expecting to be dropped which would actually mean data loss.
16854 : *
16855 : * coninhcount and conislocal for inherited constraints are adjusted in
16856 : * exactly the same way.
16857 : *
16858 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16859 : */
16860 : static void
16861 782 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16862 : {
16863 : Relation catalogRelation;
16864 : SysScanDesc scan;
16865 : ScanKeyData key[3];
16866 : HeapTuple attributeTuple,
16867 : constraintTuple;
16868 : List *connames;
16869 : List *nncolumns;
16870 : bool found;
16871 : bool is_partitioning;
16872 :
16873 782 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16874 :
16875 782 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16876 : RelationGetRelid(parent_rel),
16877 : expect_detached,
16878 782 : RelationGetRelationName(child_rel));
16879 782 : if (!found)
16880 : {
16881 24 : if (is_partitioning)
16882 18 : ereport(ERROR,
16883 : (errcode(ERRCODE_UNDEFINED_TABLE),
16884 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16885 : RelationGetRelationName(child_rel),
16886 : RelationGetRelationName(parent_rel))));
16887 : else
16888 6 : ereport(ERROR,
16889 : (errcode(ERRCODE_UNDEFINED_TABLE),
16890 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16891 : RelationGetRelationName(parent_rel),
16892 : RelationGetRelationName(child_rel))));
16893 : }
16894 :
16895 : /*
16896 : * Search through child columns looking for ones matching parent rel
16897 : */
16898 758 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16899 758 : ScanKeyInit(&key[0],
16900 : Anum_pg_attribute_attrelid,
16901 : BTEqualStrategyNumber, F_OIDEQ,
16902 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16903 758 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16904 : true, NULL, 1, key);
16905 7178 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16906 : {
16907 6420 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16908 :
16909 : /* Ignore if dropped or not inherited */
16910 6420 : if (att->attisdropped)
16911 36 : continue;
16912 6384 : if (att->attinhcount <= 0)
16913 4566 : continue;
16914 :
16915 1818 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16916 1818 : NameStr(att->attname)))
16917 : {
16918 : /* Decrement inhcount and possibly set islocal to true */
16919 1806 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
16920 1806 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16921 :
16922 1806 : copy_att->attinhcount--;
16923 1806 : if (copy_att->attinhcount == 0)
16924 1806 : copy_att->attislocal = true;
16925 :
16926 1806 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16927 1806 : heap_freetuple(copyTuple);
16928 : }
16929 : }
16930 758 : systable_endscan(scan);
16931 758 : table_close(catalogRelation, RowExclusiveLock);
16932 :
16933 : /*
16934 : * Likewise, find inherited check constraints and disinherit them. To do
16935 : * this, we first need a list of the names of the parent's check
16936 : * constraints. (We cheat a bit by only checking for name matches,
16937 : * assuming that the expressions will match.)
16938 : *
16939 : * For NOT NULL columns, we store column numbers to match.
16940 : */
16941 758 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16942 758 : ScanKeyInit(&key[0],
16943 : Anum_pg_constraint_conrelid,
16944 : BTEqualStrategyNumber, F_OIDEQ,
16945 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16946 758 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16947 : true, NULL, 1, key);
16948 :
16949 758 : connames = NIL;
16950 758 : nncolumns = NIL;
16951 :
16952 1074 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16953 : {
16954 316 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16955 :
16956 316 : if (con->contype == CONSTRAINT_CHECK)
16957 18 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
16958 316 : if (con->contype == CONSTRAINT_NOTNULL)
16959 80 : nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
16960 : }
16961 :
16962 758 : systable_endscan(scan);
16963 :
16964 : /* Now scan the child's constraints */
16965 758 : ScanKeyInit(&key[0],
16966 : Anum_pg_constraint_conrelid,
16967 : BTEqualStrategyNumber, F_OIDEQ,
16968 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16969 758 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16970 : true, NULL, 1, key);
16971 :
16972 1270 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16973 : {
16974 512 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16975 512 : bool match = false;
16976 : ListCell *lc;
16977 :
16978 : /*
16979 : * Match CHECK constraints by name, not-null constraints by column
16980 : * number, and ignore all others.
16981 : */
16982 512 : if (con->contype == CONSTRAINT_CHECK)
16983 : {
16984 190 : foreach(lc, connames)
16985 : {
16986 30 : if (con->contype == CONSTRAINT_CHECK &&
16987 30 : strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
16988 : {
16989 18 : match = true;
16990 18 : break;
16991 : }
16992 : }
16993 : }
16994 334 : else if (con->contype == CONSTRAINT_NOTNULL)
16995 : {
16996 98 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16997 :
16998 128 : foreach(lc, nncolumns)
16999 : {
17000 110 : if (lfirst_int(lc) == child_attno)
17001 : {
17002 80 : match = true;
17003 80 : break;
17004 : }
17005 : }
17006 : }
17007 : else
17008 236 : continue;
17009 :
17010 276 : if (match)
17011 : {
17012 : /* Decrement inhcount and possibly set islocal to true */
17013 98 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
17014 98 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17015 :
17016 98 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
17017 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17018 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
17019 :
17020 98 : copy_con->coninhcount--;
17021 98 : if (copy_con->coninhcount == 0)
17022 98 : copy_con->conislocal = true;
17023 :
17024 98 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17025 98 : heap_freetuple(copyTuple);
17026 : }
17027 : }
17028 :
17029 758 : systable_endscan(scan);
17030 758 : table_close(catalogRelation, RowExclusiveLock);
17031 :
17032 758 : drop_parent_dependency(RelationGetRelid(child_rel),
17033 : RelationRelationId,
17034 : RelationGetRelid(parent_rel),
17035 : child_dependency_type(is_partitioning));
17036 :
17037 : /*
17038 : * Post alter hook of this inherits. Since object_access_hook doesn't take
17039 : * multiple object identifiers, we relay oid of parent relation using
17040 : * auxiliary_id argument.
17041 : */
17042 758 : InvokeObjectPostAlterHookArg(InheritsRelationId,
17043 : RelationGetRelid(child_rel), 0,
17044 : RelationGetRelid(parent_rel), false);
17045 758 : }
17046 :
17047 : /*
17048 : * Adjust coninhcount of not-null constraints upwards or downwards when a
17049 : * table is marked as inheriting or no longer doing so a table with a primary
17050 : * key.
17051 : *
17052 : * Note: these constraints are not dropped, even if their inhcount goes to zero
17053 : * and conislocal is false. Instead we mark the constraints as locally defined.
17054 : * This is seen as more useful behavior, with no downsides. The user can always
17055 : * drop them afterwards.
17056 : */
17057 : static void
17058 274 : ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
17059 : {
17060 : Bitmapset *pkattnos;
17061 :
17062 : /* Quick exit when parent has no PK */
17063 274 : if (!parent_rel->rd_rel->relhasindex)
17064 232 : return;
17065 :
17066 42 : pkattnos = RelationGetIndexAttrBitmap(parent_rel,
17067 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
17068 42 : if (pkattnos != NULL)
17069 : {
17070 36 : Bitmapset *childattnums = NULL;
17071 : AttrMap *attmap;
17072 : int i;
17073 :
17074 36 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17075 : RelationGetDescr(child_rel), true);
17076 :
17077 36 : i = -1;
17078 72 : while ((i = bms_next_member(pkattnos, i)) >= 0)
17079 : {
17080 36 : childattnums = bms_add_member(childattnums,
17081 36 : attmap->attnums[i + FirstLowInvalidHeapAttributeNumber - 1]);
17082 : }
17083 :
17084 : /*
17085 : * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17086 : * parent: the relevant not-null constraint in the child already had
17087 : * its inhcount modified earlier.
17088 : */
17089 36 : CommandCounterIncrement();
17090 36 : AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
17091 : inhcount);
17092 : }
17093 : }
17094 :
17095 : /*
17096 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17097 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17098 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17099 : * be TypeRelationId). There's no convenient way to do this, so go trawling
17100 : * through pg_depend.
17101 : */
17102 : static void
17103 770 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17104 : DependencyType deptype)
17105 : {
17106 : Relation catalogRelation;
17107 : SysScanDesc scan;
17108 : ScanKeyData key[3];
17109 : HeapTuple depTuple;
17110 :
17111 770 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17112 :
17113 770 : ScanKeyInit(&key[0],
17114 : Anum_pg_depend_classid,
17115 : BTEqualStrategyNumber, F_OIDEQ,
17116 : ObjectIdGetDatum(RelationRelationId));
17117 770 : ScanKeyInit(&key[1],
17118 : Anum_pg_depend_objid,
17119 : BTEqualStrategyNumber, F_OIDEQ,
17120 : ObjectIdGetDatum(relid));
17121 770 : ScanKeyInit(&key[2],
17122 : Anum_pg_depend_objsubid,
17123 : BTEqualStrategyNumber, F_INT4EQ,
17124 : Int32GetDatum(0));
17125 :
17126 770 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17127 : NULL, 3, key);
17128 :
17129 2340 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17130 : {
17131 1570 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17132 :
17133 1570 : if (dep->refclassid == refclassid &&
17134 782 : dep->refobjid == refobjid &&
17135 770 : dep->refobjsubid == 0 &&
17136 770 : dep->deptype == deptype)
17137 770 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17138 : }
17139 :
17140 770 : systable_endscan(scan);
17141 770 : table_close(catalogRelation, RowExclusiveLock);
17142 770 : }
17143 :
17144 : /*
17145 : * ALTER TABLE OF
17146 : *
17147 : * Attach a table to a composite type, as though it had been created with CREATE
17148 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17149 : * subject table must not have inheritance parents. These restrictions ensure
17150 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17151 : *
17152 : * The address of the type is returned.
17153 : */
17154 : static ObjectAddress
17155 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17156 : {
17157 66 : Oid relid = RelationGetRelid(rel);
17158 : Type typetuple;
17159 : Form_pg_type typeform;
17160 : Oid typeid;
17161 : Relation inheritsRelation,
17162 : relationRelation;
17163 : SysScanDesc scan;
17164 : ScanKeyData key;
17165 : AttrNumber table_attno,
17166 : type_attno;
17167 : TupleDesc typeTupleDesc,
17168 : tableTupleDesc;
17169 : ObjectAddress tableobj,
17170 : typeobj;
17171 : HeapTuple classtuple;
17172 :
17173 : /* Validate the type. */
17174 66 : typetuple = typenameType(NULL, ofTypename, NULL);
17175 66 : check_of_type(typetuple);
17176 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17177 66 : typeid = typeform->oid;
17178 :
17179 : /* Fail if the table has any inheritance parents. */
17180 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17181 66 : ScanKeyInit(&key,
17182 : Anum_pg_inherits_inhrelid,
17183 : BTEqualStrategyNumber, F_OIDEQ,
17184 : ObjectIdGetDatum(relid));
17185 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17186 : true, NULL, 1, &key);
17187 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17188 6 : ereport(ERROR,
17189 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17190 : errmsg("typed tables cannot inherit")));
17191 60 : systable_endscan(scan);
17192 60 : table_close(inheritsRelation, AccessShareLock);
17193 :
17194 : /*
17195 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17196 : * require that the order also match. However, attnotnull need not match.
17197 : */
17198 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17199 60 : tableTupleDesc = RelationGetDescr(rel);
17200 60 : table_attno = 1;
17201 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17202 : {
17203 : Form_pg_attribute type_attr,
17204 : table_attr;
17205 : const char *type_attname,
17206 : *table_attname;
17207 :
17208 : /* Get the next non-dropped type attribute. */
17209 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17210 154 : if (type_attr->attisdropped)
17211 44 : continue;
17212 110 : type_attname = NameStr(type_attr->attname);
17213 :
17214 : /* Get the next non-dropped table attribute. */
17215 : do
17216 : {
17217 122 : if (table_attno > tableTupleDesc->natts)
17218 6 : ereport(ERROR,
17219 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17220 : errmsg("table is missing column \"%s\"",
17221 : type_attname)));
17222 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17223 116 : table_attno++;
17224 116 : } while (table_attr->attisdropped);
17225 104 : table_attname = NameStr(table_attr->attname);
17226 :
17227 : /* Compare name. */
17228 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17229 6 : ereport(ERROR,
17230 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17231 : errmsg("table has column \"%s\" where type requires \"%s\"",
17232 : table_attname, type_attname)));
17233 :
17234 : /* Compare type. */
17235 98 : if (table_attr->atttypid != type_attr->atttypid ||
17236 92 : table_attr->atttypmod != type_attr->atttypmod ||
17237 86 : table_attr->attcollation != type_attr->attcollation)
17238 12 : ereport(ERROR,
17239 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17240 : errmsg("table \"%s\" has different type for column \"%s\"",
17241 : RelationGetRelationName(rel), type_attname)));
17242 : }
17243 36 : ReleaseTupleDesc(typeTupleDesc);
17244 :
17245 : /* Any remaining columns at the end of the table had better be dropped. */
17246 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17247 : {
17248 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17249 : table_attno - 1);
17250 :
17251 6 : if (!table_attr->attisdropped)
17252 6 : ereport(ERROR,
17253 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17254 : errmsg("table has extra column \"%s\"",
17255 : NameStr(table_attr->attname))));
17256 : }
17257 :
17258 : /* If the table was already typed, drop the existing dependency. */
17259 30 : if (rel->rd_rel->reloftype)
17260 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17261 : DEPENDENCY_NORMAL);
17262 :
17263 : /* Record a dependency on the new type. */
17264 30 : tableobj.classId = RelationRelationId;
17265 30 : tableobj.objectId = relid;
17266 30 : tableobj.objectSubId = 0;
17267 30 : typeobj.classId = TypeRelationId;
17268 30 : typeobj.objectId = typeid;
17269 30 : typeobj.objectSubId = 0;
17270 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17271 :
17272 : /* Update pg_class.reloftype */
17273 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17274 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17275 30 : if (!HeapTupleIsValid(classtuple))
17276 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17277 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17278 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17279 :
17280 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17281 :
17282 30 : heap_freetuple(classtuple);
17283 30 : table_close(relationRelation, RowExclusiveLock);
17284 :
17285 30 : ReleaseSysCache(typetuple);
17286 :
17287 30 : return typeobj;
17288 : }
17289 :
17290 : /*
17291 : * ALTER TABLE NOT OF
17292 : *
17293 : * Detach a typed table from its originating type. Just clear reloftype and
17294 : * remove the dependency.
17295 : */
17296 : static void
17297 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17298 : {
17299 6 : Oid relid = RelationGetRelid(rel);
17300 : Relation relationRelation;
17301 : HeapTuple tuple;
17302 :
17303 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17304 0 : ereport(ERROR,
17305 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17306 : errmsg("\"%s\" is not a typed table",
17307 : RelationGetRelationName(rel))));
17308 :
17309 : /*
17310 : * We don't bother to check ownership of the type --- ownership of the
17311 : * table is presumed enough rights. No lock required on the type, either.
17312 : */
17313 :
17314 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17315 : DEPENDENCY_NORMAL);
17316 :
17317 : /* Clear pg_class.reloftype */
17318 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17319 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17320 6 : if (!HeapTupleIsValid(tuple))
17321 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17322 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17323 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17324 :
17325 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17326 :
17327 6 : heap_freetuple(tuple);
17328 6 : table_close(relationRelation, RowExclusiveLock);
17329 6 : }
17330 :
17331 : /*
17332 : * relation_mark_replica_identity: Update a table's replica identity
17333 : *
17334 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17335 : * index. Otherwise, it must be InvalidOid.
17336 : *
17337 : * Caller had better hold an exclusive lock on the relation, as the results
17338 : * of running two of these concurrently wouldn't be pretty.
17339 : */
17340 : static void
17341 420 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17342 : bool is_internal)
17343 : {
17344 : Relation pg_index;
17345 : Relation pg_class;
17346 : HeapTuple pg_class_tuple;
17347 : HeapTuple pg_index_tuple;
17348 : Form_pg_class pg_class_form;
17349 : Form_pg_index pg_index_form;
17350 : ListCell *index;
17351 :
17352 : /*
17353 : * Check whether relreplident has changed, and update it if so.
17354 : */
17355 420 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17356 420 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17357 : ObjectIdGetDatum(RelationGetRelid(rel)));
17358 420 : if (!HeapTupleIsValid(pg_class_tuple))
17359 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17360 : RelationGetRelationName(rel));
17361 420 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17362 420 : if (pg_class_form->relreplident != ri_type)
17363 : {
17364 370 : pg_class_form->relreplident = ri_type;
17365 370 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17366 : }
17367 420 : table_close(pg_class, RowExclusiveLock);
17368 420 : heap_freetuple(pg_class_tuple);
17369 :
17370 : /*
17371 : * Update the per-index indisreplident flags correctly.
17372 : */
17373 420 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17374 1130 : foreach(index, RelationGetIndexList(rel))
17375 : {
17376 710 : Oid thisIndexOid = lfirst_oid(index);
17377 710 : bool dirty = false;
17378 :
17379 710 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17380 : ObjectIdGetDatum(thisIndexOid));
17381 710 : if (!HeapTupleIsValid(pg_index_tuple))
17382 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17383 710 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17384 :
17385 710 : if (thisIndexOid == indexOid)
17386 : {
17387 : /* Set the bit if not already set. */
17388 234 : if (!pg_index_form->indisreplident)
17389 : {
17390 216 : dirty = true;
17391 216 : pg_index_form->indisreplident = true;
17392 : }
17393 : }
17394 : else
17395 : {
17396 : /* Unset the bit if set. */
17397 476 : if (pg_index_form->indisreplident)
17398 : {
17399 52 : dirty = true;
17400 52 : pg_index_form->indisreplident = false;
17401 : }
17402 : }
17403 :
17404 710 : if (dirty)
17405 : {
17406 268 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17407 268 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17408 : InvalidOid, is_internal);
17409 :
17410 : /*
17411 : * Invalidate the relcache for the table, so that after we commit
17412 : * all sessions will refresh the table's replica identity index
17413 : * before attempting any UPDATE or DELETE on the table. (If we
17414 : * changed the table's pg_class row above, then a relcache inval
17415 : * is already queued due to that; but we might not have.)
17416 : */
17417 268 : CacheInvalidateRelcache(rel);
17418 : }
17419 710 : heap_freetuple(pg_index_tuple);
17420 : }
17421 :
17422 420 : table_close(pg_index, RowExclusiveLock);
17423 420 : }
17424 :
17425 : /*
17426 : * ALTER TABLE <name> REPLICA IDENTITY ...
17427 : */
17428 : static void
17429 468 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17430 : {
17431 : Oid indexOid;
17432 : Relation indexRel;
17433 : int key;
17434 :
17435 468 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17436 : {
17437 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17438 6 : return;
17439 : }
17440 462 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17441 : {
17442 144 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17443 144 : return;
17444 : }
17445 318 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17446 : {
17447 36 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17448 36 : return;
17449 : }
17450 282 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17451 : {
17452 : /* fallthrough */ ;
17453 : }
17454 : else
17455 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17456 :
17457 : /* Check that the index exists */
17458 282 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17459 282 : if (!OidIsValid(indexOid))
17460 0 : ereport(ERROR,
17461 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17462 : errmsg("index \"%s\" for table \"%s\" does not exist",
17463 : stmt->name, RelationGetRelationName(rel))));
17464 :
17465 282 : indexRel = index_open(indexOid, ShareLock);
17466 :
17467 : /* Check that the index is on the relation we're altering. */
17468 282 : if (indexRel->rd_index == NULL ||
17469 282 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17470 6 : ereport(ERROR,
17471 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17472 : errmsg("\"%s\" is not an index for table \"%s\"",
17473 : RelationGetRelationName(indexRel),
17474 : RelationGetRelationName(rel))));
17475 : /* The AM must support uniqueness, and the index must in fact be unique. */
17476 276 : if (!indexRel->rd_indam->amcanunique ||
17477 270 : !indexRel->rd_index->indisunique)
17478 12 : ereport(ERROR,
17479 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17480 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17481 : RelationGetRelationName(indexRel))));
17482 : /* Deferred indexes are not guaranteed to be always unique. */
17483 264 : if (!indexRel->rd_index->indimmediate)
17484 12 : ereport(ERROR,
17485 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17486 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17487 : RelationGetRelationName(indexRel))));
17488 : /* Expression indexes aren't supported. */
17489 252 : if (RelationGetIndexExpressions(indexRel) != NIL)
17490 6 : ereport(ERROR,
17491 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17492 : errmsg("cannot use expression index \"%s\" as replica identity",
17493 : RelationGetRelationName(indexRel))));
17494 : /* Predicate indexes aren't supported. */
17495 246 : if (RelationGetIndexPredicate(indexRel) != NIL)
17496 6 : ereport(ERROR,
17497 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17498 : errmsg("cannot use partial index \"%s\" as replica identity",
17499 : RelationGetRelationName(indexRel))));
17500 :
17501 : /* Check index for nullable columns. */
17502 526 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17503 : {
17504 292 : int16 attno = indexRel->rd_index->indkey.values[key];
17505 : Form_pg_attribute attr;
17506 :
17507 : /*
17508 : * Reject any other system columns. (Going forward, we'll disallow
17509 : * indexes containing such columns in the first place, but they might
17510 : * exist in older branches.)
17511 : */
17512 292 : if (attno <= 0)
17513 0 : ereport(ERROR,
17514 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17515 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17516 : RelationGetRelationName(indexRel), attno)));
17517 :
17518 292 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17519 292 : if (!attr->attnotnull)
17520 6 : ereport(ERROR,
17521 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17522 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17523 : RelationGetRelationName(indexRel),
17524 : NameStr(attr->attname))));
17525 : }
17526 :
17527 : /* This index is suitable for use as a replica identity. Mark it. */
17528 234 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17529 :
17530 234 : index_close(indexRel, NoLock);
17531 : }
17532 :
17533 : /*
17534 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17535 : */
17536 : static void
17537 288 : ATExecSetRowSecurity(Relation rel, bool rls)
17538 : {
17539 : Relation pg_class;
17540 : Oid relid;
17541 : HeapTuple tuple;
17542 :
17543 288 : relid = RelationGetRelid(rel);
17544 :
17545 : /* Pull the record for this relation and update it */
17546 288 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17547 :
17548 288 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17549 :
17550 288 : if (!HeapTupleIsValid(tuple))
17551 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17552 :
17553 288 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17554 288 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17555 :
17556 288 : InvokeObjectPostAlterHook(RelationRelationId,
17557 : RelationGetRelid(rel), 0);
17558 :
17559 288 : table_close(pg_class, RowExclusiveLock);
17560 288 : heap_freetuple(tuple);
17561 288 : }
17562 :
17563 : /*
17564 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17565 : */
17566 : static void
17567 114 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17568 : {
17569 : Relation pg_class;
17570 : Oid relid;
17571 : HeapTuple tuple;
17572 :
17573 114 : relid = RelationGetRelid(rel);
17574 :
17575 114 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17576 :
17577 114 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17578 :
17579 114 : if (!HeapTupleIsValid(tuple))
17580 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17581 :
17582 114 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17583 114 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17584 :
17585 114 : InvokeObjectPostAlterHook(RelationRelationId,
17586 : RelationGetRelid(rel), 0);
17587 :
17588 114 : table_close(pg_class, RowExclusiveLock);
17589 114 : heap_freetuple(tuple);
17590 114 : }
17591 :
17592 : /*
17593 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
17594 : */
17595 : static void
17596 50 : ATExecGenericOptions(Relation rel, List *options)
17597 : {
17598 : Relation ftrel;
17599 : ForeignServer *server;
17600 : ForeignDataWrapper *fdw;
17601 : HeapTuple tuple;
17602 : bool isnull;
17603 : Datum repl_val[Natts_pg_foreign_table];
17604 : bool repl_null[Natts_pg_foreign_table];
17605 : bool repl_repl[Natts_pg_foreign_table];
17606 : Datum datum;
17607 : Form_pg_foreign_table tableform;
17608 :
17609 50 : if (options == NIL)
17610 0 : return;
17611 :
17612 50 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17613 :
17614 50 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17615 : ObjectIdGetDatum(rel->rd_id));
17616 50 : if (!HeapTupleIsValid(tuple))
17617 0 : ereport(ERROR,
17618 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17619 : errmsg("foreign table \"%s\" does not exist",
17620 : RelationGetRelationName(rel))));
17621 50 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17622 50 : server = GetForeignServer(tableform->ftserver);
17623 50 : fdw = GetForeignDataWrapper(server->fdwid);
17624 :
17625 50 : memset(repl_val, 0, sizeof(repl_val));
17626 50 : memset(repl_null, false, sizeof(repl_null));
17627 50 : memset(repl_repl, false, sizeof(repl_repl));
17628 :
17629 : /* Extract the current options */
17630 50 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
17631 : tuple,
17632 : Anum_pg_foreign_table_ftoptions,
17633 : &isnull);
17634 50 : if (isnull)
17635 4 : datum = PointerGetDatum(NULL);
17636 :
17637 : /* Transform the options */
17638 50 : datum = transformGenericOptions(ForeignTableRelationId,
17639 : datum,
17640 : options,
17641 : fdw->fdwvalidator);
17642 :
17643 48 : if (PointerIsValid(DatumGetPointer(datum)))
17644 48 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17645 : else
17646 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17647 :
17648 48 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17649 :
17650 : /* Everything looks good - update the tuple */
17651 :
17652 48 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17653 : repl_val, repl_null, repl_repl);
17654 :
17655 48 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17656 :
17657 : /*
17658 : * Invalidate relcache so that all sessions will refresh any cached plans
17659 : * that might depend on the old options.
17660 : */
17661 48 : CacheInvalidateRelcache(rel);
17662 :
17663 48 : InvokeObjectPostAlterHook(ForeignTableRelationId,
17664 : RelationGetRelid(rel), 0);
17665 :
17666 48 : table_close(ftrel, RowExclusiveLock);
17667 :
17668 48 : heap_freetuple(tuple);
17669 : }
17670 :
17671 : /*
17672 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
17673 : *
17674 : * Return value is the address of the modified column
17675 : */
17676 : static ObjectAddress
17677 66 : ATExecSetCompression(Relation rel,
17678 : const char *column,
17679 : Node *newValue,
17680 : LOCKMODE lockmode)
17681 : {
17682 : Relation attrel;
17683 : HeapTuple tuple;
17684 : Form_pg_attribute atttableform;
17685 : AttrNumber attnum;
17686 : char *compression;
17687 : char cmethod;
17688 : ObjectAddress address;
17689 :
17690 66 : compression = strVal(newValue);
17691 :
17692 66 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
17693 :
17694 : /* copy the cache entry so we can scribble on it below */
17695 66 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17696 66 : if (!HeapTupleIsValid(tuple))
17697 0 : ereport(ERROR,
17698 : (errcode(ERRCODE_UNDEFINED_COLUMN),
17699 : errmsg("column \"%s\" of relation \"%s\" does not exist",
17700 : column, RelationGetRelationName(rel))));
17701 :
17702 : /* prevent them from altering a system attribute */
17703 66 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17704 66 : attnum = atttableform->attnum;
17705 66 : if (attnum <= 0)
17706 0 : ereport(ERROR,
17707 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17708 : errmsg("cannot alter system column \"%s\"", column)));
17709 :
17710 : /*
17711 : * Check that column type is compressible, then get the attribute
17712 : * compression method code
17713 : */
17714 66 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17715 :
17716 : /* update pg_attribute entry */
17717 60 : atttableform->attcompression = cmethod;
17718 60 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17719 :
17720 60 : InvokeObjectPostAlterHook(RelationRelationId,
17721 : RelationGetRelid(rel),
17722 : attnum);
17723 :
17724 : /*
17725 : * Apply the change to indexes as well (only for simple index columns,
17726 : * matching behavior of index.c ConstructTupleDescriptor()).
17727 : */
17728 60 : SetIndexStorageProperties(rel, attrel, attnum,
17729 : false, 0,
17730 : true, cmethod,
17731 : lockmode);
17732 :
17733 60 : heap_freetuple(tuple);
17734 :
17735 60 : table_close(attrel, RowExclusiveLock);
17736 :
17737 : /* make changes visible */
17738 60 : CommandCounterIncrement();
17739 :
17740 60 : ObjectAddressSubSet(address, RelationRelationId,
17741 : RelationGetRelid(rel), attnum);
17742 60 : return address;
17743 : }
17744 :
17745 :
17746 : /*
17747 : * Preparation phase for SET LOGGED/UNLOGGED
17748 : *
17749 : * This verifies that we're not trying to change a temp table. Also,
17750 : * existing foreign key constraints are checked to avoid ending up with
17751 : * permanent tables referencing unlogged tables.
17752 : *
17753 : * Return value is false if the operation is a no-op (in which case the
17754 : * checks are skipped), otherwise true.
17755 : */
17756 : static bool
17757 88 : ATPrepChangePersistence(Relation rel, bool toLogged)
17758 : {
17759 : Relation pg_constraint;
17760 : HeapTuple tuple;
17761 : SysScanDesc scan;
17762 : ScanKeyData skey[1];
17763 :
17764 : /*
17765 : * Disallow changing status for a temp table. Also verify whether we can
17766 : * get away with doing nothing; in such cases we don't need to run the
17767 : * checks below, either.
17768 : */
17769 88 : switch (rel->rd_rel->relpersistence)
17770 : {
17771 0 : case RELPERSISTENCE_TEMP:
17772 0 : ereport(ERROR,
17773 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17774 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
17775 : RelationGetRelationName(rel)),
17776 : errtable(rel)));
17777 : break;
17778 50 : case RELPERSISTENCE_PERMANENT:
17779 50 : if (toLogged)
17780 : /* nothing to do */
17781 6 : return false;
17782 44 : break;
17783 38 : case RELPERSISTENCE_UNLOGGED:
17784 38 : if (!toLogged)
17785 : /* nothing to do */
17786 6 : return false;
17787 32 : break;
17788 : }
17789 :
17790 : /*
17791 : * Check that the table is not part of any publication when changing to
17792 : * UNLOGGED, as UNLOGGED tables can't be published.
17793 : */
17794 120 : if (!toLogged &&
17795 44 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
17796 0 : ereport(ERROR,
17797 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17798 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17799 : RelationGetRelationName(rel)),
17800 : errdetail("Unlogged relations cannot be replicated.")));
17801 :
17802 : /*
17803 : * Check existing foreign key constraints to preserve the invariant that
17804 : * permanent tables cannot reference unlogged ones. Self-referencing
17805 : * foreign keys can safely be ignored.
17806 : */
17807 76 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17808 :
17809 : /*
17810 : * Scan conrelid if changing to permanent, else confrelid. This also
17811 : * determines whether a useful index exists.
17812 : */
17813 76 : ScanKeyInit(&skey[0],
17814 : toLogged ? Anum_pg_constraint_conrelid :
17815 : Anum_pg_constraint_confrelid,
17816 : BTEqualStrategyNumber, F_OIDEQ,
17817 : ObjectIdGetDatum(RelationGetRelid(rel)));
17818 76 : scan = systable_beginscan(pg_constraint,
17819 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17820 : true, NULL, 1, skey);
17821 :
17822 130 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17823 : {
17824 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17825 :
17826 66 : if (con->contype == CONSTRAINT_FOREIGN)
17827 : {
17828 : Oid foreignrelid;
17829 : Relation foreignrel;
17830 :
17831 : /* the opposite end of what we used as scankey */
17832 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17833 :
17834 : /* ignore if self-referencing */
17835 30 : if (RelationGetRelid(rel) == foreignrelid)
17836 12 : continue;
17837 :
17838 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17839 :
17840 18 : if (toLogged)
17841 : {
17842 6 : if (!RelationIsPermanent(foreignrel))
17843 6 : ereport(ERROR,
17844 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17845 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17846 : RelationGetRelationName(rel),
17847 : RelationGetRelationName(foreignrel)),
17848 : errtableconstraint(rel, NameStr(con->conname))));
17849 : }
17850 : else
17851 : {
17852 12 : if (RelationIsPermanent(foreignrel))
17853 6 : ereport(ERROR,
17854 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17855 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17856 : RelationGetRelationName(rel),
17857 : RelationGetRelationName(foreignrel)),
17858 : errtableconstraint(rel, NameStr(con->conname))));
17859 : }
17860 :
17861 6 : relation_close(foreignrel, AccessShareLock);
17862 : }
17863 : }
17864 :
17865 64 : systable_endscan(scan);
17866 :
17867 64 : table_close(pg_constraint, AccessShareLock);
17868 :
17869 64 : return true;
17870 : }
17871 :
17872 : /*
17873 : * Execute ALTER TABLE SET SCHEMA
17874 : */
17875 : ObjectAddress
17876 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17877 : {
17878 : Relation rel;
17879 : Oid relid;
17880 : Oid oldNspOid;
17881 : Oid nspOid;
17882 : RangeVar *newrv;
17883 : ObjectAddresses *objsMoved;
17884 : ObjectAddress myself;
17885 :
17886 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17887 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
17888 : RangeVarCallbackForAlterRelation,
17889 : (void *) stmt);
17890 :
17891 102 : if (!OidIsValid(relid))
17892 : {
17893 12 : ereport(NOTICE,
17894 : (errmsg("relation \"%s\" does not exist, skipping",
17895 : stmt->relation->relname)));
17896 12 : return InvalidObjectAddress;
17897 : }
17898 :
17899 90 : rel = relation_open(relid, NoLock);
17900 :
17901 90 : oldNspOid = RelationGetNamespace(rel);
17902 :
17903 : /* If it's an owned sequence, disallow moving it by itself. */
17904 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17905 : {
17906 : Oid tableId;
17907 : int32 colId;
17908 :
17909 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17910 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17911 6 : ereport(ERROR,
17912 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17913 : errmsg("cannot move an owned sequence into another schema"),
17914 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
17915 : RelationGetRelationName(rel),
17916 : get_rel_name(tableId))));
17917 : }
17918 :
17919 : /* Get and lock schema OID and check its permissions. */
17920 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17921 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17922 :
17923 : /* common checks on switching namespaces */
17924 84 : CheckSetNamespace(oldNspOid, nspOid);
17925 :
17926 84 : objsMoved = new_object_addresses();
17927 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17928 84 : free_object_addresses(objsMoved);
17929 :
17930 84 : ObjectAddressSet(myself, RelationRelationId, relid);
17931 :
17932 84 : if (oldschema)
17933 84 : *oldschema = oldNspOid;
17934 :
17935 : /* close rel, but keep lock until commit */
17936 84 : relation_close(rel, NoLock);
17937 :
17938 84 : return myself;
17939 : }
17940 :
17941 : /*
17942 : * The guts of relocating a table or materialized view to another namespace:
17943 : * besides moving the relation itself, its dependent objects are relocated to
17944 : * the new schema.
17945 : */
17946 : void
17947 84 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17948 : ObjectAddresses *objsMoved)
17949 : {
17950 : Relation classRel;
17951 :
17952 : Assert(objsMoved != NULL);
17953 :
17954 : /* OK, modify the pg_class row and pg_depend entry */
17955 84 : classRel = table_open(RelationRelationId, RowExclusiveLock);
17956 :
17957 84 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17958 : nspOid, true, objsMoved);
17959 :
17960 : /* Fix the table's row type too, if it has one */
17961 84 : if (OidIsValid(rel->rd_rel->reltype))
17962 82 : AlterTypeNamespaceInternal(rel->rd_rel->reltype,
17963 : nspOid, false, false, objsMoved);
17964 :
17965 : /* Fix other dependent stuff */
17966 84 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17967 84 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17968 : objsMoved, AccessExclusiveLock);
17969 84 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17970 : false, objsMoved);
17971 :
17972 84 : table_close(classRel, RowExclusiveLock);
17973 84 : }
17974 :
17975 : /*
17976 : * The guts of relocating a relation to another namespace: fix the pg_class
17977 : * entry, and the pg_depend entry if any. Caller must already have
17978 : * opened and write-locked pg_class.
17979 : */
17980 : void
17981 182 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17982 : Oid oldNspOid, Oid newNspOid,
17983 : bool hasDependEntry,
17984 : ObjectAddresses *objsMoved)
17985 : {
17986 : HeapTuple classTup;
17987 : Form_pg_class classForm;
17988 : ObjectAddress thisobj;
17989 182 : bool already_done = false;
17990 :
17991 182 : classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17992 182 : if (!HeapTupleIsValid(classTup))
17993 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
17994 182 : classForm = (Form_pg_class) GETSTRUCT(classTup);
17995 :
17996 : Assert(classForm->relnamespace == oldNspOid);
17997 :
17998 182 : thisobj.classId = RelationRelationId;
17999 182 : thisobj.objectId = relOid;
18000 182 : thisobj.objectSubId = 0;
18001 :
18002 : /*
18003 : * If the object has already been moved, don't move it again. If it's
18004 : * already in the right place, don't move it, but still fire the object
18005 : * access hook.
18006 : */
18007 182 : already_done = object_address_present(&thisobj, objsMoved);
18008 182 : if (!already_done && oldNspOid != newNspOid)
18009 : {
18010 : /* check for duplicate name (more friendly than unique-index failure) */
18011 140 : if (get_relname_relid(NameStr(classForm->relname),
18012 : newNspOid) != InvalidOid)
18013 0 : ereport(ERROR,
18014 : (errcode(ERRCODE_DUPLICATE_TABLE),
18015 : errmsg("relation \"%s\" already exists in schema \"%s\"",
18016 : NameStr(classForm->relname),
18017 : get_namespace_name(newNspOid))));
18018 :
18019 : /* classTup is a copy, so OK to scribble on */
18020 140 : classForm->relnamespace = newNspOid;
18021 :
18022 140 : CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
18023 :
18024 : /* Update dependency on schema if caller said so */
18025 242 : if (hasDependEntry &&
18026 102 : changeDependencyFor(RelationRelationId,
18027 : relOid,
18028 : NamespaceRelationId,
18029 : oldNspOid,
18030 : newNspOid) != 1)
18031 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
18032 : NameStr(classForm->relname));
18033 : }
18034 182 : if (!already_done)
18035 : {
18036 182 : add_exact_object_address(&thisobj, objsMoved);
18037 :
18038 182 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18039 : }
18040 :
18041 182 : heap_freetuple(classTup);
18042 182 : }
18043 :
18044 : /*
18045 : * Move all indexes for the specified relation to another namespace.
18046 : *
18047 : * Note: we assume adequate permission checking was done by the caller,
18048 : * and that the caller has a suitable lock on the owning relation.
18049 : */
18050 : static void
18051 84 : AlterIndexNamespaces(Relation classRel, Relation rel,
18052 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18053 : {
18054 : List *indexList;
18055 : ListCell *l;
18056 :
18057 84 : indexList = RelationGetIndexList(rel);
18058 :
18059 128 : foreach(l, indexList)
18060 : {
18061 44 : Oid indexOid = lfirst_oid(l);
18062 : ObjectAddress thisobj;
18063 :
18064 44 : thisobj.classId = RelationRelationId;
18065 44 : thisobj.objectId = indexOid;
18066 44 : thisobj.objectSubId = 0;
18067 :
18068 : /*
18069 : * Note: currently, the index will not have its own dependency on the
18070 : * namespace, so we don't need to do changeDependencyFor(). There's no
18071 : * row type in pg_type, either.
18072 : *
18073 : * XXX this objsMoved test may be pointless -- surely we have a single
18074 : * dependency link from a relation to each index?
18075 : */
18076 44 : if (!object_address_present(&thisobj, objsMoved))
18077 : {
18078 44 : AlterRelationNamespaceInternal(classRel, indexOid,
18079 : oldNspOid, newNspOid,
18080 : false, objsMoved);
18081 44 : add_exact_object_address(&thisobj, objsMoved);
18082 : }
18083 : }
18084 :
18085 84 : list_free(indexList);
18086 84 : }
18087 :
18088 : /*
18089 : * Move all identity and SERIAL-column sequences of the specified relation to another
18090 : * namespace.
18091 : *
18092 : * Note: we assume adequate permission checking was done by the caller,
18093 : * and that the caller has a suitable lock on the owning relation.
18094 : */
18095 : static void
18096 84 : AlterSeqNamespaces(Relation classRel, Relation rel,
18097 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18098 : LOCKMODE lockmode)
18099 : {
18100 : Relation depRel;
18101 : SysScanDesc scan;
18102 : ScanKeyData key[2];
18103 : HeapTuple tup;
18104 :
18105 : /*
18106 : * SERIAL sequences are those having an auto dependency on one of the
18107 : * table's columns (we don't care *which* column, exactly).
18108 : */
18109 84 : depRel = table_open(DependRelationId, AccessShareLock);
18110 :
18111 84 : ScanKeyInit(&key[0],
18112 : Anum_pg_depend_refclassid,
18113 : BTEqualStrategyNumber, F_OIDEQ,
18114 : ObjectIdGetDatum(RelationRelationId));
18115 84 : ScanKeyInit(&key[1],
18116 : Anum_pg_depend_refobjid,
18117 : BTEqualStrategyNumber, F_OIDEQ,
18118 : ObjectIdGetDatum(RelationGetRelid(rel)));
18119 : /* we leave refobjsubid unspecified */
18120 :
18121 84 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18122 : NULL, 2, key);
18123 :
18124 588 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
18125 : {
18126 504 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18127 : Relation seqRel;
18128 :
18129 : /* skip dependencies other than auto dependencies on columns */
18130 504 : if (depForm->refobjsubid == 0 ||
18131 360 : depForm->classid != RelationRelationId ||
18132 42 : depForm->objsubid != 0 ||
18133 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18134 462 : continue;
18135 :
18136 : /* Use relation_open just in case it's an index */
18137 42 : seqRel = relation_open(depForm->objid, lockmode);
18138 :
18139 : /* skip non-sequence relations */
18140 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18141 : {
18142 : /* No need to keep the lock */
18143 0 : relation_close(seqRel, lockmode);
18144 0 : continue;
18145 : }
18146 :
18147 : /* Fix the pg_class and pg_depend entries */
18148 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
18149 : oldNspOid, newNspOid,
18150 : true, objsMoved);
18151 :
18152 : /*
18153 : * Sequences used to have entries in pg_type, but no longer do. If we
18154 : * ever re-instate that, we'll need to move the pg_type entry to the
18155 : * new namespace, too (using AlterTypeNamespaceInternal).
18156 : */
18157 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18158 :
18159 : /* Now we can close it. Keep the lock till end of transaction. */
18160 42 : relation_close(seqRel, NoLock);
18161 : }
18162 :
18163 84 : systable_endscan(scan);
18164 :
18165 84 : relation_close(depRel, AccessShareLock);
18166 84 : }
18167 :
18168 :
18169 : /*
18170 : * This code supports
18171 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18172 : *
18173 : * Because we only support this for TEMP tables, it's sufficient to remember
18174 : * the state in a backend-local data structure.
18175 : */
18176 :
18177 : /*
18178 : * Register a newly-created relation's ON COMMIT action.
18179 : */
18180 : void
18181 166 : register_on_commit_action(Oid relid, OnCommitAction action)
18182 : {
18183 : OnCommitItem *oc;
18184 : MemoryContext oldcxt;
18185 :
18186 : /*
18187 : * We needn't bother registering the relation unless there is an ON COMMIT
18188 : * action we need to take.
18189 : */
18190 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18191 24 : return;
18192 :
18193 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18194 :
18195 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18196 142 : oc->relid = relid;
18197 142 : oc->oncommit = action;
18198 142 : oc->creating_subid = GetCurrentSubTransactionId();
18199 142 : oc->deleting_subid = InvalidSubTransactionId;
18200 :
18201 : /*
18202 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18203 : * order of registration. That might not be essential but it seems
18204 : * reasonable.
18205 : */
18206 142 : on_commits = lcons(oc, on_commits);
18207 :
18208 142 : MemoryContextSwitchTo(oldcxt);
18209 : }
18210 :
18211 : /*
18212 : * Unregister any ON COMMIT action when a relation is deleted.
18213 : *
18214 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18215 : */
18216 : void
18217 42324 : remove_on_commit_action(Oid relid)
18218 : {
18219 : ListCell *l;
18220 :
18221 42458 : foreach(l, on_commits)
18222 : {
18223 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18224 :
18225 264 : if (oc->relid == relid)
18226 : {
18227 130 : oc->deleting_subid = GetCurrentSubTransactionId();
18228 130 : break;
18229 : }
18230 : }
18231 42324 : }
18232 :
18233 : /*
18234 : * Perform ON COMMIT actions.
18235 : *
18236 : * This is invoked just before actually committing, since it's possible
18237 : * to encounter errors.
18238 : */
18239 : void
18240 520040 : PreCommit_on_commit_actions(void)
18241 : {
18242 : ListCell *l;
18243 520040 : List *oids_to_truncate = NIL;
18244 520040 : List *oids_to_drop = NIL;
18245 :
18246 520756 : foreach(l, on_commits)
18247 : {
18248 716 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18249 :
18250 : /* Ignore entry if already dropped in this xact */
18251 716 : if (oc->deleting_subid != InvalidSubTransactionId)
18252 68 : continue;
18253 :
18254 648 : switch (oc->oncommit)
18255 : {
18256 0 : case ONCOMMIT_NOOP:
18257 : case ONCOMMIT_PRESERVE_ROWS:
18258 : /* Do nothing (there shouldn't be such entries, actually) */
18259 0 : break;
18260 598 : case ONCOMMIT_DELETE_ROWS:
18261 :
18262 : /*
18263 : * If this transaction hasn't accessed any temporary
18264 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18265 : * tables, as they must still be empty.
18266 : */
18267 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18268 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18269 598 : break;
18270 50 : case ONCOMMIT_DROP:
18271 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18272 50 : break;
18273 : }
18274 716 : }
18275 :
18276 : /*
18277 : * Truncate relations before dropping so that all dependencies between
18278 : * relations are removed after they are worked on. Doing it like this
18279 : * might be a waste as it is possible that a relation being truncated will
18280 : * be dropped anyway due to its parent being dropped, but this makes the
18281 : * code more robust because of not having to re-check that the relation
18282 : * exists at truncation time.
18283 : */
18284 520040 : if (oids_to_truncate != NIL)
18285 334 : heap_truncate(oids_to_truncate);
18286 :
18287 520034 : if (oids_to_drop != NIL)
18288 : {
18289 44 : ObjectAddresses *targetObjects = new_object_addresses();
18290 :
18291 94 : foreach(l, oids_to_drop)
18292 : {
18293 : ObjectAddress object;
18294 :
18295 50 : object.classId = RelationRelationId;
18296 50 : object.objectId = lfirst_oid(l);
18297 50 : object.objectSubId = 0;
18298 :
18299 : Assert(!object_address_present(&object, targetObjects));
18300 :
18301 50 : add_exact_object_address(&object, targetObjects);
18302 : }
18303 :
18304 : /*
18305 : * Object deletion might involve toast table access (to clean up
18306 : * toasted catalog entries), so ensure we have a valid snapshot.
18307 : */
18308 44 : PushActiveSnapshot(GetTransactionSnapshot());
18309 :
18310 : /*
18311 : * Since this is an automatic drop, rather than one directly initiated
18312 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18313 : */
18314 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18315 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18316 :
18317 44 : PopActiveSnapshot();
18318 :
18319 : #ifdef USE_ASSERT_CHECKING
18320 :
18321 : /*
18322 : * Note that table deletion will call remove_on_commit_action, so the
18323 : * entry should get marked as deleted.
18324 : */
18325 : foreach(l, on_commits)
18326 : {
18327 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18328 :
18329 : if (oc->oncommit != ONCOMMIT_DROP)
18330 : continue;
18331 :
18332 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18333 : }
18334 : #endif
18335 : }
18336 520034 : }
18337 :
18338 : /*
18339 : * Post-commit or post-abort cleanup for ON COMMIT management.
18340 : *
18341 : * All we do here is remove no-longer-needed OnCommitItem entries.
18342 : *
18343 : * During commit, remove entries that were deleted during this transaction;
18344 : * during abort, remove those created during this transaction.
18345 : */
18346 : void
18347 564762 : AtEOXact_on_commit_actions(bool isCommit)
18348 : {
18349 : ListCell *cur_item;
18350 :
18351 565508 : foreach(cur_item, on_commits)
18352 : {
18353 746 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18354 :
18355 848 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18356 102 : oc->creating_subid != InvalidSubTransactionId)
18357 : {
18358 : /* cur_item must be removed */
18359 142 : on_commits = foreach_delete_current(on_commits, cur_item);
18360 142 : pfree(oc);
18361 : }
18362 : else
18363 : {
18364 : /* cur_item must be preserved */
18365 604 : oc->creating_subid = InvalidSubTransactionId;
18366 604 : oc->deleting_subid = InvalidSubTransactionId;
18367 : }
18368 : }
18369 564762 : }
18370 :
18371 : /*
18372 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18373 : *
18374 : * During subabort, we can immediately remove entries created during this
18375 : * subtransaction. During subcommit, just relabel entries marked during
18376 : * this subtransaction as being the parent's responsibility.
18377 : */
18378 : void
18379 17968 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18380 : SubTransactionId parentSubid)
18381 : {
18382 : ListCell *cur_item;
18383 :
18384 17968 : foreach(cur_item, on_commits)
18385 : {
18386 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18387 :
18388 0 : if (!isCommit && oc->creating_subid == mySubid)
18389 : {
18390 : /* cur_item must be removed */
18391 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18392 0 : pfree(oc);
18393 : }
18394 : else
18395 : {
18396 : /* cur_item must be preserved */
18397 0 : if (oc->creating_subid == mySubid)
18398 0 : oc->creating_subid = parentSubid;
18399 0 : if (oc->deleting_subid == mySubid)
18400 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18401 : }
18402 : }
18403 17968 : }
18404 :
18405 : /*
18406 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18407 : * the relation to be locked only if (1) it's a plain or partitioned table,
18408 : * materialized view, or TOAST table and (2) the current user is the owner (or
18409 : * the superuser) or has been granted MAINTAIN. This meets the
18410 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18411 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18412 : */
18413 : void
18414 984 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18415 : Oid relId, Oid oldRelId, void *arg)
18416 : {
18417 : char relkind;
18418 : AclResult aclresult;
18419 :
18420 : /* Nothing to do if the relation was not found. */
18421 984 : if (!OidIsValid(relId))
18422 6 : return;
18423 :
18424 : /*
18425 : * If the relation does exist, check whether it's an index. But note that
18426 : * the relation might have been dropped between the time we did the name
18427 : * lookup and now. In that case, there's nothing to do.
18428 : */
18429 978 : relkind = get_rel_relkind(relId);
18430 978 : if (!relkind)
18431 0 : return;
18432 978 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18433 136 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18434 28 : ereport(ERROR,
18435 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18436 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18437 :
18438 : /* Check permissions */
18439 950 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18440 950 : if (aclresult != ACLCHECK_OK)
18441 30 : aclcheck_error(aclresult,
18442 30 : get_relkind_objtype(get_rel_relkind(relId)),
18443 30 : relation->relname);
18444 : }
18445 :
18446 : /*
18447 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18448 : */
18449 : static void
18450 1794 : RangeVarCallbackForTruncate(const RangeVar *relation,
18451 : Oid relId, Oid oldRelId, void *arg)
18452 : {
18453 : HeapTuple tuple;
18454 :
18455 : /* Nothing to do if the relation was not found. */
18456 1794 : if (!OidIsValid(relId))
18457 0 : return;
18458 :
18459 1794 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18460 1794 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18461 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18462 :
18463 1794 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18464 1790 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18465 :
18466 1758 : ReleaseSysCache(tuple);
18467 : }
18468 :
18469 : /*
18470 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18471 : * the owner of the relation, or superuser.
18472 : */
18473 : void
18474 14796 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18475 : Oid relId, Oid oldRelId, void *arg)
18476 : {
18477 : HeapTuple tuple;
18478 :
18479 : /* Nothing to do if the relation was not found. */
18480 14796 : if (!OidIsValid(relId))
18481 12 : return;
18482 :
18483 14784 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18484 14784 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18485 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18486 :
18487 14784 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18488 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18489 6 : relation->relname);
18490 :
18491 29436 : if (!allowSystemTableMods &&
18492 14658 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18493 2 : ereport(ERROR,
18494 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18495 : errmsg("permission denied: \"%s\" is a system catalog",
18496 : relation->relname)));
18497 :
18498 14776 : ReleaseSysCache(tuple);
18499 : }
18500 :
18501 : /*
18502 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18503 : * processing.
18504 : */
18505 : static void
18506 35940 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18507 : void *arg)
18508 : {
18509 35940 : Node *stmt = (Node *) arg;
18510 : ObjectType reltype;
18511 : HeapTuple tuple;
18512 : Form_pg_class classform;
18513 : AclResult aclresult;
18514 : char relkind;
18515 :
18516 35940 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18517 35940 : if (!HeapTupleIsValid(tuple))
18518 228 : return; /* concurrently dropped */
18519 35712 : classform = (Form_pg_class) GETSTRUCT(tuple);
18520 35712 : relkind = classform->relkind;
18521 :
18522 : /* Must own relation. */
18523 35712 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18524 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18525 :
18526 : /* No system table modifications unless explicitly allowed. */
18527 35652 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18528 28 : ereport(ERROR,
18529 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18530 : errmsg("permission denied: \"%s\" is a system catalog",
18531 : rv->relname)));
18532 :
18533 : /*
18534 : * Extract the specified relation type from the statement parse tree.
18535 : *
18536 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
18537 : * have CREATE rights on the containing namespace.
18538 : */
18539 35624 : if (IsA(stmt, RenameStmt))
18540 : {
18541 476 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18542 : GetUserId(), ACL_CREATE);
18543 476 : if (aclresult != ACLCHECK_OK)
18544 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
18545 0 : get_namespace_name(classform->relnamespace));
18546 476 : reltype = ((RenameStmt *) stmt)->renameType;
18547 : }
18548 35148 : else if (IsA(stmt, AlterObjectSchemaStmt))
18549 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18550 :
18551 35058 : else if (IsA(stmt, AlterTableStmt))
18552 35058 : reltype = ((AlterTableStmt *) stmt)->objtype;
18553 : else
18554 : {
18555 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18556 : reltype = OBJECT_TABLE; /* placate compiler */
18557 : }
18558 :
18559 : /*
18560 : * For compatibility with prior releases, we allow ALTER TABLE to be used
18561 : * with most other types of relations (but not composite types). We allow
18562 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
18563 : * otherwise. Otherwise, the user must select the correct form of the
18564 : * command for the relation at issue.
18565 : */
18566 35624 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18567 0 : ereport(ERROR,
18568 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18569 : errmsg("\"%s\" is not a sequence", rv->relname)));
18570 :
18571 35624 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18572 0 : ereport(ERROR,
18573 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18574 : errmsg("\"%s\" is not a view", rv->relname)));
18575 :
18576 35624 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18577 0 : ereport(ERROR,
18578 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18579 : errmsg("\"%s\" is not a materialized view", rv->relname)));
18580 :
18581 35624 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18582 0 : ereport(ERROR,
18583 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18584 : errmsg("\"%s\" is not a foreign table", rv->relname)));
18585 :
18586 35624 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18587 0 : ereport(ERROR,
18588 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18589 : errmsg("\"%s\" is not a composite type", rv->relname)));
18590 :
18591 35624 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18592 : relkind != RELKIND_PARTITIONED_INDEX
18593 34 : && !IsA(stmt, RenameStmt))
18594 6 : ereport(ERROR,
18595 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18596 : errmsg("\"%s\" is not an index", rv->relname)));
18597 :
18598 : /*
18599 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18600 : * TYPE for that.
18601 : */
18602 35618 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18603 0 : ereport(ERROR,
18604 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18605 : errmsg("\"%s\" is a composite type", rv->relname),
18606 : /* translator: %s is an SQL ALTER command */
18607 : errhint("Use %s instead.",
18608 : "ALTER TYPE")));
18609 :
18610 : /*
18611 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18612 : * to a different schema, such as indexes and TOAST tables.
18613 : */
18614 35618 : if (IsA(stmt, AlterObjectSchemaStmt))
18615 : {
18616 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18617 0 : ereport(ERROR,
18618 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18619 : errmsg("cannot change schema of index \"%s\"",
18620 : rv->relname),
18621 : errhint("Change the schema of the table instead.")));
18622 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
18623 0 : ereport(ERROR,
18624 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18625 : errmsg("cannot change schema of composite type \"%s\"",
18626 : rv->relname),
18627 : /* translator: %s is an SQL ALTER command */
18628 : errhint("Use %s instead.",
18629 : "ALTER TYPE")));
18630 90 : else if (relkind == RELKIND_TOASTVALUE)
18631 0 : ereport(ERROR,
18632 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18633 : errmsg("cannot change schema of TOAST table \"%s\"",
18634 : rv->relname),
18635 : errhint("Change the schema of the table instead.")));
18636 : }
18637 :
18638 35618 : ReleaseSysCache(tuple);
18639 : }
18640 :
18641 : /*
18642 : * Transform any expressions present in the partition key
18643 : *
18644 : * Returns a transformed PartitionSpec.
18645 : */
18646 : static PartitionSpec *
18647 4858 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18648 : {
18649 : PartitionSpec *newspec;
18650 : ParseState *pstate;
18651 : ParseNamespaceItem *nsitem;
18652 : ListCell *l;
18653 :
18654 4858 : newspec = makeNode(PartitionSpec);
18655 :
18656 4858 : newspec->strategy = partspec->strategy;
18657 4858 : newspec->partParams = NIL;
18658 4858 : newspec->location = partspec->location;
18659 :
18660 : /* Check valid number of columns for strategy */
18661 7194 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18662 2336 : list_length(partspec->partParams) != 1)
18663 6 : ereport(ERROR,
18664 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18665 : errmsg("cannot use \"list\" partition strategy with more than one column")));
18666 :
18667 : /*
18668 : * Create a dummy ParseState and insert the target relation as its sole
18669 : * rangetable entry. We need a ParseState for transformExpr.
18670 : */
18671 4852 : pstate = make_parsestate(NULL);
18672 4852 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18673 : NULL, false, true);
18674 4852 : addNSItemToQuery(pstate, nsitem, true, true, true);
18675 :
18676 : /* take care of any partition expressions */
18677 10136 : foreach(l, partspec->partParams)
18678 : {
18679 5308 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
18680 :
18681 5308 : if (pelem->expr)
18682 : {
18683 : /* Copy, to avoid scribbling on the input */
18684 298 : pelem = copyObject(pelem);
18685 :
18686 : /* Now do parse transformation of the expression */
18687 298 : pelem->expr = transformExpr(pstate, pelem->expr,
18688 : EXPR_KIND_PARTITION_EXPRESSION);
18689 :
18690 : /* we have to fix its collations too */
18691 274 : assign_expr_collations(pstate, pelem->expr);
18692 : }
18693 :
18694 5284 : newspec->partParams = lappend(newspec->partParams, pelem);
18695 : }
18696 :
18697 4828 : return newspec;
18698 : }
18699 :
18700 : /*
18701 : * Compute per-partition-column information from a list of PartitionElems.
18702 : * Expressions in the PartitionElems must be parse-analyzed already.
18703 : */
18704 : static void
18705 4828 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18706 : List **partexprs, Oid *partopclass, Oid *partcollation,
18707 : PartitionStrategy strategy)
18708 : {
18709 : int attn;
18710 : ListCell *lc;
18711 : Oid am_oid;
18712 :
18713 4828 : attn = 0;
18714 10028 : foreach(lc, partParams)
18715 : {
18716 5284 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18717 : Oid atttype;
18718 : Oid attcollation;
18719 :
18720 5284 : if (pelem->name != NULL)
18721 : {
18722 : /* Simple attribute reference */
18723 : HeapTuple atttuple;
18724 : Form_pg_attribute attform;
18725 :
18726 5010 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18727 5010 : pelem->name);
18728 5010 : if (!HeapTupleIsValid(atttuple))
18729 12 : ereport(ERROR,
18730 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18731 : errmsg("column \"%s\" named in partition key does not exist",
18732 : pelem->name),
18733 : parser_errposition(pstate, pelem->location)));
18734 4998 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18735 :
18736 4998 : if (attform->attnum <= 0)
18737 6 : ereport(ERROR,
18738 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18739 : errmsg("cannot use system column \"%s\" in partition key",
18740 : pelem->name),
18741 : parser_errposition(pstate, pelem->location)));
18742 :
18743 : /*
18744 : * Generated columns cannot work: They are computed after BEFORE
18745 : * triggers, but partition routing is done before all triggers.
18746 : */
18747 4992 : if (attform->attgenerated)
18748 6 : ereport(ERROR,
18749 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18750 : errmsg("cannot use generated column in partition key"),
18751 : errdetail("Column \"%s\" is a generated column.",
18752 : pelem->name),
18753 : parser_errposition(pstate, pelem->location)));
18754 :
18755 4986 : partattrs[attn] = attform->attnum;
18756 4986 : atttype = attform->atttypid;
18757 4986 : attcollation = attform->attcollation;
18758 4986 : ReleaseSysCache(atttuple);
18759 : }
18760 : else
18761 : {
18762 : /* Expression */
18763 274 : Node *expr = pelem->expr;
18764 : char partattname[16];
18765 :
18766 : Assert(expr != NULL);
18767 274 : atttype = exprType(expr);
18768 274 : attcollation = exprCollation(expr);
18769 :
18770 : /*
18771 : * The expression must be of a storable type (e.g., not RECORD).
18772 : * The test is the same as for whether a table column is of a safe
18773 : * type (which is why we needn't check for the non-expression
18774 : * case).
18775 : */
18776 274 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18777 274 : CheckAttributeType(partattname,
18778 : atttype, attcollation,
18779 : NIL, CHKATYPE_IS_PARTKEY);
18780 :
18781 : /*
18782 : * Strip any top-level COLLATE clause. This ensures that we treat
18783 : * "x COLLATE y" and "(x COLLATE y)" alike.
18784 : */
18785 262 : while (IsA(expr, CollateExpr))
18786 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
18787 :
18788 262 : if (IsA(expr, Var) &&
18789 12 : ((Var *) expr)->varattno > 0)
18790 : {
18791 : /*
18792 : * User wrote "(column)" or "(column COLLATE something)".
18793 : * Treat it like simple attribute anyway.
18794 : */
18795 6 : partattrs[attn] = ((Var *) expr)->varattno;
18796 : }
18797 : else
18798 : {
18799 256 : Bitmapset *expr_attrs = NULL;
18800 : int i;
18801 :
18802 256 : partattrs[attn] = 0; /* marks the column as expression */
18803 256 : *partexprs = lappend(*partexprs, expr);
18804 :
18805 : /*
18806 : * transformPartitionSpec() should have already rejected
18807 : * subqueries, aggregates, window functions, and SRFs, based
18808 : * on the EXPR_KIND_ for partition expressions.
18809 : */
18810 :
18811 : /*
18812 : * Cannot allow system column references, since that would
18813 : * make partition routing impossible: their values won't be
18814 : * known yet when we need to do that.
18815 : */
18816 256 : pull_varattnos(expr, 1, &expr_attrs);
18817 2048 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18818 : {
18819 1792 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18820 : expr_attrs))
18821 0 : ereport(ERROR,
18822 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18823 : errmsg("partition key expressions cannot contain system column references")));
18824 : }
18825 :
18826 : /*
18827 : * Generated columns cannot work: They are computed after
18828 : * BEFORE triggers, but partition routing is done before all
18829 : * triggers.
18830 : */
18831 256 : i = -1;
18832 564 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
18833 : {
18834 314 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18835 :
18836 314 : if (attno > 0 &&
18837 308 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18838 6 : ereport(ERROR,
18839 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18840 : errmsg("cannot use generated column in partition key"),
18841 : errdetail("Column \"%s\" is a generated column.",
18842 : get_attname(RelationGetRelid(rel), attno, false)),
18843 : parser_errposition(pstate, pelem->location)));
18844 : }
18845 :
18846 : /*
18847 : * Preprocess the expression before checking for mutability.
18848 : * This is essential for the reasons described in
18849 : * contain_mutable_functions_after_planning. However, we call
18850 : * expression_planner for ourselves rather than using that
18851 : * function, because if constant-folding reduces the
18852 : * expression to a constant, we'd like to know that so we can
18853 : * complain below.
18854 : *
18855 : * Like contain_mutable_functions_after_planning, assume that
18856 : * expression_planner won't scribble on its input, so this
18857 : * won't affect the partexprs entry we saved above.
18858 : */
18859 250 : expr = (Node *) expression_planner((Expr *) expr);
18860 :
18861 : /*
18862 : * Partition expressions cannot contain mutable functions,
18863 : * because a given row must always map to the same partition
18864 : * as long as there is no change in the partition boundary
18865 : * structure.
18866 : */
18867 250 : if (contain_mutable_functions(expr))
18868 6 : ereport(ERROR,
18869 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18870 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
18871 :
18872 : /*
18873 : * While it is not exactly *wrong* for a partition expression
18874 : * to be a constant, it seems better to reject such keys.
18875 : */
18876 244 : if (IsA(expr, Const))
18877 12 : ereport(ERROR,
18878 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18879 : errmsg("cannot use constant expression as partition key")));
18880 : }
18881 : }
18882 :
18883 : /*
18884 : * Apply collation override if any
18885 : */
18886 5224 : if (pelem->collation)
18887 30 : attcollation = get_collation_oid(pelem->collation, false);
18888 :
18889 : /*
18890 : * Check we have a collation iff it's a collatable type. The only
18891 : * expected failures here are (1) COLLATE applied to a noncollatable
18892 : * type, or (2) partition expression had an unresolved collation. But
18893 : * we might as well code this to be a complete consistency check.
18894 : */
18895 5224 : if (type_is_collatable(atttype))
18896 : {
18897 626 : if (!OidIsValid(attcollation))
18898 0 : ereport(ERROR,
18899 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
18900 : errmsg("could not determine which collation to use for partition expression"),
18901 : errhint("Use the COLLATE clause to set the collation explicitly.")));
18902 : }
18903 : else
18904 : {
18905 4598 : if (OidIsValid(attcollation))
18906 0 : ereport(ERROR,
18907 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18908 : errmsg("collations are not supported by type %s",
18909 : format_type_be(atttype))));
18910 : }
18911 :
18912 5224 : partcollation[attn] = attcollation;
18913 :
18914 : /*
18915 : * Identify the appropriate operator class. For list and range
18916 : * partitioning, we use a btree operator class; hash partitioning uses
18917 : * a hash operator class.
18918 : */
18919 5224 : if (strategy == PARTITION_STRATEGY_HASH)
18920 276 : am_oid = HASH_AM_OID;
18921 : else
18922 4948 : am_oid = BTREE_AM_OID;
18923 :
18924 5224 : if (!pelem->opclass)
18925 : {
18926 5092 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18927 :
18928 5092 : if (!OidIsValid(partopclass[attn]))
18929 : {
18930 12 : if (strategy == PARTITION_STRATEGY_HASH)
18931 0 : ereport(ERROR,
18932 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18933 : errmsg("data type %s has no default operator class for access method \"%s\"",
18934 : format_type_be(atttype), "hash"),
18935 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18936 : else
18937 12 : ereport(ERROR,
18938 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18939 : errmsg("data type %s has no default operator class for access method \"%s\"",
18940 : format_type_be(atttype), "btree"),
18941 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18942 : }
18943 : }
18944 : else
18945 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
18946 : atttype,
18947 : am_oid == HASH_AM_OID ? "hash" : "btree",
18948 : am_oid);
18949 :
18950 5200 : attn++;
18951 : }
18952 4744 : }
18953 :
18954 : /*
18955 : * PartConstraintImpliedByRelConstraint
18956 : * Do scanrel's existing constraints imply the partition constraint?
18957 : *
18958 : * "Existing constraints" include its check constraints and column-level
18959 : * not-null constraints. partConstraint describes the partition constraint,
18960 : * in implicit-AND form.
18961 : */
18962 : bool
18963 3046 : PartConstraintImpliedByRelConstraint(Relation scanrel,
18964 : List *partConstraint)
18965 : {
18966 3046 : List *existConstraint = NIL;
18967 3046 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18968 : int i;
18969 :
18970 3046 : if (constr && constr->has_not_null)
18971 : {
18972 726 : int natts = scanrel->rd_att->natts;
18973 :
18974 2334 : for (i = 1; i <= natts; i++)
18975 : {
18976 1608 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18977 :
18978 1608 : if (att->attnotnull && !att->attisdropped)
18979 : {
18980 944 : NullTest *ntest = makeNode(NullTest);
18981 :
18982 944 : ntest->arg = (Expr *) makeVar(1,
18983 : i,
18984 : att->atttypid,
18985 : att->atttypmod,
18986 : att->attcollation,
18987 : 0);
18988 944 : ntest->nulltesttype = IS_NOT_NULL;
18989 :
18990 : /*
18991 : * argisrow=false is correct even for a composite column,
18992 : * because attnotnull does not represent a SQL-spec IS NOT
18993 : * NULL test in such a case, just IS DISTINCT FROM NULL.
18994 : */
18995 944 : ntest->argisrow = false;
18996 944 : ntest->location = -1;
18997 944 : existConstraint = lappend(existConstraint, ntest);
18998 : }
18999 : }
19000 : }
19001 :
19002 3046 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19003 : }
19004 :
19005 : /*
19006 : * ConstraintImpliedByRelConstraint
19007 : * Do scanrel's existing constraints imply the given constraint?
19008 : *
19009 : * testConstraint is the constraint to validate. provenConstraint is a
19010 : * caller-provided list of conditions which this function may assume
19011 : * to be true. Both provenConstraint and testConstraint must be in
19012 : * implicit-AND form, must only contain immutable clauses, and must
19013 : * contain only Vars with varno = 1.
19014 : */
19015 : bool
19016 4612 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19017 : {
19018 4612 : List *existConstraint = list_copy(provenConstraint);
19019 4612 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19020 : int num_check,
19021 : i;
19022 :
19023 4612 : num_check = (constr != NULL) ? constr->num_check : 0;
19024 5198 : for (i = 0; i < num_check; i++)
19025 : {
19026 : Node *cexpr;
19027 :
19028 : /*
19029 : * If this constraint hasn't been fully validated yet, we must ignore
19030 : * it here.
19031 : */
19032 586 : if (!constr->check[i].ccvalid)
19033 6 : continue;
19034 :
19035 580 : cexpr = stringToNode(constr->check[i].ccbin);
19036 :
19037 : /*
19038 : * Run each expression through const-simplification and
19039 : * canonicalization. It is necessary, because we will be comparing it
19040 : * to similarly-processed partition constraint expressions, and may
19041 : * fail to detect valid matches without this.
19042 : */
19043 580 : cexpr = eval_const_expressions(NULL, cexpr);
19044 580 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19045 :
19046 580 : existConstraint = list_concat(existConstraint,
19047 580 : make_ands_implicit((Expr *) cexpr));
19048 : }
19049 :
19050 : /*
19051 : * Try to make the proof. Since we are comparing CHECK constraints, we
19052 : * need to use weak implication, i.e., we assume existConstraint is
19053 : * not-false and try to prove the same for testConstraint.
19054 : *
19055 : * Note that predicate_implied_by assumes its first argument is known
19056 : * immutable. That should always be true for both NOT NULL and partition
19057 : * constraints, so we don't test it here.
19058 : */
19059 4612 : return predicate_implied_by(testConstraint, existConstraint, true);
19060 : }
19061 :
19062 : /*
19063 : * QueuePartitionConstraintValidation
19064 : *
19065 : * Add an entry to wqueue to have the given partition constraint validated by
19066 : * Phase 3, for the given relation, and all its children.
19067 : *
19068 : * We first verify whether the given constraint is implied by pre-existing
19069 : * relation constraints; if it is, there's no need to scan the table to
19070 : * validate, so don't queue in that case.
19071 : */
19072 : static void
19073 2416 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19074 : List *partConstraint,
19075 : bool validate_default)
19076 : {
19077 : /*
19078 : * Based on the table's existing constraints, determine whether or not we
19079 : * may skip scanning the table.
19080 : */
19081 2416 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19082 : {
19083 90 : if (!validate_default)
19084 68 : ereport(DEBUG1,
19085 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19086 : RelationGetRelationName(scanrel))));
19087 : else
19088 22 : ereport(DEBUG1,
19089 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19090 : RelationGetRelationName(scanrel))));
19091 90 : return;
19092 : }
19093 :
19094 : /*
19095 : * Constraints proved insufficient. For plain relations, queue a
19096 : * validation item now; for partitioned tables, recurse to process each
19097 : * partition.
19098 : */
19099 2326 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19100 : {
19101 : AlteredTableInfo *tab;
19102 :
19103 : /* Grab a work queue entry. */
19104 1932 : tab = ATGetQueueEntry(wqueue, scanrel);
19105 : Assert(tab->partition_constraint == NULL);
19106 1932 : tab->partition_constraint = (Expr *) linitial(partConstraint);
19107 1932 : tab->validate_default = validate_default;
19108 : }
19109 394 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19110 : {
19111 346 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19112 : int i;
19113 :
19114 740 : for (i = 0; i < partdesc->nparts; i++)
19115 : {
19116 : Relation part_rel;
19117 : List *thisPartConstraint;
19118 :
19119 : /*
19120 : * This is the minimum lock we need to prevent deadlocks.
19121 : */
19122 394 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19123 :
19124 : /*
19125 : * Adjust the constraint for scanrel so that it matches this
19126 : * partition's attribute numbers.
19127 : */
19128 : thisPartConstraint =
19129 394 : map_partition_varattnos(partConstraint, 1,
19130 : part_rel, scanrel);
19131 :
19132 394 : QueuePartitionConstraintValidation(wqueue, part_rel,
19133 : thisPartConstraint,
19134 : validate_default);
19135 394 : table_close(part_rel, NoLock); /* keep lock till commit */
19136 : }
19137 : }
19138 : }
19139 :
19140 : /*
19141 : * attachPartitionTable: attach a new partition to the partitioned table
19142 : *
19143 : * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
19144 : * of an ALTER TABLE sequence.
19145 : * rel: partitioned relation;
19146 : * attachrel: relation of attached partition;
19147 : * bound: bounds of attached relation.
19148 : */
19149 : static void
19150 2452 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
19151 : {
19152 : /* OK to create inheritance. Rest of the checks performed there */
19153 2452 : CreateInheritance(attachrel, rel, true);
19154 :
19155 : /* Update the pg_class entry. */
19156 2380 : StorePartitionBound(attachrel, rel, bound);
19157 :
19158 : /* Ensure there exists a correct set of indexes in the partition. */
19159 2380 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19160 :
19161 : /* and triggers */
19162 2350 : CloneRowTriggersToPartition(rel, attachrel);
19163 :
19164 : /*
19165 : * Clone foreign key constraints. Callee is responsible for setting up
19166 : * for phase 3 constraint verification.
19167 : */
19168 2344 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19169 2344 : }
19170 :
19171 : /*
19172 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19173 : *
19174 : * Return the address of the newly attached partition.
19175 : */
19176 : static ObjectAddress
19177 2182 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19178 : AlterTableUtilityContext *context)
19179 : {
19180 : Relation attachrel,
19181 : catalog;
19182 : List *attachrel_children;
19183 : List *partConstraint;
19184 : SysScanDesc scan;
19185 : ScanKeyData skey;
19186 : AttrNumber attno;
19187 : int natts;
19188 : TupleDesc tupleDesc;
19189 : ObjectAddress address;
19190 : const char *trigger_name;
19191 : Oid defaultPartOid;
19192 : List *partBoundConstraint;
19193 2182 : ParseState *pstate = make_parsestate(NULL);
19194 :
19195 2182 : pstate->p_sourcetext = context->queryString;
19196 :
19197 : /*
19198 : * We must lock the default partition if one exists, because attaching a
19199 : * new partition will change its partition constraint.
19200 : */
19201 : defaultPartOid =
19202 2182 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19203 2182 : if (OidIsValid(defaultPartOid))
19204 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19205 :
19206 2182 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19207 :
19208 : /*
19209 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19210 : * by FKs at this point also.
19211 : */
19212 :
19213 : /*
19214 : * Must be owner of both parent and source table -- parent was checked by
19215 : * ATSimplePermissions call in ATPrepCmd
19216 : */
19217 2176 : ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
19218 :
19219 : /* A partition can only have one parent */
19220 2170 : if (attachrel->rd_rel->relispartition)
19221 6 : ereport(ERROR,
19222 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19223 : errmsg("\"%s\" is already a partition",
19224 : RelationGetRelationName(attachrel))));
19225 :
19226 2164 : if (OidIsValid(attachrel->rd_rel->reloftype))
19227 6 : ereport(ERROR,
19228 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19229 : errmsg("cannot attach a typed table as partition")));
19230 :
19231 : /*
19232 : * Table being attached should not already be part of inheritance; either
19233 : * as a child table...
19234 : */
19235 2158 : catalog = table_open(InheritsRelationId, AccessShareLock);
19236 2158 : ScanKeyInit(&skey,
19237 : Anum_pg_inherits_inhrelid,
19238 : BTEqualStrategyNumber, F_OIDEQ,
19239 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19240 2158 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19241 : NULL, 1, &skey);
19242 2158 : if (HeapTupleIsValid(systable_getnext(scan)))
19243 6 : ereport(ERROR,
19244 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19245 : errmsg("cannot attach inheritance child as partition")));
19246 2152 : systable_endscan(scan);
19247 :
19248 : /* ...or as a parent table (except the case when it is partitioned) */
19249 2152 : ScanKeyInit(&skey,
19250 : Anum_pg_inherits_inhparent,
19251 : BTEqualStrategyNumber, F_OIDEQ,
19252 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19253 2152 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19254 : 1, &skey);
19255 2152 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19256 248 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19257 6 : ereport(ERROR,
19258 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19259 : errmsg("cannot attach inheritance parent as partition")));
19260 2146 : systable_endscan(scan);
19261 2146 : table_close(catalog, AccessShareLock);
19262 :
19263 : /*
19264 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19265 : * particular, this disallows making a rel a partition of itself.)
19266 : *
19267 : * We do that by checking if rel is a member of the list of attachrel's
19268 : * partitions provided the latter is partitioned at all. We want to avoid
19269 : * having to construct this list again, so we request the strongest lock
19270 : * on all partitions. We need the strongest lock, because we may decide
19271 : * to scan them if we find out that the table being attached (or its leaf
19272 : * partitions) may contain rows that violate the partition constraint. If
19273 : * the table has a constraint that would prevent such rows, which by
19274 : * definition is present in all the partitions, we need not scan the
19275 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19276 : * weaker lock now and the stronger one only when needed.
19277 : */
19278 2146 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19279 : AccessExclusiveLock, NULL);
19280 2146 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19281 12 : ereport(ERROR,
19282 : (errcode(ERRCODE_DUPLICATE_TABLE),
19283 : errmsg("circular inheritance not allowed"),
19284 : errdetail("\"%s\" is already a child of \"%s\".",
19285 : RelationGetRelationName(rel),
19286 : RelationGetRelationName(attachrel))));
19287 :
19288 : /* If the parent is permanent, so must be all of its partitions. */
19289 2134 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19290 2110 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19291 6 : ereport(ERROR,
19292 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19293 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19294 : RelationGetRelationName(rel))));
19295 :
19296 : /* Temp parent cannot have a partition that is itself not a temp */
19297 2128 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19298 24 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19299 18 : ereport(ERROR,
19300 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19301 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19302 : RelationGetRelationName(rel))));
19303 :
19304 : /* If the parent is temp, it must belong to this session */
19305 2110 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19306 6 : !rel->rd_islocaltemp)
19307 0 : ereport(ERROR,
19308 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19309 : errmsg("cannot attach as partition of temporary relation of another session")));
19310 :
19311 : /* Ditto for the partition */
19312 2110 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19313 6 : !attachrel->rd_islocaltemp)
19314 0 : ereport(ERROR,
19315 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19316 : errmsg("cannot attach temporary relation of another session as partition")));
19317 :
19318 : /*
19319 : * Check if attachrel has any identity columns or any columns that aren't
19320 : * in the parent.
19321 : */
19322 2110 : tupleDesc = RelationGetDescr(attachrel);
19323 2110 : natts = tupleDesc->natts;
19324 7256 : for (attno = 1; attno <= natts; attno++)
19325 : {
19326 5182 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19327 5182 : char *attributeName = NameStr(attribute->attname);
19328 :
19329 : /* Ignore dropped */
19330 5182 : if (attribute->attisdropped)
19331 592 : continue;
19332 :
19333 4590 : if (attribute->attidentity)
19334 18 : ereport(ERROR,
19335 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19336 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19337 : RelationGetRelationName(attachrel), attributeName),
19338 : errdetail("The new partition may not contain an identity column."));
19339 :
19340 : /* Try to find the column in parent (matching on column name) */
19341 4572 : if (!SearchSysCacheExists2(ATTNAME,
19342 : ObjectIdGetDatum(RelationGetRelid(rel)),
19343 : CStringGetDatum(attributeName)))
19344 18 : ereport(ERROR,
19345 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19346 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19347 : RelationGetRelationName(attachrel), attributeName,
19348 : RelationGetRelationName(rel)),
19349 : errdetail("The new partition may contain only the columns present in parent.")));
19350 : }
19351 :
19352 : /*
19353 : * If child_rel has row-level triggers with transition tables, we
19354 : * currently don't allow it to become a partition. See also prohibitions
19355 : * in ATExecAddInherit() and CreateTrigger().
19356 : */
19357 2074 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19358 2074 : if (trigger_name != NULL)
19359 6 : ereport(ERROR,
19360 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19361 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19362 : trigger_name, RelationGetRelationName(attachrel)),
19363 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19364 :
19365 : /*
19366 : * Check that the new partition's bound is valid and does not overlap any
19367 : * of existing partitions of the parent - note that it does not return on
19368 : * error.
19369 : */
19370 2068 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19371 : cmd->bound, pstate);
19372 :
19373 : /* Attach a new partition to the partitioned table. */
19374 2032 : attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
19375 :
19376 : /*
19377 : * Generate partition constraint from the partition bound specification.
19378 : * If the parent itself is a partition, make sure to include its
19379 : * constraint as well.
19380 : */
19381 1924 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19382 1924 : partConstraint = list_concat(partBoundConstraint,
19383 1924 : RelationGetPartitionQual(rel));
19384 :
19385 : /* Skip validation if there are no constraints to validate. */
19386 1924 : if (partConstraint)
19387 : {
19388 : /*
19389 : * Run the partition quals through const-simplification similar to
19390 : * check constraints. We skip canonicalize_qual, though, because
19391 : * partition quals should be in canonical form already.
19392 : */
19393 : partConstraint =
19394 1876 : (List *) eval_const_expressions(NULL,
19395 : (Node *) partConstraint);
19396 :
19397 : /* XXX this sure looks wrong */
19398 1876 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19399 :
19400 : /*
19401 : * Adjust the generated constraint to match this partition's attribute
19402 : * numbers.
19403 : */
19404 1876 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19405 : rel);
19406 :
19407 : /* Validate partition constraints against the table being attached. */
19408 1876 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19409 : false);
19410 : }
19411 :
19412 : /*
19413 : * If we're attaching a partition other than the default partition and a
19414 : * default one exists, then that partition's partition constraint changes,
19415 : * so add an entry to the work queue to validate it, too. (We must not do
19416 : * this when the partition being attached is the default one; we already
19417 : * did it above!)
19418 : */
19419 1924 : if (OidIsValid(defaultPartOid))
19420 : {
19421 : Relation defaultrel;
19422 : List *defPartConstraint;
19423 :
19424 : Assert(!cmd->bound->is_default);
19425 :
19426 : /* we already hold a lock on the default partition */
19427 146 : defaultrel = table_open(defaultPartOid, NoLock);
19428 : defPartConstraint =
19429 146 : get_proposed_default_constraint(partBoundConstraint);
19430 :
19431 : /*
19432 : * Map the Vars in the constraint expression from rel's attnos to
19433 : * defaultrel's.
19434 : */
19435 : defPartConstraint =
19436 146 : map_partition_varattnos(defPartConstraint,
19437 : 1, defaultrel, rel);
19438 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19439 : defPartConstraint, true);
19440 :
19441 : /* keep our lock until commit. */
19442 146 : table_close(defaultrel, NoLock);
19443 : }
19444 :
19445 1924 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19446 :
19447 : /*
19448 : * If the partition we just attached is partitioned itself, invalidate
19449 : * relcache for all descendent partitions too to ensure that their
19450 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19451 : * the beginning of this function.
19452 : */
19453 1924 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19454 : {
19455 : ListCell *l;
19456 :
19457 996 : foreach(l, attachrel_children)
19458 : {
19459 674 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19460 : }
19461 : }
19462 :
19463 : /* keep our lock until commit */
19464 1924 : table_close(attachrel, NoLock);
19465 :
19466 1924 : return address;
19467 : }
19468 :
19469 : /*
19470 : * AttachPartitionEnsureIndexes
19471 : * subroutine for ATExecAttachPartition to create/match indexes
19472 : *
19473 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19474 : * PARTITION: every partition must have an index attached to each index on the
19475 : * partitioned table.
19476 : */
19477 : static void
19478 2380 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19479 : {
19480 : List *idxes;
19481 : List *attachRelIdxs;
19482 : Relation *attachrelIdxRels;
19483 : IndexInfo **attachInfos;
19484 : ListCell *cell;
19485 : MemoryContext cxt;
19486 : MemoryContext oldcxt;
19487 :
19488 2380 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19489 : "AttachPartitionEnsureIndexes",
19490 : ALLOCSET_DEFAULT_SIZES);
19491 2380 : oldcxt = MemoryContextSwitchTo(cxt);
19492 :
19493 2380 : idxes = RelationGetIndexList(rel);
19494 2380 : attachRelIdxs = RelationGetIndexList(attachrel);
19495 2380 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19496 2380 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19497 :
19498 : /* Build arrays of all existing indexes and their IndexInfos */
19499 2714 : foreach(cell, attachRelIdxs)
19500 : {
19501 334 : Oid cldIdxId = lfirst_oid(cell);
19502 334 : int i = foreach_current_index(cell);
19503 :
19504 334 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19505 334 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19506 : }
19507 :
19508 : /*
19509 : * If we're attaching a foreign table, we must fail if any of the indexes
19510 : * is a constraint index; otherwise, there's nothing to do here. Do this
19511 : * before starting work, to avoid wasting the effort of building a few
19512 : * non-unique indexes before coming across a unique one.
19513 : */
19514 2380 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19515 : {
19516 86 : foreach(cell, idxes)
19517 : {
19518 36 : Oid idx = lfirst_oid(cell);
19519 36 : Relation idxRel = index_open(idx, AccessShareLock);
19520 :
19521 36 : if (idxRel->rd_index->indisunique ||
19522 24 : idxRel->rd_index->indisprimary)
19523 12 : ereport(ERROR,
19524 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19525 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19526 : RelationGetRelationName(attachrel),
19527 : RelationGetRelationName(rel)),
19528 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19529 : RelationGetRelationName(rel))));
19530 24 : index_close(idxRel, AccessShareLock);
19531 : }
19532 :
19533 50 : goto out;
19534 : }
19535 :
19536 : /*
19537 : * For each index on the partitioned table, find a matching one in the
19538 : * partition-to-be; if one is not found, create one.
19539 : */
19540 2926 : foreach(cell, idxes)
19541 : {
19542 626 : Oid idx = lfirst_oid(cell);
19543 626 : Relation idxRel = index_open(idx, AccessShareLock);
19544 : IndexInfo *info;
19545 : AttrMap *attmap;
19546 626 : bool found = false;
19547 : Oid constraintOid;
19548 :
19549 : /*
19550 : * Ignore indexes in the partitioned table other than partitioned
19551 : * indexes.
19552 : */
19553 626 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19554 : {
19555 0 : index_close(idxRel, AccessShareLock);
19556 0 : continue;
19557 : }
19558 :
19559 : /* construct an indexinfo to compare existing indexes against */
19560 626 : info = BuildIndexInfo(idxRel);
19561 626 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19562 : RelationGetDescr(rel),
19563 : false);
19564 626 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19565 :
19566 : /*
19567 : * Scan the list of existing indexes in the partition-to-be, and mark
19568 : * the first matching, valid, unattached one we find, if any, as
19569 : * partition of the parent index. If we find one, we're done.
19570 : */
19571 692 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19572 : {
19573 256 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19574 256 : Oid cldConstrOid = InvalidOid;
19575 :
19576 : /* does this index have a parent? if so, can't use it */
19577 256 : if (attachrelIdxRels[i]->rd_rel->relispartition)
19578 12 : continue;
19579 :
19580 : /* If this index is invalid, can't use it */
19581 244 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
19582 6 : continue;
19583 :
19584 238 : if (CompareIndexInfo(attachInfos[i], info,
19585 238 : attachrelIdxRels[i]->rd_indcollation,
19586 238 : idxRel->rd_indcollation,
19587 238 : attachrelIdxRels[i]->rd_opfamily,
19588 238 : idxRel->rd_opfamily,
19589 : attmap))
19590 : {
19591 : /*
19592 : * If this index is being created in the parent because of a
19593 : * constraint, then the child needs to have a constraint also,
19594 : * so look for one. If there is no such constraint, this
19595 : * index is no good, so keep looking.
19596 : */
19597 202 : if (OidIsValid(constraintOid))
19598 : {
19599 : cldConstrOid =
19600 116 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19601 : cldIdxId);
19602 : /* no dice */
19603 116 : if (!OidIsValid(cldConstrOid))
19604 6 : continue;
19605 :
19606 : /* Ensure they're both the same type of constraint */
19607 220 : if (get_constraint_type(constraintOid) !=
19608 110 : get_constraint_type(cldConstrOid))
19609 6 : continue;
19610 : }
19611 :
19612 : /* bingo. */
19613 190 : IndexSetParentIndex(attachrelIdxRels[i], idx);
19614 190 : if (OidIsValid(constraintOid))
19615 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19616 : RelationGetRelid(attachrel));
19617 190 : found = true;
19618 :
19619 190 : CommandCounterIncrement();
19620 190 : break;
19621 : }
19622 : }
19623 :
19624 : /*
19625 : * If no suitable index was found in the partition-to-be, create one
19626 : * now.
19627 : */
19628 626 : if (!found)
19629 : {
19630 : IndexStmt *stmt;
19631 : Oid conOid;
19632 :
19633 436 : stmt = generateClonedIndexStmt(NULL,
19634 : idxRel, attmap,
19635 : &conOid);
19636 :
19637 : /*
19638 : * If the index is a primary key, mark all columns as NOT NULL if
19639 : * they aren't already.
19640 : */
19641 436 : if (stmt->primary)
19642 : {
19643 220 : MemoryContextSwitchTo(oldcxt);
19644 452 : for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
19645 : {
19646 : AttrNumber childattno;
19647 :
19648 232 : childattno = get_attnum(RelationGetRelid(attachrel),
19649 232 : get_attname(RelationGetRelid(rel),
19650 232 : info->ii_IndexAttrNumbers[j],
19651 : false));
19652 232 : set_attnotnull(wqueue, attachrel, childattno,
19653 : true, AccessExclusiveLock);
19654 : }
19655 220 : MemoryContextSwitchTo(cxt);
19656 : }
19657 :
19658 436 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19659 : RelationGetRelid(idxRel),
19660 : conOid,
19661 : -1,
19662 : true, false, false, false, false);
19663 : }
19664 :
19665 608 : index_close(idxRel, AccessShareLock);
19666 : }
19667 :
19668 2350 : out:
19669 : /* Clean up. */
19670 2672 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19671 322 : index_close(attachrelIdxRels[i], AccessShareLock);
19672 2350 : MemoryContextSwitchTo(oldcxt);
19673 2350 : MemoryContextDelete(cxt);
19674 2350 : }
19675 :
19676 : /*
19677 : * CloneRowTriggersToPartition
19678 : * subroutine for ATExecAttachPartition/DefineRelation to create row
19679 : * triggers on partitions
19680 : */
19681 : static void
19682 2776 : CloneRowTriggersToPartition(Relation parent, Relation partition)
19683 : {
19684 : Relation pg_trigger;
19685 : ScanKeyData key;
19686 : SysScanDesc scan;
19687 : HeapTuple tuple;
19688 : MemoryContext perTupCxt;
19689 :
19690 2776 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19691 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19692 2776 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19693 2776 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19694 : true, NULL, 1, &key);
19695 :
19696 2776 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19697 : "clone trig", ALLOCSET_SMALL_SIZES);
19698 :
19699 4356 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19700 : {
19701 1586 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19702 : CreateTrigStmt *trigStmt;
19703 1586 : Node *qual = NULL;
19704 : Datum value;
19705 : bool isnull;
19706 1586 : List *cols = NIL;
19707 1586 : List *trigargs = NIL;
19708 : MemoryContext oldcxt;
19709 :
19710 : /*
19711 : * Ignore statement-level triggers; those are not cloned.
19712 : */
19713 1586 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19714 1406 : continue;
19715 :
19716 : /*
19717 : * Don't clone internal triggers, because the constraint cloning code
19718 : * will.
19719 : */
19720 1562 : if (trigForm->tgisinternal)
19721 1382 : continue;
19722 :
19723 : /*
19724 : * Complain if we find an unexpected trigger type.
19725 : */
19726 180 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19727 162 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
19728 0 : elog(ERROR, "unexpected trigger \"%s\" found",
19729 : NameStr(trigForm->tgname));
19730 :
19731 : /* Use short-lived context for CREATE TRIGGER */
19732 180 : oldcxt = MemoryContextSwitchTo(perTupCxt);
19733 :
19734 : /*
19735 : * If there is a WHEN clause, generate a 'cooked' version of it that's
19736 : * appropriate for the partition.
19737 : */
19738 180 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19739 : RelationGetDescr(pg_trigger), &isnull);
19740 180 : if (!isnull)
19741 : {
19742 6 : qual = stringToNode(TextDatumGetCString(value));
19743 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19744 : partition, parent);
19745 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19746 : partition, parent);
19747 : }
19748 :
19749 : /*
19750 : * If there is a column list, transform it to a list of column names.
19751 : * Note we don't need to map this list in any way ...
19752 : */
19753 180 : if (trigForm->tgattr.dim1 > 0)
19754 : {
19755 : int i;
19756 :
19757 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
19758 : {
19759 : Form_pg_attribute col;
19760 :
19761 6 : col = TupleDescAttr(parent->rd_att,
19762 : trigForm->tgattr.values[i] - 1);
19763 6 : cols = lappend(cols,
19764 6 : makeString(pstrdup(NameStr(col->attname))));
19765 : }
19766 : }
19767 :
19768 : /* Reconstruct trigger arguments list. */
19769 180 : if (trigForm->tgnargs > 0)
19770 : {
19771 : char *p;
19772 :
19773 36 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19774 : RelationGetDescr(pg_trigger), &isnull);
19775 36 : if (isnull)
19776 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19777 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
19778 :
19779 36 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19780 :
19781 84 : for (int i = 0; i < trigForm->tgnargs; i++)
19782 : {
19783 48 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
19784 48 : p += strlen(p) + 1;
19785 : }
19786 : }
19787 :
19788 180 : trigStmt = makeNode(CreateTrigStmt);
19789 180 : trigStmt->replace = false;
19790 180 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19791 180 : trigStmt->trigname = NameStr(trigForm->tgname);
19792 180 : trigStmt->relation = NULL;
19793 180 : trigStmt->funcname = NULL; /* passed separately */
19794 180 : trigStmt->args = trigargs;
19795 180 : trigStmt->row = true;
19796 180 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19797 180 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19798 180 : trigStmt->columns = cols;
19799 180 : trigStmt->whenClause = NULL; /* passed separately */
19800 180 : trigStmt->transitionRels = NIL; /* not supported at present */
19801 180 : trigStmt->deferrable = trigForm->tgdeferrable;
19802 180 : trigStmt->initdeferred = trigForm->tginitdeferred;
19803 180 : trigStmt->constrrel = NULL; /* passed separately */
19804 :
19805 180 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19806 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19807 : trigForm->tgfoid, trigForm->oid, qual,
19808 180 : false, true, trigForm->tgenabled);
19809 :
19810 174 : MemoryContextSwitchTo(oldcxt);
19811 174 : MemoryContextReset(perTupCxt);
19812 : }
19813 :
19814 2770 : MemoryContextDelete(perTupCxt);
19815 :
19816 2770 : systable_endscan(scan);
19817 2770 : table_close(pg_trigger, RowExclusiveLock);
19818 2770 : }
19819 :
19820 : /*
19821 : * ALTER TABLE DETACH PARTITION
19822 : *
19823 : * Return the address of the relation that is no longer a partition of rel.
19824 : *
19825 : * If concurrent mode is requested, we run in two transactions. A side-
19826 : * effect is that this command cannot run in a multi-part ALTER TABLE.
19827 : * Currently, that's enforced by the grammar.
19828 : *
19829 : * The strategy for concurrency is to first modify the partition's
19830 : * pg_inherit catalog row to make it visible to everyone that the
19831 : * partition is detached, lock the partition against writes, and commit
19832 : * the transaction; anyone who requests the partition descriptor from
19833 : * that point onwards has to ignore such a partition. In a second
19834 : * transaction, we wait until all transactions that could have seen the
19835 : * partition as attached are gone, then we remove the rest of partition
19836 : * metadata (pg_inherits and pg_class.relpartbounds).
19837 : */
19838 : static ObjectAddress
19839 516 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19840 : RangeVar *name, bool concurrent)
19841 : {
19842 : Relation partRel;
19843 : ObjectAddress address;
19844 : Oid defaultPartOid;
19845 :
19846 : /*
19847 : * We must lock the default partition, because detaching this partition
19848 : * will change its partition constraint.
19849 : */
19850 : defaultPartOid =
19851 516 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19852 516 : if (OidIsValid(defaultPartOid))
19853 : {
19854 : /*
19855 : * Concurrent detaching when a default partition exists is not
19856 : * supported. The main problem is that the default partition
19857 : * constraint would change. And there's a definitional problem: what
19858 : * should happen to the tuples that are being inserted that belong to
19859 : * the partition being detached? Putting them on the partition being
19860 : * detached would be wrong, since they'd become "lost" after the
19861 : * detaching completes but we cannot put them in the default partition
19862 : * either until we alter its partition constraint.
19863 : *
19864 : * I think we could solve this problem if we effected the constraint
19865 : * change before committing the first transaction. But the lock would
19866 : * have to remain AEL and it would cause concurrent query planning to
19867 : * be blocked, so changing it that way would be even worse.
19868 : */
19869 112 : if (concurrent)
19870 12 : ereport(ERROR,
19871 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19872 : errmsg("cannot detach partitions concurrently when a default partition exists")));
19873 100 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19874 : }
19875 :
19876 : /*
19877 : * In concurrent mode, the partition is locked with share-update-exclusive
19878 : * in the first transaction. This allows concurrent transactions to be
19879 : * doing DML to the partition.
19880 : */
19881 504 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19882 : AccessExclusiveLock);
19883 :
19884 : /*
19885 : * Check inheritance conditions and either delete the pg_inherits row (in
19886 : * non-concurrent mode) or just set the inhdetachpending flag.
19887 : */
19888 492 : if (!concurrent)
19889 346 : RemoveInheritance(partRel, rel, false);
19890 : else
19891 146 : MarkInheritDetached(partRel, rel);
19892 :
19893 : /*
19894 : * Ensure that foreign keys still hold after this detach. This keeps
19895 : * locks on the referencing tables, which prevents concurrent transactions
19896 : * from adding rows that we wouldn't see. For this to work in concurrent
19897 : * mode, it is critical that the partition appears as no longer attached
19898 : * for the RI queries as soon as the first transaction commits.
19899 : */
19900 472 : ATDetachCheckNoForeignKeyRefs(partRel);
19901 :
19902 : /*
19903 : * Concurrent mode has to work harder; first we add a new constraint to
19904 : * the partition that matches the partition constraint. Then we close our
19905 : * existing transaction, and in a new one wait for all processes to catch
19906 : * up on the catalog updates we've done so far; at that point we can
19907 : * complete the operation.
19908 : */
19909 438 : if (concurrent)
19910 : {
19911 : Oid partrelid,
19912 : parentrelid;
19913 : LOCKTAG tag;
19914 : char *parentrelname;
19915 : char *partrelname;
19916 :
19917 : /*
19918 : * Add a new constraint to the partition being detached, which
19919 : * supplants the partition constraint (unless there is one already).
19920 : */
19921 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
19922 :
19923 : /*
19924 : * We're almost done now; the only traces that remain are the
19925 : * pg_inherits tuple and the partition's relpartbounds. Before we can
19926 : * remove those, we need to wait until all transactions that know that
19927 : * this is a partition are gone.
19928 : */
19929 :
19930 : /*
19931 : * Remember relation OIDs to re-acquire them later; and relation names
19932 : * too, for error messages if something is dropped in between.
19933 : */
19934 140 : partrelid = RelationGetRelid(partRel);
19935 140 : parentrelid = RelationGetRelid(rel);
19936 140 : parentrelname = MemoryContextStrdup(PortalContext,
19937 140 : RelationGetRelationName(rel));
19938 140 : partrelname = MemoryContextStrdup(PortalContext,
19939 140 : RelationGetRelationName(partRel));
19940 :
19941 : /* Invalidate relcache entries for the parent -- must be before close */
19942 140 : CacheInvalidateRelcache(rel);
19943 :
19944 140 : table_close(partRel, NoLock);
19945 140 : table_close(rel, NoLock);
19946 140 : tab->rel = NULL;
19947 :
19948 : /* Make updated catalog entry visible */
19949 140 : PopActiveSnapshot();
19950 140 : CommitTransactionCommand();
19951 :
19952 140 : StartTransactionCommand();
19953 :
19954 : /*
19955 : * Now wait. This ensures that all queries that were planned
19956 : * including the partition are finished before we remove the rest of
19957 : * catalog entries. We don't need or indeed want to acquire this
19958 : * lock, though -- that would block later queries.
19959 : *
19960 : * We don't need to concern ourselves with waiting for a lock on the
19961 : * partition itself, since we will acquire AccessExclusiveLock below.
19962 : */
19963 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19964 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19965 :
19966 : /*
19967 : * Now acquire locks in both relations again. Note they may have been
19968 : * removed in the meantime, so care is required.
19969 : */
19970 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19971 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
19972 :
19973 : /* If the relations aren't there, something bad happened; bail out */
19974 90 : if (rel == NULL)
19975 : {
19976 0 : if (partRel != NULL) /* shouldn't happen */
19977 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19978 : partrelname);
19979 0 : ereport(ERROR,
19980 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19981 : errmsg("partitioned table \"%s\" was removed concurrently",
19982 : parentrelname)));
19983 : }
19984 90 : if (partRel == NULL)
19985 0 : ereport(ERROR,
19986 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19987 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
19988 :
19989 90 : tab->rel = rel;
19990 : }
19991 :
19992 : /* Do the final part of detaching */
19993 388 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19994 :
19995 386 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19996 :
19997 : /* keep our lock until commit */
19998 386 : table_close(partRel, NoLock);
19999 :
20000 386 : return address;
20001 : }
20002 :
20003 : /*
20004 : * Second part of ALTER TABLE .. DETACH.
20005 : *
20006 : * This is separate so that it can be run independently when the second
20007 : * transaction of the concurrent algorithm fails (crash or abort).
20008 : */
20009 : static void
20010 690 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20011 : Oid defaultPartOid)
20012 : {
20013 : Relation classRel;
20014 : List *fks;
20015 : ListCell *cell;
20016 : List *indexes;
20017 : Datum new_val[Natts_pg_class];
20018 : bool new_null[Natts_pg_class],
20019 : new_repl[Natts_pg_class];
20020 : HeapTuple tuple,
20021 : newtuple;
20022 690 : Relation trigrel = NULL;
20023 :
20024 690 : if (concurrent)
20025 : {
20026 : /*
20027 : * We can remove the pg_inherits row now. (In the non-concurrent case,
20028 : * this was already done).
20029 : */
20030 104 : RemoveInheritance(partRel, rel, true);
20031 : }
20032 :
20033 : /* Drop any triggers that were cloned on creation/attach. */
20034 690 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20035 :
20036 : /*
20037 : * Detach any foreign keys that are inherited. This includes creating
20038 : * additional action triggers.
20039 : */
20040 690 : fks = copyObject(RelationGetFKeyList(partRel));
20041 690 : if (fks != NIL)
20042 54 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20043 774 : foreach(cell, fks)
20044 : {
20045 84 : ForeignKeyCacheInfo *fk = lfirst(cell);
20046 : HeapTuple contup;
20047 : Form_pg_constraint conform;
20048 : Constraint *fkconstraint;
20049 : Oid insertTriggerOid,
20050 : updateTriggerOid;
20051 :
20052 84 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20053 84 : if (!HeapTupleIsValid(contup))
20054 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20055 84 : conform = (Form_pg_constraint) GETSTRUCT(contup);
20056 :
20057 : /* consider only the inherited foreign keys */
20058 84 : if (conform->contype != CONSTRAINT_FOREIGN ||
20059 84 : !OidIsValid(conform->conparentid))
20060 : {
20061 18 : ReleaseSysCache(contup);
20062 18 : continue;
20063 : }
20064 :
20065 : /* unset conparentid and adjust conislocal, coninhcount, etc. */
20066 66 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20067 :
20068 : /*
20069 : * Also, look up the partition's "check" triggers corresponding to the
20070 : * constraint being detached and detach them from the parent triggers.
20071 : */
20072 66 : GetForeignKeyCheckTriggers(trigrel,
20073 : fk->conoid, fk->confrelid, fk->conrelid,
20074 : &insertTriggerOid, &updateTriggerOid);
20075 : Assert(OidIsValid(insertTriggerOid));
20076 66 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20077 : RelationGetRelid(partRel));
20078 : Assert(OidIsValid(updateTriggerOid));
20079 66 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20080 : RelationGetRelid(partRel));
20081 :
20082 : /*
20083 : * Make the action triggers on the referenced relation. When this was
20084 : * a partition the action triggers pointed to the parent rel (they
20085 : * still do), but now we need separate ones of our own.
20086 : */
20087 66 : fkconstraint = makeNode(Constraint);
20088 66 : fkconstraint->contype = CONSTRAINT_FOREIGN;
20089 66 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
20090 66 : fkconstraint->deferrable = conform->condeferrable;
20091 66 : fkconstraint->initdeferred = conform->condeferred;
20092 66 : fkconstraint->location = -1;
20093 66 : fkconstraint->pktable = NULL;
20094 66 : fkconstraint->fk_attrs = NIL;
20095 66 : fkconstraint->pk_attrs = NIL;
20096 66 : fkconstraint->fk_matchtype = conform->confmatchtype;
20097 66 : fkconstraint->fk_upd_action = conform->confupdtype;
20098 66 : fkconstraint->fk_del_action = conform->confdeltype;
20099 66 : fkconstraint->fk_del_set_cols = NIL;
20100 66 : fkconstraint->old_conpfeqop = NIL;
20101 66 : fkconstraint->old_pktable_oid = InvalidOid;
20102 66 : fkconstraint->skip_validation = false;
20103 66 : fkconstraint->initially_valid = true;
20104 :
20105 66 : createForeignKeyActionTriggers(partRel, conform->confrelid,
20106 : fkconstraint, fk->conoid,
20107 : conform->conindid,
20108 : InvalidOid, InvalidOid,
20109 : NULL, NULL);
20110 :
20111 66 : ReleaseSysCache(contup);
20112 : }
20113 690 : list_free_deep(fks);
20114 690 : if (trigrel)
20115 54 : table_close(trigrel, RowExclusiveLock);
20116 :
20117 : /*
20118 : * Any sub-constraints that are in the referenced-side of a larger
20119 : * constraint have to be removed. This partition is no longer part of the
20120 : * key space of the constraint.
20121 : */
20122 732 : foreach(cell, GetParentedForeignKeyRefs(partRel))
20123 : {
20124 44 : Oid constrOid = lfirst_oid(cell);
20125 : ObjectAddress constraint;
20126 :
20127 44 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20128 44 : deleteDependencyRecordsForClass(ConstraintRelationId,
20129 : constrOid,
20130 : ConstraintRelationId,
20131 : DEPENDENCY_INTERNAL);
20132 44 : CommandCounterIncrement();
20133 :
20134 44 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20135 44 : performDeletion(&constraint, DROP_RESTRICT, 0);
20136 : }
20137 :
20138 : /* Now we can detach indexes */
20139 688 : indexes = RelationGetIndexList(partRel);
20140 1002 : foreach(cell, indexes)
20141 : {
20142 314 : Oid idxid = lfirst_oid(cell);
20143 : Relation idx;
20144 : Oid constrOid;
20145 :
20146 314 : if (!has_superclass(idxid))
20147 12 : continue;
20148 :
20149 : Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
20150 : RelationGetRelid(rel)));
20151 :
20152 302 : idx = index_open(idxid, AccessExclusiveLock);
20153 302 : IndexSetParentIndex(idx, InvalidOid);
20154 :
20155 : /* If there's a constraint associated with the index, detach it too */
20156 302 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20157 : idxid);
20158 302 : if (OidIsValid(constrOid))
20159 120 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20160 :
20161 302 : index_close(idx, NoLock);
20162 : }
20163 :
20164 : /* Update pg_class tuple */
20165 688 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20166 688 : tuple = SearchSysCacheCopy1(RELOID,
20167 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20168 688 : if (!HeapTupleIsValid(tuple))
20169 0 : elog(ERROR, "cache lookup failed for relation %u",
20170 : RelationGetRelid(partRel));
20171 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20172 :
20173 : /* Clear relpartbound and reset relispartition */
20174 688 : memset(new_val, 0, sizeof(new_val));
20175 688 : memset(new_null, false, sizeof(new_null));
20176 688 : memset(new_repl, false, sizeof(new_repl));
20177 688 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20178 688 : new_null[Anum_pg_class_relpartbound - 1] = true;
20179 688 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20180 688 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20181 : new_val, new_null, new_repl);
20182 :
20183 688 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20184 688 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20185 688 : heap_freetuple(newtuple);
20186 688 : table_close(classRel, RowExclusiveLock);
20187 :
20188 : /*
20189 : * Drop identity property from all identity columns of partition.
20190 : */
20191 2452 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20192 : {
20193 1764 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20194 :
20195 1764 : if (!attr->attisdropped && attr->attidentity)
20196 30 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20197 : AccessExclusiveLock, true, true);
20198 : }
20199 :
20200 688 : if (OidIsValid(defaultPartOid))
20201 : {
20202 : /*
20203 : * If the relation being detached is the default partition itself,
20204 : * remove it from the parent's pg_partitioned_table entry.
20205 : *
20206 : * If not, we must invalidate default partition's relcache entry, as
20207 : * in StorePartitionBound: its partition constraint depends on every
20208 : * other partition's partition constraint.
20209 : */
20210 232 : if (RelationGetRelid(partRel) == defaultPartOid)
20211 38 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20212 : else
20213 194 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20214 : }
20215 :
20216 : /*
20217 : * Invalidate the parent's relcache so that the partition is no longer
20218 : * included in its partition descriptor.
20219 : */
20220 688 : CacheInvalidateRelcache(rel);
20221 :
20222 : /*
20223 : * If the partition we just detached is partitioned itself, invalidate
20224 : * relcache for all descendent partitions too to ensure that their
20225 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20226 : * doing so, using the same lockmode as what partRel has been locked with
20227 : * by the caller.
20228 : */
20229 688 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20230 : {
20231 : List *children;
20232 :
20233 50 : children = find_all_inheritors(RelationGetRelid(partRel),
20234 : AccessExclusiveLock, NULL);
20235 162 : foreach(cell, children)
20236 : {
20237 112 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20238 : }
20239 : }
20240 688 : }
20241 :
20242 : /*
20243 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20244 : *
20245 : * To use when a DETACH PARTITION command previously did not run to
20246 : * completion; this completes the detaching process.
20247 : */
20248 : static ObjectAddress
20249 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20250 : {
20251 : Relation partRel;
20252 : ObjectAddress address;
20253 14 : Snapshot snap = GetActiveSnapshot();
20254 :
20255 14 : partRel = table_openrv(name, AccessExclusiveLock);
20256 :
20257 : /*
20258 : * Wait until existing snapshots are gone. This is important if the
20259 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20260 : * user could immediately run DETACH FINALIZE without actually waiting for
20261 : * existing transactions. We must not complete the detach action until
20262 : * all such queries are complete (otherwise we would present them with an
20263 : * inconsistent view of catalogs).
20264 : */
20265 14 : WaitForOlderSnapshots(snap->xmin, false);
20266 :
20267 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20268 :
20269 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20270 :
20271 14 : table_close(partRel, NoLock);
20272 :
20273 14 : return address;
20274 : }
20275 :
20276 : /*
20277 : * DetachAddConstraintIfNeeded
20278 : * Subroutine for ATExecDetachPartition. Create a constraint that
20279 : * takes the place of the partition constraint, but avoid creating
20280 : * a dupe if a constraint already exists which implies the needed
20281 : * constraint.
20282 : */
20283 : static void
20284 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20285 : {
20286 : List *constraintExpr;
20287 :
20288 140 : constraintExpr = RelationGetPartitionQual(partRel);
20289 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20290 :
20291 : /*
20292 : * Avoid adding a new constraint if the needed constraint is implied by an
20293 : * existing constraint
20294 : */
20295 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20296 : {
20297 : AlteredTableInfo *tab;
20298 : Constraint *n;
20299 :
20300 134 : tab = ATGetQueueEntry(wqueue, partRel);
20301 :
20302 : /* Add constraint on partition, equivalent to the partition constraint */
20303 134 : n = makeNode(Constraint);
20304 134 : n->contype = CONSTR_CHECK;
20305 134 : n->conname = NULL;
20306 134 : n->location = -1;
20307 134 : n->is_no_inherit = false;
20308 134 : n->raw_expr = NULL;
20309 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20310 134 : n->initially_valid = true;
20311 134 : n->skip_validation = true;
20312 : /* It's a re-add, since it nominally already exists */
20313 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20314 : true, false, true, ShareUpdateExclusiveLock);
20315 : }
20316 140 : }
20317 :
20318 : /*
20319 : * DropClonedTriggersFromPartition
20320 : * subroutine for ATExecDetachPartition to remove any triggers that were
20321 : * cloned to the partition when it was created-as-partition or attached.
20322 : * This undoes what CloneRowTriggersToPartition did.
20323 : */
20324 : static void
20325 690 : DropClonedTriggersFromPartition(Oid partitionId)
20326 : {
20327 : ScanKeyData skey;
20328 : SysScanDesc scan;
20329 : HeapTuple trigtup;
20330 : Relation tgrel;
20331 : ObjectAddresses *objects;
20332 :
20333 690 : objects = new_object_addresses();
20334 :
20335 : /*
20336 : * Scan pg_trigger to search for all triggers on this rel.
20337 : */
20338 690 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20339 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20340 690 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20341 690 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20342 : true, NULL, 1, &skey);
20343 988 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20344 : {
20345 298 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20346 : ObjectAddress trig;
20347 :
20348 : /* Ignore triggers that weren't cloned */
20349 298 : if (!OidIsValid(pg_trigger->tgparentid))
20350 256 : continue;
20351 :
20352 : /*
20353 : * Ignore internal triggers that are implementation objects of foreign
20354 : * keys, because these will be detached when the foreign keys
20355 : * themselves are.
20356 : */
20357 262 : if (OidIsValid(pg_trigger->tgconstrrelid))
20358 220 : continue;
20359 :
20360 : /*
20361 : * This is ugly, but necessary: remove the dependency markings on the
20362 : * trigger so that it can be removed.
20363 : */
20364 42 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20365 : TriggerRelationId,
20366 : DEPENDENCY_PARTITION_PRI);
20367 42 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20368 : RelationRelationId,
20369 : DEPENDENCY_PARTITION_SEC);
20370 :
20371 : /* remember this trigger to remove it below */
20372 42 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20373 42 : add_exact_object_address(&trig, objects);
20374 : }
20375 :
20376 : /* make the dependency removal visible to the deletion below */
20377 690 : CommandCounterIncrement();
20378 690 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20379 :
20380 : /* done */
20381 690 : free_object_addresses(objects);
20382 690 : systable_endscan(scan);
20383 690 : table_close(tgrel, RowExclusiveLock);
20384 690 : }
20385 :
20386 : /*
20387 : * Before acquiring lock on an index, acquire the same lock on the owning
20388 : * table.
20389 : */
20390 : struct AttachIndexCallbackState
20391 : {
20392 : Oid partitionOid;
20393 : Oid parentTblOid;
20394 : bool lockedParentTbl;
20395 : };
20396 :
20397 : static void
20398 392 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20399 : void *arg)
20400 : {
20401 : struct AttachIndexCallbackState *state;
20402 : Form_pg_class classform;
20403 : HeapTuple tuple;
20404 :
20405 392 : state = (struct AttachIndexCallbackState *) arg;
20406 :
20407 392 : if (!state->lockedParentTbl)
20408 : {
20409 384 : LockRelationOid(state->parentTblOid, AccessShareLock);
20410 384 : state->lockedParentTbl = true;
20411 : }
20412 :
20413 : /*
20414 : * If we previously locked some other heap, and the name we're looking up
20415 : * no longer refers to an index on that relation, release the now-useless
20416 : * lock. XXX maybe we should do *after* we verify whether the index does
20417 : * not actually belong to the same relation ...
20418 : */
20419 392 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20420 : {
20421 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20422 0 : state->partitionOid = InvalidOid;
20423 : }
20424 :
20425 : /* Didn't find a relation, so no need for locking or permission checks. */
20426 392 : if (!OidIsValid(relOid))
20427 6 : return;
20428 :
20429 386 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20430 386 : if (!HeapTupleIsValid(tuple))
20431 0 : return; /* concurrently dropped, so nothing to do */
20432 386 : classform = (Form_pg_class) GETSTRUCT(tuple);
20433 386 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20434 296 : classform->relkind != RELKIND_INDEX)
20435 6 : ereport(ERROR,
20436 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20437 : errmsg("\"%s\" is not an index", rv->relname)));
20438 380 : ReleaseSysCache(tuple);
20439 :
20440 : /*
20441 : * Since we need only examine the heap's tupledesc, an access share lock
20442 : * on it (preventing any DDL) is sufficient.
20443 : */
20444 380 : state->partitionOid = IndexGetRelation(relOid, false);
20445 380 : LockRelationOid(state->partitionOid, AccessShareLock);
20446 : }
20447 :
20448 : /*
20449 : * ALTER INDEX i1 ATTACH PARTITION i2
20450 : */
20451 : static ObjectAddress
20452 384 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20453 : {
20454 : Relation partIdx;
20455 : Relation partTbl;
20456 : Relation parentTbl;
20457 : ObjectAddress address;
20458 : Oid partIdxId;
20459 : Oid currParent;
20460 : struct AttachIndexCallbackState state;
20461 :
20462 : /*
20463 : * We need to obtain lock on the index 'name' to modify it, but we also
20464 : * need to read its owning table's tuple descriptor -- so we need to lock
20465 : * both. To avoid deadlocks, obtain lock on the table before doing so on
20466 : * the index. Furthermore, we need to examine the parent table of the
20467 : * partition, so lock that one too.
20468 : */
20469 384 : state.partitionOid = InvalidOid;
20470 384 : state.parentTblOid = parentIdx->rd_index->indrelid;
20471 384 : state.lockedParentTbl = false;
20472 : partIdxId =
20473 384 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20474 : RangeVarCallbackForAttachIndex,
20475 : (void *) &state);
20476 : /* Not there? */
20477 372 : if (!OidIsValid(partIdxId))
20478 0 : ereport(ERROR,
20479 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20480 : errmsg("index \"%s\" does not exist", name->relname)));
20481 :
20482 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20483 372 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
20484 :
20485 : /* we already hold locks on both tables, so this is safe: */
20486 372 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20487 372 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20488 :
20489 372 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20490 :
20491 : /* Silently do nothing if already in the right state */
20492 744 : currParent = partIdx->rd_rel->relispartition ?
20493 372 : get_partition_parent(partIdxId, false) : InvalidOid;
20494 372 : if (currParent != RelationGetRelid(parentIdx))
20495 : {
20496 : IndexInfo *childInfo;
20497 : IndexInfo *parentInfo;
20498 : AttrMap *attmap;
20499 : bool found;
20500 : int i;
20501 : PartitionDesc partDesc;
20502 : Oid constraintOid,
20503 360 : cldConstrId = InvalidOid;
20504 :
20505 : /*
20506 : * If this partition already has an index attached, refuse the
20507 : * operation.
20508 : */
20509 360 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20510 :
20511 354 : if (OidIsValid(currParent))
20512 0 : ereport(ERROR,
20513 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20514 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20515 : RelationGetRelationName(partIdx),
20516 : RelationGetRelationName(parentIdx)),
20517 : errdetail("Index \"%s\" is already attached to another index.",
20518 : RelationGetRelationName(partIdx))));
20519 :
20520 : /* Make sure it indexes a partition of the other index's table */
20521 354 : partDesc = RelationGetPartitionDesc(parentTbl, true);
20522 354 : found = false;
20523 556 : for (i = 0; i < partDesc->nparts; i++)
20524 : {
20525 550 : if (partDesc->oids[i] == state.partitionOid)
20526 : {
20527 348 : found = true;
20528 348 : break;
20529 : }
20530 : }
20531 354 : if (!found)
20532 6 : ereport(ERROR,
20533 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20534 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20535 : RelationGetRelationName(partIdx),
20536 : RelationGetRelationName(parentIdx)),
20537 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20538 : RelationGetRelationName(partIdx),
20539 : RelationGetRelationName(parentTbl))));
20540 :
20541 : /* Ensure the indexes are compatible */
20542 348 : childInfo = BuildIndexInfo(partIdx);
20543 348 : parentInfo = BuildIndexInfo(parentIdx);
20544 348 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20545 : RelationGetDescr(parentTbl),
20546 : false);
20547 348 : if (!CompareIndexInfo(childInfo, parentInfo,
20548 348 : partIdx->rd_indcollation,
20549 348 : parentIdx->rd_indcollation,
20550 348 : partIdx->rd_opfamily,
20551 348 : parentIdx->rd_opfamily,
20552 : attmap))
20553 42 : ereport(ERROR,
20554 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20555 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20556 : RelationGetRelationName(partIdx),
20557 : RelationGetRelationName(parentIdx)),
20558 : errdetail("The index definitions do not match.")));
20559 :
20560 : /*
20561 : * If there is a constraint in the parent, make sure there is one in
20562 : * the child too.
20563 : */
20564 306 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20565 : RelationGetRelid(parentIdx));
20566 :
20567 306 : if (OidIsValid(constraintOid))
20568 : {
20569 128 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20570 : partIdxId);
20571 128 : if (!OidIsValid(cldConstrId))
20572 6 : ereport(ERROR,
20573 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20574 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20575 : RelationGetRelationName(partIdx),
20576 : RelationGetRelationName(parentIdx)),
20577 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20578 : RelationGetRelationName(parentIdx),
20579 : RelationGetRelationName(parentTbl),
20580 : RelationGetRelationName(partIdx))));
20581 : }
20582 :
20583 : /*
20584 : * If it's a primary key, make sure the columns in the partition are
20585 : * NOT NULL.
20586 : */
20587 300 : if (parentIdx->rd_index->indisprimary)
20588 98 : verifyPartitionIndexNotNull(childInfo, partTbl);
20589 :
20590 : /* All good -- do it */
20591 294 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20592 294 : if (OidIsValid(constraintOid))
20593 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
20594 : RelationGetRelid(partTbl));
20595 :
20596 294 : free_attrmap(attmap);
20597 :
20598 294 : validatePartitionedIndex(parentIdx, parentTbl);
20599 : }
20600 :
20601 306 : relation_close(parentTbl, AccessShareLock);
20602 : /* keep these locks till commit */
20603 306 : relation_close(partTbl, NoLock);
20604 306 : relation_close(partIdx, NoLock);
20605 :
20606 306 : return address;
20607 : }
20608 :
20609 : /*
20610 : * Verify whether the given partition already contains an index attached
20611 : * to the given partitioned index. If so, raise an error.
20612 : */
20613 : static void
20614 360 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20615 : {
20616 : Oid existingIdx;
20617 :
20618 360 : existingIdx = index_get_partition(partitionTbl,
20619 : RelationGetRelid(parentIdx));
20620 360 : if (OidIsValid(existingIdx))
20621 6 : ereport(ERROR,
20622 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20623 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20624 : RelationGetRelationName(partIdx),
20625 : RelationGetRelationName(parentIdx)),
20626 : errdetail("Another index is already attached for partition \"%s\".",
20627 : RelationGetRelationName(partitionTbl))));
20628 354 : }
20629 :
20630 : /*
20631 : * Verify whether the set of attached partition indexes to a parent index on
20632 : * a partitioned table is complete. If it is, mark the parent index valid.
20633 : *
20634 : * This should be called each time a partition index is attached.
20635 : */
20636 : static void
20637 336 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20638 : {
20639 : Relation inheritsRel;
20640 : SysScanDesc scan;
20641 : ScanKeyData key;
20642 336 : int tuples = 0;
20643 : HeapTuple inhTup;
20644 336 : bool updated = false;
20645 :
20646 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20647 :
20648 : /*
20649 : * Scan pg_inherits for this parent index. Count each valid index we find
20650 : * (verifying the pg_index entry for each), and if we reach the total
20651 : * amount we expect, we can mark this parent index as valid.
20652 : */
20653 336 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20654 336 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20655 : BTEqualStrategyNumber, F_OIDEQ,
20656 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20657 336 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20658 : NULL, 1, &key);
20659 876 : while ((inhTup = systable_getnext(scan)) != NULL)
20660 : {
20661 540 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20662 : HeapTuple indTup;
20663 : Form_pg_index indexForm;
20664 :
20665 540 : indTup = SearchSysCache1(INDEXRELID,
20666 : ObjectIdGetDatum(inhForm->inhrelid));
20667 540 : if (!HeapTupleIsValid(indTup))
20668 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20669 540 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20670 540 : if (indexForm->indisvalid)
20671 482 : tuples += 1;
20672 540 : ReleaseSysCache(indTup);
20673 : }
20674 :
20675 : /* Done with pg_inherits */
20676 336 : systable_endscan(scan);
20677 336 : table_close(inheritsRel, AccessShareLock);
20678 :
20679 : /*
20680 : * If we found as many inherited indexes as the partitioned table has
20681 : * partitions, we're good; update pg_index to set indisvalid.
20682 : */
20683 336 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20684 : {
20685 : Relation idxRel;
20686 : HeapTuple indTup;
20687 : Form_pg_index indexForm;
20688 :
20689 166 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
20690 166 : indTup = SearchSysCacheCopy1(INDEXRELID,
20691 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20692 166 : if (!HeapTupleIsValid(indTup))
20693 0 : elog(ERROR, "cache lookup failed for index %u",
20694 : RelationGetRelid(partedIdx));
20695 166 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20696 :
20697 166 : indexForm->indisvalid = true;
20698 166 : updated = true;
20699 :
20700 166 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20701 :
20702 166 : table_close(idxRel, RowExclusiveLock);
20703 166 : heap_freetuple(indTup);
20704 : }
20705 :
20706 : /*
20707 : * If this index is in turn a partition of a larger index, validating it
20708 : * might cause the parent to become valid also. Try that.
20709 : */
20710 336 : if (updated && partedIdx->rd_rel->relispartition)
20711 : {
20712 : Oid parentIdxId,
20713 : parentTblId;
20714 : Relation parentIdx,
20715 : parentTbl;
20716 :
20717 : /* make sure we see the validation we just did */
20718 42 : CommandCounterIncrement();
20719 :
20720 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20721 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20722 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20723 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20724 : Assert(!parentIdx->rd_index->indisvalid);
20725 :
20726 42 : validatePartitionedIndex(parentIdx, parentTbl);
20727 :
20728 42 : relation_close(parentIdx, AccessExclusiveLock);
20729 42 : relation_close(parentTbl, AccessExclusiveLock);
20730 : }
20731 336 : }
20732 :
20733 : /*
20734 : * When attaching an index as a partition of a partitioned index which is a
20735 : * primary key, verify that all the columns in the partition are marked NOT
20736 : * NULL.
20737 : */
20738 : static void
20739 98 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20740 : {
20741 192 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20742 : {
20743 100 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20744 : iinfo->ii_IndexAttrNumbers[i] - 1);
20745 :
20746 100 : if (!att->attnotnull)
20747 6 : ereport(ERROR,
20748 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20749 : errmsg("invalid primary key definition"),
20750 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20751 : NameStr(att->attname),
20752 : RelationGetRelationName(partition)));
20753 : }
20754 92 : }
20755 :
20756 : /*
20757 : * Return an OID list of constraints that reference the given relation
20758 : * that are marked as having a parent constraints.
20759 : */
20760 : static List *
20761 1162 : GetParentedForeignKeyRefs(Relation partition)
20762 : {
20763 : Relation pg_constraint;
20764 : HeapTuple tuple;
20765 : SysScanDesc scan;
20766 : ScanKeyData key[2];
20767 1162 : List *constraints = NIL;
20768 :
20769 : /*
20770 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
20771 : * scan.
20772 : */
20773 1644 : if (RelationGetIndexList(partition) == NIL ||
20774 482 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
20775 : INDEX_ATTR_BITMAP_KEY)))
20776 926 : return NIL;
20777 :
20778 : /* Search for constraints referencing this table */
20779 236 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20780 236 : ScanKeyInit(&key[0],
20781 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20782 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20783 236 : ScanKeyInit(&key[1],
20784 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
20785 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20786 :
20787 : /* XXX This is a seqscan, as we don't have a usable index */
20788 236 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20789 366 : while ((tuple = systable_getnext(scan)) != NULL)
20790 : {
20791 130 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20792 :
20793 : /*
20794 : * We only need to process constraints that are part of larger ones.
20795 : */
20796 130 : if (!OidIsValid(constrForm->conparentid))
20797 0 : continue;
20798 :
20799 130 : constraints = lappend_oid(constraints, constrForm->oid);
20800 : }
20801 :
20802 236 : systable_endscan(scan);
20803 236 : table_close(pg_constraint, AccessShareLock);
20804 :
20805 236 : return constraints;
20806 : }
20807 :
20808 : /*
20809 : * During DETACH PARTITION, verify that any foreign keys pointing to the
20810 : * partitioned table would not become invalid. An error is raised if any
20811 : * referenced values exist.
20812 : */
20813 : static void
20814 472 : ATDetachCheckNoForeignKeyRefs(Relation partition)
20815 : {
20816 : List *constraints;
20817 : ListCell *cell;
20818 :
20819 472 : constraints = GetParentedForeignKeyRefs(partition);
20820 :
20821 524 : foreach(cell, constraints)
20822 : {
20823 86 : Oid constrOid = lfirst_oid(cell);
20824 : HeapTuple tuple;
20825 : Form_pg_constraint constrForm;
20826 : Relation rel;
20827 86 : Trigger trig = {0};
20828 :
20829 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20830 86 : if (!HeapTupleIsValid(tuple))
20831 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20832 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20833 :
20834 : Assert(OidIsValid(constrForm->conparentid));
20835 : Assert(constrForm->confrelid == RelationGetRelid(partition));
20836 :
20837 : /* prevent data changes into the referencing table until commit */
20838 86 : rel = table_open(constrForm->conrelid, ShareLock);
20839 :
20840 86 : trig.tgoid = InvalidOid;
20841 86 : trig.tgname = NameStr(constrForm->conname);
20842 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20843 86 : trig.tgisinternal = true;
20844 86 : trig.tgconstrrelid = RelationGetRelid(partition);
20845 86 : trig.tgconstrindid = constrForm->conindid;
20846 86 : trig.tgconstraint = constrForm->oid;
20847 86 : trig.tgdeferrable = false;
20848 86 : trig.tginitdeferred = false;
20849 : /* we needn't fill in remaining fields */
20850 :
20851 86 : RI_PartitionRemove_Check(&trig, rel, partition);
20852 :
20853 52 : ReleaseSysCache(tuple);
20854 :
20855 52 : table_close(rel, NoLock);
20856 : }
20857 438 : }
20858 :
20859 : /*
20860 : * resolve column compression specification to compression method.
20861 : */
20862 : static char
20863 217540 : GetAttributeCompression(Oid atttypid, const char *compression)
20864 : {
20865 : char cmethod;
20866 :
20867 217540 : if (compression == NULL || strcmp(compression, "default") == 0)
20868 217398 : return InvalidCompressionMethod;
20869 :
20870 : /*
20871 : * To specify a nondefault method, the column data type must be toastable.
20872 : * Note this says nothing about whether the column's attstorage setting
20873 : * permits compression; we intentionally allow attstorage and
20874 : * attcompression to be independent. But with a non-toastable type,
20875 : * attstorage could not be set to a value that would permit compression.
20876 : *
20877 : * We don't actually need to enforce this, since nothing bad would happen
20878 : * if attcompression were non-default; it would never be consulted. But
20879 : * it seems more user-friendly to complain about a certainly-useless
20880 : * attempt to set the property.
20881 : */
20882 142 : if (!TypeIsToastable(atttypid))
20883 6 : ereport(ERROR,
20884 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20885 : errmsg("column data type %s does not support compression",
20886 : format_type_be(atttypid))));
20887 :
20888 136 : cmethod = CompressionNameToMethod(compression);
20889 136 : if (!CompressionMethodIsValid(cmethod))
20890 12 : ereport(ERROR,
20891 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20892 : errmsg("invalid compression method \"%s\"", compression)));
20893 :
20894 124 : return cmethod;
20895 : }
20896 :
20897 : /*
20898 : * resolve column storage specification
20899 : */
20900 : static char
20901 242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
20902 : {
20903 242 : char cstorage = 0;
20904 :
20905 242 : if (pg_strcasecmp(storagemode, "plain") == 0)
20906 50 : cstorage = TYPSTORAGE_PLAIN;
20907 192 : else if (pg_strcasecmp(storagemode, "external") == 0)
20908 156 : cstorage = TYPSTORAGE_EXTERNAL;
20909 36 : else if (pg_strcasecmp(storagemode, "extended") == 0)
20910 16 : cstorage = TYPSTORAGE_EXTENDED;
20911 20 : else if (pg_strcasecmp(storagemode, "main") == 0)
20912 14 : cstorage = TYPSTORAGE_MAIN;
20913 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
20914 6 : cstorage = get_typstorage(atttypid);
20915 : else
20916 0 : ereport(ERROR,
20917 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20918 : errmsg("invalid storage type \"%s\"",
20919 : storagemode)));
20920 :
20921 : /*
20922 : * safety check: do not allow toasted storage modes unless column datatype
20923 : * is TOAST-aware.
20924 : */
20925 242 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20926 6 : ereport(ERROR,
20927 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20928 : errmsg("column data type %s can only have storage PLAIN",
20929 : format_type_be(atttypid))));
20930 :
20931 236 : return cstorage;
20932 : }
20933 :
20934 : /*
20935 : * Struct with context of new partition for inserting rows from split partition
20936 : */
20937 : typedef struct SplitPartitionContext
20938 : {
20939 : ExprState *partqualstate; /* expression for checking slot for partition
20940 : * (NULL for DEFAULT partition) */
20941 : BulkInsertState bistate; /* state of bulk inserts for partition */
20942 : TupleTableSlot *dstslot; /* slot for inserting row into partition */
20943 : Relation partRel; /* relation for partition */
20944 : } SplitPartitionContext;
20945 :
20946 :
20947 : /*
20948 : * createSplitPartitionContext: create context for partition and fill it
20949 : */
20950 : static SplitPartitionContext *
20951 402 : createSplitPartitionContext(Relation partRel)
20952 : {
20953 : SplitPartitionContext *pc;
20954 :
20955 402 : pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
20956 402 : pc->partRel = partRel;
20957 :
20958 : /*
20959 : * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20960 : * don't bother using it.
20961 : */
20962 402 : pc->bistate = GetBulkInsertState();
20963 :
20964 : /* Create tuple slot for new partition. */
20965 402 : pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
20966 : table_slot_callbacks(pc->partRel));
20967 402 : ExecStoreAllNullTuple(pc->dstslot);
20968 :
20969 402 : return pc;
20970 : }
20971 :
20972 : /*
20973 : * deleteSplitPartitionContext: delete context for partition
20974 : */
20975 : static void
20976 402 : deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
20977 : {
20978 402 : ExecDropSingleTupleTableSlot(pc->dstslot);
20979 402 : FreeBulkInsertState(pc->bistate);
20980 :
20981 402 : table_finish_bulk_insert(pc->partRel, ti_options);
20982 :
20983 402 : pfree(pc);
20984 402 : }
20985 :
20986 : /*
20987 : * moveSplitTableRows: scan split partition (splitRel) of partitioned table
20988 : * (rel) and move rows into new partitions.
20989 : *
20990 : * New partitions description:
20991 : * partlist: list of pointers to SinglePartitionSpec structures.
20992 : * newPartRels: list of Relations.
20993 : * defaultPartOid: oid of DEFAULT partition, for table rel.
20994 : */
20995 : static void
20996 114 : moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
20997 : {
20998 : /* The FSM is empty, so don't bother using it. */
20999 114 : int ti_options = TABLE_INSERT_SKIP_FSM;
21000 : CommandId mycid;
21001 : EState *estate;
21002 : ListCell *listptr,
21003 : *listptr2;
21004 : TupleTableSlot *srcslot;
21005 : ExprContext *econtext;
21006 : TableScanDesc scan;
21007 : Snapshot snapshot;
21008 : MemoryContext oldCxt;
21009 114 : List *partContexts = NIL;
21010 : TupleConversionMap *tuple_map;
21011 114 : SplitPartitionContext *defaultPartCtx = NULL,
21012 : *pc;
21013 114 : bool isOldDefaultPart = false;
21014 :
21015 114 : mycid = GetCurrentCommandId(true);
21016 :
21017 114 : estate = CreateExecutorState();
21018 :
21019 468 : forboth(listptr, partlist, listptr2, newPartRels)
21020 : {
21021 354 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21022 :
21023 354 : pc = createSplitPartitionContext((Relation) lfirst(listptr2));
21024 :
21025 354 : if (sps->bound->is_default)
21026 : {
21027 : /* We should not create constraint for detached DEFAULT partition. */
21028 30 : defaultPartCtx = pc;
21029 : }
21030 : else
21031 : {
21032 : List *partConstraint;
21033 :
21034 : /* Build expression execution states for partition check quals. */
21035 324 : partConstraint = get_qual_from_partbound(rel, sps->bound);
21036 : partConstraint =
21037 324 : (List *) eval_const_expressions(NULL,
21038 : (Node *) partConstraint);
21039 : /* Make boolean expression for ExecCheck(). */
21040 324 : partConstraint = list_make1(make_ands_explicit(partConstraint));
21041 :
21042 : /*
21043 : * Map the vars in the constraint expression from rel's attnos to
21044 : * splitRel's.
21045 : */
21046 324 : partConstraint = map_partition_varattnos(partConstraint,
21047 : 1, splitRel, rel);
21048 :
21049 324 : pc->partqualstate =
21050 324 : ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
21051 : Assert(pc->partqualstate != NULL);
21052 : }
21053 :
21054 : /* Store partition context into list. */
21055 354 : partContexts = lappend(partContexts, pc);
21056 : }
21057 :
21058 : /*
21059 : * Create partition context for DEFAULT partition. We can insert values
21060 : * into this partition in case spaces with values between new partitions.
21061 : */
21062 114 : if (!defaultPartCtx && OidIsValid(defaultPartOid))
21063 : {
21064 : /* Indicate that we allocate context for old DEFAULT partition */
21065 48 : isOldDefaultPart = true;
21066 48 : defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
21067 : }
21068 :
21069 114 : econtext = GetPerTupleExprContext(estate);
21070 :
21071 : /* Create necessary tuple slot. */
21072 114 : srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
21073 : table_slot_callbacks(splitRel));
21074 :
21075 : /*
21076 : * Map computing for moving attributes of split partition to new partition
21077 : * (for first new partition, but other new partitions can use the same
21078 : * map).
21079 : */
21080 114 : pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
21081 114 : tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
21082 114 : RelationGetDescr(pc->partRel));
21083 :
21084 : /* Scan through the rows. */
21085 114 : snapshot = RegisterSnapshot(GetLatestSnapshot());
21086 114 : scan = table_beginscan(splitRel, snapshot, 0, NULL);
21087 :
21088 : /*
21089 : * Switch to per-tuple memory context and reset it for each tuple
21090 : * produced, so we don't leak memory.
21091 : */
21092 114 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
21093 :
21094 654 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21095 : {
21096 540 : bool found = false;
21097 : TupleTableSlot *insertslot;
21098 :
21099 : /* Extract data from old tuple. */
21100 540 : slot_getallattrs(srcslot);
21101 :
21102 540 : econtext->ecxt_scantuple = srcslot;
21103 :
21104 : /* Search partition for current slot srcslot. */
21105 1488 : foreach(listptr, partContexts)
21106 : {
21107 1374 : pc = (SplitPartitionContext *) lfirst(listptr);
21108 :
21109 2640 : if (pc->partqualstate /* skip DEFAULT partition */ &&
21110 1266 : ExecCheck(pc->partqualstate, econtext))
21111 : {
21112 426 : found = true;
21113 426 : break;
21114 : }
21115 948 : ResetExprContext(econtext);
21116 : }
21117 540 : if (!found)
21118 : {
21119 : /* Use DEFAULT partition if it exists. */
21120 114 : if (defaultPartCtx)
21121 114 : pc = defaultPartCtx;
21122 : else
21123 0 : ereport(ERROR,
21124 : (errcode(ERRCODE_CHECK_VIOLATION),
21125 : errmsg("can not find partition for split partition row"),
21126 : errtable(splitRel)));
21127 : }
21128 :
21129 540 : if (tuple_map)
21130 : {
21131 : /* Need to use map to copy attributes. */
21132 24 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
21133 : }
21134 : else
21135 : {
21136 : /* Copy attributes directly. */
21137 516 : insertslot = pc->dstslot;
21138 :
21139 516 : ExecClearTuple(insertslot);
21140 :
21141 516 : memcpy(insertslot->tts_values, srcslot->tts_values,
21142 516 : sizeof(Datum) * srcslot->tts_nvalid);
21143 516 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21144 516 : sizeof(bool) * srcslot->tts_nvalid);
21145 :
21146 516 : ExecStoreVirtualTuple(insertslot);
21147 : }
21148 :
21149 : /* Write the tuple out to the new relation. */
21150 540 : table_tuple_insert(pc->partRel, insertslot, mycid,
21151 : ti_options, pc->bistate);
21152 :
21153 540 : ResetExprContext(econtext);
21154 :
21155 540 : CHECK_FOR_INTERRUPTS();
21156 : }
21157 :
21158 114 : MemoryContextSwitchTo(oldCxt);
21159 :
21160 114 : table_endscan(scan);
21161 114 : UnregisterSnapshot(snapshot);
21162 :
21163 114 : if (tuple_map)
21164 6 : free_conversion_map(tuple_map);
21165 :
21166 114 : ExecDropSingleTupleTableSlot(srcslot);
21167 :
21168 114 : FreeExecutorState(estate);
21169 :
21170 468 : foreach(listptr, partContexts)
21171 354 : deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
21172 :
21173 : /* Need to close table and free buffers for DEFAULT partition. */
21174 114 : if (isOldDefaultPart)
21175 : {
21176 48 : Relation defaultPartRel = defaultPartCtx->partRel;
21177 :
21178 48 : deleteSplitPartitionContext(defaultPartCtx, ti_options);
21179 : /* Keep the lock until commit. */
21180 48 : table_close(defaultPartRel, NoLock);
21181 : }
21182 114 : }
21183 :
21184 : /*
21185 : * createPartitionTable: create table for a new partition with given name
21186 : * (newPartName) like table (modelRelName)
21187 : *
21188 : * Emulates command: CREATE TABLE <newPartName> (LIKE <modelRelName>
21189 : * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
21190 : */
21191 : static void
21192 420 : createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName,
21193 : AlterTableUtilityContext *context)
21194 : {
21195 : CreateStmt *createStmt;
21196 : TableLikeClause *tlc;
21197 : PlannedStmt *wrapper;
21198 :
21199 420 : createStmt = makeNode(CreateStmt);
21200 420 : createStmt->relation = newPartName;
21201 420 : createStmt->tableElts = NIL;
21202 420 : createStmt->inhRelations = NIL;
21203 420 : createStmt->constraints = NIL;
21204 420 : createStmt->options = NIL;
21205 420 : createStmt->oncommit = ONCOMMIT_NOOP;
21206 420 : createStmt->tablespacename = NULL;
21207 420 : createStmt->if_not_exists = false;
21208 :
21209 420 : tlc = makeNode(TableLikeClause);
21210 420 : tlc->relation = modelRelName;
21211 :
21212 : /*
21213 : * Indexes will be inherited on "attach new partitions" stage, after data
21214 : * moving.
21215 : */
21216 420 : tlc->options = CREATE_TABLE_LIKE_ALL & ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY);
21217 420 : tlc->relationOid = InvalidOid;
21218 420 : createStmt->tableElts = lappend(createStmt->tableElts, tlc);
21219 :
21220 : /* Need to make a wrapper PlannedStmt. */
21221 420 : wrapper = makeNode(PlannedStmt);
21222 420 : wrapper->commandType = CMD_UTILITY;
21223 420 : wrapper->canSetTag = false;
21224 420 : wrapper->utilityStmt = (Node *) createStmt;
21225 420 : wrapper->stmt_location = context->pstmt->stmt_location;
21226 420 : wrapper->stmt_len = context->pstmt->stmt_len;
21227 :
21228 420 : ProcessUtility(wrapper,
21229 : context->queryString,
21230 : false,
21231 : PROCESS_UTILITY_SUBCOMMAND,
21232 : NULL,
21233 : NULL,
21234 : None_Receiver,
21235 : NULL);
21236 420 : }
21237 :
21238 : /*
21239 : * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
21240 : */
21241 : static void
21242 120 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
21243 : PartitionCmd *cmd, AlterTableUtilityContext *context)
21244 : {
21245 : Relation splitRel;
21246 : Oid splitRelOid;
21247 : char relname[NAMEDATALEN];
21248 : Oid namespaceId;
21249 : ListCell *listptr,
21250 : *listptr2;
21251 120 : bool isSameName = false;
21252 : char tmpRelName[NAMEDATALEN];
21253 120 : List *newPartRels = NIL;
21254 : ObjectAddress object;
21255 : RangeVar *parentName;
21256 : Oid defaultPartOid;
21257 :
21258 120 : defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21259 :
21260 : /*
21261 : * We are going to detach and remove this partition: need to use exclusive
21262 : * lock for preventing DML-queries to the partition.
21263 : */
21264 120 : splitRel = table_openrv(cmd->name, AccessExclusiveLock);
21265 :
21266 120 : splitRelOid = RelationGetRelid(splitRel);
21267 :
21268 : /* Check descriptions of new partitions. */
21269 474 : foreach(listptr, cmd->partlist)
21270 : {
21271 : Oid existing_relid;
21272 360 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21273 :
21274 360 : strlcpy(relname, sps->name->relname, NAMEDATALEN);
21275 :
21276 : /*
21277 : * Look up the namespace in which we are supposed to create the
21278 : * partition, check we have permission to create there, lock it
21279 : * against concurrent drop, and mark stmt->relation as
21280 : * RELPERSISTENCE_TEMP if a temporary namespace is selected.
21281 : */
21282 : namespaceId =
21283 360 : RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
21284 :
21285 : /*
21286 : * This would fail later on anyway if the relation already exists. But
21287 : * by catching it here we can emit a nicer error message.
21288 : */
21289 360 : existing_relid = get_relname_relid(relname, namespaceId);
21290 360 : if (existing_relid == splitRelOid && !isSameName)
21291 : /* One new partition can have the same name as split partition. */
21292 24 : isSameName = true;
21293 336 : else if (existing_relid != InvalidOid)
21294 6 : ereport(ERROR,
21295 : (errcode(ERRCODE_DUPLICATE_TABLE),
21296 : errmsg("relation \"%s\" already exists", relname)));
21297 : }
21298 :
21299 : /* Detach split partition. */
21300 114 : RemoveInheritance(splitRel, rel, false);
21301 : /* Do the final part of detaching. */
21302 114 : DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
21303 :
21304 : /*
21305 : * If new partition has the same name as split partition then we should
21306 : * rename split partition for reusing name.
21307 : */
21308 114 : if (isSameName)
21309 : {
21310 : /*
21311 : * We must bump the command counter to make the split partition tuple
21312 : * visible for renaming.
21313 : */
21314 24 : CommandCounterIncrement();
21315 : /* Rename partition. */
21316 24 : sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21317 24 : RenameRelationInternal(splitRelOid, tmpRelName, false, false);
21318 :
21319 : /*
21320 : * We must bump the command counter to make the split partition tuple
21321 : * visible after renaming.
21322 : */
21323 24 : CommandCounterIncrement();
21324 : }
21325 :
21326 : /* Create new partitions (like split partition), without indexes. */
21327 114 : parentName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
21328 114 : RelationGetRelationName(rel), -1);
21329 468 : foreach(listptr, cmd->partlist)
21330 : {
21331 354 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21332 : Relation newPartRel;
21333 :
21334 354 : createPartitionTable(sps->name, parentName, context);
21335 :
21336 : /* Open the new partition and acquire exclusive lock on it. */
21337 354 : newPartRel = table_openrv(sps->name, AccessExclusiveLock);
21338 :
21339 354 : newPartRels = lappend(newPartRels, newPartRel);
21340 : }
21341 :
21342 : /* Copy data from split partition to new partitions. */
21343 114 : moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
21344 : /* Keep the lock until commit. */
21345 114 : table_close(splitRel, NoLock);
21346 :
21347 : /* Attach new partitions to partitioned table. */
21348 468 : forboth(listptr, cmd->partlist, listptr2, newPartRels)
21349 : {
21350 354 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21351 354 : Relation newPartRel = (Relation) lfirst(listptr2);
21352 :
21353 : /*
21354 : * wqueue = NULL: verification for each cloned constraint is not
21355 : * needed.
21356 : */
21357 354 : attachPartitionTable(NULL, rel, newPartRel, sps->bound);
21358 : /* Keep the lock until commit. */
21359 354 : table_close(newPartRel, NoLock);
21360 : }
21361 :
21362 : /* Drop split partition. */
21363 114 : object.classId = RelationRelationId;
21364 114 : object.objectId = splitRelOid;
21365 114 : object.objectSubId = 0;
21366 : /* Probably DROP_CASCADE is not needed. */
21367 114 : performDeletion(&object, DROP_RESTRICT, 0);
21368 114 : }
21369 :
21370 : /*
21371 : * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
21372 : * of the partitioned table (rel) and move rows into the new partition
21373 : * (newPartRel).
21374 : */
21375 : static void
21376 66 : moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
21377 : Relation newPartRel)
21378 : {
21379 : CommandId mycid;
21380 :
21381 : /* The FSM is empty, so don't bother using it. */
21382 66 : int ti_options = TABLE_INSERT_SKIP_FSM;
21383 : ListCell *listptr;
21384 : BulkInsertState bistate; /* state of bulk inserts for partition */
21385 : TupleTableSlot *dstslot;
21386 :
21387 66 : mycid = GetCurrentCommandId(true);
21388 :
21389 : /* Prepare a BulkInsertState for table_tuple_insert. */
21390 66 : bistate = GetBulkInsertState();
21391 :
21392 : /* Create necessary tuple slot. */
21393 66 : dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
21394 : table_slot_callbacks(newPartRel));
21395 66 : ExecStoreAllNullTuple(dstslot);
21396 :
21397 240 : foreach(listptr, mergingPartitionsList)
21398 : {
21399 174 : Relation mergingPartition = (Relation) lfirst(listptr);
21400 : TupleTableSlot *srcslot;
21401 : TupleConversionMap *tuple_map;
21402 : TableScanDesc scan;
21403 : Snapshot snapshot;
21404 :
21405 : /* Create tuple slot for new partition. */
21406 174 : srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
21407 : table_slot_callbacks(mergingPartition));
21408 :
21409 : /*
21410 : * Map computing for moving attributes of merged partition to new
21411 : * partition.
21412 : */
21413 174 : tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
21414 : RelationGetDescr(newPartRel));
21415 :
21416 : /* Scan through the rows. */
21417 174 : snapshot = RegisterSnapshot(GetLatestSnapshot());
21418 174 : scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
21419 :
21420 480 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21421 : {
21422 : TupleTableSlot *insertslot;
21423 :
21424 : /* Extract data from old tuple. */
21425 306 : slot_getallattrs(srcslot);
21426 :
21427 306 : if (tuple_map)
21428 : {
21429 : /* Need to use map to copy attributes. */
21430 30 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
21431 : }
21432 : else
21433 : {
21434 : /* Copy attributes directly. */
21435 276 : insertslot = dstslot;
21436 :
21437 276 : ExecClearTuple(insertslot);
21438 :
21439 276 : memcpy(insertslot->tts_values, srcslot->tts_values,
21440 276 : sizeof(Datum) * srcslot->tts_nvalid);
21441 276 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21442 276 : sizeof(bool) * srcslot->tts_nvalid);
21443 :
21444 276 : ExecStoreVirtualTuple(insertslot);
21445 : }
21446 :
21447 : /* Write the tuple out to the new relation. */
21448 306 : table_tuple_insert(newPartRel, insertslot, mycid,
21449 : ti_options, bistate);
21450 :
21451 306 : CHECK_FOR_INTERRUPTS();
21452 : }
21453 :
21454 174 : table_endscan(scan);
21455 174 : UnregisterSnapshot(snapshot);
21456 :
21457 174 : if (tuple_map)
21458 18 : free_conversion_map(tuple_map);
21459 :
21460 174 : ExecDropSingleTupleTableSlot(srcslot);
21461 : }
21462 :
21463 66 : ExecDropSingleTupleTableSlot(dstslot);
21464 66 : FreeBulkInsertState(bistate);
21465 :
21466 66 : table_finish_bulk_insert(newPartRel, ti_options);
21467 66 : }
21468 :
21469 : /*
21470 : * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
21471 : */
21472 : static void
21473 66 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
21474 : PartitionCmd *cmd, AlterTableUtilityContext *context)
21475 : {
21476 : Relation newPartRel;
21477 : ListCell *listptr;
21478 66 : List *mergingPartitionsList = NIL;
21479 : Oid defaultPartOid;
21480 : char tmpRelName[NAMEDATALEN];
21481 66 : RangeVar *mergePartName = cmd->name;
21482 66 : bool isSameName = false;
21483 :
21484 : /*
21485 : * Lock all merged partitions, check them and create list with partitions
21486 : * contexts.
21487 : */
21488 240 : foreach(listptr, cmd->partlist)
21489 : {
21490 174 : RangeVar *name = (RangeVar *) lfirst(listptr);
21491 : Relation mergingPartition;
21492 :
21493 : /*
21494 : * We are going to detach and remove this partition: need to use
21495 : * exclusive lock for preventing DML-queries to the partition.
21496 : */
21497 174 : mergingPartition = table_openrv(name, AccessExclusiveLock);
21498 :
21499 : /*
21500 : * Checking that two partitions have the same name was before, in
21501 : * function transformPartitionCmdForMerge().
21502 : */
21503 174 : if (equal(name, cmd->name))
21504 : /* One new partition can have the same name as merged partition. */
21505 6 : isSameName = true;
21506 :
21507 : /* Store a next merging partition into the list. */
21508 174 : mergingPartitionsList = lappend(mergingPartitionsList,
21509 : mergingPartition);
21510 : }
21511 :
21512 : /* Detach all merged partitions. */
21513 : defaultPartOid =
21514 66 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21515 240 : foreach(listptr, mergingPartitionsList)
21516 : {
21517 174 : Relation mergingPartition = (Relation) lfirst(listptr);
21518 :
21519 : /* Remove the pg_inherits row first. */
21520 174 : RemoveInheritance(mergingPartition, rel, false);
21521 : /* Do the final part of detaching. */
21522 174 : DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
21523 : }
21524 :
21525 : /* Create table for new partition, use partitioned table as model. */
21526 66 : if (isSameName)
21527 : {
21528 : /* Create partition table with generated temporary name. */
21529 6 : sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21530 6 : mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
21531 : tmpRelName, -1);
21532 : }
21533 66 : createPartitionTable(mergePartName,
21534 66 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
21535 66 : RelationGetRelationName(rel), -1),
21536 : context);
21537 :
21538 : /*
21539 : * Open the new partition and acquire exclusive lock on it. This will
21540 : * stop all the operations with partitioned table. This might seem
21541 : * excessive, but this is the way we make sure nobody is planning queries
21542 : * involving merging partitions.
21543 : */
21544 66 : newPartRel = table_openrv(mergePartName, AccessExclusiveLock);
21545 :
21546 : /* Copy data from merged partitions to new partition. */
21547 66 : moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
21548 :
21549 : /*
21550 : * Attach a new partition to the partitioned table. wqueue = NULL:
21551 : * verification for each cloned constraint is not need.
21552 : */
21553 66 : attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
21554 :
21555 : /* Unlock and drop merged partitions. */
21556 240 : foreach(listptr, mergingPartitionsList)
21557 : {
21558 : ObjectAddress object;
21559 174 : Relation mergingPartition = (Relation) lfirst(listptr);
21560 :
21561 : /* Get relation id before table_close() call. */
21562 174 : object.objectId = RelationGetRelid(mergingPartition);
21563 174 : object.classId = RelationRelationId;
21564 174 : object.objectSubId = 0;
21565 :
21566 : /* Keep the lock until commit. */
21567 174 : table_close(mergingPartition, NoLock);
21568 :
21569 174 : performDeletion(&object, DROP_RESTRICT, 0);
21570 : }
21571 66 : list_free(mergingPartitionsList);
21572 :
21573 : /* Rename new partition if it is needed. */
21574 66 : if (isSameName)
21575 : {
21576 : /*
21577 : * We must bump the command counter to make the new partition tuple
21578 : * visible for rename.
21579 : */
21580 6 : CommandCounterIncrement();
21581 : /* Rename partition. */
21582 6 : RenameRelationInternal(RelationGetRelid(newPartRel),
21583 6 : cmd->name->relname, false, false);
21584 : }
21585 : /* Keep the lock until commit. */
21586 66 : table_close(newPartRel, NoLock);
21587 66 : }
|