Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * tablecmds.c
4 : * Commands for creating and altering table structures and settings
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/commands/tablecmds.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/attmap.h"
18 : #include "access/genam.h"
19 : #include "access/gist.h"
20 : #include "access/heapam.h"
21 : #include "access/heapam_xlog.h"
22 : #include "access/multixact.h"
23 : #include "access/reloptions.h"
24 : #include "access/relscan.h"
25 : #include "access/sysattr.h"
26 : #include "access/tableam.h"
27 : #include "access/toast_compression.h"
28 : #include "access/xact.h"
29 : #include "access/xlog.h"
30 : #include "access/xloginsert.h"
31 : #include "catalog/catalog.h"
32 : #include "catalog/heap.h"
33 : #include "catalog/index.h"
34 : #include "catalog/namespace.h"
35 : #include "catalog/objectaccess.h"
36 : #include "catalog/partition.h"
37 : #include "catalog/pg_am.h"
38 : #include "catalog/pg_attrdef.h"
39 : #include "catalog/pg_collation.h"
40 : #include "catalog/pg_constraint.h"
41 : #include "catalog/pg_depend.h"
42 : #include "catalog/pg_foreign_table.h"
43 : #include "catalog/pg_inherits.h"
44 : #include "catalog/pg_largeobject.h"
45 : #include "catalog/pg_namespace.h"
46 : #include "catalog/pg_opclass.h"
47 : #include "catalog/pg_policy.h"
48 : #include "catalog/pg_proc.h"
49 : #include "catalog/pg_publication_rel.h"
50 : #include "catalog/pg_rewrite.h"
51 : #include "catalog/pg_statistic_ext.h"
52 : #include "catalog/pg_tablespace.h"
53 : #include "catalog/pg_trigger.h"
54 : #include "catalog/pg_type.h"
55 : #include "catalog/storage.h"
56 : #include "catalog/storage_xlog.h"
57 : #include "catalog/toasting.h"
58 : #include "commands/cluster.h"
59 : #include "commands/comment.h"
60 : #include "commands/defrem.h"
61 : #include "commands/event_trigger.h"
62 : #include "commands/sequence.h"
63 : #include "commands/tablecmds.h"
64 : #include "commands/tablespace.h"
65 : #include "commands/trigger.h"
66 : #include "commands/typecmds.h"
67 : #include "commands/user.h"
68 : #include "commands/vacuum.h"
69 : #include "common/int.h"
70 : #include "executor/executor.h"
71 : #include "foreign/fdwapi.h"
72 : #include "foreign/foreign.h"
73 : #include "miscadmin.h"
74 : #include "nodes/makefuncs.h"
75 : #include "nodes/nodeFuncs.h"
76 : #include "nodes/parsenodes.h"
77 : #include "optimizer/optimizer.h"
78 : #include "parser/parse_coerce.h"
79 : #include "parser/parse_collate.h"
80 : #include "parser/parse_expr.h"
81 : #include "parser/parse_relation.h"
82 : #include "parser/parse_type.h"
83 : #include "parser/parse_utilcmd.h"
84 : #include "parser/parser.h"
85 : #include "partitioning/partbounds.h"
86 : #include "partitioning/partdesc.h"
87 : #include "pgstat.h"
88 : #include "rewrite/rewriteDefine.h"
89 : #include "rewrite/rewriteHandler.h"
90 : #include "rewrite/rewriteManip.h"
91 : #include "storage/bufmgr.h"
92 : #include "storage/lmgr.h"
93 : #include "storage/lock.h"
94 : #include "storage/predicate.h"
95 : #include "storage/smgr.h"
96 : #include "tcop/utility.h"
97 : #include "utils/acl.h"
98 : #include "utils/builtins.h"
99 : #include "utils/fmgroids.h"
100 : #include "utils/inval.h"
101 : #include "utils/lsyscache.h"
102 : #include "utils/memutils.h"
103 : #include "utils/partcache.h"
104 : #include "utils/relcache.h"
105 : #include "utils/ruleutils.h"
106 : #include "utils/snapmgr.h"
107 : #include "utils/syscache.h"
108 : #include "utils/timestamp.h"
109 : #include "utils/typcache.h"
110 : #include "utils/usercontext.h"
111 :
112 : /*
113 : * ON COMMIT action list
114 : */
115 : typedef struct OnCommitItem
116 : {
117 : Oid relid; /* relid of relation */
118 : OnCommitAction oncommit; /* what to do at end of xact */
119 :
120 : /*
121 : * If this entry was created during the current transaction,
122 : * creating_subid is the ID of the creating subxact; if created in a prior
123 : * transaction, creating_subid is zero. If deleted during the current
124 : * transaction, deleting_subid is the ID of the deleting subxact; if no
125 : * deletion request is pending, deleting_subid is zero.
126 : */
127 : SubTransactionId creating_subid;
128 : SubTransactionId deleting_subid;
129 : } OnCommitItem;
130 :
131 : static List *on_commits = NIL;
132 :
133 :
134 : /*
135 : * State information for ALTER TABLE
136 : *
137 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 : * structs, one for each table modified by the operation (the named table
139 : * plus any child tables that are affected). We save lists of subcommands
140 : * to apply to this table (possibly modified by parse transformation steps);
141 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 : * necessary information is stored in the constraints and newvals lists.
143 : *
144 : * Phase 2 is divided into multiple passes; subcommands are executed in
145 : * a pass determined by subcommand type.
146 : */
147 :
148 : typedef enum AlterTablePass
149 : {
150 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 : AT_PASS_DROP, /* DROP (all flavors) */
152 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 : AT_PASS_ADD_COL, /* ADD COLUMN */
154 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 : /* We could support a RENAME COLUMN pass here, but not currently used */
158 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 : AT_PASS_ADD_INDEX, /* ADD indexes */
162 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 : AT_PASS_MISC, /* other stuff */
164 : } AlterTablePass;
165 :
166 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
167 :
168 : typedef struct AlteredTableInfo
169 : {
170 : /* Information saved before any work commences: */
171 : Oid relid; /* Relation to work on */
172 : char relkind; /* Its relkind */
173 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174 :
175 : /*
176 : * Transiently set during Phase 2, normally set to NULL.
177 : *
178 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 : * returns control. This can be exploited by ATExecCmd subroutines to
180 : * close/reopen across transaction boundaries.
181 : */
182 : Relation rel;
183 :
184 : /* Information saved by Phase 1 for Phase 2: */
185 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 : /* Information saved by Phases 1/2 for Phase 3: */
187 : List *constraints; /* List of NewConstraint */
188 : List *newvals; /* List of NewColumnValue */
189 : List *afterStmts; /* List of utility command parsetrees */
190 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 : int rewrite; /* Reason for forced rewrite, if any */
192 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 : Oid newAccessMethod; /* new access method; 0 means no change,
194 : * if above is true */
195 : Oid newTableSpace; /* new tablespace; 0 means no change */
196 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 : char newrelpersistence; /* if above is true */
198 : Expr *partition_constraint; /* for attach partition validation */
199 : /* true, if validating default due to some other attach/detach */
200 : bool validate_default;
201 : /* Objects to rebuild after completing ALTER TYPE operations */
202 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 : List *changedConstraintDefs; /* string definitions of same */
204 : List *changedIndexOids; /* OIDs of indexes to rebuild */
205 : List *changedIndexDefs; /* string definitions of same */
206 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 : char *clusterOnIndex; /* index to use for CLUSTER */
208 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 : List *changedStatisticsDefs; /* string definitions of same */
210 : } AlteredTableInfo;
211 :
212 : /* Struct describing one new constraint to check in Phase 3 scan */
213 : /* Note: new not-null constraints are handled elsewhere */
214 : typedef struct NewConstraint
215 : {
216 : char *name; /* Constraint name, or NULL if none */
217 : ConstrType contype; /* CHECK or FOREIGN */
218 : Oid refrelid; /* PK rel, if FOREIGN */
219 : Oid refindid; /* OID of PK's index, if FOREIGN */
220 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 : ExprState *qualstate; /* Execution state for CHECK expr */
224 : } NewConstraint;
225 :
226 : /*
227 : * Struct describing one new column value that needs to be computed during
228 : * Phase 3 copy (this could be either a new column with a non-null default, or
229 : * a column that we're changing the type of). Columns without such an entry
230 : * are just copied from the old table during ATRewriteTable. Note that the
231 : * expr is an expression over *old* table values, except when is_generated
232 : * is true; then it is an expression over columns of the *new* tuple.
233 : */
234 : typedef struct NewColumnValue
235 : {
236 : AttrNumber attnum; /* which column */
237 : Expr *expr; /* expression to compute */
238 : ExprState *exprstate; /* execution state */
239 : bool is_generated; /* is it a GENERATED expression? */
240 : } NewColumnValue;
241 :
242 : /*
243 : * Error-reporting support for RemoveRelations
244 : */
245 : struct dropmsgstrings
246 : {
247 : char kind;
248 : int nonexistent_code;
249 : const char *nonexistent_msg;
250 : const char *skipping_msg;
251 : const char *nota_msg;
252 : const char *drophint_msg;
253 : };
254 :
255 : static const struct dropmsgstrings dropmsgstringarray[] = {
256 : {RELKIND_RELATION,
257 : ERRCODE_UNDEFINED_TABLE,
258 : gettext_noop("table \"%s\" does not exist"),
259 : gettext_noop("table \"%s\" does not exist, skipping"),
260 : gettext_noop("\"%s\" is not a table"),
261 : gettext_noop("Use DROP TABLE to remove a table.")},
262 : {RELKIND_SEQUENCE,
263 : ERRCODE_UNDEFINED_TABLE,
264 : gettext_noop("sequence \"%s\" does not exist"),
265 : gettext_noop("sequence \"%s\" does not exist, skipping"),
266 : gettext_noop("\"%s\" is not a sequence"),
267 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 : {RELKIND_VIEW,
269 : ERRCODE_UNDEFINED_TABLE,
270 : gettext_noop("view \"%s\" does not exist"),
271 : gettext_noop("view \"%s\" does not exist, skipping"),
272 : gettext_noop("\"%s\" is not a view"),
273 : gettext_noop("Use DROP VIEW to remove a view.")},
274 : {RELKIND_MATVIEW,
275 : ERRCODE_UNDEFINED_TABLE,
276 : gettext_noop("materialized view \"%s\" does not exist"),
277 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 : gettext_noop("\"%s\" is not a materialized view"),
279 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 : {RELKIND_INDEX,
281 : ERRCODE_UNDEFINED_OBJECT,
282 : gettext_noop("index \"%s\" does not exist"),
283 : gettext_noop("index \"%s\" does not exist, skipping"),
284 : gettext_noop("\"%s\" is not an index"),
285 : gettext_noop("Use DROP INDEX to remove an index.")},
286 : {RELKIND_COMPOSITE_TYPE,
287 : ERRCODE_UNDEFINED_OBJECT,
288 : gettext_noop("type \"%s\" does not exist"),
289 : gettext_noop("type \"%s\" does not exist, skipping"),
290 : gettext_noop("\"%s\" is not a type"),
291 : gettext_noop("Use DROP TYPE to remove a type.")},
292 : {RELKIND_FOREIGN_TABLE,
293 : ERRCODE_UNDEFINED_OBJECT,
294 : gettext_noop("foreign table \"%s\" does not exist"),
295 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 : gettext_noop("\"%s\" is not a foreign table"),
297 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 : {RELKIND_PARTITIONED_TABLE,
299 : ERRCODE_UNDEFINED_TABLE,
300 : gettext_noop("table \"%s\" does not exist"),
301 : gettext_noop("table \"%s\" does not exist, skipping"),
302 : gettext_noop("\"%s\" is not a table"),
303 : gettext_noop("Use DROP TABLE to remove a table.")},
304 : {RELKIND_PARTITIONED_INDEX,
305 : ERRCODE_UNDEFINED_OBJECT,
306 : gettext_noop("index \"%s\" does not exist"),
307 : gettext_noop("index \"%s\" does not exist, skipping"),
308 : gettext_noop("\"%s\" is not an index"),
309 : gettext_noop("Use DROP INDEX to remove an index.")},
310 : {'\0', 0, NULL, NULL, NULL, NULL}
311 : };
312 :
313 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 : struct DropRelationCallbackState
315 : {
316 : /* These fields are set by RemoveRelations: */
317 : char expected_relkind;
318 : LOCKMODE heap_lockmode;
319 : /* These fields are state to track which subsidiary locks are held: */
320 : Oid heapOid;
321 : Oid partParentOid;
322 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
323 : char actual_relkind;
324 : char actual_relpersistence;
325 : };
326 :
327 : /* Alter table target-type flags for ATSimplePermissions */
328 : #define ATT_TABLE 0x0001
329 : #define ATT_VIEW 0x0002
330 : #define ATT_MATVIEW 0x0004
331 : #define ATT_INDEX 0x0008
332 : #define ATT_COMPOSITE_TYPE 0x0010
333 : #define ATT_FOREIGN_TABLE 0x0020
334 : #define ATT_PARTITIONED_INDEX 0x0040
335 : #define ATT_SEQUENCE 0x0080
336 : #define ATT_PARTITIONED_TABLE 0x0100
337 :
338 : /*
339 : * ForeignTruncateInfo
340 : *
341 : * Information related to truncation of foreign tables. This is used for
342 : * the elements in a hash table. It uses the server OID as lookup key,
343 : * and includes a per-server list of all foreign tables involved in the
344 : * truncation.
345 : */
346 : typedef struct ForeignTruncateInfo
347 : {
348 : Oid serverid;
349 : List *rels;
350 : } ForeignTruncateInfo;
351 :
352 : /* Partial or complete FK creation in addFkConstraint() */
353 : typedef enum addFkConstraintSides
354 : {
355 : addFkReferencedSide,
356 : addFkReferencingSide,
357 : addFkBothSides,
358 : } addFkConstraintSides;
359 :
360 : /*
361 : * Partition tables are expected to be dropped when the parent partitioned
362 : * table gets dropped. Hence for partitioning we use AUTO dependency.
363 : * Otherwise, for regular inheritance use NORMAL dependency.
364 : */
365 : #define child_dependency_type(child_is_partition) \
366 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
367 :
368 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370 : static void truncate_check_activity(Relation rel);
371 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 : Oid relId, Oid oldRelId, void *arg);
373 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 : bool is_partition, List **supconstr,
375 : List **supnotnulls);
376 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
377 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381 : static void StoreCatalogInheritance(Oid relationId, List *supers,
382 : bool child_is_partition);
383 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 : int32 seqNumber, Relation inhRelation,
385 : bool child_is_partition);
386 : static int findAttrByName(const char *attributeName, const List *columns);
387 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 : LOCKMODE lockmode);
392 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
393 : bool recurse, bool recursing, LOCKMODE lockmode);
394 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
395 : Relation rel, HeapTuple contuple, List **otherrelids,
396 : LOCKMODE lockmode);
397 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
398 : Relation rel, char *constrName,
399 : bool recurse, bool recursing, LOCKMODE lockmode);
400 : static int transformColumnNameList(Oid relId, List *colList,
401 : int16 *attnums, Oid *atttypids, Oid *attcollids);
402 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
403 : List **attnamelist,
404 : int16 *attnums, Oid *atttypids, Oid *attcollids,
405 : Oid *opclasses, bool *pk_has_without_overlaps);
406 : static Oid transformFkeyCheckAttrs(Relation pkrel,
407 : int numattrs, int16 *attnums,
408 : bool with_period, Oid *opclasses,
409 : bool *pk_has_without_overlaps);
410 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
411 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
412 : Oid *funcid);
413 : static void validateForeignKeyConstraint(char *conname,
414 : Relation rel, Relation pkrel,
415 : Oid pkindOid, Oid constraintOid, bool hasperiod);
416 : static void CheckAlterTableIsSafe(Relation rel);
417 : static void ATController(AlterTableStmt *parsetree,
418 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
419 : AlterTableUtilityContext *context);
420 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
421 : bool recurse, bool recursing, LOCKMODE lockmode,
422 : AlterTableUtilityContext *context);
423 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
424 : AlterTableUtilityContext *context);
425 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
426 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
427 : AlterTableUtilityContext *context);
428 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
429 : Relation rel, AlterTableCmd *cmd,
430 : bool recurse, LOCKMODE lockmode,
431 : AlterTablePass cur_pass,
432 : AlterTableUtilityContext *context);
433 : static void ATRewriteTables(AlterTableStmt *parsetree,
434 : List **wqueue, LOCKMODE lockmode,
435 : AlterTableUtilityContext *context);
436 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
437 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
438 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
439 : static void ATSimpleRecursion(List **wqueue, Relation rel,
440 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
441 : AlterTableUtilityContext *context);
442 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
443 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
444 : LOCKMODE lockmode,
445 : AlterTableUtilityContext *context);
446 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
447 : DropBehavior behavior);
448 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
449 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
450 : AlterTableUtilityContext *context);
451 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
452 : Relation rel, AlterTableCmd **cmd,
453 : bool recurse, bool recursing,
454 : LOCKMODE lockmode, AlterTablePass cur_pass,
455 : AlterTableUtilityContext *context);
456 : static bool check_for_column_name_collision(Relation rel, const char *colname,
457 : bool if_not_exists);
458 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
459 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
460 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
461 : LOCKMODE lockmode);
462 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
463 : LOCKMODE lockmode);
464 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
465 : char *constrname, char *colName,
466 : bool recurse, bool recursing,
467 : LOCKMODE lockmode);
468 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
469 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
470 : List *testConstraint, List *provenConstraint);
471 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
472 : Node *newDefault, LOCKMODE lockmode);
473 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
474 : Node *newDefault);
475 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
476 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
477 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
478 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
479 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
480 : bool recurse, bool recursing);
481 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
482 : Node *newExpr, LOCKMODE lockmode);
483 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
484 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
485 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
486 : Node *newValue, LOCKMODE lockmode);
487 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
488 : Node *options, bool isReset, LOCKMODE lockmode);
489 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
490 : Node *newValue, LOCKMODE lockmode);
491 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
492 : AlterTableCmd *cmd, LOCKMODE lockmode,
493 : AlterTableUtilityContext *context);
494 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
495 : DropBehavior behavior,
496 : bool recurse, bool recursing,
497 : bool missing_ok, LOCKMODE lockmode,
498 : ObjectAddresses *addrs);
499 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
500 : bool recurse, LOCKMODE lockmode,
501 : AlterTableUtilityContext *context);
502 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
503 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
504 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
505 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
506 : static ObjectAddress ATExecAddConstraint(List **wqueue,
507 : AlteredTableInfo *tab, Relation rel,
508 : Constraint *newConstraint, bool recurse, bool is_readd,
509 : LOCKMODE lockmode);
510 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
511 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
512 : IndexStmt *stmt, LOCKMODE lockmode);
513 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
514 : AlteredTableInfo *tab, Relation rel,
515 : Constraint *constr,
516 : bool recurse, bool recursing, bool is_readd,
517 : LOCKMODE lockmode);
518 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
519 : Relation rel, Constraint *fkconstraint,
520 : bool recurse, bool recursing,
521 : LOCKMODE lockmode);
522 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
523 : int numfksetcols, const int16 *fksetcolsattnums,
524 : List *fksetcols);
525 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
526 : char *constraintname,
527 : Constraint *fkconstraint, Relation rel,
528 : Relation pkrel, Oid indexOid,
529 : Oid parentConstr,
530 : int numfks, int16 *pkattnum, int16 *fkattnum,
531 : Oid *pfeqoperators, Oid *ppeqoperators,
532 : Oid *ffeqoperators, int numfkdelsetcols,
533 : int16 *fkdelsetcols, bool is_internal,
534 : bool with_period);
535 : static void addFkRecurseReferenced(Constraint *fkconstraint,
536 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
537 : int numfks, int16 *pkattnum, int16 *fkattnum,
538 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
539 : int numfkdelsetcols, int16 *fkdelsetcols,
540 : bool old_check_ok,
541 : Oid parentDelTrigger, Oid parentUpdTrigger,
542 : bool with_period);
543 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
544 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
545 : int numfks, int16 *pkattnum, int16 *fkattnum,
546 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
547 : int numfkdelsetcols, int16 *fkdelsetcols,
548 : bool old_check_ok, LOCKMODE lockmode,
549 : Oid parentInsTrigger, Oid parentUpdTrigger,
550 : bool with_period);
551 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
552 : Relation partitionRel);
553 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
554 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
555 : Relation partRel);
556 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
557 : Constraint *fkconstraint, Oid constraintOid,
558 : Oid indexOid,
559 : Oid parentInsTrigger, Oid parentUpdTrigger,
560 : Oid *insertTrigOid, Oid *updateTrigOid);
561 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
562 : Constraint *fkconstraint, Oid constraintOid,
563 : Oid indexOid,
564 : Oid parentDelTrigger, Oid parentUpdTrigger,
565 : Oid *deleteTrigOid, Oid *updateTrigOid);
566 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
567 : Oid partRelid,
568 : Oid parentConstrOid, int numfks,
569 : AttrNumber *mapped_conkey, AttrNumber *confkey,
570 : Oid *conpfeqop,
571 : Oid parentInsTrigger,
572 : Oid parentUpdTrigger,
573 : Relation trigrel);
574 : static void GetForeignKeyActionTriggers(Relation trigrel,
575 : Oid conoid, Oid confrelid, Oid conrelid,
576 : Oid *deleteTriggerOid,
577 : Oid *updateTriggerOid);
578 : static void GetForeignKeyCheckTriggers(Relation trigrel,
579 : Oid conoid, Oid confrelid, Oid conrelid,
580 : Oid *insertTriggerOid,
581 : Oid *updateTriggerOid);
582 : static void ATExecDropConstraint(Relation rel, const char *constrName,
583 : DropBehavior behavior, bool recurse,
584 : bool missing_ok, LOCKMODE lockmode);
585 : static ObjectAddress dropconstraint_internal(Relation rel,
586 : HeapTuple constraintTup, DropBehavior behavior,
587 : bool recurse, bool recursing,
588 : bool missing_ok, LOCKMODE lockmode);
589 : static void ATPrepAlterColumnType(List **wqueue,
590 : AlteredTableInfo *tab, Relation rel,
591 : bool recurse, bool recursing,
592 : AlterTableCmd *cmd, LOCKMODE lockmode,
593 : AlterTableUtilityContext *context);
594 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
595 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
596 : AlterTableCmd *cmd, LOCKMODE lockmode);
597 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
598 : Relation rel, AttrNumber attnum, const char *colName);
599 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
600 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
601 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
602 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
603 : LOCKMODE lockmode);
604 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
605 : char *cmd, List **wqueue, LOCKMODE lockmode,
606 : bool rewrite);
607 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
608 : Oid objid, Relation rel, List *domname,
609 : const char *conname);
610 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
611 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
612 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
613 : List *options, LOCKMODE lockmode);
614 : static void change_owner_fix_column_acls(Oid relationOid,
615 : Oid oldOwnerId, Oid newOwnerId);
616 : static void change_owner_recurse_to_sequences(Oid relationOid,
617 : Oid newOwnerId, LOCKMODE lockmode);
618 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
619 : LOCKMODE lockmode);
620 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
621 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
622 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
623 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
624 : bool toLogged);
625 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
626 : const char *tablespacename, LOCKMODE lockmode);
627 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
628 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
629 : static void ATExecSetRelOptions(Relation rel, List *defList,
630 : AlterTableType operation,
631 : LOCKMODE lockmode);
632 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
633 : char fires_when, bool skip_system, bool recurse,
634 : LOCKMODE lockmode);
635 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
636 : char fires_when, LOCKMODE lockmode);
637 : static void ATPrepAddInherit(Relation child_rel);
638 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
639 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
640 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
641 : DependencyType deptype);
642 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
643 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
644 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
645 : static void ATExecGenericOptions(Relation rel, List *options);
646 : static void ATExecSetRowSecurity(Relation rel, bool rls);
647 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
648 : static ObjectAddress ATExecSetCompression(Relation rel,
649 : const char *column, Node *newValue, LOCKMODE lockmode);
650 :
651 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
652 : static const char *storage_name(char c);
653 :
654 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
655 : Oid oldRelOid, void *arg);
656 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
657 : Oid oldrelid, void *arg);
658 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
659 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
660 : List **partexprs, Oid *partopclass, Oid *partcollation,
661 : PartitionStrategy strategy);
662 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
663 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
664 : bool expect_detached);
665 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
666 : PartitionCmd *cmd,
667 : AlterTableUtilityContext *context);
668 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
669 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
670 : List *partConstraint,
671 : bool validate_default);
672 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
673 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
674 : static void DropClonedTriggersFromPartition(Oid partitionId);
675 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
676 : Relation rel, RangeVar *name,
677 : bool concurrent);
678 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
679 : bool concurrent, Oid defaultPartOid);
680 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
681 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
682 : RangeVar *name);
683 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
684 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
685 : Relation partitionTbl);
686 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
687 : static List *GetParentedForeignKeyRefs(Relation partition);
688 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
689 : static char GetAttributeCompression(Oid atttypid, const char *compression);
690 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
691 :
692 :
693 : /* ----------------------------------------------------------------
694 : * DefineRelation
695 : * Creates a new relation.
696 : *
697 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
698 : * The other arguments are used to extend the behavior for other cases:
699 : * relkind: relkind to assign to the new relation
700 : * ownerId: if not InvalidOid, use this as the new relation's owner.
701 : * typaddress: if not null, it's set to the pg_type entry's address.
702 : * queryString: for error reporting
703 : *
704 : * Note that permissions checks are done against current user regardless of
705 : * ownerId. A nonzero ownerId is used when someone is creating a relation
706 : * "on behalf of" someone else, so we still want to see that the current user
707 : * has permissions to do it.
708 : *
709 : * If successful, returns the address of the new relation.
710 : * ----------------------------------------------------------------
711 : */
712 : ObjectAddress
713 57026 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
714 : ObjectAddress *typaddress, const char *queryString)
715 : {
716 : char relname[NAMEDATALEN];
717 : Oid namespaceId;
718 : Oid relationId;
719 : Oid tablespaceId;
720 : Relation rel;
721 : TupleDesc descriptor;
722 : List *inheritOids;
723 : List *old_constraints;
724 : List *old_notnulls;
725 : List *rawDefaults;
726 : List *cookedDefaults;
727 : List *nncols;
728 : Datum reloptions;
729 : ListCell *listptr;
730 : AttrNumber attnum;
731 : bool partitioned;
732 57026 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
733 : Oid ofTypeId;
734 : ObjectAddress address;
735 : LOCKMODE parentLockmode;
736 57026 : Oid accessMethodId = InvalidOid;
737 :
738 : /*
739 : * Truncate relname to appropriate length (probably a waste of time, as
740 : * parser should have done this already).
741 : */
742 57026 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
743 :
744 : /*
745 : * Check consistency of arguments
746 : */
747 57026 : if (stmt->oncommit != ONCOMMIT_NOOP
748 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
749 12 : ereport(ERROR,
750 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
751 : errmsg("ON COMMIT can only be used on temporary tables")));
752 :
753 57014 : if (stmt->partspec != NULL)
754 : {
755 4784 : if (relkind != RELKIND_RELATION)
756 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
757 :
758 4784 : relkind = RELKIND_PARTITIONED_TABLE;
759 4784 : partitioned = true;
760 : }
761 : else
762 52230 : partitioned = false;
763 :
764 57014 : if (relkind == RELKIND_PARTITIONED_TABLE &&
765 4784 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
766 6 : ereport(ERROR,
767 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
768 : errmsg("partitioned tables cannot be unlogged")));
769 :
770 : /*
771 : * Look up the namespace in which we are supposed to create the relation,
772 : * check we have permission to create there, lock it against concurrent
773 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
774 : * namespace is selected.
775 : */
776 : namespaceId =
777 57008 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
778 :
779 : /*
780 : * Security check: disallow creating temp tables from security-restricted
781 : * code. This is needed because calling code might not expect untrusted
782 : * tables to appear in pg_temp at the front of its search path.
783 : */
784 57008 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
785 3036 : && InSecurityRestrictedOperation())
786 0 : ereport(ERROR,
787 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
788 : errmsg("cannot create temporary table within security-restricted operation")));
789 :
790 : /*
791 : * Determine the lockmode to use when scanning parents. A self-exclusive
792 : * lock is needed here.
793 : *
794 : * For regular inheritance, if two backends attempt to add children to the
795 : * same parent simultaneously, and that parent has no pre-existing
796 : * children, then both will attempt to update the parent's relhassubclass
797 : * field, leading to a "tuple concurrently updated" error. Also, this
798 : * interlocks against a concurrent ANALYZE on the parent table, which
799 : * might otherwise be attempting to clear the parent's relhassubclass
800 : * field, if its previous children were recently dropped.
801 : *
802 : * If the child table is a partition, then we instead grab an exclusive
803 : * lock on the parent because its partition descriptor will be changed by
804 : * addition of the new partition.
805 : */
806 57008 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
807 : ShareUpdateExclusiveLock);
808 :
809 : /* Determine the list of OIDs of the parents. */
810 57008 : inheritOids = NIL;
811 66948 : foreach(listptr, stmt->inhRelations)
812 : {
813 9940 : RangeVar *rv = (RangeVar *) lfirst(listptr);
814 : Oid parentOid;
815 :
816 9940 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
817 :
818 : /*
819 : * Reject duplications in the list of parents.
820 : */
821 9940 : if (list_member_oid(inheritOids, parentOid))
822 0 : ereport(ERROR,
823 : (errcode(ERRCODE_DUPLICATE_TABLE),
824 : errmsg("relation \"%s\" would be inherited from more than once",
825 : get_rel_name(parentOid))));
826 :
827 9940 : inheritOids = lappend_oid(inheritOids, parentOid);
828 : }
829 :
830 : /*
831 : * Select tablespace to use: an explicitly indicated one, or (in the case
832 : * of a partitioned table) the parent's, if it has one.
833 : */
834 57008 : if (stmt->tablespacename)
835 : {
836 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
837 :
838 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
839 6 : ereport(ERROR,
840 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
841 : errmsg("cannot specify default tablespace for partitioned relations")));
842 : }
843 56890 : else if (stmt->partbound)
844 : {
845 : Assert(list_length(inheritOids) == 1);
846 7748 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
847 : }
848 : else
849 49142 : tablespaceId = InvalidOid;
850 :
851 : /* still nothing? use the default */
852 56996 : if (!OidIsValid(tablespaceId))
853 56868 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
854 : partitioned);
855 :
856 : /* Check permissions except when using database's default */
857 56990 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
858 : {
859 : AclResult aclresult;
860 :
861 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
862 : ACL_CREATE);
863 146 : if (aclresult != ACLCHECK_OK)
864 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
865 6 : get_tablespace_name(tablespaceId));
866 : }
867 :
868 : /* In all cases disallow placing user relations in pg_global */
869 56984 : if (tablespaceId == GLOBALTABLESPACE_OID)
870 18 : ereport(ERROR,
871 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
872 : errmsg("only shared relations can be placed in pg_global tablespace")));
873 :
874 : /* Identify user ID that will own the table */
875 56966 : if (!OidIsValid(ownerId))
876 56726 : ownerId = GetUserId();
877 :
878 : /*
879 : * Parse and validate reloptions, if any.
880 : */
881 56966 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
882 : true, false);
883 :
884 56948 : switch (relkind)
885 : {
886 14878 : case RELKIND_VIEW:
887 14878 : (void) view_reloptions(reloptions, true);
888 14860 : break;
889 4760 : case RELKIND_PARTITIONED_TABLE:
890 4760 : (void) partitioned_table_reloptions(reloptions, true);
891 4754 : break;
892 37310 : default:
893 37310 : (void) heap_reloptions(relkind, reloptions, true);
894 : }
895 :
896 56828 : if (stmt->ofTypename)
897 : {
898 : AclResult aclresult;
899 :
900 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
901 :
902 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
903 86 : if (aclresult != ACLCHECK_OK)
904 6 : aclcheck_error_type(aclresult, ofTypeId);
905 : }
906 : else
907 56742 : ofTypeId = InvalidOid;
908 :
909 : /*
910 : * Look up inheritance ancestors and generate relation schema, including
911 : * inherited attributes. (Note that stmt->tableElts is destructively
912 : * modified by MergeAttributes.)
913 : */
914 56654 : stmt->tableElts =
915 56822 : MergeAttributes(stmt->tableElts, inheritOids,
916 56822 : stmt->relation->relpersistence,
917 56822 : stmt->partbound != NULL,
918 : &old_constraints, &old_notnulls);
919 :
920 : /*
921 : * Create a tuple descriptor from the relation schema. Note that this
922 : * deals with column names, types, and in-descriptor NOT NULL flags, but
923 : * not default values, NOT NULL or CHECK constraints; we handle those
924 : * below.
925 : */
926 56654 : descriptor = BuildDescForRelation(stmt->tableElts);
927 :
928 : /*
929 : * Find columns with default values and prepare for insertion of the
930 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
931 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
932 : * while raw defaults go into a list of RawColumnDefault structs that will
933 : * be processed by AddRelationNewConstraints. (We can't deal with raw
934 : * expressions until we can do transformExpr.)
935 : *
936 : * We can set the atthasdef flags now in the tuple descriptor; this just
937 : * saves StoreAttrDefault from having to do an immediate update of the
938 : * pg_attribute rows.
939 : */
940 56606 : rawDefaults = NIL;
941 56606 : cookedDefaults = NIL;
942 56606 : attnum = 0;
943 :
944 284454 : foreach(listptr, stmt->tableElts)
945 : {
946 227848 : ColumnDef *colDef = lfirst(listptr);
947 : Form_pg_attribute attr;
948 :
949 227848 : attnum++;
950 227848 : attr = TupleDescAttr(descriptor, attnum - 1);
951 :
952 227848 : if (colDef->raw_default != NULL)
953 : {
954 : RawColumnDefault *rawEnt;
955 :
956 : Assert(colDef->cooked_default == NULL);
957 :
958 2414 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
959 2414 : rawEnt->attnum = attnum;
960 2414 : rawEnt->raw_default = colDef->raw_default;
961 2414 : rawEnt->missingMode = false;
962 2414 : rawEnt->generated = colDef->generated;
963 2414 : rawDefaults = lappend(rawDefaults, rawEnt);
964 2414 : attr->atthasdef = true;
965 : }
966 225434 : else if (colDef->cooked_default != NULL)
967 : {
968 : CookedConstraint *cooked;
969 :
970 344 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
971 344 : cooked->contype = CONSTR_DEFAULT;
972 344 : cooked->conoid = InvalidOid; /* until created */
973 344 : cooked->name = NULL;
974 344 : cooked->attnum = attnum;
975 344 : cooked->expr = colDef->cooked_default;
976 344 : cooked->skip_validation = false;
977 344 : cooked->is_local = true; /* not used for defaults */
978 344 : cooked->inhcount = 0; /* ditto */
979 344 : cooked->is_no_inherit = false;
980 344 : cookedDefaults = lappend(cookedDefaults, cooked);
981 344 : attr->atthasdef = true;
982 : }
983 : }
984 :
985 : /*
986 : * For relations with table AM and partitioned tables, select access
987 : * method to use: an explicitly indicated one, or (in the case of a
988 : * partitioned table) the parent's, if it has one.
989 : */
990 56606 : if (stmt->accessMethod != NULL)
991 : {
992 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
993 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
994 : }
995 56484 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
996 : {
997 36010 : if (stmt->partbound)
998 : {
999 : Assert(list_length(inheritOids) == 1);
1000 7590 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1001 : }
1002 :
1003 36010 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1004 31242 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1005 : }
1006 :
1007 : /*
1008 : * Create the relation. Inherited defaults and constraints are passed in
1009 : * for immediate handling --- since they don't need parsing, they can be
1010 : * stored immediately.
1011 : */
1012 56588 : relationId = heap_create_with_catalog(relname,
1013 : namespaceId,
1014 : tablespaceId,
1015 : InvalidOid,
1016 : InvalidOid,
1017 : ofTypeId,
1018 : ownerId,
1019 : accessMethodId,
1020 : descriptor,
1021 : list_concat(cookedDefaults,
1022 : old_constraints),
1023 : relkind,
1024 56588 : stmt->relation->relpersistence,
1025 : false,
1026 : false,
1027 : stmt->oncommit,
1028 : reloptions,
1029 : true,
1030 : allowSystemTableMods,
1031 : false,
1032 : InvalidOid,
1033 : typaddress);
1034 :
1035 : /*
1036 : * We must bump the command counter to make the newly-created relation
1037 : * tuple visible for opening.
1038 : */
1039 56564 : CommandCounterIncrement();
1040 :
1041 : /*
1042 : * Open the new relation and acquire exclusive lock on it. This isn't
1043 : * really necessary for locking out other backends (since they can't see
1044 : * the new rel anyway until we commit), but it keeps the lock manager from
1045 : * complaining about deadlock risks.
1046 : */
1047 56564 : rel = relation_open(relationId, AccessExclusiveLock);
1048 :
1049 : /*
1050 : * Now add any newly specified column default and generation expressions
1051 : * to the new relation. These are passed to us in the form of raw
1052 : * parsetrees; we need to transform them to executable expression trees
1053 : * before they can be added. The most convenient way to do that is to
1054 : * apply the parser's transformExpr routine, but transformExpr doesn't
1055 : * work unless we have a pre-existing relation. So, the transformation has
1056 : * to be postponed to this final step of CREATE TABLE.
1057 : *
1058 : * This needs to be before processing the partitioning clauses because
1059 : * those could refer to generated columns.
1060 : */
1061 56564 : if (rawDefaults)
1062 2052 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1063 : true, true, false, queryString);
1064 :
1065 : /*
1066 : * Make column generation expressions visible for use by partitioning.
1067 : */
1068 56462 : CommandCounterIncrement();
1069 :
1070 : /* Process and store partition bound, if any. */
1071 56462 : if (stmt->partbound)
1072 : {
1073 : PartitionBoundSpec *bound;
1074 : ParseState *pstate;
1075 7694 : Oid parentId = linitial_oid(inheritOids),
1076 : defaultPartOid;
1077 : Relation parent,
1078 7694 : defaultRel = NULL;
1079 : ParseNamespaceItem *nsitem;
1080 :
1081 : /* Already have strong enough lock on the parent */
1082 7694 : parent = table_open(parentId, NoLock);
1083 :
1084 : /*
1085 : * We are going to try to validate the partition bound specification
1086 : * against the partition key of parentRel, so it better have one.
1087 : */
1088 7694 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1089 18 : ereport(ERROR,
1090 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1091 : errmsg("\"%s\" is not partitioned",
1092 : RelationGetRelationName(parent))));
1093 :
1094 : /*
1095 : * The partition constraint of the default partition depends on the
1096 : * partition bounds of every other partition. It is possible that
1097 : * another backend might be about to execute a query on the default
1098 : * partition table, and that the query relies on previously cached
1099 : * default partition constraints. We must therefore take a table lock
1100 : * strong enough to prevent all queries on the default partition from
1101 : * proceeding until we commit and send out a shared-cache-inval notice
1102 : * that will make them update their index lists.
1103 : *
1104 : * Order of locking: The relation being added won't be visible to
1105 : * other backends until it is committed, hence here in
1106 : * DefineRelation() the order of locking the default partition and the
1107 : * relation being added does not matter. But at all other places we
1108 : * need to lock the default relation before we lock the relation being
1109 : * added or removed i.e. we should take the lock in same order at all
1110 : * the places such that lock parent, lock default partition and then
1111 : * lock the partition so as to avoid a deadlock.
1112 : */
1113 : defaultPartOid =
1114 7676 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1115 : true));
1116 7676 : if (OidIsValid(defaultPartOid))
1117 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1118 :
1119 : /* Transform the bound values */
1120 7676 : pstate = make_parsestate(NULL);
1121 7676 : pstate->p_sourcetext = queryString;
1122 :
1123 : /*
1124 : * Add an nsitem containing this relation, so that transformExpr
1125 : * called on partition bound expressions is able to report errors
1126 : * using a proper context.
1127 : */
1128 7676 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1129 : NULL, false, false);
1130 7676 : addNSItemToQuery(pstate, nsitem, false, true, true);
1131 :
1132 7676 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1133 :
1134 : /*
1135 : * Check first that the new partition's bound is valid and does not
1136 : * overlap with any of existing partitions of the parent.
1137 : */
1138 7472 : check_new_partition_bound(relname, parent, bound, pstate);
1139 :
1140 : /*
1141 : * If the default partition exists, its partition constraints will
1142 : * change after the addition of this new partition such that it won't
1143 : * allow any row that qualifies for this new partition. So, check that
1144 : * the existing data in the default partition satisfies the constraint
1145 : * as it will exist after adding this partition.
1146 : */
1147 7358 : if (OidIsValid(defaultPartOid))
1148 : {
1149 348 : check_default_partition_contents(parent, defaultRel, bound);
1150 : /* Keep the lock until commit. */
1151 330 : table_close(defaultRel, NoLock);
1152 : }
1153 :
1154 : /* Update the pg_class entry. */
1155 7340 : StorePartitionBound(rel, parent, bound);
1156 :
1157 7340 : table_close(parent, NoLock);
1158 : }
1159 :
1160 : /* Store inheritance information for new rel. */
1161 56108 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1162 :
1163 : /*
1164 : * Process the partitioning specification (if any) and store the partition
1165 : * key information into the catalog.
1166 : */
1167 56108 : if (partitioned)
1168 : {
1169 : ParseState *pstate;
1170 : int partnatts;
1171 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1172 : Oid partopclass[PARTITION_MAX_KEYS];
1173 : Oid partcollation[PARTITION_MAX_KEYS];
1174 4754 : List *partexprs = NIL;
1175 :
1176 4754 : pstate = make_parsestate(NULL);
1177 4754 : pstate->p_sourcetext = queryString;
1178 :
1179 4754 : partnatts = list_length(stmt->partspec->partParams);
1180 :
1181 : /* Protect fixed-size arrays here and in executor */
1182 4754 : if (partnatts > PARTITION_MAX_KEYS)
1183 0 : ereport(ERROR,
1184 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1185 : errmsg("cannot partition using more than %d columns",
1186 : PARTITION_MAX_KEYS)));
1187 :
1188 : /*
1189 : * We need to transform the raw parsetrees corresponding to partition
1190 : * expressions into executable expression trees. Like column defaults
1191 : * and CHECK constraints, we could not have done the transformation
1192 : * earlier.
1193 : */
1194 4754 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1195 :
1196 4724 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1197 : partattrs, &partexprs, partopclass,
1198 4724 : partcollation, stmt->partspec->strategy);
1199 :
1200 4640 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1201 : partexprs,
1202 : partopclass, partcollation);
1203 :
1204 : /* make it all visible */
1205 4640 : CommandCounterIncrement();
1206 : }
1207 :
1208 : /*
1209 : * If we're creating a partition, create now all the indexes, triggers,
1210 : * FKs defined in the parent.
1211 : *
1212 : * We can't do it earlier, because DefineIndex wants to know the partition
1213 : * key which we just stored.
1214 : */
1215 55994 : if (stmt->partbound)
1216 : {
1217 7334 : Oid parentId = linitial_oid(inheritOids);
1218 : Relation parent;
1219 : List *idxlist;
1220 : ListCell *cell;
1221 :
1222 : /* Already have strong enough lock on the parent */
1223 7334 : parent = table_open(parentId, NoLock);
1224 7334 : idxlist = RelationGetIndexList(parent);
1225 :
1226 : /*
1227 : * For each index in the parent table, create one in the partition
1228 : */
1229 8672 : foreach(cell, idxlist)
1230 : {
1231 1356 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1232 : AttrMap *attmap;
1233 : IndexStmt *idxstmt;
1234 : Oid constraintOid;
1235 :
1236 1356 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1237 : {
1238 36 : if (idxRel->rd_index->indisunique)
1239 12 : ereport(ERROR,
1240 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1241 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1242 : RelationGetRelationName(parent)),
1243 : errdetail("Table \"%s\" contains indexes that are unique.",
1244 : RelationGetRelationName(parent))));
1245 : else
1246 : {
1247 24 : index_close(idxRel, AccessShareLock);
1248 24 : continue;
1249 : }
1250 : }
1251 :
1252 1320 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1253 : RelationGetDescr(parent),
1254 : false);
1255 : idxstmt =
1256 1320 : generateClonedIndexStmt(NULL, idxRel,
1257 : attmap, &constraintOid);
1258 1320 : DefineIndex(RelationGetRelid(rel),
1259 : idxstmt,
1260 : InvalidOid,
1261 : RelationGetRelid(idxRel),
1262 : constraintOid,
1263 : -1,
1264 : false, false, false, false, false);
1265 :
1266 1314 : index_close(idxRel, AccessShareLock);
1267 : }
1268 :
1269 7316 : list_free(idxlist);
1270 :
1271 : /*
1272 : * If there are any row-level triggers, clone them to the new
1273 : * partition.
1274 : */
1275 7316 : if (parent->trigdesc != NULL)
1276 408 : CloneRowTriggersToPartition(parent, rel);
1277 :
1278 : /*
1279 : * And foreign keys too. Note that because we're freshly creating the
1280 : * table, there is no need to verify these new constraints.
1281 : */
1282 7316 : CloneForeignKeyConstraints(NULL, parent, rel);
1283 :
1284 7316 : table_close(parent, NoLock);
1285 : }
1286 :
1287 : /*
1288 : * Now add any newly specified CHECK constraints to the new relation. Same
1289 : * as for defaults above, but these need to come after partitioning is set
1290 : * up.
1291 : */
1292 55976 : if (stmt->constraints)
1293 652 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1294 : true, true, false, queryString);
1295 :
1296 : /*
1297 : * Finally, merge the not-null constraints that are declared directly with
1298 : * those that come from parent relations (making sure to count inheritance
1299 : * appropriately for each), create them, and set the attnotnull flag on
1300 : * columns that don't yet have it.
1301 : */
1302 55958 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1303 : old_notnulls);
1304 125828 : foreach_int(attrnum, nncols)
1305 14068 : set_attnotnull(NULL, rel, attrnum, NoLock);
1306 :
1307 55880 : ObjectAddressSet(address, RelationRelationId, relationId);
1308 :
1309 : /*
1310 : * Clean up. We keep lock on new relation (although it shouldn't be
1311 : * visible to anyone else anyway, until commit).
1312 : */
1313 55880 : relation_close(rel, NoLock);
1314 :
1315 55880 : return address;
1316 : }
1317 :
1318 : /*
1319 : * BuildDescForRelation
1320 : *
1321 : * Given a list of ColumnDef nodes, build a TupleDesc.
1322 : *
1323 : * Note: This is only for the limited purpose of table and view creation. Not
1324 : * everything is filled in. A real tuple descriptor should be obtained from
1325 : * the relcache.
1326 : */
1327 : TupleDesc
1328 59348 : BuildDescForRelation(const List *columns)
1329 : {
1330 : int natts;
1331 : AttrNumber attnum;
1332 : ListCell *l;
1333 : TupleDesc desc;
1334 : char *attname;
1335 : Oid atttypid;
1336 : int32 atttypmod;
1337 : Oid attcollation;
1338 : int attdim;
1339 :
1340 : /*
1341 : * allocate a new tuple descriptor
1342 : */
1343 59348 : natts = list_length(columns);
1344 59348 : desc = CreateTemplateTupleDesc(natts);
1345 :
1346 59348 : attnum = 0;
1347 :
1348 290154 : foreach(l, columns)
1349 : {
1350 230866 : ColumnDef *entry = lfirst(l);
1351 : AclResult aclresult;
1352 : Form_pg_attribute att;
1353 :
1354 : /*
1355 : * for each entry in the list, get the name and type information from
1356 : * the list and have TupleDescInitEntry fill in the attribute
1357 : * information we need.
1358 : */
1359 230866 : attnum++;
1360 :
1361 230866 : attname = entry->colname;
1362 230866 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1363 :
1364 230866 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1365 230866 : if (aclresult != ACLCHECK_OK)
1366 42 : aclcheck_error_type(aclresult, atttypid);
1367 :
1368 230824 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1369 230824 : attdim = list_length(entry->typeName->arrayBounds);
1370 230824 : if (attdim > PG_INT16_MAX)
1371 0 : ereport(ERROR,
1372 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1373 : errmsg("too many array dimensions"));
1374 :
1375 230824 : if (entry->typeName->setof)
1376 0 : ereport(ERROR,
1377 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1378 : errmsg("column \"%s\" cannot be declared SETOF",
1379 : attname)));
1380 :
1381 230824 : TupleDescInitEntry(desc, attnum, attname,
1382 : atttypid, atttypmod, attdim);
1383 230824 : att = TupleDescAttr(desc, attnum - 1);
1384 :
1385 : /* Override TupleDescInitEntry's settings as requested */
1386 230824 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1387 :
1388 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1389 230824 : att->attnotnull = entry->is_not_null;
1390 230824 : att->attislocal = entry->is_local;
1391 230824 : att->attinhcount = entry->inhcount;
1392 230824 : att->attidentity = entry->identity;
1393 230824 : att->attgenerated = entry->generated;
1394 230824 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1395 230812 : if (entry->storage)
1396 19270 : att->attstorage = entry->storage;
1397 211542 : else if (entry->storage_name)
1398 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1399 : }
1400 :
1401 59288 : 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 1056 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1410 : {
1411 : const struct dropmsgstrings *rentry;
1412 :
1413 1176 : 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 1334 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1432 : {
1433 1334 : if (rentry->kind == rightkind)
1434 : {
1435 1014 : if (!missing_ok)
1436 : {
1437 138 : ereport(ERROR,
1438 : (errcode(rentry->nonexistent_code),
1439 : errmsg(rentry->nonexistent_msg, rel->relname)));
1440 : }
1441 : else
1442 : {
1443 876 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1444 876 : 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 16296 : RemoveRelations(DropStmt *drop)
1485 : {
1486 : ObjectAddresses *objects;
1487 : char relkind;
1488 : ListCell *cell;
1489 16296 : int flags = 0;
1490 16296 : LOCKMODE lockmode = AccessExclusiveLock;
1491 :
1492 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1493 16296 : 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 126 : lockmode = ShareUpdateExclusiveLock;
1501 : Assert(drop->removeType == OBJECT_INDEX);
1502 126 : 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 120 : 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 16290 : switch (drop->removeType)
1520 : {
1521 14158 : case OBJECT_TABLE:
1522 14158 : relkind = RELKIND_RELATION;
1523 14158 : break;
1524 :
1525 772 : case OBJECT_INDEX:
1526 772 : relkind = RELKIND_INDEX;
1527 772 : break;
1528 :
1529 176 : case OBJECT_SEQUENCE:
1530 176 : relkind = RELKIND_SEQUENCE;
1531 176 : break;
1532 :
1533 914 : case OBJECT_VIEW:
1534 914 : relkind = RELKIND_VIEW;
1535 914 : break;
1536 :
1537 120 : case OBJECT_MATVIEW:
1538 120 : relkind = RELKIND_MATVIEW;
1539 120 : break;
1540 :
1541 150 : case OBJECT_FOREIGN_TABLE:
1542 150 : relkind = RELKIND_FOREIGN_TABLE;
1543 150 : 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 16290 : objects = new_object_addresses();
1554 :
1555 36244 : foreach(cell, drop->objects)
1556 : {
1557 20118 : 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 20118 : AcceptInvalidationMessages();
1573 :
1574 : /* Look up the appropriate relation using namespace search. */
1575 20118 : state.expected_relkind = relkind;
1576 40236 : state.heap_lockmode = drop->concurrent ?
1577 20118 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1578 : /* We must initialize these fields to show that no locks are held: */
1579 20118 : state.heapOid = InvalidOid;
1580 20118 : state.partParentOid = InvalidOid;
1581 :
1582 20118 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1583 : RangeVarCallbackForDropRelation,
1584 : (void *) &state);
1585 :
1586 : /* Not there? */
1587 20098 : if (!OidIsValid(relOid))
1588 : {
1589 1056 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1590 918 : 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 19042 : if (drop->concurrent &&
1598 114 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1599 : {
1600 : Assert(list_length(drop->objects) == 1 &&
1601 : drop->removeType == OBJECT_INDEX);
1602 96 : flags |= PERFORM_DELETION_CONCURRENTLY;
1603 : }
1604 :
1605 : /*
1606 : * Concurrent index drop cannot be used with partitioned indexes,
1607 : * either.
1608 : */
1609 19042 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1610 96 : 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 19036 : 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 19036 : obj.classId = RelationRelationId;
1630 19036 : obj.objectId = relOid;
1631 19036 : obj.objectSubId = 0;
1632 :
1633 19036 : add_exact_object_address(&obj, objects);
1634 : }
1635 :
1636 16126 : performMultipleDeletions(objects, drop->behavior, flags);
1637 :
1638 15990 : free_object_addresses(objects);
1639 15990 : }
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 20446 : 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 20446 : bool invalid_system_index = false;
1658 :
1659 20446 : state = (struct DropRelationCallbackState *) arg;
1660 20446 : 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 20446 : 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 20446 : 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 20446 : if (!OidIsValid(relOid))
1686 1058 : return;
1687 :
1688 19388 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1689 19388 : if (!HeapTupleIsValid(tuple))
1690 0 : return; /* concurrently dropped, so nothing to do */
1691 19388 : classform = (Form_pg_class) GETSTRUCT(tuple);
1692 19388 : is_partition = classform->relispartition;
1693 :
1694 : /* Pass back some data to save lookups in RemoveRelations */
1695 19388 : state->actual_relkind = classform->relkind;
1696 19388 : 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 19388 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1707 2822 : expected_relkind = RELKIND_RELATION;
1708 16566 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1709 88 : expected_relkind = RELKIND_INDEX;
1710 : else
1711 16478 : expected_relkind = classform->relkind;
1712 :
1713 19388 : 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 19388 : 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 19370 : 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 19370 : 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 19368 : 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 19368 : if (expected_relkind == RELKIND_INDEX &&
1770 : relOid != oldRelOid)
1771 : {
1772 748 : state->heapOid = IndexGetRelation(relOid, true);
1773 748 : if (OidIsValid(state->heapOid))
1774 748 : 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 19368 : if (is_partition && relOid != oldRelOid)
1784 : {
1785 600 : state->partParentOid = get_partition_parent(relOid, true);
1786 600 : if (OidIsValid(state->partParentOid))
1787 600 : 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 1600 : ExecuteTruncate(TruncateStmt *stmt)
1808 : {
1809 1600 : List *rels = NIL;
1810 1600 : List *relids = NIL;
1811 1600 : List *relids_logged = NIL;
1812 : ListCell *cell;
1813 :
1814 : /*
1815 : * Open, exclusive-lock, and check all the explicitly-specified relations
1816 : */
1817 3448 : foreach(cell, stmt->relations)
1818 : {
1819 1898 : RangeVar *rv = lfirst(cell);
1820 : Relation rel;
1821 1898 : bool recurse = rv->inh;
1822 : Oid myrelid;
1823 1898 : LOCKMODE lockmode = AccessExclusiveLock;
1824 :
1825 1898 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1826 : 0, RangeVarCallbackForTruncate,
1827 : NULL);
1828 :
1829 : /* don't throw error for "TRUNCATE foo, foo" */
1830 1860 : if (list_member_oid(relids, myrelid))
1831 2 : continue;
1832 :
1833 : /* open the relation, we already hold a lock on it */
1834 1858 : 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 1858 : truncate_check_activity(rel);
1841 :
1842 1858 : rels = lappend(rels, rel);
1843 1858 : relids = lappend_oid(relids, myrelid);
1844 :
1845 : /* Log this relation only if needed for logical decoding */
1846 1858 : if (RelationIsLogicallyLogged(rel))
1847 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1848 :
1849 1858 : if (recurse)
1850 : {
1851 : ListCell *child;
1852 : List *children;
1853 :
1854 1796 : children = find_all_inheritors(myrelid, lockmode, NULL);
1855 :
1856 5420 : foreach(child, children)
1857 : {
1858 3624 : Oid childrelid = lfirst_oid(child);
1859 :
1860 3624 : if (list_member_oid(relids, childrelid))
1861 1796 : continue;
1862 :
1863 : /* find_all_inheritors already got lock */
1864 1828 : 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 1828 : 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 1820 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1888 1820 : truncate_check_activity(rel);
1889 :
1890 1820 : rels = lappend(rels, rel);
1891 1820 : relids = lappend_oid(relids, childrelid);
1892 :
1893 : /* Log this relation only if needed for logical decoding */
1894 1820 : 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 1550 : ExecuteTruncateGuts(rels, relids, relids_logged,
1906 1550 : stmt->behavior, stmt->restart_seqs, false);
1907 :
1908 : /* And close the rels */
1909 4980 : foreach(cell, rels)
1910 : {
1911 3512 : Relation rel = (Relation) lfirst(cell);
1912 :
1913 3512 : table_close(rel, NoLock);
1914 : }
1915 1468 : }
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 1588 : 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 1588 : List *seq_relids = NIL;
1939 1588 : 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 1588 : rels = list_copy(explicit_rels);
1958 1588 : 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 1588 : if (behavior == DROP_RESTRICT)
1999 1548 : 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 1514 : 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 1514 : 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 1514 : estate = CreateExecutorState();
2048 : resultRelInfos = (ResultRelInfo *)
2049 1514 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2050 1514 : resultRelInfo = resultRelInfos;
2051 5198 : foreach(cell, rels)
2052 : {
2053 3684 : Relation rel = (Relation) lfirst(cell);
2054 :
2055 3684 : InitResultRelInfo(resultRelInfo,
2056 : rel,
2057 : 0, /* dummy rangetable index */
2058 : NULL,
2059 : 0);
2060 3684 : estate->es_opened_result_relations =
2061 3684 : lappend(estate->es_opened_result_relations, resultRelInfo);
2062 3684 : 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 1514 : resultRelInfo = resultRelInfos;
2072 5198 : foreach(cell, rels)
2073 : {
2074 : UserContext ucxt;
2075 :
2076 3684 : if (run_as_table_owner)
2077 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2078 : &ucxt);
2079 3684 : ExecBSTruncateTriggers(estate, resultRelInfo);
2080 3684 : if (run_as_table_owner)
2081 70 : RestoreUserContext(&ucxt);
2082 3684 : resultRelInfo++;
2083 : }
2084 :
2085 : /*
2086 : * OK, truncate each table.
2087 : */
2088 1514 : mySubid = GetCurrentSubTransactionId();
2089 :
2090 5198 : foreach(cell, rels)
2091 : {
2092 3684 : Relation rel = (Relation) lfirst(cell);
2093 :
2094 : /* Skip partitioned tables as there is nothing to do */
2095 3684 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2096 740 : 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 2944 : 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 2910 : if (rel->rd_createSubid == mySubid ||
2148 2884 : 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 2820 : 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 2820 : 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 2820 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2175 :
2176 2820 : heap_relid = RelationGetRelid(rel);
2177 :
2178 : /*
2179 : * The same for the toast table, if any.
2180 : */
2181 2820 : toast_relid = rel->rd_rel->reltoastrelid;
2182 2820 : if (OidIsValid(toast_relid))
2183 : {
2184 1824 : Relation toastrel = relation_open(toast_relid,
2185 : AccessExclusiveLock);
2186 :
2187 1824 : RelationSetNewRelfilenumber(toastrel,
2188 1824 : toastrel->rd_rel->relpersistence);
2189 1824 : table_close(toastrel, NoLock);
2190 : }
2191 :
2192 : /*
2193 : * Reconstruct the indexes to match, and we're done.
2194 : */
2195 2820 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2196 : &reindex_params);
2197 : }
2198 :
2199 2910 : pgstat_count_truncate(rel);
2200 : }
2201 :
2202 : /* Now go through the hash table, and truncate foreign tables */
2203 1514 : 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 1542 : 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 1506 : if (relids_logged != NIL)
2249 : {
2250 : xl_heap_truncate xlrec;
2251 54 : int i = 0;
2252 :
2253 : /* should only get here if wal_level >= logical */
2254 : Assert(XLogLogicalInfoActive());
2255 :
2256 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2257 144 : foreach(cell, relids_logged)
2258 90 : logrelids[i++] = lfirst_oid(cell);
2259 :
2260 54 : xlrec.dbId = MyDatabaseId;
2261 54 : xlrec.nrelids = list_length(relids_logged);
2262 54 : xlrec.flags = 0;
2263 54 : if (behavior == DROP_CASCADE)
2264 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2265 54 : if (restart_seqs)
2266 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2267 :
2268 54 : XLogBeginInsert();
2269 54 : XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2270 54 : XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2271 :
2272 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2273 :
2274 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2275 : }
2276 :
2277 : /*
2278 : * Process all AFTER STATEMENT TRUNCATE triggers.
2279 : */
2280 1506 : resultRelInfo = resultRelInfos;
2281 5182 : foreach(cell, rels)
2282 : {
2283 : UserContext ucxt;
2284 :
2285 3676 : if (run_as_table_owner)
2286 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2287 : &ucxt);
2288 3676 : ExecASTruncateTriggers(estate, resultRelInfo);
2289 3676 : if (run_as_table_owner)
2290 70 : RestoreUserContext(&ucxt);
2291 3676 : resultRelInfo++;
2292 : }
2293 :
2294 : /* Handle queued AFTER triggers */
2295 1506 : AfterTriggerEndQuery(estate);
2296 :
2297 : /* We can clean up the EState now */
2298 1506 : FreeExecutorState(estate);
2299 :
2300 : /*
2301 : * Close any rels opened by CASCADE (can't do this while EState still
2302 : * holds refs)
2303 : */
2304 1506 : rels = list_difference_ptr(rels, explicit_rels);
2305 1600 : foreach(cell, rels)
2306 : {
2307 94 : Relation rel = (Relation) lfirst(cell);
2308 :
2309 94 : table_close(rel, NoLock);
2310 : }
2311 1506 : }
2312 :
2313 : /*
2314 : * Check that a given relation is safe to truncate. Subroutine for
2315 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2316 : */
2317 : static void
2318 3936 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2319 : {
2320 3936 : 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 3936 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2329 : {
2330 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2331 38 : 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 3898 : else if (reltuple->relkind != RELKIND_RELATION &&
2340 766 : 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 3932 : 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 3930 : InvokeObjectTruncateHook(relid);
2360 3930 : }
2361 :
2362 : /*
2363 : * Check that current user has the permission to truncate given relation.
2364 : */
2365 : static void
2366 2110 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2367 : {
2368 2110 : char *relname = NameStr(reltuple->relname);
2369 : AclResult aclresult;
2370 :
2371 : /* Permissions checks */
2372 2110 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2373 2110 : if (aclresult != ACLCHECK_OK)
2374 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2375 : relname);
2376 2078 : }
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 3772 : 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 3772 : 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 3772 : CheckTableNotInUse(rel, "TRUNCATE");
2400 3772 : }
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 56822 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2492 : bool is_partition, List **supconstr, List **supnotnulls)
2493 : {
2494 56822 : List *inh_columns = NIL;
2495 56822 : List *constraints = NIL;
2496 56822 : List *nnconstraints = NIL;
2497 56822 : bool have_bogus_defaults = false;
2498 : int child_attno;
2499 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2500 56822 : 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 56822 : 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 266156 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2535 : {
2536 209358 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2537 :
2538 209358 : 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 6329096 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2554 : {
2555 6119762 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2556 :
2557 6119762 : 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 6119712 : 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 56798 : if (is_partition)
2588 : {
2589 7736 : saved_columns = columns;
2590 7736 : 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 56798 : child_attno = 0;
2598 66642 : foreach(lc, supers)
2599 : {
2600 9916 : 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 9916 : Bitmapset *nncols = NULL;
2611 :
2612 : /* caller already got lock */
2613 9916 : relation = table_open(parent, NoLock);
2614 :
2615 : /*
2616 : * Check for active uses of the parent partitioned table in the
2617 : * current transaction, such as being used in some manner by an
2618 : * enclosing command.
2619 : */
2620 9916 : if (is_partition)
2621 7736 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2622 :
2623 : /*
2624 : * We do not allow partitioned tables and partitions to participate in
2625 : * regular inheritance.
2626 : */
2627 9910 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2628 6 : ereport(ERROR,
2629 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2630 : errmsg("cannot inherit from partitioned table \"%s\"",
2631 : RelationGetRelationName(relation))));
2632 9904 : if (relation->rd_rel->relispartition && !is_partition)
2633 6 : ereport(ERROR,
2634 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2635 : errmsg("cannot inherit from partition \"%s\"",
2636 : RelationGetRelationName(relation))));
2637 :
2638 9898 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2639 7732 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2640 7712 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2641 0 : ereport(ERROR,
2642 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2643 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2644 : RelationGetRelationName(relation))));
2645 :
2646 : /*
2647 : * If the parent is permanent, so must be all of its partitions. Note
2648 : * that inheritance allows that case.
2649 : */
2650 9898 : if (is_partition &&
2651 7730 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2652 : relpersistence == RELPERSISTENCE_TEMP)
2653 6 : ereport(ERROR,
2654 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2655 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2656 : RelationGetRelationName(relation))));
2657 :
2658 : /* Permanent rels cannot inherit from temporary ones */
2659 9892 : if (relpersistence != RELPERSISTENCE_TEMP &&
2660 9544 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2661 24 : ereport(ERROR,
2662 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2663 : errmsg(!is_partition
2664 : ? "cannot inherit from temporary relation \"%s\""
2665 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2666 : RelationGetRelationName(relation))));
2667 :
2668 : /* If existing rel is temp, it must belong to this session */
2669 9868 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2670 294 : !relation->rd_islocaltemp)
2671 0 : ereport(ERROR,
2672 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2673 : errmsg(!is_partition
2674 : ? "cannot inherit from temporary relation of another session"
2675 : : "cannot create as partition of temporary relation of another session")));
2676 :
2677 : /*
2678 : * We should have an UNDER permission flag for this, but for now,
2679 : * demand that creator of a child table own the parent.
2680 : */
2681 9868 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2682 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2683 0 : RelationGetRelationName(relation));
2684 :
2685 9868 : tupleDesc = RelationGetDescr(relation);
2686 9868 : constr = tupleDesc->constr;
2687 :
2688 : /*
2689 : * newattmap->attnums[] will contain the child-table attribute numbers
2690 : * for the attributes of this parent table. (They are not the same
2691 : * for parents after the first one, nor if we have dropped columns.)
2692 : */
2693 9868 : newattmap = make_attrmap(tupleDesc->natts);
2694 :
2695 : /* We can't process inherited defaults until newattmap is complete. */
2696 9868 : inherited_defaults = cols_with_defaults = NIL;
2697 :
2698 : /*
2699 : * Request attnotnull on columns that have a not-null constraint
2700 : * that's not marked NO INHERIT.
2701 : */
2702 9868 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2703 : true, false);
2704 21900 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2705 2164 : nncols = bms_add_member(nncols, cc->attnum);
2706 :
2707 29680 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2708 19812 : parent_attno++)
2709 : {
2710 19836 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2711 : parent_attno - 1);
2712 19836 : char *attributeName = NameStr(attribute->attname);
2713 : int exist_attno;
2714 : ColumnDef *newdef;
2715 : ColumnDef *mergeddef;
2716 :
2717 : /*
2718 : * Ignore dropped columns in the parent.
2719 : */
2720 19836 : if (attribute->attisdropped)
2721 192 : continue; /* leave newattmap->attnums entry as zero */
2722 :
2723 : /*
2724 : * Create new column definition
2725 : */
2726 19644 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2727 : attribute->atttypmod, attribute->attcollation);
2728 19644 : newdef->storage = attribute->attstorage;
2729 19644 : newdef->generated = attribute->attgenerated;
2730 19644 : if (CompressionMethodIsValid(attribute->attcompression))
2731 30 : newdef->compression =
2732 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2733 :
2734 : /*
2735 : * Regular inheritance children are independent enough not to
2736 : * inherit identity columns. But partitions are integral part of
2737 : * a partitioned table and inherit identity column.
2738 : */
2739 19644 : if (is_partition)
2740 15604 : newdef->identity = attribute->attidentity;
2741 :
2742 : /*
2743 : * Does it match some previously considered column from another
2744 : * parent?
2745 : */
2746 19644 : exist_attno = findAttrByName(attributeName, inh_columns);
2747 19644 : if (exist_attno > 0)
2748 : {
2749 : /*
2750 : * Yes, try to merge the two column definitions.
2751 : */
2752 302 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2753 :
2754 278 : newattmap->attnums[parent_attno - 1] = exist_attno;
2755 :
2756 : /*
2757 : * Partitions have only one parent, so conflict should never
2758 : * occur.
2759 : */
2760 : Assert(!is_partition);
2761 : }
2762 : else
2763 : {
2764 : /*
2765 : * No, create a new inherited column
2766 : */
2767 19342 : newdef->inhcount = 1;
2768 19342 : newdef->is_local = false;
2769 19342 : inh_columns = lappend(inh_columns, newdef);
2770 :
2771 19342 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2772 19342 : mergeddef = newdef;
2773 : }
2774 :
2775 : /*
2776 : * mark attnotnull if parent has it
2777 : */
2778 19620 : if (bms_is_member(parent_attno, nncols))
2779 2164 : mergeddef->is_not_null = true;
2780 :
2781 : /*
2782 : * Locate default/generation expression if any
2783 : */
2784 19620 : if (attribute->atthasdef)
2785 : {
2786 : Node *this_default;
2787 :
2788 556 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2789 556 : if (this_default == NULL)
2790 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2791 : parent_attno, RelationGetRelationName(relation));
2792 :
2793 : /*
2794 : * If it's a GENERATED default, it might contain Vars that
2795 : * need to be mapped to the inherited column(s)' new numbers.
2796 : * We can't do that till newattmap is ready, so just remember
2797 : * all the inherited default expressions for the moment.
2798 : */
2799 556 : inherited_defaults = lappend(inherited_defaults, this_default);
2800 556 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2801 : }
2802 : }
2803 :
2804 : /*
2805 : * Now process any inherited default expressions, adjusting attnos
2806 : * using the completed newattmap map.
2807 : */
2808 10400 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2809 : {
2810 556 : Node *this_default = (Node *) lfirst(lc1);
2811 556 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2812 : bool found_whole_row;
2813 :
2814 : /* Adjust Vars to match new table's column numbering */
2815 556 : this_default = map_variable_attnos(this_default,
2816 : 1, 0,
2817 : newattmap,
2818 : InvalidOid, &found_whole_row);
2819 :
2820 : /*
2821 : * For the moment we have to reject whole-row variables. We could
2822 : * convert them, if we knew the new table's rowtype OID, but that
2823 : * hasn't been assigned yet. (A variable could only appear in a
2824 : * generation expression, so the error message is correct.)
2825 : */
2826 556 : if (found_whole_row)
2827 0 : ereport(ERROR,
2828 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2829 : errmsg("cannot convert whole-row table reference"),
2830 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2831 : def->colname,
2832 : RelationGetRelationName(relation))));
2833 :
2834 : /*
2835 : * If we already had a default from some prior parent, check to
2836 : * see if they are the same. If so, no problem; if not, mark the
2837 : * column as having a bogus default. Below, we will complain if
2838 : * the bogus default isn't overridden by the child columns.
2839 : */
2840 : Assert(def->raw_default == NULL);
2841 556 : if (def->cooked_default == NULL)
2842 526 : def->cooked_default = this_default;
2843 30 : else if (!equal(def->cooked_default, this_default))
2844 : {
2845 24 : def->cooked_default = &bogus_marker;
2846 24 : have_bogus_defaults = true;
2847 : }
2848 : }
2849 :
2850 : /*
2851 : * Now copy the CHECK constraints of this parent, adjusting attnos
2852 : * using the completed newattmap map. Identically named constraints
2853 : * are merged if possible, else we throw error.
2854 : */
2855 9844 : if (constr && constr->num_check > 0)
2856 : {
2857 292 : ConstrCheck *check = constr->check;
2858 :
2859 614 : for (int i = 0; i < constr->num_check; i++)
2860 : {
2861 322 : char *name = check[i].ccname;
2862 : Node *expr;
2863 : bool found_whole_row;
2864 :
2865 : /* ignore if the constraint is non-inheritable */
2866 322 : if (check[i].ccnoinherit)
2867 48 : continue;
2868 :
2869 : /* Adjust Vars to match new table's column numbering */
2870 274 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2871 : 1, 0,
2872 : newattmap,
2873 : InvalidOid, &found_whole_row);
2874 :
2875 : /*
2876 : * For the moment we have to reject whole-row variables. We
2877 : * could convert them, if we knew the new table's rowtype OID,
2878 : * but that hasn't been assigned yet.
2879 : */
2880 274 : if (found_whole_row)
2881 0 : ereport(ERROR,
2882 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2883 : errmsg("cannot convert whole-row table reference"),
2884 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2885 : name,
2886 : RelationGetRelationName(relation))));
2887 :
2888 274 : constraints = MergeCheckConstraint(constraints, name, expr);
2889 : }
2890 : }
2891 :
2892 : /*
2893 : * Also copy the not-null constraints from this parent. The
2894 : * attnotnull markings were already installed above.
2895 : */
2896 21852 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2897 : {
2898 : Assert(nn->contype == CONSTR_NOTNULL);
2899 :
2900 2164 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2901 :
2902 2164 : nnconstraints = lappend(nnconstraints, nn);
2903 : }
2904 :
2905 9844 : free_attrmap(newattmap);
2906 :
2907 : /*
2908 : * Close the parent rel, but keep our lock on it until xact commit.
2909 : * That will prevent someone else from deleting or ALTERing the parent
2910 : * before the child is committed.
2911 : */
2912 9844 : table_close(relation, NoLock);
2913 : }
2914 :
2915 : /*
2916 : * If we had no inherited attributes, the result columns are just the
2917 : * explicitly declared columns. Otherwise, we need to merge the declared
2918 : * columns into the inherited column list. Although, we never have any
2919 : * explicitly declared columns if the table is a partition.
2920 : */
2921 56726 : if (inh_columns != NIL)
2922 : {
2923 9482 : int newcol_attno = 0;
2924 :
2925 10320 : foreach(lc, columns)
2926 : {
2927 886 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2928 886 : char *attributeName = newdef->colname;
2929 : int exist_attno;
2930 :
2931 : /*
2932 : * Partitions have only one parent and have no column definitions
2933 : * of their own, so conflict should never occur.
2934 : */
2935 : Assert(!is_partition);
2936 :
2937 886 : newcol_attno++;
2938 :
2939 : /*
2940 : * Does it match some inherited column?
2941 : */
2942 886 : exist_attno = findAttrByName(attributeName, inh_columns);
2943 886 : if (exist_attno > 0)
2944 : {
2945 : /*
2946 : * Yes, try to merge the two column definitions.
2947 : */
2948 286 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2949 : }
2950 : else
2951 : {
2952 : /*
2953 : * No, attach new column unchanged to result columns.
2954 : */
2955 600 : inh_columns = lappend(inh_columns, newdef);
2956 : }
2957 : }
2958 :
2959 9434 : columns = inh_columns;
2960 :
2961 : /*
2962 : * Check that we haven't exceeded the legal # of columns after merging
2963 : * in inherited columns.
2964 : */
2965 9434 : if (list_length(columns) > MaxHeapAttributeNumber)
2966 0 : ereport(ERROR,
2967 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2968 : errmsg("tables can have at most %d columns",
2969 : MaxHeapAttributeNumber)));
2970 : }
2971 :
2972 : /*
2973 : * Now that we have the column definition list for a partition, we can
2974 : * check whether the columns referenced in the column constraint specs
2975 : * actually exist. Also, merge column defaults.
2976 : */
2977 56678 : if (is_partition)
2978 : {
2979 7906 : foreach(lc, saved_columns)
2980 : {
2981 212 : ColumnDef *restdef = lfirst(lc);
2982 212 : bool found = false;
2983 : ListCell *l;
2984 :
2985 804 : foreach(l, columns)
2986 : {
2987 604 : ColumnDef *coldef = lfirst(l);
2988 :
2989 604 : if (strcmp(coldef->colname, restdef->colname) == 0)
2990 : {
2991 212 : found = true;
2992 :
2993 : /*
2994 : * Check for conflicts related to generated columns.
2995 : *
2996 : * Same rules as above: generated-ness has to match the
2997 : * parent, but the contents of the generation expression
2998 : * can be different.
2999 : */
3000 212 : if (coldef->generated)
3001 : {
3002 110 : if (restdef->raw_default && !restdef->generated)
3003 6 : ereport(ERROR,
3004 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3005 : errmsg("column \"%s\" inherits from generated column but specifies default",
3006 : restdef->colname)));
3007 104 : if (restdef->identity)
3008 0 : ereport(ERROR,
3009 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3010 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3011 : restdef->colname)));
3012 : }
3013 : else
3014 : {
3015 102 : if (restdef->generated)
3016 6 : ereport(ERROR,
3017 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3018 : errmsg("child column \"%s\" specifies generation expression",
3019 : restdef->colname),
3020 : errhint("A child table column cannot be generated unless its parent column is.")));
3021 : }
3022 :
3023 : /*
3024 : * Override the parent's default value for this column
3025 : * (coldef->cooked_default) with the partition's local
3026 : * definition (restdef->raw_default), if there's one. It
3027 : * should be physically impossible to get a cooked default
3028 : * in the local definition or a raw default in the
3029 : * inherited definition, but make sure they're nulls, for
3030 : * future-proofing.
3031 : */
3032 : Assert(restdef->cooked_default == NULL);
3033 : Assert(coldef->raw_default == NULL);
3034 200 : if (restdef->raw_default)
3035 : {
3036 128 : coldef->raw_default = restdef->raw_default;
3037 128 : coldef->cooked_default = NULL;
3038 : }
3039 : }
3040 : }
3041 :
3042 : /* complain for constraints on columns not in parent */
3043 200 : if (!found)
3044 0 : ereport(ERROR,
3045 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3046 : errmsg("column \"%s\" does not exist",
3047 : restdef->colname)));
3048 : }
3049 : }
3050 :
3051 : /*
3052 : * If we found any conflicting parent default values, check to make sure
3053 : * they were overridden by the child.
3054 : */
3055 56666 : if (have_bogus_defaults)
3056 : {
3057 54 : foreach(lc, columns)
3058 : {
3059 42 : ColumnDef *def = lfirst(lc);
3060 :
3061 42 : if (def->cooked_default == &bogus_marker)
3062 : {
3063 12 : if (def->generated)
3064 6 : ereport(ERROR,
3065 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3066 : errmsg("column \"%s\" inherits conflicting generation expressions",
3067 : def->colname),
3068 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3069 : else
3070 6 : ereport(ERROR,
3071 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3072 : errmsg("column \"%s\" inherits conflicting default values",
3073 : def->colname),
3074 : errhint("To resolve the conflict, specify a default explicitly.")));
3075 : }
3076 : }
3077 : }
3078 :
3079 56654 : *supconstr = constraints;
3080 56654 : *supnotnulls = nnconstraints;
3081 :
3082 56654 : return columns;
3083 : }
3084 :
3085 :
3086 : /*
3087 : * MergeCheckConstraint
3088 : * Try to merge an inherited CHECK constraint with previous ones
3089 : *
3090 : * If we inherit identically-named constraints from multiple parents, we must
3091 : * merge them, or throw an error if they don't have identical definitions.
3092 : *
3093 : * constraints is a list of CookedConstraint structs for previous constraints.
3094 : *
3095 : * If the new constraint matches an existing one, then the existing
3096 : * constraint's inheritance count is updated. If there is a conflict (same
3097 : * name but different expression), throw an error. If the constraint neither
3098 : * matches nor conflicts with an existing one, a new constraint is appended to
3099 : * the list.
3100 : */
3101 : static List *
3102 274 : MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3103 : {
3104 : ListCell *lc;
3105 : CookedConstraint *newcon;
3106 :
3107 304 : foreach(lc, constraints)
3108 : {
3109 72 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3110 :
3111 : Assert(ccon->contype == CONSTR_CHECK);
3112 :
3113 : /* Non-matching names never conflict */
3114 72 : if (strcmp(ccon->name, name) != 0)
3115 30 : continue;
3116 :
3117 42 : if (equal(expr, ccon->expr))
3118 : {
3119 : /* OK to merge constraint with existing */
3120 42 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3121 : &ccon->inhcount))
3122 0 : ereport(ERROR,
3123 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3124 : errmsg("too many inheritance parents"));
3125 42 : return constraints;
3126 : }
3127 :
3128 0 : ereport(ERROR,
3129 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3130 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3131 : name)));
3132 : }
3133 :
3134 : /*
3135 : * Constraint couldn't be merged with an existing one and also didn't
3136 : * conflict with an existing one, so add it as a new one to the list.
3137 : */
3138 232 : newcon = palloc0_object(CookedConstraint);
3139 232 : newcon->contype = CONSTR_CHECK;
3140 232 : newcon->name = pstrdup(name);
3141 232 : newcon->expr = expr;
3142 232 : newcon->inhcount = 1;
3143 232 : return lappend(constraints, newcon);
3144 : }
3145 :
3146 : /*
3147 : * MergeChildAttribute
3148 : * Merge given child attribute definition into given inherited attribute.
3149 : *
3150 : * Input arguments:
3151 : * 'inh_columns' is the list of inherited ColumnDefs.
3152 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3153 : * 'newcol_attno' is the attribute number in child table's schema definition
3154 : * 'newdef' is the column/attribute definition from the child table.
3155 : *
3156 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3157 : * ColumnDef remains unchanged.
3158 : *
3159 : * Notes:
3160 : * - The attribute is merged according to the rules laid out in the prologue
3161 : * of MergeAttributes().
3162 : * - If matching inherited attribute exists but the child attribute can not be
3163 : * merged into it, the function throws respective errors.
3164 : * - A partition can not have its own column definitions. Hence this function
3165 : * is applicable only to a regular inheritance child.
3166 : */
3167 : static void
3168 286 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3169 : {
3170 286 : char *attributeName = newdef->colname;
3171 : ColumnDef *inhdef;
3172 : Oid inhtypeid,
3173 : newtypeid;
3174 : int32 inhtypmod,
3175 : newtypmod;
3176 : Oid inhcollid,
3177 : newcollid;
3178 :
3179 286 : if (exist_attno == newcol_attno)
3180 258 : ereport(NOTICE,
3181 : (errmsg("merging column \"%s\" with inherited definition",
3182 : attributeName)));
3183 : else
3184 28 : ereport(NOTICE,
3185 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3186 : errdetail("User-specified column moved to the position of the inherited column.")));
3187 :
3188 286 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3189 :
3190 : /*
3191 : * Must have the same type and typmod
3192 : */
3193 286 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3194 286 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3195 286 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3196 12 : ereport(ERROR,
3197 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3198 : errmsg("column \"%s\" has a type conflict",
3199 : attributeName),
3200 : errdetail("%s versus %s",
3201 : format_type_with_typemod(inhtypeid, inhtypmod),
3202 : format_type_with_typemod(newtypeid, newtypmod))));
3203 :
3204 : /*
3205 : * Must have the same collation
3206 : */
3207 274 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3208 274 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3209 274 : if (inhcollid != newcollid)
3210 6 : ereport(ERROR,
3211 : (errcode(ERRCODE_COLLATION_MISMATCH),
3212 : errmsg("column \"%s\" has a collation conflict",
3213 : attributeName),
3214 : errdetail("\"%s\" versus \"%s\"",
3215 : get_collation_name(inhcollid),
3216 : get_collation_name(newcollid))));
3217 :
3218 : /*
3219 : * Identity is never inherited by a regular inheritance child. Pick
3220 : * child's identity definition if there's one.
3221 : */
3222 268 : inhdef->identity = newdef->identity;
3223 :
3224 : /*
3225 : * Copy storage parameter
3226 : */
3227 268 : if (inhdef->storage == 0)
3228 0 : inhdef->storage = newdef->storage;
3229 268 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3230 6 : ereport(ERROR,
3231 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3232 : errmsg("column \"%s\" has a storage parameter conflict",
3233 : attributeName),
3234 : errdetail("%s versus %s",
3235 : storage_name(inhdef->storage),
3236 : storage_name(newdef->storage))));
3237 :
3238 : /*
3239 : * Copy compression parameter
3240 : */
3241 262 : if (inhdef->compression == NULL)
3242 256 : inhdef->compression = newdef->compression;
3243 6 : else if (newdef->compression != NULL)
3244 : {
3245 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3246 6 : ereport(ERROR,
3247 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3248 : errmsg("column \"%s\" has a compression method conflict",
3249 : attributeName),
3250 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3251 : }
3252 :
3253 : /*
3254 : * Merge of not-null constraints = OR 'em together
3255 : */
3256 256 : inhdef->is_not_null |= newdef->is_not_null;
3257 :
3258 : /*
3259 : * Check for conflicts related to generated columns.
3260 : *
3261 : * If the parent column is generated, the child column will be made a
3262 : * generated column if it isn't already. If it is a generated column,
3263 : * we'll take its generation expression in preference to the parent's. We
3264 : * must check that the child column doesn't specify a default value or
3265 : * identity, which matches the rules for a single column in
3266 : * parse_utilcmd.c.
3267 : *
3268 : * Conversely, if the parent column is not generated, the child column
3269 : * can't be either. (We used to allow that, but it results in being able
3270 : * to override the generation expression via UPDATEs through the parent.)
3271 : */
3272 256 : if (inhdef->generated)
3273 : {
3274 26 : if (newdef->raw_default && !newdef->generated)
3275 6 : ereport(ERROR,
3276 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3277 : errmsg("column \"%s\" inherits from generated column but specifies default",
3278 : inhdef->colname)));
3279 20 : if (newdef->identity)
3280 6 : ereport(ERROR,
3281 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3282 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3283 : inhdef->colname)));
3284 : }
3285 : else
3286 : {
3287 230 : if (newdef->generated)
3288 6 : ereport(ERROR,
3289 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3290 : errmsg("child column \"%s\" specifies generation expression",
3291 : inhdef->colname),
3292 : errhint("A child table column cannot be generated unless its parent column is.")));
3293 : }
3294 :
3295 : /*
3296 : * If new def has a default, override previous default
3297 : */
3298 238 : if (newdef->raw_default != NULL)
3299 : {
3300 18 : inhdef->raw_default = newdef->raw_default;
3301 18 : inhdef->cooked_default = newdef->cooked_default;
3302 : }
3303 :
3304 : /* Mark the column as locally defined */
3305 238 : inhdef->is_local = true;
3306 238 : }
3307 :
3308 : /*
3309 : * MergeInheritedAttribute
3310 : * Merge given parent attribute definition into specified attribute
3311 : * inherited from the previous parents.
3312 : *
3313 : * Input arguments:
3314 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3315 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3316 : * 'newdef' is the new parent column/attribute definition to be merged.
3317 : *
3318 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3319 : *
3320 : * Notes:
3321 : * - The attribute is merged according to the rules laid out in the prologue
3322 : * of MergeAttributes().
3323 : * - If matching inherited attribute exists but the new attribute can not be
3324 : * merged into it, the function throws respective errors.
3325 : * - A partition inherits from only a single parent. Hence this function is
3326 : * applicable only to a regular inheritance.
3327 : */
3328 : static ColumnDef *
3329 302 : MergeInheritedAttribute(List *inh_columns,
3330 : int exist_attno,
3331 : const ColumnDef *newdef)
3332 : {
3333 302 : char *attributeName = newdef->colname;
3334 : ColumnDef *prevdef;
3335 : Oid prevtypeid,
3336 : newtypeid;
3337 : int32 prevtypmod,
3338 : newtypmod;
3339 : Oid prevcollid,
3340 : newcollid;
3341 :
3342 302 : ereport(NOTICE,
3343 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3344 : attributeName)));
3345 302 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3346 :
3347 : /*
3348 : * Must have the same type and typmod
3349 : */
3350 302 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3351 302 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3352 302 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3353 0 : ereport(ERROR,
3354 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3355 : errmsg("inherited column \"%s\" has a type conflict",
3356 : attributeName),
3357 : errdetail("%s versus %s",
3358 : format_type_with_typemod(prevtypeid, prevtypmod),
3359 : format_type_with_typemod(newtypeid, newtypmod))));
3360 :
3361 : /*
3362 : * Must have the same collation
3363 : */
3364 302 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3365 302 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3366 302 : if (prevcollid != newcollid)
3367 0 : ereport(ERROR,
3368 : (errcode(ERRCODE_COLLATION_MISMATCH),
3369 : errmsg("inherited column \"%s\" has a collation conflict",
3370 : attributeName),
3371 : errdetail("\"%s\" versus \"%s\"",
3372 : get_collation_name(prevcollid),
3373 : get_collation_name(newcollid))));
3374 :
3375 : /*
3376 : * Copy/check storage parameter
3377 : */
3378 302 : if (prevdef->storage == 0)
3379 0 : prevdef->storage = newdef->storage;
3380 302 : else if (prevdef->storage != newdef->storage)
3381 6 : ereport(ERROR,
3382 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3383 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3384 : attributeName),
3385 : errdetail("%s versus %s",
3386 : storage_name(prevdef->storage),
3387 : storage_name(newdef->storage))));
3388 :
3389 : /*
3390 : * Copy/check compression parameter
3391 : */
3392 296 : if (prevdef->compression == NULL)
3393 284 : prevdef->compression = newdef->compression;
3394 12 : else if (newdef->compression != NULL)
3395 : {
3396 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3397 6 : ereport(ERROR,
3398 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3399 : errmsg("column \"%s\" has a compression method conflict",
3400 : attributeName),
3401 : errdetail("%s versus %s",
3402 : prevdef->compression, newdef->compression)));
3403 : }
3404 :
3405 : /*
3406 : * Check for GENERATED conflicts
3407 : */
3408 290 : if (prevdef->generated != newdef->generated)
3409 12 : ereport(ERROR,
3410 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3411 : errmsg("inherited column \"%s\" has a generation conflict",
3412 : attributeName)));
3413 :
3414 : /*
3415 : * Default and other constraints are handled by the caller.
3416 : */
3417 :
3418 278 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3419 : &prevdef->inhcount))
3420 0 : ereport(ERROR,
3421 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3422 : errmsg("too many inheritance parents"));
3423 :
3424 278 : return prevdef;
3425 : }
3426 :
3427 : /*
3428 : * StoreCatalogInheritance
3429 : * Updates the system catalogs with proper inheritance information.
3430 : *
3431 : * supers is a list of the OIDs of the new relation's direct ancestors.
3432 : */
3433 : static void
3434 56108 : StoreCatalogInheritance(Oid relationId, List *supers,
3435 : bool child_is_partition)
3436 : {
3437 : Relation relation;
3438 : int32 seqNumber;
3439 : ListCell *entry;
3440 :
3441 : /*
3442 : * sanity checks
3443 : */
3444 : Assert(OidIsValid(relationId));
3445 :
3446 56108 : if (supers == NIL)
3447 47010 : return;
3448 :
3449 : /*
3450 : * Store INHERITS information in pg_inherits using direct ancestors only.
3451 : * Also enter dependencies on the direct ancestors, and make sure they are
3452 : * marked with relhassubclass = true.
3453 : *
3454 : * (Once upon a time, both direct and indirect ancestors were found here
3455 : * and then entered into pg_ipl. Since that catalog doesn't exist
3456 : * anymore, there's no need to look for indirect ancestors.)
3457 : */
3458 9098 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3459 :
3460 9098 : seqNumber = 1;
3461 18480 : foreach(entry, supers)
3462 : {
3463 9382 : Oid parentOid = lfirst_oid(entry);
3464 :
3465 9382 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3466 : child_is_partition);
3467 9382 : seqNumber++;
3468 : }
3469 :
3470 9098 : table_close(relation, RowExclusiveLock);
3471 : }
3472 :
3473 : /*
3474 : * Make catalog entries showing relationId as being an inheritance child
3475 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3476 : */
3477 : static void
3478 11578 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3479 : int32 seqNumber, Relation inhRelation,
3480 : bool child_is_partition)
3481 : {
3482 : ObjectAddress childobject,
3483 : parentobject;
3484 :
3485 : /* store the pg_inherits row */
3486 11578 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3487 :
3488 : /*
3489 : * Store a dependency too
3490 : */
3491 11578 : parentobject.classId = RelationRelationId;
3492 11578 : parentobject.objectId = parentOid;
3493 11578 : parentobject.objectSubId = 0;
3494 11578 : childobject.classId = RelationRelationId;
3495 11578 : childobject.objectId = relationId;
3496 11578 : childobject.objectSubId = 0;
3497 :
3498 11578 : recordDependencyOn(&childobject, &parentobject,
3499 : child_dependency_type(child_is_partition));
3500 :
3501 : /*
3502 : * Post creation hook of this inheritance. Since object_access_hook
3503 : * doesn't take multiple object identifiers, we relay oid of parent
3504 : * relation using auxiliary_id argument.
3505 : */
3506 11578 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3507 : relationId, 0,
3508 : parentOid, false);
3509 :
3510 : /*
3511 : * Mark the parent as having subclasses.
3512 : */
3513 11578 : SetRelationHasSubclass(parentOid, true);
3514 11578 : }
3515 :
3516 : /*
3517 : * Look for an existing column entry with the given name.
3518 : *
3519 : * Returns the index (starting with 1) if attribute already exists in columns,
3520 : * 0 if it doesn't.
3521 : */
3522 : static int
3523 20530 : findAttrByName(const char *attributeName, const List *columns)
3524 : {
3525 : ListCell *lc;
3526 20530 : int i = 1;
3527 :
3528 36640 : foreach(lc, columns)
3529 : {
3530 16698 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3531 588 : return i;
3532 :
3533 16110 : i++;
3534 : }
3535 19942 : return 0;
3536 : }
3537 :
3538 :
3539 : /*
3540 : * SetRelationHasSubclass
3541 : * Set the value of the relation's relhassubclass field in pg_class.
3542 : *
3543 : * It's always safe to set this field to true, because all SQL commands are
3544 : * ready to see true and then find no children. On the other hand, commands
3545 : * generally assume zero children if this is false.
3546 : *
3547 : * Caller must hold any self-exclusive lock until end of transaction. If the
3548 : * new value is false, caller must have acquired that lock before reading the
3549 : * evidence that justified the false value. That way, it properly waits if
3550 : * another backend is simultaneously concluding no need to change the tuple
3551 : * (new and old values are true).
3552 : *
3553 : * NOTE: an important side-effect of this operation is that an SI invalidation
3554 : * message is sent out to all backends --- including me --- causing plans
3555 : * referencing the relation to be rebuilt with the new list of children.
3556 : * This must happen even if we find that no change is needed in the pg_class
3557 : * row.
3558 : */
3559 : void
3560 14490 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3561 : {
3562 : Relation relationRelation;
3563 : HeapTuple tuple;
3564 : Form_pg_class classtuple;
3565 :
3566 : Assert(CheckRelationOidLockedByMe(relationId,
3567 : ShareUpdateExclusiveLock, false) ||
3568 : CheckRelationOidLockedByMe(relationId,
3569 : ShareRowExclusiveLock, true));
3570 :
3571 : /*
3572 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3573 : */
3574 14490 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3575 14490 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3576 14490 : if (!HeapTupleIsValid(tuple))
3577 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3578 14490 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3579 :
3580 14490 : if (classtuple->relhassubclass != relhassubclass)
3581 : {
3582 7328 : classtuple->relhassubclass = relhassubclass;
3583 7328 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3584 : }
3585 : else
3586 : {
3587 : /* no need to change tuple, but force relcache rebuild anyway */
3588 7162 : CacheInvalidateRelcacheByTuple(tuple);
3589 : }
3590 :
3591 14490 : heap_freetuple(tuple);
3592 14490 : table_close(relationRelation, RowExclusiveLock);
3593 14490 : }
3594 :
3595 : /*
3596 : * CheckRelationTableSpaceMove
3597 : * Check if relation can be moved to new tablespace.
3598 : *
3599 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3600 : *
3601 : * Returns true if the relation can be moved to the new tablespace; raises
3602 : * an error if it is not possible to do the move; returns false if the move
3603 : * would have no effect.
3604 : */
3605 : bool
3606 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3607 : {
3608 : Oid oldTableSpaceId;
3609 :
3610 : /*
3611 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3612 : * stored as 0.
3613 : */
3614 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3615 226 : if (newTableSpaceId == oldTableSpaceId ||
3616 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3617 10 : return false;
3618 :
3619 : /*
3620 : * We cannot support moving mapped relations into different tablespaces.
3621 : * (In particular this eliminates all shared catalogs.)
3622 : */
3623 216 : if (RelationIsMapped(rel))
3624 0 : ereport(ERROR,
3625 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3626 : errmsg("cannot move system relation \"%s\"",
3627 : RelationGetRelationName(rel))));
3628 :
3629 : /* Cannot move a non-shared relation into pg_global */
3630 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3631 12 : ereport(ERROR,
3632 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3633 : errmsg("only shared relations can be placed in pg_global tablespace")));
3634 :
3635 : /*
3636 : * Do not allow moving temp tables of other backends ... their local
3637 : * buffer manager is not going to cope.
3638 : */
3639 204 : if (RELATION_IS_OTHER_TEMP(rel))
3640 0 : ereport(ERROR,
3641 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3642 : errmsg("cannot move temporary tables of other sessions")));
3643 :
3644 204 : return true;
3645 : }
3646 :
3647 : /*
3648 : * SetRelationTableSpace
3649 : * Set new reltablespace and relfilenumber in pg_class entry.
3650 : *
3651 : * newTableSpaceId is the new tablespace for the relation, and
3652 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3653 : * InvalidRelFileNumber, this field is not updated.
3654 : *
3655 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3656 : *
3657 : * The caller of this routine had better check if a relation can be
3658 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3659 : * first, and is responsible for making the change visible with
3660 : * CommandCounterIncrement().
3661 : */
3662 : void
3663 204 : SetRelationTableSpace(Relation rel,
3664 : Oid newTableSpaceId,
3665 : RelFileNumber newRelFilenumber)
3666 : {
3667 : Relation pg_class;
3668 : HeapTuple tuple;
3669 : ItemPointerData otid;
3670 : Form_pg_class rd_rel;
3671 204 : Oid reloid = RelationGetRelid(rel);
3672 :
3673 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3674 :
3675 : /* Get a modifiable copy of the relation's pg_class row. */
3676 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3677 :
3678 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3679 204 : if (!HeapTupleIsValid(tuple))
3680 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3681 204 : otid = tuple->t_self;
3682 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3683 :
3684 : /* Update the pg_class row. */
3685 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3686 204 : InvalidOid : newTableSpaceId;
3687 204 : if (RelFileNumberIsValid(newRelFilenumber))
3688 160 : rd_rel->relfilenode = newRelFilenumber;
3689 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3690 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3691 :
3692 : /*
3693 : * Record dependency on tablespace. This is only required for relations
3694 : * that have no physical storage.
3695 : */
3696 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3697 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3698 : rd_rel->reltablespace);
3699 :
3700 204 : heap_freetuple(tuple);
3701 204 : table_close(pg_class, RowExclusiveLock);
3702 204 : }
3703 :
3704 : /*
3705 : * renameatt_check - basic sanity checks before attribute rename
3706 : */
3707 : static void
3708 1018 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3709 : {
3710 1018 : char relkind = classform->relkind;
3711 :
3712 1018 : if (classform->reloftype && !recursing)
3713 6 : ereport(ERROR,
3714 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3715 : errmsg("cannot rename column of typed table")));
3716 :
3717 : /*
3718 : * Renaming the columns of sequences or toast tables doesn't actually
3719 : * break anything from the system's point of view, since internal
3720 : * references are by attnum. But it doesn't seem right to allow users to
3721 : * change names that are hardcoded into the system, hence the following
3722 : * restriction.
3723 : */
3724 1012 : if (relkind != RELKIND_RELATION &&
3725 84 : relkind != RELKIND_VIEW &&
3726 84 : relkind != RELKIND_MATVIEW &&
3727 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3728 36 : relkind != RELKIND_INDEX &&
3729 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3730 0 : relkind != RELKIND_FOREIGN_TABLE &&
3731 : relkind != RELKIND_PARTITIONED_TABLE)
3732 0 : ereport(ERROR,
3733 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3734 : errmsg("cannot rename columns of relation \"%s\"",
3735 : NameStr(classform->relname)),
3736 : errdetail_relkind_not_supported(relkind)));
3737 :
3738 : /*
3739 : * permissions checking. only the owner of a class can change its schema.
3740 : */
3741 1012 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3742 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3743 0 : NameStr(classform->relname));
3744 1012 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3745 2 : ereport(ERROR,
3746 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3747 : errmsg("permission denied: \"%s\" is a system catalog",
3748 : NameStr(classform->relname))));
3749 1010 : }
3750 :
3751 : /*
3752 : * renameatt_internal - workhorse for renameatt
3753 : *
3754 : * Return value is the attribute number in the 'myrelid' relation.
3755 : */
3756 : static AttrNumber
3757 552 : renameatt_internal(Oid myrelid,
3758 : const char *oldattname,
3759 : const char *newattname,
3760 : bool recurse,
3761 : bool recursing,
3762 : int expected_parents,
3763 : DropBehavior behavior)
3764 : {
3765 : Relation targetrelation;
3766 : Relation attrelation;
3767 : HeapTuple atttup;
3768 : Form_pg_attribute attform;
3769 : AttrNumber attnum;
3770 :
3771 : /*
3772 : * Grab an exclusive lock on the target table, which we will NOT release
3773 : * until end of transaction.
3774 : */
3775 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3776 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3777 :
3778 : /*
3779 : * if the 'recurse' flag is set then we are supposed to rename this
3780 : * attribute in all classes that inherit from 'relname' (as well as in
3781 : * 'relname').
3782 : *
3783 : * any permissions or problems with duplicate attributes will cause the
3784 : * whole transaction to abort, which is what we want -- all or nothing.
3785 : */
3786 552 : if (recurse)
3787 : {
3788 : List *child_oids,
3789 : *child_numparents;
3790 : ListCell *lo,
3791 : *li;
3792 :
3793 : /*
3794 : * we need the number of parents for each child so that the recursive
3795 : * calls to renameatt() can determine whether there are any parents
3796 : * outside the inheritance hierarchy being processed.
3797 : */
3798 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3799 : &child_numparents);
3800 :
3801 : /*
3802 : * find_all_inheritors does the recursive search of the inheritance
3803 : * hierarchy, so all we have to do is process all of the relids in the
3804 : * list that it returns.
3805 : */
3806 734 : forboth(lo, child_oids, li, child_numparents)
3807 : {
3808 516 : Oid childrelid = lfirst_oid(lo);
3809 516 : int numparents = lfirst_int(li);
3810 :
3811 516 : if (childrelid == myrelid)
3812 248 : continue;
3813 : /* note we need not recurse again */
3814 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3815 : }
3816 : }
3817 : else
3818 : {
3819 : /*
3820 : * If we are told not to recurse, there had better not be any child
3821 : * tables; else the rename would put them out of step.
3822 : *
3823 : * expected_parents will only be 0 if we are not already recursing.
3824 : */
3825 340 : if (expected_parents == 0 &&
3826 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3827 12 : ereport(ERROR,
3828 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3829 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3830 : oldattname)));
3831 : }
3832 :
3833 : /* rename attributes in typed tables of composite type */
3834 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3835 : {
3836 : List *child_oids;
3837 : ListCell *lo;
3838 :
3839 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3840 24 : RelationGetRelationName(targetrelation),
3841 : behavior);
3842 :
3843 24 : foreach(lo, child_oids)
3844 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3845 : }
3846 :
3847 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3848 :
3849 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3850 504 : if (!HeapTupleIsValid(atttup))
3851 24 : ereport(ERROR,
3852 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3853 : errmsg("column \"%s\" does not exist",
3854 : oldattname)));
3855 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3856 :
3857 480 : attnum = attform->attnum;
3858 480 : if (attnum <= 0)
3859 0 : ereport(ERROR,
3860 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3861 : errmsg("cannot rename system column \"%s\"",
3862 : oldattname)));
3863 :
3864 : /*
3865 : * if the attribute is inherited, forbid the renaming. if this is a
3866 : * top-level call to renameatt(), then expected_parents will be 0, so the
3867 : * effect of this code will be to prohibit the renaming if the attribute
3868 : * is inherited at all. if this is a recursive call to renameatt(),
3869 : * expected_parents will be the number of parents the current relation has
3870 : * within the inheritance hierarchy being processed, so we'll prohibit the
3871 : * renaming only if there are additional parents from elsewhere.
3872 : */
3873 480 : if (attform->attinhcount > expected_parents)
3874 30 : ereport(ERROR,
3875 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3876 : errmsg("cannot rename inherited column \"%s\"",
3877 : oldattname)));
3878 :
3879 : /* new name should not already exist */
3880 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3881 :
3882 : /* apply the update */
3883 438 : namestrcpy(&(attform->attname), newattname);
3884 :
3885 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3886 :
3887 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3888 :
3889 438 : heap_freetuple(atttup);
3890 :
3891 438 : table_close(attrelation, RowExclusiveLock);
3892 :
3893 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3894 :
3895 438 : return attnum;
3896 : }
3897 :
3898 : /*
3899 : * Perform permissions and integrity checks before acquiring a relation lock.
3900 : */
3901 : static void
3902 420 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3903 : void *arg)
3904 : {
3905 : HeapTuple tuple;
3906 : Form_pg_class form;
3907 :
3908 420 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3909 420 : if (!HeapTupleIsValid(tuple))
3910 38 : return; /* concurrently dropped */
3911 382 : form = (Form_pg_class) GETSTRUCT(tuple);
3912 382 : renameatt_check(relid, form, false);
3913 374 : ReleaseSysCache(tuple);
3914 : }
3915 :
3916 : /*
3917 : * renameatt - changes the name of an attribute in a relation
3918 : *
3919 : * The returned ObjectAddress is that of the renamed column.
3920 : */
3921 : ObjectAddress
3922 316 : renameatt(RenameStmt *stmt)
3923 : {
3924 : Oid relid;
3925 : AttrNumber attnum;
3926 : ObjectAddress address;
3927 :
3928 : /* lock level taken here should match renameatt_internal */
3929 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3930 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3931 : RangeVarCallbackForRenameAttribute,
3932 : NULL);
3933 :
3934 302 : if (!OidIsValid(relid))
3935 : {
3936 24 : ereport(NOTICE,
3937 : (errmsg("relation \"%s\" does not exist, skipping",
3938 : stmt->relation->relname)));
3939 24 : return InvalidObjectAddress;
3940 : }
3941 :
3942 : attnum =
3943 278 : renameatt_internal(relid,
3944 278 : stmt->subname, /* old att name */
3945 278 : stmt->newname, /* new att name */
3946 278 : stmt->relation->inh, /* recursive? */
3947 : false, /* recursing? */
3948 : 0, /* expected inhcount */
3949 : stmt->behavior);
3950 :
3951 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3952 :
3953 194 : return address;
3954 : }
3955 :
3956 : /*
3957 : * same logic as renameatt_internal
3958 : */
3959 : static ObjectAddress
3960 90 : rename_constraint_internal(Oid myrelid,
3961 : Oid mytypid,
3962 : const char *oldconname,
3963 : const char *newconname,
3964 : bool recurse,
3965 : bool recursing,
3966 : int expected_parents)
3967 : {
3968 90 : Relation targetrelation = NULL;
3969 : Oid constraintOid;
3970 : HeapTuple tuple;
3971 : Form_pg_constraint con;
3972 : ObjectAddress address;
3973 :
3974 : Assert(!myrelid || !mytypid);
3975 :
3976 90 : if (mytypid)
3977 : {
3978 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3979 : }
3980 : else
3981 : {
3982 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3983 :
3984 : /*
3985 : * don't tell it whether we're recursing; we allow changing typed
3986 : * tables here
3987 : */
3988 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3989 :
3990 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3991 : }
3992 :
3993 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3994 90 : if (!HeapTupleIsValid(tuple))
3995 0 : elog(ERROR, "cache lookup failed for constraint %u",
3996 : constraintOid);
3997 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
3998 :
3999 90 : if (myrelid &&
4000 84 : (con->contype == CONSTRAINT_CHECK ||
4001 24 : con->contype == CONSTRAINT_NOTNULL) &&
4002 66 : !con->connoinherit)
4003 : {
4004 54 : if (recurse)
4005 : {
4006 : List *child_oids,
4007 : *child_numparents;
4008 : ListCell *lo,
4009 : *li;
4010 :
4011 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4012 : &child_numparents);
4013 :
4014 84 : forboth(lo, child_oids, li, child_numparents)
4015 : {
4016 48 : Oid childrelid = lfirst_oid(lo);
4017 48 : int numparents = lfirst_int(li);
4018 :
4019 48 : if (childrelid == myrelid)
4020 36 : continue;
4021 :
4022 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4023 : }
4024 : }
4025 : else
4026 : {
4027 24 : if (expected_parents == 0 &&
4028 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4029 6 : ereport(ERROR,
4030 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4031 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4032 : oldconname)));
4033 : }
4034 :
4035 48 : if (con->coninhcount > expected_parents)
4036 6 : ereport(ERROR,
4037 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4038 : errmsg("cannot rename inherited constraint \"%s\"",
4039 : oldconname)));
4040 : }
4041 :
4042 78 : if (con->conindid
4043 18 : && (con->contype == CONSTRAINT_PRIMARY
4044 6 : || con->contype == CONSTRAINT_UNIQUE
4045 0 : || con->contype == CONSTRAINT_EXCLUSION))
4046 : /* rename the index; this renames the constraint as well */
4047 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4048 : else
4049 60 : RenameConstraintById(constraintOid, newconname);
4050 :
4051 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4052 :
4053 78 : ReleaseSysCache(tuple);
4054 :
4055 78 : if (targetrelation)
4056 : {
4057 : /*
4058 : * Invalidate relcache so as others can see the new constraint name.
4059 : */
4060 72 : CacheInvalidateRelcache(targetrelation);
4061 :
4062 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4063 : }
4064 :
4065 78 : return address;
4066 : }
4067 :
4068 : ObjectAddress
4069 84 : RenameConstraint(RenameStmt *stmt)
4070 : {
4071 84 : Oid relid = InvalidOid;
4072 84 : Oid typid = InvalidOid;
4073 :
4074 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4075 : {
4076 : Relation rel;
4077 : HeapTuple tup;
4078 :
4079 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4080 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4081 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4082 6 : if (!HeapTupleIsValid(tup))
4083 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4084 6 : checkDomainOwner(tup);
4085 6 : ReleaseSysCache(tup);
4086 6 : table_close(rel, NoLock);
4087 : }
4088 : else
4089 : {
4090 : /* lock level taken here should match rename_constraint_internal */
4091 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4092 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4093 : RangeVarCallbackForRenameAttribute,
4094 : NULL);
4095 78 : if (!OidIsValid(relid))
4096 : {
4097 6 : ereport(NOTICE,
4098 : (errmsg("relation \"%s\" does not exist, skipping",
4099 : stmt->relation->relname)));
4100 6 : return InvalidObjectAddress;
4101 : }
4102 : }
4103 :
4104 : return
4105 78 : rename_constraint_internal(relid, typid,
4106 78 : stmt->subname,
4107 78 : stmt->newname,
4108 150 : (stmt->relation &&
4109 72 : stmt->relation->inh), /* recursive? */
4110 : false, /* recursing? */
4111 : 0 /* expected inhcount */ );
4112 : }
4113 :
4114 : /*
4115 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4116 : * RENAME
4117 : */
4118 : ObjectAddress
4119 510 : RenameRelation(RenameStmt *stmt)
4120 : {
4121 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4122 : Oid relid;
4123 : ObjectAddress address;
4124 :
4125 : /*
4126 : * Grab an exclusive lock on the target table, index, sequence, view,
4127 : * materialized view, or foreign table, which we will NOT release until
4128 : * end of transaction.
4129 : *
4130 : * Lock level used here should match RenameRelationInternal, to avoid lock
4131 : * escalation. However, because ALTER INDEX can be used with any relation
4132 : * type, we mustn't believe without verification.
4133 : */
4134 : for (;;)
4135 12 : {
4136 : LOCKMODE lockmode;
4137 : char relkind;
4138 : bool obj_is_index;
4139 :
4140 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4141 :
4142 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4143 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4144 : RangeVarCallbackForAlterRelation,
4145 : (void *) stmt);
4146 :
4147 472 : if (!OidIsValid(relid))
4148 : {
4149 18 : ereport(NOTICE,
4150 : (errmsg("relation \"%s\" does not exist, skipping",
4151 : stmt->relation->relname)));
4152 18 : return InvalidObjectAddress;
4153 : }
4154 :
4155 : /*
4156 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4157 : * to rename a table), but we might've used the wrong lock level. If
4158 : * that happens, retry with the correct lock level. We don't bother
4159 : * if we already acquired AccessExclusiveLock with an index, however.
4160 : */
4161 454 : relkind = get_rel_relkind(relid);
4162 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4163 : relkind == RELKIND_PARTITIONED_INDEX);
4164 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4165 : break;
4166 :
4167 12 : UnlockRelationOid(relid, lockmode);
4168 12 : is_index_stmt = obj_is_index;
4169 : }
4170 :
4171 : /* Do the work */
4172 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4173 :
4174 430 : ObjectAddressSet(address, RelationRelationId, relid);
4175 :
4176 430 : return address;
4177 : }
4178 :
4179 : /*
4180 : * RenameRelationInternal - change the name of a relation
4181 : */
4182 : void
4183 1638 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4184 : {
4185 : Relation targetrelation;
4186 : Relation relrelation; /* for RELATION relation */
4187 : ItemPointerData otid;
4188 : HeapTuple reltup;
4189 : Form_pg_class relform;
4190 : Oid namespaceId;
4191 :
4192 : /*
4193 : * Grab a lock on the target relation, which we will NOT release until end
4194 : * of transaction. We need at least a self-exclusive lock so that
4195 : * concurrent DDL doesn't overwrite the rename if they start updating
4196 : * while still seeing the old version. The lock also guards against
4197 : * triggering relcache reloads in concurrent sessions, which might not
4198 : * handle this information changing under them. For indexes, we can use a
4199 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4200 : * specially.
4201 : */
4202 1638 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4203 1638 : namespaceId = RelationGetNamespace(targetrelation);
4204 :
4205 : /*
4206 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4207 : */
4208 1638 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4209 :
4210 1638 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4211 1638 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4212 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4213 1638 : otid = reltup->t_self;
4214 1638 : relform = (Form_pg_class) GETSTRUCT(reltup);
4215 :
4216 1638 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4217 12 : ereport(ERROR,
4218 : (errcode(ERRCODE_DUPLICATE_TABLE),
4219 : errmsg("relation \"%s\" already exists",
4220 : newrelname)));
4221 :
4222 : /*
4223 : * RenameRelation is careful not to believe the caller's idea of the
4224 : * relation kind being handled. We don't have to worry about this, but
4225 : * let's not be totally oblivious to it. We can process an index as
4226 : * not-an-index, but not the other way around.
4227 : */
4228 : Assert(!is_index ||
4229 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4230 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4231 :
4232 : /*
4233 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4234 : * because it's a copy...)
4235 : */
4236 1626 : namestrcpy(&(relform->relname), newrelname);
4237 :
4238 1626 : CatalogTupleUpdate(relrelation, &otid, reltup);
4239 1626 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4240 :
4241 1626 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4242 : InvalidOid, is_internal);
4243 :
4244 1626 : heap_freetuple(reltup);
4245 1626 : table_close(relrelation, RowExclusiveLock);
4246 :
4247 : /*
4248 : * Also rename the associated type, if any.
4249 : */
4250 1626 : if (OidIsValid(targetrelation->rd_rel->reltype))
4251 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4252 : newrelname, namespaceId);
4253 :
4254 : /*
4255 : * Also rename the associated constraint, if any.
4256 : */
4257 1626 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4258 854 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4259 : {
4260 790 : Oid constraintId = get_index_constraint(myrelid);
4261 :
4262 790 : if (OidIsValid(constraintId))
4263 36 : RenameConstraintById(constraintId, newrelname);
4264 : }
4265 :
4266 : /*
4267 : * Close rel, but keep lock!
4268 : */
4269 1626 : relation_close(targetrelation, NoLock);
4270 1626 : }
4271 :
4272 : /*
4273 : * ResetRelRewrite - reset relrewrite
4274 : */
4275 : void
4276 578 : ResetRelRewrite(Oid myrelid)
4277 : {
4278 : Relation relrelation; /* for RELATION relation */
4279 : HeapTuple reltup;
4280 : Form_pg_class relform;
4281 :
4282 : /*
4283 : * Find relation's pg_class tuple.
4284 : */
4285 578 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4286 :
4287 578 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4288 578 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4289 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4290 578 : relform = (Form_pg_class) GETSTRUCT(reltup);
4291 :
4292 : /*
4293 : * Update pg_class tuple.
4294 : */
4295 578 : relform->relrewrite = InvalidOid;
4296 :
4297 578 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4298 :
4299 578 : heap_freetuple(reltup);
4300 578 : table_close(relrelation, RowExclusiveLock);
4301 578 : }
4302 :
4303 : /*
4304 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4305 : * any open reference to the target table besides the one just acquired by
4306 : * the calling command; this implies there's an open cursor or active plan.
4307 : * We need this check because our lock doesn't protect us against stomping
4308 : * on our own foot, only other people's feet!
4309 : *
4310 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4311 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4312 : * possibly be relaxed to only error out for certain types of alterations.
4313 : * But the use-case for allowing any of these things is not obvious, so we
4314 : * won't work hard at it for now.
4315 : *
4316 : * We also reject these commands if there are any pending AFTER trigger events
4317 : * for the rel. This is certainly necessary for the rewriting variants of
4318 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4319 : * events would try to fetch the wrong tuples. It might be overly cautious
4320 : * in other cases, but again it seems better to err on the side of paranoia.
4321 : *
4322 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4323 : * we are worried about active indexscans on the index. The trigger-event
4324 : * check can be skipped, since we are doing no damage to the parent table.
4325 : *
4326 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4327 : */
4328 : void
4329 157374 : CheckTableNotInUse(Relation rel, const char *stmt)
4330 : {
4331 : int expected_refcnt;
4332 :
4333 157374 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4334 157374 : if (rel->rd_refcnt != expected_refcnt)
4335 30 : ereport(ERROR,
4336 : (errcode(ERRCODE_OBJECT_IN_USE),
4337 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4338 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4339 : stmt, RelationGetRelationName(rel))));
4340 :
4341 157344 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4342 254564 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4343 126262 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4344 18 : ereport(ERROR,
4345 : (errcode(ERRCODE_OBJECT_IN_USE),
4346 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4347 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4348 : stmt, RelationGetRelationName(rel))));
4349 157326 : }
4350 :
4351 : /*
4352 : * CheckAlterTableIsSafe
4353 : * Verify that it's safe to allow ALTER TABLE on this relation.
4354 : *
4355 : * This consists of CheckTableNotInUse() plus a check that the relation
4356 : * isn't another session's temp table. We must split out the temp-table
4357 : * check because there are callers of CheckTableNotInUse() that don't want
4358 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4359 : * an orphaned temp schema.) Compare truncate_check_activity().
4360 : */
4361 : static void
4362 55282 : CheckAlterTableIsSafe(Relation rel)
4363 : {
4364 : /*
4365 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4366 : * manager is not going to cope if we need to change the table's contents.
4367 : * Even if we don't, there may be optimizations that assume temp tables
4368 : * aren't subject to such interference.
4369 : */
4370 55282 : if (RELATION_IS_OTHER_TEMP(rel))
4371 0 : ereport(ERROR,
4372 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4373 : errmsg("cannot alter temporary tables of other sessions")));
4374 :
4375 : /*
4376 : * Also check for active uses of the relation in the current transaction,
4377 : * including open scans and pending AFTER trigger events.
4378 : */
4379 55282 : CheckTableNotInUse(rel, "ALTER TABLE");
4380 55246 : }
4381 :
4382 : /*
4383 : * AlterTableLookupRelation
4384 : * Look up, and lock, the OID for the relation named by an alter table
4385 : * statement.
4386 : */
4387 : Oid
4388 29046 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4389 : {
4390 58006 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4391 29046 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4392 : RangeVarCallbackForAlterRelation,
4393 : (void *) stmt);
4394 : }
4395 :
4396 : /*
4397 : * AlterTable
4398 : * Execute ALTER TABLE, which can be a list of subcommands
4399 : *
4400 : * ALTER TABLE is performed in three phases:
4401 : * 1. Examine subcommands and perform pre-transformation checking.
4402 : * 2. Validate and transform subcommands, and update system catalogs.
4403 : * 3. Scan table(s) to check new constraints, and optionally recopy
4404 : * the data into new table(s).
4405 : * Phase 3 is not performed unless one or more of the subcommands requires
4406 : * it. The intention of this design is to allow multiple independent
4407 : * updates of the table schema to be performed with only one pass over the
4408 : * data.
4409 : *
4410 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4411 : * each table to be affected (there may be multiple affected tables if the
4412 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4413 : * validation of the subcommands. Because earlier subcommands may change
4414 : * the catalog state seen by later commands, there are limits to what can
4415 : * be done in this phase. Generally, this phase acquires table locks,
4416 : * checks permissions and relkind, and recurses to find child tables.
4417 : *
4418 : * ATRewriteCatalogs performs phase 2 for each affected table.
4419 : * Certain subcommands need to be performed before others to avoid
4420 : * unnecessary conflicts; for example, DROP COLUMN should come before
4421 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4422 : * lists, one for each logical "pass" of phase 2.
4423 : *
4424 : * ATRewriteTables performs phase 3 for those tables that need it.
4425 : *
4426 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4427 : * since phase 1 already does it. However, for certain subcommand types
4428 : * it is only possible to determine how to recurse at phase 2 time; for
4429 : * those cases, phase 1 sets the cmd->recurse flag.
4430 : *
4431 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4432 : * the whole operation; we don't have to do anything special to clean up.
4433 : *
4434 : * The caller must lock the relation, with an appropriate lock level
4435 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4436 : * or higher. We pass the lock level down
4437 : * so that we can apply it recursively to inherited tables. Note that the
4438 : * lock level we want as we recurse might well be higher than required for
4439 : * that specific subcommand. So we pass down the overall lock requirement,
4440 : * rather than reassess it at lower levels.
4441 : *
4442 : * The caller also provides a "context" which is to be passed back to
4443 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4444 : * Some of the fields therein, such as the relid, are used here as well.
4445 : */
4446 : void
4447 28822 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4448 : AlterTableUtilityContext *context)
4449 : {
4450 : Relation rel;
4451 :
4452 : /* Caller is required to provide an adequate lock. */
4453 28822 : rel = relation_open(context->relid, NoLock);
4454 :
4455 28822 : CheckAlterTableIsSafe(rel);
4456 :
4457 28804 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4458 25626 : }
4459 :
4460 : /*
4461 : * AlterTableInternal
4462 : *
4463 : * ALTER TABLE with target specified by OID
4464 : *
4465 : * We do not reject if the relation is already open, because it's quite
4466 : * likely that one or more layers of caller have it open. That means it
4467 : * is unsafe to use this entry point for alterations that could break
4468 : * existing query plans. On the assumption it's not used for such, we
4469 : * don't have to reject pending AFTER triggers, either.
4470 : *
4471 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4472 : * used for any subcommand types that require parse transformation or
4473 : * could generate subcommands that have to be passed to ProcessUtility.
4474 : */
4475 : void
4476 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4477 : {
4478 : Relation rel;
4479 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4480 :
4481 278 : rel = relation_open(relid, lockmode);
4482 :
4483 278 : EventTriggerAlterTableRelid(relid);
4484 :
4485 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4486 278 : }
4487 :
4488 : /*
4489 : * AlterTableGetLockLevel
4490 : *
4491 : * Sets the overall lock level required for the supplied list of subcommands.
4492 : * Policy for doing this set according to needs of AlterTable(), see
4493 : * comments there for overall explanation.
4494 : *
4495 : * Function is called before and after parsing, so it must give same
4496 : * answer each time it is called. Some subcommands are transformed
4497 : * into other subcommand types, so the transform must never be made to a
4498 : * lower lock level than previously assigned. All transforms are noted below.
4499 : *
4500 : * Since this is called before we lock the table we cannot use table metadata
4501 : * to influence the type of lock we acquire.
4502 : *
4503 : * There should be no lockmodes hardcoded into the subcommand functions. All
4504 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4505 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4506 : * and does not travel through this section of code and cannot be combined with
4507 : * any of the subcommands given here.
4508 : *
4509 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4510 : * so any changes that might affect SELECTs running on standbys need to use
4511 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4512 : * have a solution for that also.
4513 : *
4514 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4515 : * that takes a lock less than AccessExclusiveLock can change object definitions
4516 : * while pg_dump is running. Be careful to check that the appropriate data is
4517 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4518 : * otherwise we might end up with an inconsistent dump that can't restore.
4519 : */
4520 : LOCKMODE
4521 29324 : AlterTableGetLockLevel(List *cmds)
4522 : {
4523 : /*
4524 : * This only works if we read catalog tables using MVCC snapshots.
4525 : */
4526 : ListCell *lcmd;
4527 29324 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4528 :
4529 59694 : foreach(lcmd, cmds)
4530 : {
4531 30370 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4532 30370 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4533 :
4534 30370 : switch (cmd->subtype)
4535 : {
4536 : /*
4537 : * These subcommands rewrite the heap, so require full locks.
4538 : */
4539 3248 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4540 : * to SELECT */
4541 : case AT_SetAccessMethod: /* must rewrite heap */
4542 : case AT_SetTableSpace: /* must rewrite heap */
4543 : case AT_AlterColumnType: /* must rewrite heap */
4544 3248 : cmd_lockmode = AccessExclusiveLock;
4545 3248 : break;
4546 :
4547 : /*
4548 : * These subcommands may require addition of toast tables. If
4549 : * we add a toast table to a table currently being scanned, we
4550 : * might miss data added to the new toast table by concurrent
4551 : * insert transactions.
4552 : */
4553 212 : case AT_SetStorage: /* may add toast tables, see
4554 : * ATRewriteCatalogs() */
4555 212 : cmd_lockmode = AccessExclusiveLock;
4556 212 : break;
4557 :
4558 : /*
4559 : * Removing constraints can affect SELECTs that have been
4560 : * optimized assuming the constraint holds true. See also
4561 : * CloneFkReferenced.
4562 : */
4563 1116 : case AT_DropConstraint: /* as DROP INDEX */
4564 : case AT_DropNotNull: /* may change some SQL plans */
4565 1116 : cmd_lockmode = AccessExclusiveLock;
4566 1116 : break;
4567 :
4568 : /*
4569 : * Subcommands that may be visible to concurrent SELECTs
4570 : */
4571 1698 : case AT_DropColumn: /* change visible to SELECT */
4572 : case AT_AddColumnToView: /* CREATE VIEW */
4573 : case AT_DropOids: /* used to equiv to DropColumn */
4574 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4575 : case AT_EnableReplicaRule: /* may change SELECT rules */
4576 : case AT_EnableRule: /* may change SELECT rules */
4577 : case AT_DisableRule: /* may change SELECT rules */
4578 1698 : cmd_lockmode = AccessExclusiveLock;
4579 1698 : break;
4580 :
4581 : /*
4582 : * Changing owner may remove implicit SELECT privileges
4583 : */
4584 1818 : case AT_ChangeOwner: /* change visible to SELECT */
4585 1818 : cmd_lockmode = AccessExclusiveLock;
4586 1818 : break;
4587 :
4588 : /*
4589 : * Changing foreign table options may affect optimization.
4590 : */
4591 246 : case AT_GenericOptions:
4592 : case AT_AlterColumnGenericOptions:
4593 246 : cmd_lockmode = AccessExclusiveLock;
4594 246 : break;
4595 :
4596 : /*
4597 : * These subcommands affect write operations only.
4598 : */
4599 340 : case AT_EnableTrig:
4600 : case AT_EnableAlwaysTrig:
4601 : case AT_EnableReplicaTrig:
4602 : case AT_EnableTrigAll:
4603 : case AT_EnableTrigUser:
4604 : case AT_DisableTrig:
4605 : case AT_DisableTrigAll:
4606 : case AT_DisableTrigUser:
4607 340 : cmd_lockmode = ShareRowExclusiveLock;
4608 340 : break;
4609 :
4610 : /*
4611 : * These subcommands affect write operations only. XXX
4612 : * Theoretically, these could be ShareRowExclusiveLock.
4613 : */
4614 2420 : case AT_ColumnDefault:
4615 : case AT_CookedColumnDefault:
4616 : case AT_AlterConstraint:
4617 : case AT_AddIndex: /* from ADD CONSTRAINT */
4618 : case AT_AddIndexConstraint:
4619 : case AT_ReplicaIdentity:
4620 : case AT_SetNotNull:
4621 : case AT_EnableRowSecurity:
4622 : case AT_DisableRowSecurity:
4623 : case AT_ForceRowSecurity:
4624 : case AT_NoForceRowSecurity:
4625 : case AT_AddIdentity:
4626 : case AT_DropIdentity:
4627 : case AT_SetIdentity:
4628 : case AT_SetExpression:
4629 : case AT_DropExpression:
4630 : case AT_SetCompression:
4631 2420 : cmd_lockmode = AccessExclusiveLock;
4632 2420 : break;
4633 :
4634 13890 : case AT_AddConstraint:
4635 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4636 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4637 13890 : if (IsA(cmd->def, Constraint))
4638 : {
4639 13890 : Constraint *con = (Constraint *) cmd->def;
4640 :
4641 13890 : switch (con->contype)
4642 : {
4643 10742 : case CONSTR_EXCLUSION:
4644 : case CONSTR_PRIMARY:
4645 : case CONSTR_UNIQUE:
4646 :
4647 : /*
4648 : * Cases essentially the same as CREATE INDEX. We
4649 : * could reduce the lock strength to ShareLock if
4650 : * we can work out how to allow concurrent catalog
4651 : * updates. XXX Might be set down to
4652 : * ShareRowExclusiveLock but requires further
4653 : * analysis.
4654 : */
4655 10742 : cmd_lockmode = AccessExclusiveLock;
4656 10742 : break;
4657 2422 : case CONSTR_FOREIGN:
4658 :
4659 : /*
4660 : * We add triggers to both tables when we add a
4661 : * Foreign Key, so the lock level must be at least
4662 : * as strong as CREATE TRIGGER.
4663 : */
4664 2422 : cmd_lockmode = ShareRowExclusiveLock;
4665 2422 : break;
4666 :
4667 726 : default:
4668 726 : cmd_lockmode = AccessExclusiveLock;
4669 : }
4670 0 : }
4671 13890 : break;
4672 :
4673 : /*
4674 : * These subcommands affect inheritance behaviour. Queries
4675 : * started before us will continue to see the old inheritance
4676 : * behaviour, while queries started after we commit will see
4677 : * new behaviour. No need to prevent reads or writes to the
4678 : * subtable while we hook it up though. Changing the TupDesc
4679 : * may be a problem, so keep highest lock.
4680 : */
4681 420 : case AT_AddInherit:
4682 : case AT_DropInherit:
4683 420 : cmd_lockmode = AccessExclusiveLock;
4684 420 : break;
4685 :
4686 : /*
4687 : * These subcommands affect implicit row type conversion. They
4688 : * have affects similar to CREATE/DROP CAST on queries. don't
4689 : * provide for invalidating parse trees as a result of such
4690 : * changes, so we keep these at AccessExclusiveLock.
4691 : */
4692 72 : case AT_AddOf:
4693 : case AT_DropOf:
4694 72 : cmd_lockmode = AccessExclusiveLock;
4695 72 : break;
4696 :
4697 : /*
4698 : * Only used by CREATE OR REPLACE VIEW which must conflict
4699 : * with an SELECTs currently using the view.
4700 : */
4701 194 : case AT_ReplaceRelOptions:
4702 194 : cmd_lockmode = AccessExclusiveLock;
4703 194 : break;
4704 :
4705 : /*
4706 : * These subcommands affect general strategies for performance
4707 : * and maintenance, though don't change the semantic results
4708 : * from normal data reads and writes. Delaying an ALTER TABLE
4709 : * behind currently active writes only delays the point where
4710 : * the new strategy begins to take effect, so there is no
4711 : * benefit in waiting. In this case the minimum restriction
4712 : * applies: we don't currently allow concurrent catalog
4713 : * updates.
4714 : */
4715 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4716 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4717 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4718 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4719 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4720 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4721 234 : break;
4722 :
4723 112 : case AT_SetLogged:
4724 : case AT_SetUnLogged:
4725 112 : cmd_lockmode = AccessExclusiveLock;
4726 112 : break;
4727 :
4728 388 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4729 388 : cmd_lockmode = ShareUpdateExclusiveLock;
4730 388 : break;
4731 :
4732 : /*
4733 : * Rel options are more complex than first appears. Options
4734 : * are set here for tables, views and indexes; for historical
4735 : * reasons these can all be used with ALTER TABLE, so we can't
4736 : * decide between them using the basic grammar.
4737 : */
4738 752 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4739 : * getTables() */
4740 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4741 : * getTables() */
4742 752 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4743 752 : break;
4744 :
4745 2626 : case AT_AttachPartition:
4746 2626 : cmd_lockmode = ShareUpdateExclusiveLock;
4747 2626 : break;
4748 :
4749 564 : case AT_DetachPartition:
4750 564 : if (((PartitionCmd *) cmd->def)->concurrent)
4751 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4752 : else
4753 400 : cmd_lockmode = AccessExclusiveLock;
4754 564 : break;
4755 :
4756 20 : case AT_DetachPartitionFinalize:
4757 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4758 20 : break;
4759 :
4760 0 : default: /* oops */
4761 0 : elog(ERROR, "unrecognized alter table type: %d",
4762 : (int) cmd->subtype);
4763 : break;
4764 : }
4765 :
4766 : /*
4767 : * Take the greatest lockmode from any subcommand
4768 : */
4769 30370 : if (cmd_lockmode > lockmode)
4770 25326 : lockmode = cmd_lockmode;
4771 : }
4772 :
4773 29324 : return lockmode;
4774 : }
4775 :
4776 : /*
4777 : * ATController provides top level control over the phases.
4778 : *
4779 : * parsetree is passed in to allow it to be passed to event triggers
4780 : * when requested.
4781 : */
4782 : static void
4783 29082 : ATController(AlterTableStmt *parsetree,
4784 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4785 : AlterTableUtilityContext *context)
4786 : {
4787 29082 : List *wqueue = NIL;
4788 : ListCell *lcmd;
4789 :
4790 : /* Phase 1: preliminary examination of commands, create work queue */
4791 58884 : foreach(lcmd, cmds)
4792 : {
4793 30122 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4794 :
4795 30122 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4796 : }
4797 :
4798 : /* Close the relation, but keep lock until commit */
4799 28762 : relation_close(rel, NoLock);
4800 :
4801 : /* Phase 2: update system catalogs */
4802 28762 : ATRewriteCatalogs(&wqueue, lockmode, context);
4803 :
4804 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4805 26234 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4806 25904 : }
4807 :
4808 : /*
4809 : * ATPrepCmd
4810 : *
4811 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4812 : * recursion and permission checks.
4813 : *
4814 : * Caller must have acquired appropriate lock type on relation already.
4815 : * This lock should be held until commit.
4816 : */
4817 : static void
4818 31484 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4819 : bool recurse, bool recursing, LOCKMODE lockmode,
4820 : AlterTableUtilityContext *context)
4821 : {
4822 : AlteredTableInfo *tab;
4823 31484 : AlterTablePass pass = AT_PASS_UNSET;
4824 :
4825 : /* Find or create work queue entry for this table */
4826 31484 : tab = ATGetQueueEntry(wqueue, rel);
4827 :
4828 : /*
4829 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4830 : * partitions that are pending detach.
4831 : */
4832 31484 : if (rel->rd_rel->relispartition &&
4833 2712 : cmd->subtype != AT_DetachPartitionFinalize &&
4834 1356 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4835 2 : ereport(ERROR,
4836 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4837 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4838 : RelationGetRelationName(rel)),
4839 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4840 :
4841 : /*
4842 : * Copy the original subcommand for each table, so we can scribble on it.
4843 : * This avoids conflicts when different child tables need to make
4844 : * different parse transformations (for example, the same column may have
4845 : * different column numbers in different children).
4846 : */
4847 31482 : cmd = copyObject(cmd);
4848 :
4849 : /*
4850 : * Do permissions and relkind checking, recursion to child tables if
4851 : * needed, and any additional phase-1 processing needed. (But beware of
4852 : * adding any processing that looks at table details that another
4853 : * subcommand could change. In some cases we reject multiple subcommands
4854 : * that could try to change the same state in contrary ways.)
4855 : */
4856 31482 : switch (cmd->subtype)
4857 : {
4858 1978 : case AT_AddColumn: /* ADD COLUMN */
4859 1978 : ATSimplePermissions(cmd->subtype, rel,
4860 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4861 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4862 1978 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4863 : lockmode, context);
4864 : /* Recursion occurs during execution phase */
4865 1966 : pass = AT_PASS_ADD_COL;
4866 1966 : break;
4867 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4868 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4869 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4870 : lockmode, context);
4871 : /* Recursion occurs during execution phase */
4872 24 : pass = AT_PASS_ADD_COL;
4873 24 : break;
4874 598 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4875 :
4876 : /*
4877 : * We allow defaults on views so that INSERT into a view can have
4878 : * default-ish behavior. This works because the rewriter
4879 : * substitutes default values into INSERTs before it expands
4880 : * rules.
4881 : */
4882 598 : ATSimplePermissions(cmd->subtype, rel,
4883 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4884 : ATT_FOREIGN_TABLE);
4885 598 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4886 : /* No command-specific prep needed */
4887 598 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4888 598 : break;
4889 56 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4890 : /* This is currently used only in CREATE TABLE */
4891 : /* (so the permission check really isn't necessary) */
4892 56 : ATSimplePermissions(cmd->subtype, rel,
4893 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4894 : /* This command never recurses */
4895 56 : pass = AT_PASS_ADD_OTHERCONSTR;
4896 56 : break;
4897 160 : case AT_AddIdentity:
4898 160 : ATSimplePermissions(cmd->subtype, rel,
4899 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4900 : ATT_FOREIGN_TABLE);
4901 : /* Set up recursion for phase 2; no other prep needed */
4902 160 : if (recurse)
4903 154 : cmd->recurse = true;
4904 160 : pass = AT_PASS_ADD_OTHERCONSTR;
4905 160 : break;
4906 62 : case AT_SetIdentity:
4907 62 : ATSimplePermissions(cmd->subtype, rel,
4908 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4909 : ATT_FOREIGN_TABLE);
4910 : /* Set up recursion for phase 2; no other prep needed */
4911 62 : if (recurse)
4912 56 : cmd->recurse = true;
4913 : /* This should run after AddIdentity, so do it in MISC pass */
4914 62 : pass = AT_PASS_MISC;
4915 62 : break;
4916 56 : case AT_DropIdentity:
4917 56 : ATSimplePermissions(cmd->subtype, rel,
4918 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4919 : ATT_FOREIGN_TABLE);
4920 : /* Set up recursion for phase 2; no other prep needed */
4921 56 : if (recurse)
4922 50 : cmd->recurse = true;
4923 56 : pass = AT_PASS_DROP;
4924 56 : break;
4925 256 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4926 256 : ATSimplePermissions(cmd->subtype, rel,
4927 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4928 : /* Set up recursion for phase 2; no other prep needed */
4929 250 : if (recurse)
4930 232 : cmd->recurse = true;
4931 250 : pass = AT_PASS_DROP;
4932 250 : break;
4933 378 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4934 378 : ATSimplePermissions(cmd->subtype, rel,
4935 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4936 : /* Set up recursion for phase 2; no other prep needed */
4937 372 : if (recurse)
4938 348 : cmd->recurse = true;
4939 372 : pass = AT_PASS_COL_ATTRS;
4940 372 : break;
4941 84 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4942 84 : ATSimplePermissions(cmd->subtype, rel,
4943 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4944 84 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4945 84 : pass = AT_PASS_SET_EXPRESSION;
4946 84 : break;
4947 44 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4948 44 : ATSimplePermissions(cmd->subtype, rel,
4949 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4950 44 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4951 44 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4952 32 : pass = AT_PASS_DROP;
4953 32 : break;
4954 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4955 164 : ATSimplePermissions(cmd->subtype, rel,
4956 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
4957 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4958 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4959 : /* No command-specific prep needed */
4960 164 : pass = AT_PASS_MISC;
4961 164 : break;
4962 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4963 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4964 44 : ATSimplePermissions(cmd->subtype, rel,
4965 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4966 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
4967 : /* This command never recurses */
4968 32 : pass = AT_PASS_MISC;
4969 32 : break;
4970 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4971 234 : ATSimplePermissions(cmd->subtype, rel,
4972 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4973 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
4974 234 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4975 : /* No command-specific prep needed */
4976 234 : pass = AT_PASS_MISC;
4977 234 : break;
4978 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4979 68 : ATSimplePermissions(cmd->subtype, rel,
4980 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
4981 : /* This command never recurses */
4982 : /* No command-specific prep needed */
4983 68 : pass = AT_PASS_MISC;
4984 68 : break;
4985 1604 : case AT_DropColumn: /* DROP COLUMN */
4986 1604 : ATSimplePermissions(cmd->subtype, rel,
4987 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4988 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4989 1598 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4990 : lockmode, context);
4991 : /* Recursion occurs during execution phase */
4992 1586 : pass = AT_PASS_DROP;
4993 1586 : break;
4994 0 : case AT_AddIndex: /* ADD INDEX */
4995 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
4996 : /* This command never recurses */
4997 : /* No command-specific prep needed */
4998 0 : pass = AT_PASS_ADD_INDEX;
4999 0 : break;
5000 14812 : case AT_AddConstraint: /* ADD CONSTRAINT */
5001 14812 : ATSimplePermissions(cmd->subtype, rel,
5002 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5003 14812 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5004 14806 : if (recurse)
5005 : {
5006 : /* recurses at exec time; lock descendants and set flag */
5007 14458 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5008 14458 : cmd->recurse = true;
5009 : }
5010 14806 : pass = AT_PASS_ADD_CONSTR;
5011 14806 : break;
5012 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5013 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5014 : /* This command never recurses */
5015 : /* No command-specific prep needed */
5016 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5017 0 : break;
5018 822 : case AT_DropConstraint: /* DROP CONSTRAINT */
5019 822 : ATSimplePermissions(cmd->subtype, rel,
5020 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5021 822 : ATCheckPartitionsNotInUse(rel, lockmode);
5022 : /* Other recursion occurs during execution phase */
5023 : /* No command-specific prep needed except saving recurse flag */
5024 816 : if (recurse)
5025 780 : cmd->recurse = true;
5026 816 : pass = AT_PASS_DROP;
5027 816 : break;
5028 1156 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5029 1156 : ATSimplePermissions(cmd->subtype, rel,
5030 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5031 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5032 : /* See comments for ATPrepAlterColumnType */
5033 1156 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5034 : AT_PASS_UNSET, context);
5035 : Assert(cmd != NULL);
5036 : /* Performs own recursion */
5037 1150 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5038 : lockmode, context);
5039 1000 : pass = AT_PASS_ALTER_TYPE;
5040 1000 : break;
5041 164 : case AT_AlterColumnGenericOptions:
5042 164 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5043 : /* This command never recurses */
5044 : /* No command-specific prep needed */
5045 164 : pass = AT_PASS_MISC;
5046 164 : break;
5047 1794 : case AT_ChangeOwner: /* ALTER OWNER */
5048 : /* This command never recurses */
5049 : /* No command-specific prep needed */
5050 1794 : pass = AT_PASS_MISC;
5051 1794 : break;
5052 64 : case AT_ClusterOn: /* CLUSTER ON */
5053 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5054 64 : ATSimplePermissions(cmd->subtype, rel,
5055 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5056 : /* These commands never recurse */
5057 : /* No command-specific prep needed */
5058 64 : pass = AT_PASS_MISC;
5059 64 : break;
5060 112 : case AT_SetLogged: /* SET LOGGED */
5061 : case AT_SetUnLogged: /* SET UNLOGGED */
5062 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5063 100 : if (tab->chgPersistence)
5064 0 : ereport(ERROR,
5065 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5066 : errmsg("cannot change persistence setting twice")));
5067 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5068 88 : pass = AT_PASS_MISC;
5069 88 : break;
5070 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5071 6 : ATSimplePermissions(cmd->subtype, rel,
5072 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5073 6 : pass = AT_PASS_DROP;
5074 6 : break;
5075 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5076 128 : ATSimplePermissions(cmd->subtype, rel,
5077 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5078 :
5079 : /* check if another access method change was already requested */
5080 128 : if (tab->chgAccessMethod)
5081 18 : ereport(ERROR,
5082 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5083 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5084 :
5085 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5086 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5087 110 : break;
5088 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5089 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5090 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5091 : /* This command never recurses */
5092 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5093 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5094 158 : break;
5095 946 : case AT_SetRelOptions: /* SET (...) */
5096 : case AT_ResetRelOptions: /* RESET (...) */
5097 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5098 946 : ATSimplePermissions(cmd->subtype, rel,
5099 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5100 : ATT_MATVIEW | ATT_INDEX);
5101 : /* This command never recurses */
5102 : /* No command-specific prep needed */
5103 946 : pass = AT_PASS_MISC;
5104 946 : break;
5105 334 : case AT_AddInherit: /* INHERIT */
5106 334 : ATSimplePermissions(cmd->subtype, rel,
5107 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5108 : /* This command never recurses */
5109 334 : ATPrepAddInherit(rel);
5110 316 : pass = AT_PASS_MISC;
5111 316 : break;
5112 86 : case AT_DropInherit: /* NO INHERIT */
5113 86 : ATSimplePermissions(cmd->subtype, rel,
5114 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5115 : /* This command never recurses */
5116 : /* No command-specific prep needed */
5117 86 : pass = AT_PASS_MISC;
5118 86 : break;
5119 132 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5120 132 : ATSimplePermissions(cmd->subtype, rel,
5121 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5122 : /* Recursion occurs during execution phase */
5123 126 : pass = AT_PASS_MISC;
5124 126 : break;
5125 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5126 388 : ATSimplePermissions(cmd->subtype, rel,
5127 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5128 : /* Recursion occurs during execution phase */
5129 : /* No command-specific prep needed except saving recurse flag */
5130 388 : if (recurse)
5131 388 : cmd->recurse = true;
5132 388 : pass = AT_PASS_MISC;
5133 388 : break;
5134 442 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5135 442 : ATSimplePermissions(cmd->subtype, rel,
5136 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5137 442 : pass = AT_PASS_MISC;
5138 : /* This command never recurses */
5139 : /* No command-specific prep needed */
5140 442 : break;
5141 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5142 : case AT_EnableAlwaysTrig:
5143 : case AT_EnableReplicaTrig:
5144 : case AT_EnableTrigAll:
5145 : case AT_EnableTrigUser:
5146 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5147 : case AT_DisableTrigAll:
5148 : case AT_DisableTrigUser:
5149 340 : ATSimplePermissions(cmd->subtype, rel,
5150 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5151 : /* Set up recursion for phase 2; no other prep needed */
5152 340 : if (recurse)
5153 312 : cmd->recurse = true;
5154 340 : pass = AT_PASS_MISC;
5155 340 : break;
5156 532 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5157 : case AT_EnableAlwaysRule:
5158 : case AT_EnableReplicaRule:
5159 : case AT_DisableRule:
5160 : case AT_AddOf: /* OF */
5161 : case AT_DropOf: /* NOT OF */
5162 : case AT_EnableRowSecurity:
5163 : case AT_DisableRowSecurity:
5164 : case AT_ForceRowSecurity:
5165 : case AT_NoForceRowSecurity:
5166 532 : ATSimplePermissions(cmd->subtype, rel,
5167 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5168 : /* These commands never recurse */
5169 : /* No command-specific prep needed */
5170 532 : pass = AT_PASS_MISC;
5171 532 : break;
5172 58 : case AT_GenericOptions:
5173 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5174 : /* No command-specific prep needed */
5175 58 : pass = AT_PASS_MISC;
5176 58 : break;
5177 2614 : case AT_AttachPartition:
5178 2614 : ATSimplePermissions(cmd->subtype, rel,
5179 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5180 : /* No command-specific prep needed */
5181 2608 : pass = AT_PASS_MISC;
5182 2608 : break;
5183 564 : case AT_DetachPartition:
5184 564 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5185 : /* No command-specific prep needed */
5186 546 : pass = AT_PASS_MISC;
5187 546 : break;
5188 20 : case AT_DetachPartitionFinalize:
5189 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5190 : /* No command-specific prep needed */
5191 14 : pass = AT_PASS_MISC;
5192 14 : break;
5193 0 : default: /* oops */
5194 0 : elog(ERROR, "unrecognized alter table type: %d",
5195 : (int) cmd->subtype);
5196 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5197 : break;
5198 : }
5199 : Assert(pass > AT_PASS_UNSET);
5200 :
5201 : /* Add the subcommand to the appropriate list for phase 2 */
5202 31152 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5203 31152 : }
5204 :
5205 : /*
5206 : * ATRewriteCatalogs
5207 : *
5208 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5209 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5210 : * conflicts).
5211 : */
5212 : static void
5213 28762 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5214 : AlterTableUtilityContext *context)
5215 : {
5216 : ListCell *ltab;
5217 :
5218 : /*
5219 : * We process all the tables "in parallel", one pass at a time. This is
5220 : * needed because we may have to propagate work from one table to another
5221 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5222 : * re-adding of the foreign key constraint to the other table). Work can
5223 : * only be propagated into later passes, however.
5224 : */
5225 362588 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5226 : {
5227 : /* Go through each table that needs to be processed */
5228 684084 : foreach(ltab, *wqueue)
5229 : {
5230 350258 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5231 350258 : List *subcmds = tab->subcmds[pass];
5232 : ListCell *lcmd;
5233 :
5234 350258 : if (subcmds == NIL)
5235 299964 : continue;
5236 :
5237 : /*
5238 : * Open the relation and store it in tab. This allows subroutines
5239 : * close and reopen, if necessary. Appropriate lock was obtained
5240 : * by phase 1, needn't get it again.
5241 : */
5242 50294 : tab->rel = relation_open(tab->relid, NoLock);
5243 :
5244 101952 : foreach(lcmd, subcmds)
5245 54186 : ATExecCmd(wqueue, tab,
5246 54186 : lfirst_node(AlterTableCmd, lcmd),
5247 : lockmode, pass, context);
5248 :
5249 : /*
5250 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5251 : * (this is not done in ATExecAlterColumnType since it should be
5252 : * done only once if multiple columns of a table are altered).
5253 : */
5254 47766 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5255 988 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5256 :
5257 47766 : if (tab->rel)
5258 : {
5259 47766 : relation_close(tab->rel, NoLock);
5260 47766 : tab->rel = NULL;
5261 : }
5262 : }
5263 : }
5264 :
5265 : /* Check to see if a toast table must be added. */
5266 56186 : foreach(ltab, *wqueue)
5267 : {
5268 29952 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5269 :
5270 : /*
5271 : * If the table is source table of ATTACH PARTITION command, we did
5272 : * not modify anything about it that will change its toasting
5273 : * requirement, so no need to check.
5274 : */
5275 29952 : if (((tab->relkind == RELKIND_RELATION ||
5276 5742 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5277 28088 : tab->partition_constraint == NULL) ||
5278 3776 : tab->relkind == RELKIND_MATVIEW)
5279 26226 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5280 : }
5281 26234 : }
5282 :
5283 : /*
5284 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5285 : */
5286 : static void
5287 54186 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5288 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5289 : AlterTableUtilityContext *context)
5290 : {
5291 54186 : ObjectAddress address = InvalidObjectAddress;
5292 54186 : Relation rel = tab->rel;
5293 :
5294 54186 : switch (cmd->subtype)
5295 : {
5296 1984 : case AT_AddColumn: /* ADD COLUMN */
5297 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5298 1984 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5299 1984 : cmd->recurse, false,
5300 : lockmode, cur_pass, context);
5301 1870 : break;
5302 562 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5303 562 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5304 502 : break;
5305 56 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5306 56 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5307 56 : break;
5308 160 : case AT_AddIdentity:
5309 160 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5310 : cur_pass, context);
5311 : Assert(cmd != NULL);
5312 154 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5313 106 : break;
5314 62 : case AT_SetIdentity:
5315 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5316 : cur_pass, context);
5317 : Assert(cmd != NULL);
5318 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5319 38 : break;
5320 56 : case AT_DropIdentity:
5321 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5322 38 : break;
5323 250 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5324 250 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5325 148 : break;
5326 372 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5327 372 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5328 372 : cmd->recurse, false, lockmode);
5329 342 : break;
5330 84 : case AT_SetExpression:
5331 84 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5332 78 : break;
5333 32 : case AT_DropExpression:
5334 32 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5335 26 : break;
5336 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5337 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5338 116 : break;
5339 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5340 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5341 26 : break;
5342 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5343 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5344 6 : break;
5345 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5346 234 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5347 222 : break;
5348 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5349 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5350 : lockmode);
5351 62 : break;
5352 1586 : case AT_DropColumn: /* DROP COLUMN */
5353 1586 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5354 1586 : cmd->behavior, cmd->recurse, false,
5355 1586 : cmd->missing_ok, lockmode,
5356 : NULL);
5357 1412 : break;
5358 1144 : case AT_AddIndex: /* ADD INDEX */
5359 1144 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5360 : lockmode);
5361 974 : break;
5362 444 : case AT_ReAddIndex: /* ADD INDEX */
5363 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5364 : lockmode);
5365 444 : break;
5366 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5367 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5368 : true, lockmode);
5369 14 : break;
5370 26334 : case AT_AddConstraint: /* ADD CONSTRAINT */
5371 : /* Transform the command only during initial examination */
5372 26334 : if (cur_pass == AT_PASS_ADD_CONSTR)
5373 14776 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5374 14806 : cmd->recurse, lockmode,
5375 : cur_pass, context);
5376 : /* Depending on constraint type, might be no more work to do now */
5377 26304 : if (cmd != NULL)
5378 : address =
5379 11528 : ATExecAddConstraint(wqueue, tab, rel,
5380 11528 : (Constraint *) cmd->def,
5381 11528 : cmd->recurse, false, lockmode);
5382 25690 : break;
5383 326 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5384 : address =
5385 326 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5386 : true, true, lockmode);
5387 314 : break;
5388 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5389 : * constraint */
5390 : address =
5391 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5392 14 : ((AlterDomainStmt *) cmd->def)->def,
5393 : NULL);
5394 8 : break;
5395 78 : case AT_ReAddComment: /* Re-add existing comment */
5396 78 : address = CommentObject((CommentStmt *) cmd->def);
5397 78 : break;
5398 9540 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5399 9540 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5400 : lockmode);
5401 9528 : break;
5402 126 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5403 126 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5404 114 : break;
5405 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5406 388 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5407 : false, lockmode);
5408 388 : break;
5409 816 : case AT_DropConstraint: /* DROP CONSTRAINT */
5410 816 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5411 816 : cmd->recurse,
5412 816 : cmd->missing_ok, lockmode);
5413 606 : break;
5414 970 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5415 : /* parse transformation was done earlier */
5416 970 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5417 940 : break;
5418 164 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5419 : address =
5420 164 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5421 164 : (List *) cmd->def, lockmode);
5422 158 : break;
5423 1794 : case AT_ChangeOwner: /* ALTER OWNER */
5424 1788 : ATExecChangeOwner(RelationGetRelid(rel),
5425 1794 : get_rolespec_oid(cmd->newowner, false),
5426 : false, lockmode);
5427 1776 : break;
5428 64 : case AT_ClusterOn: /* CLUSTER ON */
5429 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5430 58 : break;
5431 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5432 18 : ATExecDropCluster(rel, lockmode);
5433 12 : break;
5434 88 : case AT_SetLogged: /* SET LOGGED */
5435 : case AT_SetUnLogged: /* SET UNLOGGED */
5436 88 : break;
5437 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5438 : /* nothing to do here, oid columns don't exist anymore */
5439 6 : break;
5440 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5441 :
5442 : /*
5443 : * Only do this for partitioned tables, for which this is just a
5444 : * catalog change. Tables with storage are handled by Phase 3.
5445 : */
5446 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5447 50 : tab->chgAccessMethod)
5448 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5449 92 : break;
5450 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5451 :
5452 : /*
5453 : * Only do this for partitioned tables and indexes, for which this
5454 : * is just a catalog change. Other relation types which have
5455 : * storage are handled by Phase 3.
5456 : */
5457 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5458 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5459 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5460 :
5461 152 : break;
5462 946 : case AT_SetRelOptions: /* SET (...) */
5463 : case AT_ResetRelOptions: /* RESET (...) */
5464 : case AT_ReplaceRelOptions: /* replace entire option list */
5465 946 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5466 894 : break;
5467 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5468 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5469 : TRIGGER_FIRES_ON_ORIGIN, false,
5470 122 : cmd->recurse,
5471 : lockmode);
5472 122 : break;
5473 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5474 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5475 : TRIGGER_FIRES_ALWAYS, false,
5476 40 : cmd->recurse,
5477 : lockmode);
5478 40 : break;
5479 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5480 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5481 : TRIGGER_FIRES_ON_REPLICA, false,
5482 16 : cmd->recurse,
5483 : lockmode);
5484 16 : break;
5485 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5486 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5487 : TRIGGER_DISABLED, false,
5488 138 : cmd->recurse,
5489 : lockmode);
5490 138 : break;
5491 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5492 0 : ATExecEnableDisableTrigger(rel, NULL,
5493 : TRIGGER_FIRES_ON_ORIGIN, false,
5494 0 : cmd->recurse,
5495 : lockmode);
5496 0 : break;
5497 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5498 12 : ATExecEnableDisableTrigger(rel, NULL,
5499 : TRIGGER_DISABLED, false,
5500 12 : cmd->recurse,
5501 : lockmode);
5502 12 : break;
5503 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5504 0 : ATExecEnableDisableTrigger(rel, NULL,
5505 : TRIGGER_FIRES_ON_ORIGIN, true,
5506 0 : cmd->recurse,
5507 : lockmode);
5508 0 : break;
5509 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5510 12 : ATExecEnableDisableTrigger(rel, NULL,
5511 : TRIGGER_DISABLED, true,
5512 12 : cmd->recurse,
5513 : lockmode);
5514 12 : break;
5515 :
5516 8 : case AT_EnableRule: /* ENABLE RULE name */
5517 8 : ATExecEnableDisableRule(rel, cmd->name,
5518 : RULE_FIRES_ON_ORIGIN, lockmode);
5519 8 : break;
5520 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5521 0 : ATExecEnableDisableRule(rel, cmd->name,
5522 : RULE_FIRES_ALWAYS, lockmode);
5523 0 : break;
5524 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5525 6 : ATExecEnableDisableRule(rel, cmd->name,
5526 : RULE_FIRES_ON_REPLICA, lockmode);
5527 6 : break;
5528 32 : case AT_DisableRule: /* DISABLE RULE name */
5529 32 : ATExecEnableDisableRule(rel, cmd->name,
5530 : RULE_DISABLED, lockmode);
5531 32 : break;
5532 :
5533 316 : case AT_AddInherit:
5534 316 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5535 226 : break;
5536 86 : case AT_DropInherit:
5537 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5538 80 : break;
5539 66 : case AT_AddOf:
5540 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5541 30 : break;
5542 6 : case AT_DropOf:
5543 6 : ATExecDropOf(rel, lockmode);
5544 6 : break;
5545 460 : case AT_ReplicaIdentity:
5546 460 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5547 406 : break;
5548 284 : case AT_EnableRowSecurity:
5549 284 : ATExecSetRowSecurity(rel, true);
5550 284 : break;
5551 10 : case AT_DisableRowSecurity:
5552 10 : ATExecSetRowSecurity(rel, false);
5553 10 : break;
5554 88 : case AT_ForceRowSecurity:
5555 88 : ATExecForceNoForceRowSecurity(rel, true);
5556 88 : break;
5557 32 : case AT_NoForceRowSecurity:
5558 32 : ATExecForceNoForceRowSecurity(rel, false);
5559 32 : break;
5560 58 : case AT_GenericOptions:
5561 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5562 56 : break;
5563 2608 : case AT_AttachPartition:
5564 2608 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5565 : cur_pass, context);
5566 : Assert(cmd != NULL);
5567 2584 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5568 2192 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5569 : context);
5570 : else
5571 392 : address = ATExecAttachPartitionIdx(wqueue, rel,
5572 392 : ((PartitionCmd *) cmd->def)->name);
5573 2242 : break;
5574 546 : case AT_DetachPartition:
5575 546 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5576 : cur_pass, context);
5577 : Assert(cmd != NULL);
5578 : /* ATPrepCmd ensures it must be a table */
5579 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5580 546 : address = ATExecDetachPartition(wqueue, tab, rel,
5581 546 : ((PartitionCmd *) cmd->def)->name,
5582 546 : ((PartitionCmd *) cmd->def)->concurrent);
5583 416 : break;
5584 14 : case AT_DetachPartitionFinalize:
5585 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5586 14 : break;
5587 0 : default: /* oops */
5588 0 : elog(ERROR, "unrecognized alter table type: %d",
5589 : (int) cmd->subtype);
5590 : break;
5591 : }
5592 :
5593 : /*
5594 : * Report the subcommand to interested event triggers.
5595 : */
5596 51658 : if (cmd)
5597 36882 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5598 :
5599 : /*
5600 : * Bump the command counter to ensure the next subcommand in the sequence
5601 : * can see the changes so far
5602 : */
5603 51658 : CommandCounterIncrement();
5604 51658 : }
5605 :
5606 : /*
5607 : * ATParseTransformCmd: perform parse transformation for one subcommand
5608 : *
5609 : * Returns the transformed subcommand tree, if there is one, else NULL.
5610 : *
5611 : * The parser may hand back additional AlterTableCmd(s) and/or other
5612 : * utility statements, either before or after the original subcommand.
5613 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5614 : * AlteredTableInfo (they had better be for later passes than the current one).
5615 : * Utility statements that are supposed to happen before the AlterTableCmd
5616 : * are executed immediately. Those that are supposed to happen afterwards
5617 : * are added to the tab->afterStmts list to be done at the very end.
5618 : */
5619 : static AlterTableCmd *
5620 21202 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5621 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5622 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5623 : {
5624 21202 : AlterTableCmd *newcmd = NULL;
5625 21202 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5626 : List *beforeStmts;
5627 : List *afterStmts;
5628 : ListCell *lc;
5629 :
5630 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5631 21202 : atstmt->relation =
5632 21202 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5633 21202 : pstrdup(RelationGetRelationName(rel)),
5634 : -1);
5635 21202 : atstmt->relation->inh = recurse;
5636 21202 : atstmt->cmds = list_make1(cmd);
5637 21202 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5638 21202 : atstmt->missing_ok = false;
5639 :
5640 : /* Transform the AlterTableStmt */
5641 21202 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5642 : atstmt,
5643 : context->queryString,
5644 : &beforeStmts,
5645 : &afterStmts);
5646 :
5647 : /* Execute any statements that should happen before these subcommand(s) */
5648 21622 : foreach(lc, beforeStmts)
5649 : {
5650 486 : Node *stmt = (Node *) lfirst(lc);
5651 :
5652 486 : ProcessUtilityForAlterTable(stmt, context);
5653 474 : CommandCounterIncrement();
5654 : }
5655 :
5656 : /* Examine the transformed subcommands and schedule them appropriately */
5657 49744 : foreach(lc, atstmt->cmds)
5658 : {
5659 28608 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5660 : AlterTablePass pass;
5661 :
5662 : /*
5663 : * This switch need only cover the subcommand types that can be added
5664 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5665 : * executing the subcommand immediately, as a substitute for the
5666 : * original subcommand. (Note, however, that this does cause
5667 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5668 : * which is important for index and foreign key constraints.)
5669 : *
5670 : * We assume we needn't do any phase-1 checks for added subcommands.
5671 : */
5672 28608 : switch (cmd2->subtype)
5673 : {
5674 1168 : case AT_AddIndex:
5675 1168 : pass = AT_PASS_ADD_INDEX;
5676 1168 : break;
5677 9540 : case AT_AddIndexConstraint:
5678 9540 : pass = AT_PASS_ADD_INDEXCONSTR;
5679 9540 : break;
5680 11540 : case AT_AddConstraint:
5681 : /* Recursion occurs during execution phase */
5682 11540 : if (recurse)
5683 11490 : cmd2->recurse = true;
5684 11540 : switch (castNode(Constraint, cmd2->def)->contype)
5685 : {
5686 8422 : case CONSTR_NOTNULL:
5687 8422 : pass = AT_PASS_COL_ATTRS;
5688 8422 : break;
5689 0 : case CONSTR_PRIMARY:
5690 : case CONSTR_UNIQUE:
5691 : case CONSTR_EXCLUSION:
5692 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5693 0 : break;
5694 3118 : default:
5695 3118 : pass = AT_PASS_ADD_OTHERCONSTR;
5696 3118 : break;
5697 : }
5698 11540 : break;
5699 0 : case AT_AlterColumnGenericOptions:
5700 : /* This command never recurses */
5701 : /* No command-specific prep needed */
5702 0 : pass = AT_PASS_MISC;
5703 0 : break;
5704 6360 : default:
5705 6360 : pass = cur_pass;
5706 6360 : break;
5707 : }
5708 :
5709 28608 : if (pass < cur_pass)
5710 : {
5711 : /* Cannot schedule into a pass we already finished */
5712 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5713 : pass);
5714 : }
5715 28608 : else if (pass > cur_pass)
5716 : {
5717 : /* OK, queue it up for later */
5718 22248 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5719 : }
5720 : else
5721 : {
5722 : /*
5723 : * We should see at most one subcommand for the current pass,
5724 : * which is the transformed version of the original subcommand.
5725 : */
5726 6360 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5727 : {
5728 : /* Found the transformed version of our subcommand */
5729 6360 : newcmd = cmd2;
5730 : }
5731 : else
5732 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5733 : pass);
5734 : }
5735 : }
5736 :
5737 : /* Queue up any after-statements to happen at the end */
5738 21136 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5739 :
5740 21136 : return newcmd;
5741 : }
5742 :
5743 : /*
5744 : * ATRewriteTables: ALTER TABLE phase 3
5745 : */
5746 : static void
5747 26234 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5748 : AlterTableUtilityContext *context)
5749 : {
5750 : ListCell *ltab;
5751 :
5752 : /* Go through each table that needs to be checked or rewritten */
5753 55924 : foreach(ltab, *wqueue)
5754 : {
5755 29940 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5756 :
5757 : /* Relations without storage may be ignored here */
5758 29940 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5759 5454 : continue;
5760 :
5761 : /*
5762 : * If we change column data types, the operation has to be propagated
5763 : * to tables that use this table's rowtype as a column type.
5764 : * tab->newvals will also be non-NULL in the case where we're adding a
5765 : * column with a default. We choose to forbid that case as well,
5766 : * since composite types might eventually support defaults.
5767 : *
5768 : * (Eventually we'll probably need to check for composite type
5769 : * dependencies even when we're just scanning the table without a
5770 : * rewrite, but at the moment a composite type does not enforce any
5771 : * constraints, so it's not necessary/appropriate to enforce them just
5772 : * during ALTER.)
5773 : */
5774 24486 : if (tab->newvals != NIL || tab->rewrite > 0)
5775 : {
5776 : Relation rel;
5777 :
5778 1494 : rel = table_open(tab->relid, NoLock);
5779 1494 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5780 1482 : table_close(rel, NoLock);
5781 : }
5782 :
5783 : /*
5784 : * We only need to rewrite the table if at least one column needs to
5785 : * be recomputed, or we are changing its persistence or access method.
5786 : *
5787 : * There are two reasons for requiring a rewrite when changing
5788 : * persistence: on one hand, we need to ensure that the buffers
5789 : * belonging to each of the two relations are marked with or without
5790 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5791 : * and assigns a new relfilenumber, we automatically create or drop an
5792 : * init fork for the relation as appropriate.
5793 : */
5794 24474 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5795 856 : {
5796 : /* Build a temporary relation and copy data */
5797 : Relation OldHeap;
5798 : Oid OIDNewHeap;
5799 : Oid NewAccessMethod;
5800 : Oid NewTableSpace;
5801 : char persistence;
5802 :
5803 894 : OldHeap = table_open(tab->relid, NoLock);
5804 :
5805 : /*
5806 : * We don't support rewriting of system catalogs; there are too
5807 : * many corner cases and too little benefit. In particular this
5808 : * is certainly not going to work for mapped catalogs.
5809 : */
5810 894 : if (IsSystemRelation(OldHeap))
5811 0 : ereport(ERROR,
5812 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5813 : errmsg("cannot rewrite system relation \"%s\"",
5814 : RelationGetRelationName(OldHeap))));
5815 :
5816 894 : if (RelationIsUsedAsCatalogTable(OldHeap))
5817 2 : ereport(ERROR,
5818 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5819 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5820 : RelationGetRelationName(OldHeap))));
5821 :
5822 : /*
5823 : * Don't allow rewrite on temp tables of other backends ... their
5824 : * local buffer manager is not going to cope. (This is redundant
5825 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5826 : * check here too.)
5827 : */
5828 892 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5829 0 : ereport(ERROR,
5830 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5831 : errmsg("cannot rewrite temporary tables of other sessions")));
5832 :
5833 : /*
5834 : * Select destination tablespace (same as original unless user
5835 : * requested a change)
5836 : */
5837 892 : if (tab->newTableSpace)
5838 0 : NewTableSpace = tab->newTableSpace;
5839 : else
5840 892 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5841 :
5842 : /*
5843 : * Select destination access method (same as original unless user
5844 : * requested a change)
5845 : */
5846 892 : if (tab->chgAccessMethod)
5847 36 : NewAccessMethod = tab->newAccessMethod;
5848 : else
5849 856 : NewAccessMethod = OldHeap->rd_rel->relam;
5850 :
5851 : /*
5852 : * Select persistence of transient table (same as original unless
5853 : * user requested a change)
5854 : */
5855 892 : persistence = tab->chgPersistence ?
5856 840 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5857 :
5858 892 : table_close(OldHeap, NoLock);
5859 :
5860 : /*
5861 : * Fire off an Event Trigger now, before actually rewriting the
5862 : * table.
5863 : *
5864 : * We don't support Event Trigger for nested commands anywhere,
5865 : * here included, and parsetree is given NULL when coming from
5866 : * AlterTableInternal.
5867 : *
5868 : * And fire it only once.
5869 : */
5870 892 : if (parsetree)
5871 892 : EventTriggerTableRewrite((Node *) parsetree,
5872 : tab->relid,
5873 : tab->rewrite);
5874 :
5875 : /*
5876 : * Create transient table that will receive the modified data.
5877 : *
5878 : * Ensure it is marked correctly as logged or unlogged. We have
5879 : * to do this here so that buffers for the new relfilenumber will
5880 : * have the right persistence set, and at the same time ensure
5881 : * that the original filenumbers's buffers will get read in with
5882 : * the correct setting (i.e. the original one). Otherwise a
5883 : * rollback after the rewrite would possibly result with buffers
5884 : * for the original filenumbers having the wrong persistence
5885 : * setting.
5886 : *
5887 : * NB: This relies on swap_relation_files() also swapping the
5888 : * persistence. That wouldn't work for pg_class, but that can't be
5889 : * unlogged anyway.
5890 : */
5891 886 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5892 : persistence, lockmode);
5893 :
5894 : /*
5895 : * Copy the heap data into the new table with the desired
5896 : * modifications, and test the current data within the table
5897 : * against new constraints generated by ALTER TABLE commands.
5898 : */
5899 886 : ATRewriteTable(tab, OIDNewHeap, lockmode);
5900 :
5901 : /*
5902 : * Swap the physical files of the old and new heaps, then rebuild
5903 : * indexes and discard the old heap. We can use RecentXmin for
5904 : * the table's new relfrozenxid because we rewrote all the tuples
5905 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5906 : * we never try to swap toast tables by content, since we have no
5907 : * interest in letting this code work on system catalogs.
5908 : */
5909 862 : finish_heap_swap(tab->relid, OIDNewHeap,
5910 : false, false, true,
5911 862 : !OidIsValid(tab->newTableSpace),
5912 : RecentXmin,
5913 : ReadNextMultiXactId(),
5914 : persistence);
5915 :
5916 856 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5917 : }
5918 23580 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5919 : {
5920 24 : if (tab->chgPersistence)
5921 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5922 : }
5923 : else
5924 : {
5925 : /*
5926 : * If required, test the current data within the table against new
5927 : * constraints generated by ALTER TABLE commands, but don't
5928 : * rebuild data.
5929 : */
5930 23556 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5931 21028 : tab->partition_constraint != NULL)
5932 4292 : ATRewriteTable(tab, InvalidOid, lockmode);
5933 :
5934 : /*
5935 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5936 : * just do a block-by-block copy.
5937 : */
5938 23356 : if (tab->newTableSpace)
5939 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5940 : }
5941 :
5942 : /*
5943 : * Also change persistence of owned sequences, so that it matches the
5944 : * table persistence.
5945 : */
5946 24236 : if (tab->chgPersistence)
5947 : {
5948 76 : List *seqlist = getOwnedSequences(tab->relid);
5949 : ListCell *lc;
5950 :
5951 124 : foreach(lc, seqlist)
5952 : {
5953 48 : Oid seq_relid = lfirst_oid(lc);
5954 :
5955 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5956 : }
5957 : }
5958 : }
5959 :
5960 : /*
5961 : * Foreign key constraints are checked in a final pass, since (a) it's
5962 : * generally best to examine each one separately, and (b) it's at least
5963 : * theoretically possible that we have changed both relations of the
5964 : * foreign key, and we'd better have finished both rewrites before we try
5965 : * to read the tables.
5966 : */
5967 55464 : foreach(ltab, *wqueue)
5968 : {
5969 29560 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5970 29560 : Relation rel = NULL;
5971 : ListCell *lcon;
5972 :
5973 : /* Relations without storage may be ignored here too */
5974 29560 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5975 5374 : continue;
5976 :
5977 25872 : foreach(lcon, tab->constraints)
5978 : {
5979 1766 : NewConstraint *con = lfirst(lcon);
5980 :
5981 1766 : if (con->contype == CONSTR_FOREIGN)
5982 : {
5983 1108 : Constraint *fkconstraint = (Constraint *) con->qual;
5984 : Relation refrel;
5985 :
5986 1108 : if (rel == NULL)
5987 : {
5988 : /* Long since locked, no need for another */
5989 1096 : rel = table_open(tab->relid, NoLock);
5990 : }
5991 :
5992 1108 : refrel = table_open(con->refrelid, RowShareLock);
5993 :
5994 1108 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5995 : con->refindid,
5996 : con->conid,
5997 1108 : con->conwithperiod);
5998 :
5999 : /*
6000 : * No need to mark the constraint row as validated, we did
6001 : * that when we inserted the row earlier.
6002 : */
6003 :
6004 1028 : table_close(refrel, NoLock);
6005 : }
6006 : }
6007 :
6008 24106 : if (rel)
6009 1016 : table_close(rel, NoLock);
6010 : }
6011 :
6012 : /* Finally, run any afterStmts that were queued up */
6013 55346 : foreach(ltab, *wqueue)
6014 : {
6015 29442 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6016 : ListCell *lc;
6017 :
6018 29528 : foreach(lc, tab->afterStmts)
6019 : {
6020 86 : Node *stmt = (Node *) lfirst(lc);
6021 :
6022 86 : ProcessUtilityForAlterTable(stmt, context);
6023 86 : CommandCounterIncrement();
6024 : }
6025 : }
6026 25904 : }
6027 :
6028 : /*
6029 : * ATRewriteTable: scan or rewrite one table
6030 : *
6031 : * OIDNewHeap is InvalidOid if we don't need to rewrite
6032 : */
6033 : static void
6034 5178 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
6035 : {
6036 : Relation oldrel;
6037 : Relation newrel;
6038 : TupleDesc oldTupDesc;
6039 : TupleDesc newTupDesc;
6040 5178 : bool needscan = false;
6041 : List *notnull_attrs;
6042 : int i;
6043 : ListCell *l;
6044 : EState *estate;
6045 : CommandId mycid;
6046 : BulkInsertState bistate;
6047 : int ti_options;
6048 5178 : ExprState *partqualstate = NULL;
6049 :
6050 : /*
6051 : * Open the relation(s). We have surely already locked the existing
6052 : * table.
6053 : */
6054 5178 : oldrel = table_open(tab->relid, NoLock);
6055 5178 : oldTupDesc = tab->oldDesc;
6056 5178 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6057 :
6058 5178 : if (OidIsValid(OIDNewHeap))
6059 886 : newrel = table_open(OIDNewHeap, lockmode);
6060 : else
6061 4292 : newrel = NULL;
6062 :
6063 : /*
6064 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6065 : * is empty, so don't bother using it.
6066 : */
6067 5178 : if (newrel)
6068 : {
6069 886 : mycid = GetCurrentCommandId(true);
6070 886 : bistate = GetBulkInsertState();
6071 886 : ti_options = TABLE_INSERT_SKIP_FSM;
6072 : }
6073 : else
6074 : {
6075 : /* keep compiler quiet about using these uninitialized */
6076 4292 : mycid = 0;
6077 4292 : bistate = NULL;
6078 4292 : ti_options = 0;
6079 : }
6080 :
6081 : /*
6082 : * Generate the constraint and default execution states
6083 : */
6084 :
6085 5178 : estate = CreateExecutorState();
6086 :
6087 : /* Build the needed expression execution states */
6088 7040 : foreach(l, tab->constraints)
6089 : {
6090 1862 : NewConstraint *con = lfirst(l);
6091 :
6092 1862 : switch (con->contype)
6093 : {
6094 748 : case CONSTR_CHECK:
6095 748 : needscan = true;
6096 748 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6097 748 : break;
6098 1114 : case CONSTR_FOREIGN:
6099 : /* Nothing to do here */
6100 1114 : break;
6101 0 : default:
6102 0 : elog(ERROR, "unrecognized constraint type: %d",
6103 : (int) con->contype);
6104 : }
6105 : }
6106 :
6107 : /* Build expression execution states for partition check quals */
6108 5178 : if (tab->partition_constraint)
6109 : {
6110 1906 : needscan = true;
6111 1906 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6112 : }
6113 :
6114 6060 : foreach(l, tab->newvals)
6115 : {
6116 882 : NewColumnValue *ex = lfirst(l);
6117 :
6118 : /* expr already planned */
6119 882 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6120 : }
6121 :
6122 5178 : notnull_attrs = NIL;
6123 5178 : if (newrel || tab->verify_new_notnull)
6124 : {
6125 : /*
6126 : * If we are rebuilding the tuples OR if we added any new but not
6127 : * verified not-null constraints, check all not-null constraints. This
6128 : * is a bit of overkill but it minimizes risk of bugs.
6129 : */
6130 6240 : for (i = 0; i < newTupDesc->natts; i++)
6131 : {
6132 4542 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6133 :
6134 4542 : if (attr->attnotnull && !attr->attisdropped)
6135 1790 : notnull_attrs = lappend_int(notnull_attrs, i);
6136 : }
6137 1698 : if (notnull_attrs)
6138 1282 : needscan = true;
6139 : }
6140 :
6141 5178 : if (newrel || needscan)
6142 : {
6143 : ExprContext *econtext;
6144 : TupleTableSlot *oldslot;
6145 : TupleTableSlot *newslot;
6146 : TableScanDesc scan;
6147 : MemoryContext oldCxt;
6148 4260 : List *dropped_attrs = NIL;
6149 : ListCell *lc;
6150 : Snapshot snapshot;
6151 :
6152 4260 : if (newrel)
6153 886 : ereport(DEBUG1,
6154 : (errmsg_internal("rewriting table \"%s\"",
6155 : RelationGetRelationName(oldrel))));
6156 : else
6157 3374 : ereport(DEBUG1,
6158 : (errmsg_internal("verifying table \"%s\"",
6159 : RelationGetRelationName(oldrel))));
6160 :
6161 4260 : if (newrel)
6162 : {
6163 : /*
6164 : * All predicate locks on the tuples or pages are about to be made
6165 : * invalid, because we move tuples around. Promote them to
6166 : * relation locks.
6167 : */
6168 886 : TransferPredicateLocksToHeapRelation(oldrel);
6169 : }
6170 :
6171 4260 : econtext = GetPerTupleExprContext(estate);
6172 :
6173 : /*
6174 : * Create necessary tuple slots. When rewriting, two slots are needed,
6175 : * otherwise one suffices. In the case where one slot suffices, we
6176 : * need to use the new tuple descriptor, otherwise some constraints
6177 : * can't be evaluated. Note that even when the tuple layout is the
6178 : * same and no rewrite is required, the tupDescs might not be
6179 : * (consider ADD COLUMN without a default).
6180 : */
6181 4260 : if (tab->rewrite)
6182 : {
6183 : Assert(newrel != NULL);
6184 886 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6185 : table_slot_callbacks(oldrel));
6186 886 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6187 : table_slot_callbacks(newrel));
6188 :
6189 : /*
6190 : * Set all columns in the new slot to NULL initially, to ensure
6191 : * columns added as part of the rewrite are initialized to NULL.
6192 : * That is necessary as tab->newvals will not contain an
6193 : * expression for columns with a NULL default, e.g. when adding a
6194 : * column without a default together with a column with a default
6195 : * requiring an actual rewrite.
6196 : */
6197 886 : ExecStoreAllNullTuple(newslot);
6198 : }
6199 : else
6200 : {
6201 3374 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6202 : table_slot_callbacks(oldrel));
6203 3374 : newslot = NULL;
6204 : }
6205 :
6206 : /*
6207 : * Any attributes that are dropped according to the new tuple
6208 : * descriptor can be set to NULL. We precompute the list of dropped
6209 : * attributes to avoid needing to do so in the per-tuple loop.
6210 : */
6211 14994 : for (i = 0; i < newTupDesc->natts; i++)
6212 : {
6213 10734 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6214 778 : dropped_attrs = lappend_int(dropped_attrs, i);
6215 : }
6216 :
6217 : /*
6218 : * Scan through the rows, generating a new row if needed and then
6219 : * checking all the constraints.
6220 : */
6221 4260 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6222 4260 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6223 :
6224 : /*
6225 : * Switch to per-tuple memory context and reset it for each tuple
6226 : * produced, so we don't leak memory.
6227 : */
6228 4260 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6229 :
6230 768796 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6231 : {
6232 : TupleTableSlot *insertslot;
6233 :
6234 764760 : if (tab->rewrite > 0)
6235 : {
6236 : /* Extract data from old tuple */
6237 99570 : slot_getallattrs(oldslot);
6238 99570 : ExecClearTuple(newslot);
6239 :
6240 : /* copy attributes */
6241 99570 : memcpy(newslot->tts_values, oldslot->tts_values,
6242 99570 : sizeof(Datum) * oldslot->tts_nvalid);
6243 99570 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6244 99570 : sizeof(bool) * oldslot->tts_nvalid);
6245 :
6246 : /* Set dropped attributes to null in new tuple */
6247 99656 : foreach(lc, dropped_attrs)
6248 86 : newslot->tts_isnull[lfirst_int(lc)] = true;
6249 :
6250 : /*
6251 : * Constraints and GENERATED expressions might reference the
6252 : * tableoid column, so fill tts_tableOid with the desired
6253 : * value. (We must do this each time, because it gets
6254 : * overwritten with newrel's OID during storing.)
6255 : */
6256 99570 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6257 :
6258 : /*
6259 : * Process supplied expressions to replace selected columns.
6260 : *
6261 : * First, evaluate expressions whose inputs come from the old
6262 : * tuple.
6263 : */
6264 99570 : econtext->ecxt_scantuple = oldslot;
6265 :
6266 204960 : foreach(l, tab->newvals)
6267 : {
6268 105402 : NewColumnValue *ex = lfirst(l);
6269 :
6270 105402 : if (ex->is_generated)
6271 150 : continue;
6272 :
6273 105252 : newslot->tts_values[ex->attnum - 1]
6274 105240 : = ExecEvalExpr(ex->exprstate,
6275 : econtext,
6276 105252 : &newslot->tts_isnull[ex->attnum - 1]);
6277 : }
6278 :
6279 99558 : ExecStoreVirtualTuple(newslot);
6280 :
6281 : /*
6282 : * Now, evaluate any expressions whose inputs come from the
6283 : * new tuple. We assume these columns won't reference each
6284 : * other, so that there's no ordering dependency.
6285 : */
6286 99558 : econtext->ecxt_scantuple = newslot;
6287 :
6288 204948 : foreach(l, tab->newvals)
6289 : {
6290 105390 : NewColumnValue *ex = lfirst(l);
6291 :
6292 105390 : if (!ex->is_generated)
6293 105240 : continue;
6294 :
6295 150 : newslot->tts_values[ex->attnum - 1]
6296 150 : = ExecEvalExpr(ex->exprstate,
6297 : econtext,
6298 150 : &newslot->tts_isnull[ex->attnum - 1]);
6299 : }
6300 :
6301 99558 : insertslot = newslot;
6302 : }
6303 : else
6304 : {
6305 : /*
6306 : * If there's no rewrite, old and new table are guaranteed to
6307 : * have the same AM, so we can just use the old slot to verify
6308 : * new constraints etc.
6309 : */
6310 665190 : insertslot = oldslot;
6311 : }
6312 :
6313 : /* Now check any constraints on the possibly-changed tuple */
6314 764748 : econtext->ecxt_scantuple = insertslot;
6315 :
6316 3341030 : foreach(l, notnull_attrs)
6317 : {
6318 2576348 : int attn = lfirst_int(l);
6319 :
6320 2576348 : if (slot_attisnull(insertslot, attn + 1))
6321 : {
6322 66 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6323 :
6324 66 : ereport(ERROR,
6325 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6326 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6327 : NameStr(attr->attname),
6328 : RelationGetRelationName(oldrel)),
6329 : errtablecol(oldrel, attn + 1)));
6330 : }
6331 : }
6332 :
6333 772782 : foreach(l, tab->constraints)
6334 : {
6335 8172 : NewConstraint *con = lfirst(l);
6336 :
6337 8172 : switch (con->contype)
6338 : {
6339 8072 : case CONSTR_CHECK:
6340 8072 : if (!ExecCheck(con->qualstate, econtext))
6341 72 : ereport(ERROR,
6342 : (errcode(ERRCODE_CHECK_VIOLATION),
6343 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6344 : con->name,
6345 : RelationGetRelationName(oldrel)),
6346 : errtableconstraint(oldrel, con->name)));
6347 8000 : break;
6348 100 : case CONSTR_NOTNULL:
6349 : case CONSTR_FOREIGN:
6350 : /* Nothing to do here */
6351 100 : break;
6352 0 : default:
6353 0 : elog(ERROR, "unrecognized constraint type: %d",
6354 : (int) con->contype);
6355 : }
6356 : }
6357 :
6358 764610 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6359 : {
6360 74 : if (tab->validate_default)
6361 26 : ereport(ERROR,
6362 : (errcode(ERRCODE_CHECK_VIOLATION),
6363 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6364 : RelationGetRelationName(oldrel)),
6365 : errtable(oldrel)));
6366 : else
6367 48 : ereport(ERROR,
6368 : (errcode(ERRCODE_CHECK_VIOLATION),
6369 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6370 : RelationGetRelationName(oldrel)),
6371 : errtable(oldrel)));
6372 : }
6373 :
6374 : /* Write the tuple out to the new relation */
6375 764536 : if (newrel)
6376 99546 : table_tuple_insert(newrel, insertslot, mycid,
6377 : ti_options, bistate);
6378 :
6379 764536 : ResetExprContext(econtext);
6380 :
6381 764536 : CHECK_FOR_INTERRUPTS();
6382 : }
6383 :
6384 4036 : MemoryContextSwitchTo(oldCxt);
6385 4036 : table_endscan(scan);
6386 4036 : UnregisterSnapshot(snapshot);
6387 :
6388 4036 : ExecDropSingleTupleTableSlot(oldslot);
6389 4036 : if (newslot)
6390 862 : ExecDropSingleTupleTableSlot(newslot);
6391 : }
6392 :
6393 4954 : FreeExecutorState(estate);
6394 :
6395 4954 : table_close(oldrel, NoLock);
6396 4954 : if (newrel)
6397 : {
6398 862 : FreeBulkInsertState(bistate);
6399 :
6400 862 : table_finish_bulk_insert(newrel, ti_options);
6401 :
6402 862 : table_close(newrel, NoLock);
6403 : }
6404 4954 : }
6405 :
6406 : /*
6407 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6408 : */
6409 : static AlteredTableInfo *
6410 37910 : ATGetQueueEntry(List **wqueue, Relation rel)
6411 : {
6412 37910 : Oid relid = RelationGetRelid(rel);
6413 : AlteredTableInfo *tab;
6414 : ListCell *ltab;
6415 :
6416 46778 : foreach(ltab, *wqueue)
6417 : {
6418 13800 : tab = (AlteredTableInfo *) lfirst(ltab);
6419 13800 : if (tab->relid == relid)
6420 4932 : return tab;
6421 : }
6422 :
6423 : /*
6424 : * Not there, so add it. Note that we make a copy of the relation's
6425 : * existing descriptor before anything interesting can happen to it.
6426 : */
6427 32978 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6428 32978 : tab->relid = relid;
6429 32978 : tab->rel = NULL; /* set later */
6430 32978 : tab->relkind = rel->rd_rel->relkind;
6431 32978 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6432 32978 : tab->newAccessMethod = InvalidOid;
6433 32978 : tab->chgAccessMethod = false;
6434 32978 : tab->newTableSpace = InvalidOid;
6435 32978 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6436 32978 : tab->chgPersistence = false;
6437 :
6438 32978 : *wqueue = lappend(*wqueue, tab);
6439 :
6440 32978 : return tab;
6441 : }
6442 :
6443 : static const char *
6444 78 : alter_table_type_to_string(AlterTableType cmdtype)
6445 : {
6446 78 : switch (cmdtype)
6447 : {
6448 0 : case AT_AddColumn:
6449 : case AT_AddColumnToView:
6450 0 : return "ADD COLUMN";
6451 0 : case AT_ColumnDefault:
6452 : case AT_CookedColumnDefault:
6453 0 : return "ALTER COLUMN ... SET DEFAULT";
6454 6 : case AT_DropNotNull:
6455 6 : return "ALTER COLUMN ... DROP NOT NULL";
6456 6 : case AT_SetNotNull:
6457 6 : return "ALTER COLUMN ... SET NOT NULL";
6458 0 : case AT_SetExpression:
6459 0 : return "ALTER COLUMN ... SET EXPRESSION";
6460 0 : case AT_DropExpression:
6461 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6462 0 : case AT_SetStatistics:
6463 0 : return "ALTER COLUMN ... SET STATISTICS";
6464 12 : case AT_SetOptions:
6465 12 : return "ALTER COLUMN ... SET";
6466 0 : case AT_ResetOptions:
6467 0 : return "ALTER COLUMN ... RESET";
6468 0 : case AT_SetStorage:
6469 0 : return "ALTER COLUMN ... SET STORAGE";
6470 0 : case AT_SetCompression:
6471 0 : return "ALTER COLUMN ... SET COMPRESSION";
6472 6 : case AT_DropColumn:
6473 6 : return "DROP COLUMN";
6474 0 : case AT_AddIndex:
6475 : case AT_ReAddIndex:
6476 0 : return NULL; /* not real grammar */
6477 0 : case AT_AddConstraint:
6478 : case AT_ReAddConstraint:
6479 : case AT_ReAddDomainConstraint:
6480 : case AT_AddIndexConstraint:
6481 0 : return "ADD CONSTRAINT";
6482 6 : case AT_AlterConstraint:
6483 6 : return "ALTER CONSTRAINT";
6484 0 : case AT_ValidateConstraint:
6485 0 : return "VALIDATE CONSTRAINT";
6486 0 : case AT_DropConstraint:
6487 0 : return "DROP CONSTRAINT";
6488 0 : case AT_ReAddComment:
6489 0 : return NULL; /* not real grammar */
6490 0 : case AT_AlterColumnType:
6491 0 : return "ALTER COLUMN ... SET DATA TYPE";
6492 0 : case AT_AlterColumnGenericOptions:
6493 0 : return "ALTER COLUMN ... OPTIONS";
6494 0 : case AT_ChangeOwner:
6495 0 : return "OWNER TO";
6496 0 : case AT_ClusterOn:
6497 0 : return "CLUSTER ON";
6498 0 : case AT_DropCluster:
6499 0 : return "SET WITHOUT CLUSTER";
6500 0 : case AT_SetAccessMethod:
6501 0 : return "SET ACCESS METHOD";
6502 6 : case AT_SetLogged:
6503 6 : return "SET LOGGED";
6504 6 : case AT_SetUnLogged:
6505 6 : return "SET UNLOGGED";
6506 0 : case AT_DropOids:
6507 0 : return "SET WITHOUT OIDS";
6508 0 : case AT_SetTableSpace:
6509 0 : return "SET TABLESPACE";
6510 0 : case AT_SetRelOptions:
6511 0 : return "SET";
6512 0 : case AT_ResetRelOptions:
6513 0 : return "RESET";
6514 0 : case AT_ReplaceRelOptions:
6515 0 : return NULL; /* not real grammar */
6516 0 : case AT_EnableTrig:
6517 0 : return "ENABLE TRIGGER";
6518 0 : case AT_EnableAlwaysTrig:
6519 0 : return "ENABLE ALWAYS TRIGGER";
6520 0 : case AT_EnableReplicaTrig:
6521 0 : return "ENABLE REPLICA TRIGGER";
6522 0 : case AT_DisableTrig:
6523 0 : return "DISABLE TRIGGER";
6524 0 : case AT_EnableTrigAll:
6525 0 : return "ENABLE TRIGGER ALL";
6526 0 : case AT_DisableTrigAll:
6527 0 : return "DISABLE TRIGGER ALL";
6528 0 : case AT_EnableTrigUser:
6529 0 : return "ENABLE TRIGGER USER";
6530 0 : case AT_DisableTrigUser:
6531 0 : return "DISABLE TRIGGER USER";
6532 0 : case AT_EnableRule:
6533 0 : return "ENABLE RULE";
6534 0 : case AT_EnableAlwaysRule:
6535 0 : return "ENABLE ALWAYS RULE";
6536 0 : case AT_EnableReplicaRule:
6537 0 : return "ENABLE REPLICA RULE";
6538 0 : case AT_DisableRule:
6539 0 : return "DISABLE RULE";
6540 0 : case AT_AddInherit:
6541 0 : return "INHERIT";
6542 0 : case AT_DropInherit:
6543 0 : return "NO INHERIT";
6544 0 : case AT_AddOf:
6545 0 : return "OF";
6546 0 : case AT_DropOf:
6547 0 : return "NOT OF";
6548 0 : case AT_ReplicaIdentity:
6549 0 : return "REPLICA IDENTITY";
6550 0 : case AT_EnableRowSecurity:
6551 0 : return "ENABLE ROW SECURITY";
6552 0 : case AT_DisableRowSecurity:
6553 0 : return "DISABLE ROW SECURITY";
6554 0 : case AT_ForceRowSecurity:
6555 0 : return "FORCE ROW SECURITY";
6556 0 : case AT_NoForceRowSecurity:
6557 0 : return "NO FORCE ROW SECURITY";
6558 0 : case AT_GenericOptions:
6559 0 : return "OPTIONS";
6560 6 : case AT_AttachPartition:
6561 6 : return "ATTACH PARTITION";
6562 18 : case AT_DetachPartition:
6563 18 : return "DETACH PARTITION";
6564 6 : case AT_DetachPartitionFinalize:
6565 6 : return "DETACH PARTITION ... FINALIZE";
6566 0 : case AT_AddIdentity:
6567 0 : return "ALTER COLUMN ... ADD IDENTITY";
6568 0 : case AT_SetIdentity:
6569 0 : return "ALTER COLUMN ... SET";
6570 0 : case AT_DropIdentity:
6571 0 : return "ALTER COLUMN ... DROP IDENTITY";
6572 0 : case AT_ReAddStatistics:
6573 0 : return NULL; /* not real grammar */
6574 : }
6575 :
6576 0 : return NULL;
6577 : }
6578 :
6579 : /*
6580 : * ATSimplePermissions
6581 : *
6582 : * - Ensure that it is a relation (or possibly a view)
6583 : * - Ensure this user is the owner
6584 : * - Ensure that it is not a system table
6585 : */
6586 : static void
6587 34414 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6588 : {
6589 : int actual_target;
6590 :
6591 34414 : switch (rel->rd_rel->relkind)
6592 : {
6593 26900 : case RELKIND_RELATION:
6594 26900 : actual_target = ATT_TABLE;
6595 26900 : break;
6596 5274 : case RELKIND_PARTITIONED_TABLE:
6597 5274 : actual_target = ATT_PARTITIONED_TABLE;
6598 5274 : break;
6599 396 : case RELKIND_VIEW:
6600 396 : actual_target = ATT_VIEW;
6601 396 : break;
6602 46 : case RELKIND_MATVIEW:
6603 46 : actual_target = ATT_MATVIEW;
6604 46 : break;
6605 226 : case RELKIND_INDEX:
6606 226 : actual_target = ATT_INDEX;
6607 226 : break;
6608 434 : case RELKIND_PARTITIONED_INDEX:
6609 434 : actual_target = ATT_PARTITIONED_INDEX;
6610 434 : break;
6611 214 : case RELKIND_COMPOSITE_TYPE:
6612 214 : actual_target = ATT_COMPOSITE_TYPE;
6613 214 : break;
6614 900 : case RELKIND_FOREIGN_TABLE:
6615 900 : actual_target = ATT_FOREIGN_TABLE;
6616 900 : break;
6617 24 : case RELKIND_SEQUENCE:
6618 24 : actual_target = ATT_SEQUENCE;
6619 24 : break;
6620 0 : default:
6621 0 : actual_target = 0;
6622 0 : break;
6623 : }
6624 :
6625 : /* Wrong target type? */
6626 34414 : if ((actual_target & allowed_targets) == 0)
6627 : {
6628 78 : const char *action_str = alter_table_type_to_string(cmdtype);
6629 :
6630 78 : if (action_str)
6631 78 : ereport(ERROR,
6632 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6633 : /* translator: %s is a group of some SQL keywords */
6634 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6635 : action_str, RelationGetRelationName(rel)),
6636 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6637 : else
6638 : /* internal error? */
6639 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6640 : RelationGetRelationName(rel));
6641 : }
6642 :
6643 : /* Permissions checks */
6644 34336 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6645 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6646 12 : RelationGetRelationName(rel));
6647 :
6648 34324 : if (!allowSystemTableMods && IsSystemRelation(rel))
6649 0 : ereport(ERROR,
6650 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6651 : errmsg("permission denied: \"%s\" is a system catalog",
6652 : RelationGetRelationName(rel))));
6653 34324 : }
6654 :
6655 : /*
6656 : * ATSimpleRecursion
6657 : *
6658 : * Simple table recursion sufficient for most ALTER TABLE operations.
6659 : * All direct and indirect children are processed in an unspecified order.
6660 : * Note that if a child inherits from the original table via multiple
6661 : * inheritance paths, it will be visited just once.
6662 : */
6663 : static void
6664 1124 : ATSimpleRecursion(List **wqueue, Relation rel,
6665 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6666 : AlterTableUtilityContext *context)
6667 : {
6668 : /*
6669 : * Propagate to children, if desired and if there are (or might be) any
6670 : * children.
6671 : */
6672 1124 : if (recurse && rel->rd_rel->relhassubclass)
6673 : {
6674 66 : Oid relid = RelationGetRelid(rel);
6675 : ListCell *child;
6676 : List *children;
6677 :
6678 66 : children = find_all_inheritors(relid, lockmode, NULL);
6679 :
6680 : /*
6681 : * find_all_inheritors does the recursive search of the inheritance
6682 : * hierarchy, so all we have to do is process all of the relids in the
6683 : * list that it returns.
6684 : */
6685 294 : foreach(child, children)
6686 : {
6687 228 : Oid childrelid = lfirst_oid(child);
6688 : Relation childrel;
6689 :
6690 228 : if (childrelid == relid)
6691 66 : continue;
6692 : /* find_all_inheritors already got lock */
6693 162 : childrel = relation_open(childrelid, NoLock);
6694 162 : CheckAlterTableIsSafe(childrel);
6695 162 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6696 162 : relation_close(childrel, NoLock);
6697 : }
6698 : }
6699 1124 : }
6700 :
6701 : /*
6702 : * Obtain list of partitions of the given table, locking them all at the given
6703 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6704 : *
6705 : * This function is a no-op if the given relation is not a partitioned table;
6706 : * in particular, nothing is done if it's a legacy inheritance parent.
6707 : */
6708 : static void
6709 822 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6710 : {
6711 822 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6712 : {
6713 : List *inh;
6714 : ListCell *cell;
6715 :
6716 188 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6717 : /* first element is the parent rel; must ignore it */
6718 610 : for_each_from(cell, inh, 1)
6719 : {
6720 : Relation childrel;
6721 :
6722 : /* find_all_inheritors already got lock */
6723 428 : childrel = table_open(lfirst_oid(cell), NoLock);
6724 428 : CheckAlterTableIsSafe(childrel);
6725 422 : table_close(childrel, NoLock);
6726 : }
6727 182 : list_free(inh);
6728 : }
6729 816 : }
6730 :
6731 : /*
6732 : * ATTypedTableRecursion
6733 : *
6734 : * Propagate ALTER TYPE operations to the typed tables of that type.
6735 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6736 : * recursion to inheritance children of the typed tables.
6737 : */
6738 : static void
6739 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6740 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6741 : {
6742 : ListCell *child;
6743 : List *children;
6744 :
6745 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6746 :
6747 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6748 190 : RelationGetRelationName(rel),
6749 : cmd->behavior);
6750 :
6751 202 : foreach(child, children)
6752 : {
6753 30 : Oid childrelid = lfirst_oid(child);
6754 : Relation childrel;
6755 :
6756 30 : childrel = relation_open(childrelid, lockmode);
6757 30 : CheckAlterTableIsSafe(childrel);
6758 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6759 30 : relation_close(childrel, NoLock);
6760 : }
6761 172 : }
6762 :
6763 :
6764 : /*
6765 : * find_composite_type_dependencies
6766 : *
6767 : * Check to see if the type "typeOid" is being used as a column in some table
6768 : * (possibly nested several levels deep in composite types, arrays, etc!).
6769 : * Eventually, we'd like to propagate the check or rewrite operation
6770 : * into such tables, but for now, just error out if we find any.
6771 : *
6772 : * Caller should provide either the associated relation of a rowtype,
6773 : * or a type name (not both) for use in the error message, if any.
6774 : *
6775 : * Note that "typeOid" is not necessarily a composite type; it could also be
6776 : * another container type such as an array or range, or a domain over one of
6777 : * these things. The name of this function is therefore somewhat historical,
6778 : * but it's not worth changing.
6779 : *
6780 : * We assume that functions and views depending on the type are not reasons
6781 : * to reject the ALTER. (How safe is this really?)
6782 : */
6783 : void
6784 3942 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6785 : const char *origTypeName)
6786 : {
6787 : Relation depRel;
6788 : ScanKeyData key[2];
6789 : SysScanDesc depScan;
6790 : HeapTuple depTup;
6791 :
6792 : /* since this function recurses, it could be driven to stack overflow */
6793 3942 : check_stack_depth();
6794 :
6795 : /*
6796 : * We scan pg_depend to find those things that depend on the given type.
6797 : * (We assume we can ignore refobjsubid for a type.)
6798 : */
6799 3942 : depRel = table_open(DependRelationId, AccessShareLock);
6800 :
6801 3942 : ScanKeyInit(&key[0],
6802 : Anum_pg_depend_refclassid,
6803 : BTEqualStrategyNumber, F_OIDEQ,
6804 : ObjectIdGetDatum(TypeRelationId));
6805 3942 : ScanKeyInit(&key[1],
6806 : Anum_pg_depend_refobjid,
6807 : BTEqualStrategyNumber, F_OIDEQ,
6808 : ObjectIdGetDatum(typeOid));
6809 :
6810 3942 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6811 : NULL, 2, key);
6812 :
6813 6084 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6814 : {
6815 2238 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6816 : Relation rel;
6817 : TupleDesc tupleDesc;
6818 : Form_pg_attribute att;
6819 :
6820 : /* Check for directly dependent types */
6821 2238 : if (pg_depend->classid == TypeRelationId)
6822 : {
6823 : /*
6824 : * This must be an array, domain, or range containing the given
6825 : * type, so recursively check for uses of this type. Note that
6826 : * any error message will mention the original type not the
6827 : * container; this is intentional.
6828 : */
6829 1892 : find_composite_type_dependencies(pg_depend->objid,
6830 : origRelation, origTypeName);
6831 1868 : continue;
6832 : }
6833 :
6834 : /* Else, ignore dependees that aren't relations */
6835 346 : if (pg_depend->classid != RelationRelationId)
6836 122 : continue;
6837 :
6838 224 : rel = relation_open(pg_depend->objid, AccessShareLock);
6839 224 : tupleDesc = RelationGetDescr(rel);
6840 :
6841 : /*
6842 : * If objsubid identifies a specific column, refer to that in error
6843 : * messages. Otherwise, search to see if there's a user column of the
6844 : * type. (We assume system columns are never of interesting types.)
6845 : * The search is needed because an index containing an expression
6846 : * column of the target type will just be recorded as a whole-relation
6847 : * dependency. If we do not find a column of the type, the dependency
6848 : * must indicate that the type is transiently referenced in an index
6849 : * expression but not stored on disk, which we assume is OK, just as
6850 : * we do for references in views. (It could also be that the target
6851 : * type is embedded in some container type that is stored in an index
6852 : * column, but the previous recursion should catch such cases.)
6853 : */
6854 224 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6855 66 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6856 : else
6857 : {
6858 158 : att = NULL;
6859 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6860 : {
6861 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6862 254 : if (att->atttypid == typeOid && !att->attisdropped)
6863 6 : break;
6864 248 : att = NULL;
6865 : }
6866 158 : if (att == NULL)
6867 : {
6868 : /* No such column, so assume OK */
6869 152 : relation_close(rel, AccessShareLock);
6870 152 : continue;
6871 : }
6872 : }
6873 :
6874 : /*
6875 : * We definitely should reject if the relation has storage. If it's
6876 : * partitioned, then perhaps we don't have to reject: if there are
6877 : * partitions then we'll fail when we find one, else there is no
6878 : * stored data to worry about. However, it's possible that the type
6879 : * change would affect conclusions about whether the type is sortable
6880 : * or hashable and thus (if it's a partitioning column) break the
6881 : * partitioning rule. For now, reject for partitioned rels too.
6882 : */
6883 72 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6884 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6885 : {
6886 72 : if (origTypeName)
6887 30 : ereport(ERROR,
6888 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6889 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6890 : origTypeName,
6891 : RelationGetRelationName(rel),
6892 : NameStr(att->attname))));
6893 42 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6894 18 : ereport(ERROR,
6895 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6896 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6897 : RelationGetRelationName(origRelation),
6898 : RelationGetRelationName(rel),
6899 : NameStr(att->attname))));
6900 24 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6901 6 : ereport(ERROR,
6902 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6903 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6904 : RelationGetRelationName(origRelation),
6905 : RelationGetRelationName(rel),
6906 : NameStr(att->attname))));
6907 : else
6908 18 : ereport(ERROR,
6909 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6910 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6911 : RelationGetRelationName(origRelation),
6912 : RelationGetRelationName(rel),
6913 : NameStr(att->attname))));
6914 : }
6915 0 : else if (OidIsValid(rel->rd_rel->reltype))
6916 : {
6917 : /*
6918 : * A view or composite type itself isn't a problem, but we must
6919 : * recursively check for indirect dependencies via its rowtype.
6920 : */
6921 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6922 : origRelation, origTypeName);
6923 : }
6924 :
6925 0 : relation_close(rel, AccessShareLock);
6926 : }
6927 :
6928 3846 : systable_endscan(depScan);
6929 :
6930 3846 : relation_close(depRel, AccessShareLock);
6931 3846 : }
6932 :
6933 :
6934 : /*
6935 : * find_typed_table_dependencies
6936 : *
6937 : * Check to see if a composite type is being used as the type of a
6938 : * typed table. Abort if any are found and behavior is RESTRICT.
6939 : * Else return the list of tables.
6940 : */
6941 : static List *
6942 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6943 : {
6944 : Relation classRel;
6945 : ScanKeyData key[1];
6946 : TableScanDesc scan;
6947 : HeapTuple tuple;
6948 214 : List *result = NIL;
6949 :
6950 214 : classRel = table_open(RelationRelationId, AccessShareLock);
6951 :
6952 214 : ScanKeyInit(&key[0],
6953 : Anum_pg_class_reloftype,
6954 : BTEqualStrategyNumber, F_OIDEQ,
6955 : ObjectIdGetDatum(typeOid));
6956 :
6957 214 : scan = table_beginscan_catalog(classRel, 1, key);
6958 :
6959 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6960 : {
6961 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6962 :
6963 60 : if (behavior == DROP_RESTRICT)
6964 24 : ereport(ERROR,
6965 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6966 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6967 : typeName),
6968 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6969 : else
6970 36 : result = lappend_oid(result, classform->oid);
6971 : }
6972 :
6973 190 : table_endscan(scan);
6974 190 : table_close(classRel, AccessShareLock);
6975 :
6976 190 : return result;
6977 : }
6978 :
6979 :
6980 : /*
6981 : * check_of_type
6982 : *
6983 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6984 : * isn't suitable, throw an error. Currently, we require that the type
6985 : * originated with CREATE TYPE AS. We could support any row type, but doing so
6986 : * would require handling a number of extra corner cases in the DDL commands.
6987 : * (Also, allowing domain-over-composite would open up a can of worms about
6988 : * whether and how the domain's constraints should apply to derived tables.)
6989 : */
6990 : void
6991 176 : check_of_type(HeapTuple typetuple)
6992 : {
6993 176 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6994 176 : bool typeOk = false;
6995 :
6996 176 : if (typ->typtype == TYPTYPE_COMPOSITE)
6997 : {
6998 : Relation typeRelation;
6999 :
7000 : Assert(OidIsValid(typ->typrelid));
7001 170 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7002 170 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7003 :
7004 : /*
7005 : * Close the parent rel, but keep our AccessShareLock on it until xact
7006 : * commit. That will prevent someone else from deleting or ALTERing
7007 : * the type before the typed table creation/conversion commits.
7008 : */
7009 170 : relation_close(typeRelation, NoLock);
7010 :
7011 170 : if (!typeOk)
7012 6 : ereport(ERROR,
7013 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7014 : errmsg("type %s is the row type of another table",
7015 : format_type_be(typ->oid)),
7016 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7017 : }
7018 : else
7019 6 : ereport(ERROR,
7020 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7021 : errmsg("type %s is not a composite type",
7022 : format_type_be(typ->oid))));
7023 164 : }
7024 :
7025 :
7026 : /*
7027 : * ALTER TABLE ADD COLUMN
7028 : *
7029 : * Adds an additional attribute to a relation making the assumption that
7030 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7031 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7032 : * AlterTableCmd's.
7033 : *
7034 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7035 : * have to decide at runtime whether to recurse or not depending on whether we
7036 : * actually add a column or merely merge with an existing column. (We can't
7037 : * check this in a static pre-pass because it won't handle multiple inheritance
7038 : * situations correctly.)
7039 : */
7040 : static void
7041 2002 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7042 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7043 : AlterTableUtilityContext *context)
7044 : {
7045 2002 : if (rel->rd_rel->reloftype && !recursing)
7046 6 : ereport(ERROR,
7047 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7048 : errmsg("cannot add column to typed table")));
7049 :
7050 1996 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7051 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7052 :
7053 1990 : if (recurse && !is_view)
7054 1890 : cmd->recurse = true;
7055 1990 : }
7056 :
7057 : /*
7058 : * Add a column to a table. The return value is the address of the
7059 : * new column in the parent relation.
7060 : *
7061 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7062 : * copy (but that happens only after we check for IF NOT EXISTS).
7063 : */
7064 : static ObjectAddress
7065 2620 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7066 : AlterTableCmd **cmd, bool recurse, bool recursing,
7067 : LOCKMODE lockmode, AlterTablePass cur_pass,
7068 : AlterTableUtilityContext *context)
7069 : {
7070 2620 : Oid myrelid = RelationGetRelid(rel);
7071 2620 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7072 2620 : bool if_not_exists = (*cmd)->missing_ok;
7073 : Relation pgclass,
7074 : attrdesc;
7075 : HeapTuple reltup;
7076 : Form_pg_class relform;
7077 : Form_pg_attribute attribute;
7078 : int newattnum;
7079 : char relkind;
7080 : Expr *defval;
7081 : List *children;
7082 : ListCell *child;
7083 : AlterTableCmd *childcmd;
7084 : ObjectAddress address;
7085 : TupleDesc tupdesc;
7086 :
7087 : /* since this function recurses, it could be driven to stack overflow */
7088 2620 : check_stack_depth();
7089 :
7090 : /* At top level, permission check was done in ATPrepCmd, else do it */
7091 2620 : if (recursing)
7092 636 : ATSimplePermissions((*cmd)->subtype, rel,
7093 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7094 :
7095 2620 : if (rel->rd_rel->relispartition && !recursing)
7096 12 : ereport(ERROR,
7097 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7098 : errmsg("cannot add column to a partition")));
7099 :
7100 2608 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7101 :
7102 : /*
7103 : * Are we adding the column to a recursion child? If so, check whether to
7104 : * merge with an existing definition for the column. If we do merge, we
7105 : * must not recurse. Children will already have the column, and recursing
7106 : * into them would mess up attinhcount.
7107 : */
7108 2608 : if (colDef->inhcount > 0)
7109 : {
7110 : HeapTuple tuple;
7111 :
7112 : /* Does child already have a column by this name? */
7113 636 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7114 636 : if (HeapTupleIsValid(tuple))
7115 : {
7116 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7117 : Oid ctypeId;
7118 : int32 ctypmod;
7119 : Oid ccollid;
7120 :
7121 : /* Child column must match on type, typmod, and collation */
7122 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7123 48 : if (ctypeId != childatt->atttypid ||
7124 48 : ctypmod != childatt->atttypmod)
7125 0 : ereport(ERROR,
7126 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7127 : errmsg("child table \"%s\" has different type for column \"%s\"",
7128 : RelationGetRelationName(rel), colDef->colname)));
7129 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7130 48 : if (ccollid != childatt->attcollation)
7131 0 : ereport(ERROR,
7132 : (errcode(ERRCODE_COLLATION_MISMATCH),
7133 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7134 : RelationGetRelationName(rel), colDef->colname),
7135 : errdetail("\"%s\" versus \"%s\"",
7136 : get_collation_name(ccollid),
7137 : get_collation_name(childatt->attcollation))));
7138 :
7139 : /* Bump the existing child att's inhcount */
7140 48 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7141 : &childatt->attinhcount))
7142 0 : ereport(ERROR,
7143 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7144 : errmsg("too many inheritance parents"));
7145 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7146 :
7147 48 : heap_freetuple(tuple);
7148 :
7149 : /* Inform the user about the merge */
7150 48 : ereport(NOTICE,
7151 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7152 : colDef->colname, RelationGetRelationName(rel))));
7153 :
7154 48 : table_close(attrdesc, RowExclusiveLock);
7155 :
7156 : /* Make the child column change visible */
7157 48 : CommandCounterIncrement();
7158 :
7159 48 : return InvalidObjectAddress;
7160 : }
7161 : }
7162 :
7163 : /* skip if the name already exists and if_not_exists is true */
7164 2560 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7165 : {
7166 54 : table_close(attrdesc, RowExclusiveLock);
7167 54 : return InvalidObjectAddress;
7168 : }
7169 :
7170 : /*
7171 : * Okay, we need to add the column, so go ahead and do parse
7172 : * transformation. This can result in queueing up, or even immediately
7173 : * executing, subsidiary operations (such as creation of unique indexes);
7174 : * so we mustn't do it until we have made the if_not_exists check.
7175 : *
7176 : * When recursing, the command was already transformed and we needn't do
7177 : * so again. Also, if context isn't given we can't transform. (That
7178 : * currently happens only for AT_AddColumnToView; we expect that view.c
7179 : * passed us a ColumnDef that doesn't need work.)
7180 : */
7181 2476 : if (context != NULL && !recursing)
7182 : {
7183 1864 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7184 : cur_pass, context);
7185 : Assert(*cmd != NULL);
7186 1864 : colDef = castNode(ColumnDef, (*cmd)->def);
7187 : }
7188 :
7189 : /*
7190 : * Regular inheritance children are independent enough not to inherit the
7191 : * identity column from parent hence cannot recursively add identity
7192 : * column if the table has inheritance children.
7193 : *
7194 : * Partitions, on the other hand, are integral part of a partitioned table
7195 : * and inherit identity column. Hence propagate identity column down the
7196 : * partition hierarchy.
7197 : */
7198 2476 : if (colDef->identity &&
7199 54 : recurse &&
7200 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7201 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7202 6 : ereport(ERROR,
7203 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7204 : errmsg("cannot recursively add identity column to table that has child tables")));
7205 :
7206 2470 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7207 :
7208 2470 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7209 2470 : if (!HeapTupleIsValid(reltup))
7210 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7211 2470 : relform = (Form_pg_class) GETSTRUCT(reltup);
7212 2470 : relkind = relform->relkind;
7213 :
7214 : /* Determine the new attribute's number */
7215 2470 : newattnum = relform->relnatts + 1;
7216 2470 : if (newattnum > MaxHeapAttributeNumber)
7217 0 : ereport(ERROR,
7218 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7219 : errmsg("tables can have at most %d columns",
7220 : MaxHeapAttributeNumber)));
7221 :
7222 : /*
7223 : * Construct new attribute's pg_attribute entry.
7224 : */
7225 2470 : tupdesc = BuildDescForRelation(list_make1(colDef));
7226 :
7227 2458 : attribute = TupleDescAttr(tupdesc, 0);
7228 :
7229 : /* Fix up attribute number */
7230 2458 : attribute->attnum = newattnum;
7231 :
7232 : /* make sure datatype is legal for a column */
7233 2458 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7234 2458 : list_make1_oid(rel->rd_rel->reltype),
7235 : 0);
7236 :
7237 2428 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7238 :
7239 2428 : table_close(attrdesc, RowExclusiveLock);
7240 :
7241 : /*
7242 : * Update pg_class tuple as appropriate
7243 : */
7244 2428 : relform->relnatts = newattnum;
7245 :
7246 2428 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7247 :
7248 2428 : heap_freetuple(reltup);
7249 :
7250 : /* Post creation hook for new attribute */
7251 2428 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7252 :
7253 2428 : table_close(pgclass, RowExclusiveLock);
7254 :
7255 : /* Make the attribute's catalog entry visible */
7256 2428 : CommandCounterIncrement();
7257 :
7258 : /*
7259 : * Store the DEFAULT, if any, in the catalogs
7260 : */
7261 2428 : if (colDef->raw_default)
7262 : {
7263 : RawColumnDefault *rawEnt;
7264 :
7265 708 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7266 708 : rawEnt->attnum = attribute->attnum;
7267 708 : rawEnt->raw_default = copyObject(colDef->raw_default);
7268 :
7269 : /*
7270 : * Attempt to skip a complete table rewrite by storing the specified
7271 : * DEFAULT value outside of the heap. This may be disabled inside
7272 : * AddRelationNewConstraints if the optimization cannot be applied.
7273 : */
7274 708 : rawEnt->missingMode = (!colDef->generated);
7275 :
7276 708 : rawEnt->generated = colDef->generated;
7277 :
7278 : /*
7279 : * This function is intended for CREATE TABLE, so it processes a
7280 : * _list_ of defaults, but we just do one.
7281 : */
7282 708 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7283 : false, true, false, NULL);
7284 :
7285 : /* Make the additional catalog changes visible */
7286 696 : CommandCounterIncrement();
7287 :
7288 : /*
7289 : * Did the request for a missing value work? If not we'll have to do a
7290 : * rewrite
7291 : */
7292 696 : if (!rawEnt->missingMode)
7293 108 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7294 : }
7295 :
7296 : /*
7297 : * Tell Phase 3 to fill in the default expression, if there is one.
7298 : *
7299 : * If there is no default, Phase 3 doesn't have to do anything, because
7300 : * that effectively means that the default is NULL. The heap tuple access
7301 : * routines always check for attnum > # of attributes in tuple, and return
7302 : * NULL if so, so without any modification of the tuple data we will get
7303 : * the effect of NULL values in the new column.
7304 : *
7305 : * An exception occurs when the new column is of a domain type: the domain
7306 : * might have a not-null constraint, or a check constraint that indirectly
7307 : * rejects nulls. If there are any domain constraints then we construct
7308 : * an explicit NULL default value that will be passed through
7309 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7310 : * rewriting the table which we really don't have to do, but the present
7311 : * design of domain processing doesn't offer any simple way of checking
7312 : * the constraints more directly.)
7313 : *
7314 : * Note: we use build_column_default, and not just the cooked default
7315 : * returned by AddRelationNewConstraints, so that the right thing happens
7316 : * when a datatype's default applies.
7317 : *
7318 : * Note: it might seem that this should happen at the end of Phase 2, so
7319 : * that the effects of subsequent subcommands can be taken into account.
7320 : * It's intentional that we do it now, though. The new column should be
7321 : * filled according to what is said in the ADD COLUMN subcommand, so that
7322 : * the effects are the same as if this subcommand had been run by itself
7323 : * and the later subcommands had been issued in new ALTER TABLE commands.
7324 : *
7325 : * We can skip this entirely for relations without storage, since Phase 3
7326 : * is certainly not going to touch them. System attributes don't have
7327 : * interesting defaults, either.
7328 : */
7329 2416 : if (RELKIND_HAS_STORAGE(relkind))
7330 : {
7331 : /*
7332 : * For an identity column, we can't use build_column_default(),
7333 : * because the sequence ownership isn't set yet. So do it manually.
7334 : */
7335 2064 : if (colDef->identity)
7336 : {
7337 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7338 :
7339 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7340 42 : nve->typeId = attribute->atttypid;
7341 :
7342 42 : defval = (Expr *) nve;
7343 :
7344 : /* must do a rewrite for identity columns */
7345 42 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7346 : }
7347 : else
7348 2022 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7349 :
7350 2064 : if (!defval && DomainHasConstraints(attribute->atttypid))
7351 : {
7352 : Oid baseTypeId;
7353 : int32 baseTypeMod;
7354 : Oid baseTypeColl;
7355 :
7356 6 : baseTypeMod = attribute->atttypmod;
7357 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7358 6 : baseTypeColl = get_typcollation(baseTypeId);
7359 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7360 6 : defval = (Expr *) coerce_to_target_type(NULL,
7361 : (Node *) defval,
7362 : baseTypeId,
7363 : attribute->atttypid,
7364 : attribute->atttypmod,
7365 : COERCION_ASSIGNMENT,
7366 : COERCE_IMPLICIT_CAST,
7367 : -1);
7368 6 : if (defval == NULL) /* should not happen */
7369 0 : elog(ERROR, "failed to coerce base type to domain");
7370 : }
7371 :
7372 2064 : if (defval)
7373 : {
7374 : NewColumnValue *newval;
7375 :
7376 610 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7377 610 : newval->attnum = attribute->attnum;
7378 610 : newval->expr = expression_planner(defval);
7379 610 : newval->is_generated = (colDef->generated != '\0');
7380 :
7381 610 : tab->newvals = lappend(tab->newvals, newval);
7382 : }
7383 :
7384 2064 : if (DomainHasConstraints(attribute->atttypid))
7385 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7386 :
7387 2064 : if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7388 : {
7389 : /*
7390 : * If the new column is NOT NULL, and there is no missing value,
7391 : * tell Phase 3 it needs to check for NULLs.
7392 : */
7393 1610 : tab->verify_new_notnull |= colDef->is_not_null;
7394 : }
7395 : }
7396 :
7397 : /*
7398 : * Add needed dependency entries for the new column.
7399 : */
7400 2416 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7401 2416 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7402 :
7403 : /*
7404 : * Propagate to children as appropriate. Unlike most other ALTER
7405 : * routines, we have to do this one level of recursion at a time; we can't
7406 : * use find_all_inheritors to do it in one pass.
7407 : */
7408 : children =
7409 2416 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7410 :
7411 : /*
7412 : * If we are told not to recurse, there had better not be any child
7413 : * tables; else the addition would put them out of step.
7414 : */
7415 2416 : if (children && !recurse)
7416 12 : ereport(ERROR,
7417 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7418 : errmsg("column must be added to child tables too")));
7419 :
7420 : /* Children should see column as singly inherited */
7421 2404 : if (!recursing)
7422 : {
7423 1816 : childcmd = copyObject(*cmd);
7424 1816 : colDef = castNode(ColumnDef, childcmd->def);
7425 1816 : colDef->inhcount = 1;
7426 1816 : colDef->is_local = false;
7427 : }
7428 : else
7429 588 : childcmd = *cmd; /* no need to copy again */
7430 :
7431 3040 : foreach(child, children)
7432 : {
7433 636 : Oid childrelid = lfirst_oid(child);
7434 : Relation childrel;
7435 : AlteredTableInfo *childtab;
7436 :
7437 : /* find_inheritance_children already got lock */
7438 636 : childrel = table_open(childrelid, NoLock);
7439 636 : CheckAlterTableIsSafe(childrel);
7440 :
7441 : /* Find or create work queue entry for this table */
7442 636 : childtab = ATGetQueueEntry(wqueue, childrel);
7443 :
7444 : /* Recurse to child; return value is ignored */
7445 636 : ATExecAddColumn(wqueue, childtab, childrel,
7446 : &childcmd, recurse, true,
7447 : lockmode, cur_pass, context);
7448 :
7449 636 : table_close(childrel, NoLock);
7450 : }
7451 :
7452 2404 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7453 2404 : return address;
7454 : }
7455 :
7456 : /*
7457 : * If a new or renamed column will collide with the name of an existing
7458 : * column and if_not_exists is false then error out, else do nothing.
7459 : */
7460 : static bool
7461 3010 : check_for_column_name_collision(Relation rel, const char *colname,
7462 : bool if_not_exists)
7463 : {
7464 : HeapTuple attTuple;
7465 : int attnum;
7466 :
7467 : /*
7468 : * this test is deliberately not attisdropped-aware, since if one tries to
7469 : * add a column matching a dropped column name, it's gonna fail anyway.
7470 : */
7471 3010 : attTuple = SearchSysCache2(ATTNAME,
7472 : ObjectIdGetDatum(RelationGetRelid(rel)),
7473 : PointerGetDatum(colname));
7474 3010 : if (!HeapTupleIsValid(attTuple))
7475 2914 : return true;
7476 :
7477 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7478 96 : ReleaseSysCache(attTuple);
7479 :
7480 : /*
7481 : * We throw a different error message for conflicts with system column
7482 : * names, since they are normally not shown and the user might otherwise
7483 : * be confused about the reason for the conflict.
7484 : */
7485 96 : if (attnum <= 0)
7486 12 : ereport(ERROR,
7487 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7488 : errmsg("column name \"%s\" conflicts with a system column name",
7489 : colname)));
7490 : else
7491 : {
7492 84 : if (if_not_exists)
7493 : {
7494 54 : ereport(NOTICE,
7495 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7496 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7497 : colname, RelationGetRelationName(rel))));
7498 54 : return false;
7499 : }
7500 :
7501 30 : ereport(ERROR,
7502 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7503 : errmsg("column \"%s\" of relation \"%s\" already exists",
7504 : colname, RelationGetRelationName(rel))));
7505 : }
7506 :
7507 : return true;
7508 : }
7509 :
7510 : /*
7511 : * Install a column's dependency on its datatype.
7512 : */
7513 : static void
7514 3356 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7515 : {
7516 : ObjectAddress myself,
7517 : referenced;
7518 :
7519 3356 : myself.classId = RelationRelationId;
7520 3356 : myself.objectId = relid;
7521 3356 : myself.objectSubId = attnum;
7522 3356 : referenced.classId = TypeRelationId;
7523 3356 : referenced.objectId = typid;
7524 3356 : referenced.objectSubId = 0;
7525 3356 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7526 3356 : }
7527 :
7528 : /*
7529 : * Install a column's dependency on its collation.
7530 : */
7531 : static void
7532 3356 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7533 : {
7534 : ObjectAddress myself,
7535 : referenced;
7536 :
7537 : /* We know the default collation is pinned, so don't bother recording it */
7538 3356 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7539 : {
7540 18 : myself.classId = RelationRelationId;
7541 18 : myself.objectId = relid;
7542 18 : myself.objectSubId = attnum;
7543 18 : referenced.classId = CollationRelationId;
7544 18 : referenced.objectId = collid;
7545 18 : referenced.objectSubId = 0;
7546 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7547 : }
7548 3356 : }
7549 :
7550 : /*
7551 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7552 : *
7553 : * Return the address of the modified column. If the column was already
7554 : * nullable, InvalidObjectAddress is returned.
7555 : */
7556 : static ObjectAddress
7557 250 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7558 : LOCKMODE lockmode)
7559 : {
7560 : HeapTuple tuple;
7561 : HeapTuple conTup;
7562 : Form_pg_attribute attTup;
7563 : AttrNumber attnum;
7564 : Relation attr_rel;
7565 : ObjectAddress address;
7566 :
7567 : /*
7568 : * lookup the attribute
7569 : */
7570 250 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7571 :
7572 250 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7573 250 : if (!HeapTupleIsValid(tuple))
7574 18 : ereport(ERROR,
7575 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7576 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7577 : colName, RelationGetRelationName(rel))));
7578 232 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7579 232 : attnum = attTup->attnum;
7580 232 : ObjectAddressSubSet(address, RelationRelationId,
7581 : RelationGetRelid(rel), attnum);
7582 :
7583 : /* If the column is already nullable there's nothing to do. */
7584 232 : if (!attTup->attnotnull)
7585 : {
7586 0 : table_close(attr_rel, RowExclusiveLock);
7587 0 : return InvalidObjectAddress;
7588 : }
7589 :
7590 : /* Prevent them from altering a system attribute */
7591 232 : if (attnum <= 0)
7592 0 : ereport(ERROR,
7593 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7594 : errmsg("cannot alter system column \"%s\"",
7595 : colName)));
7596 :
7597 232 : if (attTup->attidentity)
7598 18 : ereport(ERROR,
7599 : (errcode(ERRCODE_SYNTAX_ERROR),
7600 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7601 : colName, RelationGetRelationName(rel))));
7602 :
7603 : /*
7604 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7605 : */
7606 214 : if (rel->rd_rel->relispartition)
7607 : {
7608 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7609 12 : Relation parent = table_open(parentId, AccessShareLock);
7610 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7611 : AttrNumber parent_attnum;
7612 :
7613 12 : parent_attnum = get_attnum(parentId, colName);
7614 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7615 12 : ereport(ERROR,
7616 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7617 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7618 : colName)));
7619 0 : table_close(parent, AccessShareLock);
7620 : }
7621 :
7622 : /*
7623 : * Find the constraint that makes this column NOT NULL, and drop it.
7624 : * dropconstraint_internal() resets attnotnull.
7625 : */
7626 202 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7627 202 : if (conTup == NULL)
7628 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7629 : colName, RelationGetRelationName(rel));
7630 :
7631 : /* The normal case: we have a pg_constraint row, remove it */
7632 202 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7633 : false, lockmode);
7634 148 : heap_freetuple(conTup);
7635 :
7636 148 : InvokeObjectPostAlterHook(RelationRelationId,
7637 : RelationGetRelid(rel), attnum);
7638 :
7639 148 : table_close(attr_rel, RowExclusiveLock);
7640 :
7641 148 : return address;
7642 : }
7643 :
7644 : /*
7645 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7646 : * to verify it.
7647 : *
7648 : * When called to alter an existing table, 'wqueue' must be given so that we
7649 : * can queue a check that existing tuples pass the constraint. When called
7650 : * from table creation, 'wqueue' should be passed as NULL.
7651 : */
7652 : static void
7653 22766 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7654 : LOCKMODE lockmode)
7655 : {
7656 : Form_pg_attribute attr;
7657 :
7658 22766 : CheckAlterTableIsSafe(rel);
7659 :
7660 : /*
7661 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7662 : * attribute.
7663 : */
7664 22766 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7665 22766 : if (attr->attisdropped)
7666 0 : return;
7667 :
7668 22766 : if (!attr->attnotnull)
7669 : {
7670 : Relation attr_rel;
7671 : HeapTuple tuple;
7672 :
7673 1190 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7674 :
7675 1190 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7676 1190 : if (!HeapTupleIsValid(tuple))
7677 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7678 : attnum, RelationGetRelid(rel));
7679 :
7680 1190 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7681 : Assert(!attr->attnotnull);
7682 1190 : attr->attnotnull = true;
7683 1190 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7684 :
7685 : /*
7686 : * If the nullness isn't already proven by validated constraints, have
7687 : * ALTER TABLE phase 3 test for it.
7688 : */
7689 1190 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7690 : {
7691 : AlteredTableInfo *tab;
7692 :
7693 1092 : tab = ATGetQueueEntry(wqueue, rel);
7694 1092 : tab->verify_new_notnull = true;
7695 : }
7696 :
7697 1190 : CommandCounterIncrement();
7698 :
7699 1190 : table_close(attr_rel, RowExclusiveLock);
7700 1190 : heap_freetuple(tuple);
7701 : }
7702 : }
7703 :
7704 : /*
7705 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7706 : *
7707 : * Add a not-null constraint to a single table and its children. Returns
7708 : * the address of the constraint added to the parent relation, if one gets
7709 : * added, or InvalidObjectAddress otherwise.
7710 : *
7711 : * We must recurse to child tables during execution, rather than using
7712 : * ALTER TABLE's normal prep-time recursion.
7713 : */
7714 : static ObjectAddress
7715 574 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7716 : bool recurse, bool recursing, LOCKMODE lockmode)
7717 : {
7718 : HeapTuple tuple;
7719 : AttrNumber attnum;
7720 : ObjectAddress address;
7721 : Constraint *constraint;
7722 : CookedConstraint *ccon;
7723 : List *cooked;
7724 574 : bool is_no_inherit = false;
7725 :
7726 : /* Guard against stack overflow due to overly deep inheritance tree. */
7727 574 : check_stack_depth();
7728 :
7729 : /* At top level, permission check was done in ATPrepCmd, else do it */
7730 574 : if (recursing)
7731 : {
7732 202 : ATSimplePermissions(AT_AddConstraint, rel,
7733 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7734 : Assert(conName != NULL);
7735 : }
7736 :
7737 574 : attnum = get_attnum(RelationGetRelid(rel), colName);
7738 574 : if (attnum == InvalidAttrNumber)
7739 18 : ereport(ERROR,
7740 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7741 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7742 : colName, RelationGetRelationName(rel))));
7743 :
7744 : /* Prevent them from altering a system attribute */
7745 556 : if (attnum <= 0)
7746 0 : ereport(ERROR,
7747 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7748 : errmsg("cannot alter system column \"%s\"",
7749 : colName)));
7750 :
7751 : /* See if there's already a constraint */
7752 556 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7753 556 : if (HeapTupleIsValid(tuple))
7754 : {
7755 92 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7756 92 : bool changed = false;
7757 :
7758 : /*
7759 : * Don't let a NO INHERIT constraint be changed into inherit.
7760 : */
7761 92 : if (conForm->connoinherit && recurse)
7762 6 : ereport(ERROR,
7763 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7764 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7765 : NameStr(conForm->conname),
7766 : RelationGetRelationName(rel)));
7767 :
7768 : /*
7769 : * If we find an appropriate constraint, we're almost done, but just
7770 : * need to change some properties on it: if we're recursing, increment
7771 : * coninhcount; if not, set conislocal if not already set.
7772 : */
7773 86 : if (recursing)
7774 : {
7775 66 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7776 : &conForm->coninhcount))
7777 0 : ereport(ERROR,
7778 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7779 : errmsg("too many inheritance parents"));
7780 66 : changed = true;
7781 : }
7782 20 : else if (!conForm->conislocal)
7783 : {
7784 0 : conForm->conislocal = true;
7785 0 : changed = true;
7786 : }
7787 :
7788 86 : if (changed)
7789 : {
7790 : Relation constr_rel;
7791 :
7792 66 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7793 :
7794 66 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7795 66 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7796 66 : table_close(constr_rel, RowExclusiveLock);
7797 : }
7798 :
7799 86 : if (changed)
7800 66 : return address;
7801 : else
7802 20 : return InvalidObjectAddress;
7803 : }
7804 :
7805 : /*
7806 : * If we're asked not to recurse, and children exist, raise an error for
7807 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7808 : * specified.
7809 : */
7810 488 : if (!recurse &&
7811 24 : find_inheritance_children(RelationGetRelid(rel),
7812 : NoLock) != NIL)
7813 : {
7814 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7815 6 : ereport(ERROR,
7816 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7817 : errmsg("constraint must be added to child tables too"),
7818 : errhint("Do not specify the ONLY keyword."));
7819 : else
7820 12 : is_no_inherit = true;
7821 : }
7822 :
7823 : /*
7824 : * No constraint exists; we must add one. First determine a name to use,
7825 : * if we haven't already.
7826 : */
7827 458 : if (!recursing)
7828 : {
7829 : Assert(conName == NULL);
7830 322 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7831 : colName, "not_null",
7832 322 : RelationGetNamespace(rel),
7833 : NIL);
7834 : }
7835 :
7836 458 : constraint = makeNotNullConstraint(makeString(colName));
7837 458 : constraint->is_no_inherit = is_no_inherit;
7838 458 : constraint->conname = conName;
7839 :
7840 : /* and do it */
7841 458 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7842 458 : false, !recursing, false, NULL);
7843 458 : ccon = linitial(cooked);
7844 458 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7845 :
7846 458 : InvokeObjectPostAlterHook(RelationRelationId,
7847 : RelationGetRelid(rel), attnum);
7848 :
7849 : /* Mark pg_attribute.attnotnull for the column */
7850 458 : set_attnotnull(wqueue, rel, attnum, lockmode);
7851 :
7852 : /*
7853 : * Recurse to propagate the constraint to children that don't have one.
7854 : */
7855 458 : if (recurse)
7856 : {
7857 : List *children;
7858 :
7859 440 : children = find_inheritance_children(RelationGetRelid(rel),
7860 : lockmode);
7861 :
7862 1082 : foreach_oid(childoid, children)
7863 : {
7864 202 : Relation childrel = table_open(childoid, NoLock);
7865 :
7866 202 : CommandCounterIncrement();
7867 :
7868 202 : ATExecSetNotNull(wqueue, childrel, conName, colName,
7869 : recurse, true, lockmode);
7870 202 : table_close(childrel, NoLock);
7871 : }
7872 : }
7873 :
7874 458 : return address;
7875 : }
7876 :
7877 : /*
7878 : * NotNullImpliedByRelConstraints
7879 : * Does rel's existing constraints imply NOT NULL for the given attribute?
7880 : */
7881 : static bool
7882 1142 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7883 : {
7884 1142 : NullTest *nnulltest = makeNode(NullTest);
7885 :
7886 2284 : nnulltest->arg = (Expr *) makeVar(1,
7887 1142 : attr->attnum,
7888 : attr->atttypid,
7889 : attr->atttypmod,
7890 : attr->attcollation,
7891 : 0);
7892 1142 : nnulltest->nulltesttype = IS_NOT_NULL;
7893 :
7894 : /*
7895 : * argisrow = false is correct even for a composite column, because
7896 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7897 : * case, just IS DISTINCT FROM NULL.
7898 : */
7899 1142 : nnulltest->argisrow = false;
7900 1142 : nnulltest->location = -1;
7901 :
7902 1142 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7903 : {
7904 50 : ereport(DEBUG1,
7905 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7906 : RelationGetRelationName(rel), NameStr(attr->attname))));
7907 50 : return true;
7908 : }
7909 :
7910 1092 : return false;
7911 : }
7912 :
7913 : /*
7914 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7915 : *
7916 : * Return the address of the affected column.
7917 : */
7918 : static ObjectAddress
7919 562 : ATExecColumnDefault(Relation rel, const char *colName,
7920 : Node *newDefault, LOCKMODE lockmode)
7921 : {
7922 562 : TupleDesc tupdesc = RelationGetDescr(rel);
7923 : AttrNumber attnum;
7924 : ObjectAddress address;
7925 :
7926 : /*
7927 : * get the number of the attribute
7928 : */
7929 562 : attnum = get_attnum(RelationGetRelid(rel), colName);
7930 562 : if (attnum == InvalidAttrNumber)
7931 30 : ereport(ERROR,
7932 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7933 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7934 : colName, RelationGetRelationName(rel))));
7935 :
7936 : /* Prevent them from altering a system attribute */
7937 532 : if (attnum <= 0)
7938 0 : ereport(ERROR,
7939 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7940 : errmsg("cannot alter system column \"%s\"",
7941 : colName)));
7942 :
7943 532 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7944 18 : ereport(ERROR,
7945 : (errcode(ERRCODE_SYNTAX_ERROR),
7946 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7947 : colName, RelationGetRelationName(rel)),
7948 : /* translator: %s is an SQL ALTER command */
7949 : newDefault ? 0 : errhint("Use %s instead.",
7950 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7951 :
7952 514 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7953 6 : ereport(ERROR,
7954 : (errcode(ERRCODE_SYNTAX_ERROR),
7955 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
7956 : colName, RelationGetRelationName(rel)),
7957 : newDefault ?
7958 : /* translator: %s is an SQL ALTER command */
7959 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7960 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
7961 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7962 :
7963 : /*
7964 : * Remove any old default for the column. We use RESTRICT here for
7965 : * safety, but at present we do not expect anything to depend on the
7966 : * default.
7967 : *
7968 : * We treat removing the existing default as an internal operation when it
7969 : * is preparatory to adding a new default, but as a user-initiated
7970 : * operation when the user asked for a drop.
7971 : */
7972 508 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7973 : newDefault != NULL);
7974 :
7975 508 : if (newDefault)
7976 : {
7977 : /* SET DEFAULT */
7978 : RawColumnDefault *rawEnt;
7979 :
7980 334 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7981 334 : rawEnt->attnum = attnum;
7982 334 : rawEnt->raw_default = newDefault;
7983 334 : rawEnt->missingMode = false;
7984 334 : rawEnt->generated = '\0';
7985 :
7986 : /*
7987 : * This function is intended for CREATE TABLE, so it processes a
7988 : * _list_ of defaults, but we just do one.
7989 : */
7990 334 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7991 : false, true, false, NULL);
7992 : }
7993 :
7994 502 : ObjectAddressSubSet(address, RelationRelationId,
7995 : RelationGetRelid(rel), attnum);
7996 502 : return address;
7997 : }
7998 :
7999 : /*
8000 : * Add a pre-cooked default expression.
8001 : *
8002 : * Return the address of the affected column.
8003 : */
8004 : static ObjectAddress
8005 56 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8006 : Node *newDefault)
8007 : {
8008 : ObjectAddress address;
8009 :
8010 : /* We assume no checking is required */
8011 :
8012 : /*
8013 : * Remove any old default for the column. We use RESTRICT here for
8014 : * safety, but at present we do not expect anything to depend on the
8015 : * default. (In ordinary cases, there could not be a default in place
8016 : * anyway, but it's possible when combining LIKE with inheritance.)
8017 : */
8018 56 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8019 : true);
8020 :
8021 56 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8022 :
8023 56 : ObjectAddressSubSet(address, RelationRelationId,
8024 : RelationGetRelid(rel), attnum);
8025 56 : return address;
8026 : }
8027 :
8028 : /*
8029 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8030 : *
8031 : * Return the address of the affected column.
8032 : */
8033 : static ObjectAddress
8034 160 : ATExecAddIdentity(Relation rel, const char *colName,
8035 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8036 : {
8037 : Relation attrelation;
8038 : HeapTuple tuple;
8039 : Form_pg_attribute attTup;
8040 : AttrNumber attnum;
8041 : ObjectAddress address;
8042 160 : ColumnDef *cdef = castNode(ColumnDef, def);
8043 : bool ispartitioned;
8044 :
8045 160 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8046 160 : if (ispartitioned && !recurse)
8047 6 : ereport(ERROR,
8048 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8049 : errmsg("cannot add identity to a column of only the partitioned table"),
8050 : errhint("Do not specify the ONLY keyword.")));
8051 :
8052 154 : if (rel->rd_rel->relispartition && !recursing)
8053 12 : ereport(ERROR,
8054 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8055 : errmsg("cannot add identity to a column of a partition"));
8056 :
8057 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8058 :
8059 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8060 142 : if (!HeapTupleIsValid(tuple))
8061 0 : ereport(ERROR,
8062 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8063 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8064 : colName, RelationGetRelationName(rel))));
8065 142 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8066 142 : attnum = attTup->attnum;
8067 :
8068 : /* Can't alter a system attribute */
8069 142 : if (attnum <= 0)
8070 0 : ereport(ERROR,
8071 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8072 : errmsg("cannot alter system column \"%s\"",
8073 : colName)));
8074 :
8075 : /*
8076 : * Creating a column as identity implies NOT NULL, so adding the identity
8077 : * to an existing column that is not NOT NULL would create a state that
8078 : * cannot be reproduced without contortions.
8079 : */
8080 142 : if (!attTup->attnotnull)
8081 6 : ereport(ERROR,
8082 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8083 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8084 : colName, RelationGetRelationName(rel))));
8085 :
8086 136 : if (attTup->attidentity)
8087 18 : ereport(ERROR,
8088 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8089 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8090 : colName, RelationGetRelationName(rel))));
8091 :
8092 118 : if (attTup->atthasdef)
8093 6 : ereport(ERROR,
8094 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8095 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8096 : colName, RelationGetRelationName(rel))));
8097 :
8098 112 : attTup->attidentity = cdef->identity;
8099 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8100 :
8101 112 : InvokeObjectPostAlterHook(RelationRelationId,
8102 : RelationGetRelid(rel),
8103 : attTup->attnum);
8104 112 : ObjectAddressSubSet(address, RelationRelationId,
8105 : RelationGetRelid(rel), attnum);
8106 112 : heap_freetuple(tuple);
8107 :
8108 112 : table_close(attrelation, RowExclusiveLock);
8109 :
8110 : /*
8111 : * Recurse to propagate the identity column to partitions. Identity is
8112 : * not inherited in regular inheritance children.
8113 : */
8114 112 : if (recurse && ispartitioned)
8115 : {
8116 : List *children;
8117 : ListCell *lc;
8118 :
8119 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8120 :
8121 16 : foreach(lc, children)
8122 : {
8123 : Relation childrel;
8124 :
8125 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8126 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8127 6 : table_close(childrel, NoLock);
8128 : }
8129 : }
8130 :
8131 112 : return address;
8132 : }
8133 :
8134 : /*
8135 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8136 : *
8137 : * Return the address of the affected column.
8138 : */
8139 : static ObjectAddress
8140 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8141 : LOCKMODE lockmode, bool recurse, bool recursing)
8142 : {
8143 : ListCell *option;
8144 74 : DefElem *generatedEl = NULL;
8145 : HeapTuple tuple;
8146 : Form_pg_attribute attTup;
8147 : AttrNumber attnum;
8148 : Relation attrelation;
8149 : ObjectAddress address;
8150 : bool ispartitioned;
8151 :
8152 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8153 74 : if (ispartitioned && !recurse)
8154 6 : ereport(ERROR,
8155 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8156 : errmsg("cannot change identity column of only the partitioned table"),
8157 : errhint("Do not specify the ONLY keyword.")));
8158 :
8159 68 : if (rel->rd_rel->relispartition && !recursing)
8160 12 : ereport(ERROR,
8161 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8162 : errmsg("cannot change identity column of a partition"));
8163 :
8164 100 : foreach(option, castNode(List, def))
8165 : {
8166 44 : DefElem *defel = lfirst_node(DefElem, option);
8167 :
8168 44 : if (strcmp(defel->defname, "generated") == 0)
8169 : {
8170 44 : if (generatedEl)
8171 0 : ereport(ERROR,
8172 : (errcode(ERRCODE_SYNTAX_ERROR),
8173 : errmsg("conflicting or redundant options")));
8174 44 : generatedEl = defel;
8175 : }
8176 : else
8177 0 : elog(ERROR, "option \"%s\" not recognized",
8178 : defel->defname);
8179 : }
8180 :
8181 : /*
8182 : * Even if there is nothing to change here, we run all the checks. There
8183 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8184 : * there.
8185 : */
8186 :
8187 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8188 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8189 56 : if (!HeapTupleIsValid(tuple))
8190 0 : ereport(ERROR,
8191 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8192 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8193 : colName, RelationGetRelationName(rel))));
8194 :
8195 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8196 56 : attnum = attTup->attnum;
8197 :
8198 56 : if (attnum <= 0)
8199 0 : ereport(ERROR,
8200 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8201 : errmsg("cannot alter system column \"%s\"",
8202 : colName)));
8203 :
8204 56 : if (!attTup->attidentity)
8205 6 : ereport(ERROR,
8206 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8207 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8208 : colName, RelationGetRelationName(rel))));
8209 :
8210 50 : if (generatedEl)
8211 : {
8212 44 : attTup->attidentity = defGetInt32(generatedEl);
8213 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8214 :
8215 44 : InvokeObjectPostAlterHook(RelationRelationId,
8216 : RelationGetRelid(rel),
8217 : attTup->attnum);
8218 44 : ObjectAddressSubSet(address, RelationRelationId,
8219 : RelationGetRelid(rel), attnum);
8220 : }
8221 : else
8222 6 : address = InvalidObjectAddress;
8223 :
8224 50 : heap_freetuple(tuple);
8225 50 : table_close(attrelation, RowExclusiveLock);
8226 :
8227 : /*
8228 : * Recurse to propagate the identity change to partitions. Identity is not
8229 : * inherited in regular inheritance children.
8230 : */
8231 50 : if (generatedEl && recurse && ispartitioned)
8232 : {
8233 : List *children;
8234 : ListCell *lc;
8235 :
8236 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8237 :
8238 18 : foreach(lc, children)
8239 : {
8240 : Relation childrel;
8241 :
8242 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8243 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8244 12 : table_close(childrel, NoLock);
8245 : }
8246 : }
8247 :
8248 50 : return address;
8249 : }
8250 :
8251 : /*
8252 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8253 : *
8254 : * Return the address of the affected column.
8255 : */
8256 : static ObjectAddress
8257 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8258 : bool recurse, bool recursing)
8259 : {
8260 : HeapTuple tuple;
8261 : Form_pg_attribute attTup;
8262 : AttrNumber attnum;
8263 : Relation attrelation;
8264 : ObjectAddress address;
8265 : Oid seqid;
8266 : ObjectAddress seqaddress;
8267 : bool ispartitioned;
8268 :
8269 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8270 68 : if (ispartitioned && !recurse)
8271 6 : ereport(ERROR,
8272 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8273 : errmsg("cannot drop identity from a column of only the partitioned table"),
8274 : errhint("Do not specify the ONLY keyword.")));
8275 :
8276 62 : if (rel->rd_rel->relispartition && !recursing)
8277 6 : ereport(ERROR,
8278 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8279 : errmsg("cannot drop identity from a column of a partition"));
8280 :
8281 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8282 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8283 56 : if (!HeapTupleIsValid(tuple))
8284 0 : ereport(ERROR,
8285 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8286 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8287 : colName, RelationGetRelationName(rel))));
8288 :
8289 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8290 56 : attnum = attTup->attnum;
8291 :
8292 56 : if (attnum <= 0)
8293 0 : ereport(ERROR,
8294 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8295 : errmsg("cannot alter system column \"%s\"",
8296 : colName)));
8297 :
8298 56 : if (!attTup->attidentity)
8299 : {
8300 12 : if (!missing_ok)
8301 6 : ereport(ERROR,
8302 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8303 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8304 : colName, RelationGetRelationName(rel))));
8305 : else
8306 : {
8307 6 : ereport(NOTICE,
8308 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8309 : colName, RelationGetRelationName(rel))));
8310 6 : heap_freetuple(tuple);
8311 6 : table_close(attrelation, RowExclusiveLock);
8312 6 : return InvalidObjectAddress;
8313 : }
8314 : }
8315 :
8316 44 : attTup->attidentity = '\0';
8317 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8318 :
8319 44 : InvokeObjectPostAlterHook(RelationRelationId,
8320 : RelationGetRelid(rel),
8321 : attTup->attnum);
8322 44 : ObjectAddressSubSet(address, RelationRelationId,
8323 : RelationGetRelid(rel), attnum);
8324 44 : heap_freetuple(tuple);
8325 :
8326 44 : table_close(attrelation, RowExclusiveLock);
8327 :
8328 : /*
8329 : * Recurse to drop the identity from column in partitions. Identity is
8330 : * not inherited in regular inheritance children so ignore them.
8331 : */
8332 44 : if (recurse && ispartitioned)
8333 : {
8334 : List *children;
8335 : ListCell *lc;
8336 :
8337 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8338 :
8339 12 : foreach(lc, children)
8340 : {
8341 : Relation childrel;
8342 :
8343 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8344 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8345 6 : table_close(childrel, NoLock);
8346 : }
8347 : }
8348 :
8349 44 : if (!recursing)
8350 : {
8351 : /* drop the internal sequence */
8352 32 : seqid = getIdentitySequence(rel, attnum, false);
8353 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8354 : RelationRelationId, DEPENDENCY_INTERNAL);
8355 32 : CommandCounterIncrement();
8356 32 : seqaddress.classId = RelationRelationId;
8357 32 : seqaddress.objectId = seqid;
8358 32 : seqaddress.objectSubId = 0;
8359 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8360 : }
8361 :
8362 44 : return address;
8363 : }
8364 :
8365 : /*
8366 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8367 : *
8368 : * Return the address of the affected column.
8369 : */
8370 : static ObjectAddress
8371 84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8372 : Node *newExpr, LOCKMODE lockmode)
8373 : {
8374 : HeapTuple tuple;
8375 : Form_pg_attribute attTup;
8376 : AttrNumber attnum;
8377 : Oid attrdefoid;
8378 : ObjectAddress address;
8379 : Expr *defval;
8380 : NewColumnValue *newval;
8381 : RawColumnDefault *rawEnt;
8382 :
8383 84 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8384 84 : if (!HeapTupleIsValid(tuple))
8385 0 : ereport(ERROR,
8386 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8387 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8388 : colName, RelationGetRelationName(rel))));
8389 :
8390 84 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8391 84 : attnum = attTup->attnum;
8392 :
8393 84 : if (attnum <= 0)
8394 0 : ereport(ERROR,
8395 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8396 : errmsg("cannot alter system column \"%s\"",
8397 : colName)));
8398 :
8399 84 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8400 6 : ereport(ERROR,
8401 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8402 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8403 : colName, RelationGetRelationName(rel))));
8404 78 : ReleaseSysCache(tuple);
8405 :
8406 : /*
8407 : * Clear all the missing values if we're rewriting the table, since this
8408 : * renders them pointless.
8409 : */
8410 78 : RelationClearMissing(rel);
8411 :
8412 : /* make sure we don't conflict with later attribute modifications */
8413 78 : CommandCounterIncrement();
8414 :
8415 : /*
8416 : * Find everything that depends on the column (constraints, indexes, etc),
8417 : * and record enough information to let us recreate the objects after
8418 : * rewrite.
8419 : */
8420 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8421 :
8422 : /*
8423 : * Drop the dependency records of the GENERATED expression, in particular
8424 : * its INTERNAL dependency on the column, which would otherwise cause
8425 : * dependency.c to refuse to perform the deletion.
8426 : */
8427 78 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8428 78 : if (!OidIsValid(attrdefoid))
8429 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8430 : RelationGetRelid(rel), attnum);
8431 78 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8432 :
8433 : /* Make above changes visible */
8434 78 : CommandCounterIncrement();
8435 :
8436 : /*
8437 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8438 : * safety, but at present we do not expect anything to depend on the
8439 : * expression.
8440 : */
8441 78 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8442 : false, false);
8443 :
8444 : /* Prepare to store the new expression, in the catalogs */
8445 78 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8446 78 : rawEnt->attnum = attnum;
8447 78 : rawEnt->raw_default = newExpr;
8448 78 : rawEnt->missingMode = false;
8449 78 : rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8450 :
8451 : /* Store the generated expression */
8452 78 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8453 : false, true, false, NULL);
8454 :
8455 : /* Make above new expression visible */
8456 78 : CommandCounterIncrement();
8457 :
8458 : /* Prepare for table rewrite */
8459 78 : defval = (Expr *) build_column_default(rel, attnum);
8460 :
8461 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8462 78 : newval->attnum = attnum;
8463 78 : newval->expr = expression_planner(defval);
8464 78 : newval->is_generated = true;
8465 :
8466 78 : tab->newvals = lappend(tab->newvals, newval);
8467 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8468 :
8469 : /* Drop any pg_statistic entry for the column */
8470 78 : RemoveStatistics(RelationGetRelid(rel), attnum);
8471 :
8472 78 : InvokeObjectPostAlterHook(RelationRelationId,
8473 : RelationGetRelid(rel), attnum);
8474 :
8475 78 : ObjectAddressSubSet(address, RelationRelationId,
8476 : RelationGetRelid(rel), attnum);
8477 78 : return address;
8478 : }
8479 :
8480 : /*
8481 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8482 : */
8483 : static void
8484 44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8485 : {
8486 : /*
8487 : * Reject ONLY if there are child tables. We could implement this, but it
8488 : * is a bit complicated. GENERATED clauses must be attached to the column
8489 : * definition and cannot be added later like DEFAULT, so if a child table
8490 : * has a generation expression that the parent does not have, the child
8491 : * column will necessarily be an attislocal column. So to implement ONLY
8492 : * here, we'd need extra code to update attislocal of the direct child
8493 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8494 : * resulting state can be properly dumped and restored.
8495 : */
8496 56 : if (!recurse &&
8497 12 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8498 6 : ereport(ERROR,
8499 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8500 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8501 :
8502 : /*
8503 : * Cannot drop generation expression from inherited columns.
8504 : */
8505 38 : if (!recursing)
8506 : {
8507 : HeapTuple tuple;
8508 : Form_pg_attribute attTup;
8509 :
8510 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8511 32 : if (!HeapTupleIsValid(tuple))
8512 0 : ereport(ERROR,
8513 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8514 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8515 : cmd->name, RelationGetRelationName(rel))));
8516 :
8517 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8518 :
8519 32 : if (attTup->attinhcount > 0)
8520 6 : ereport(ERROR,
8521 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8522 : errmsg("cannot drop generation expression from inherited column")));
8523 : }
8524 32 : }
8525 :
8526 : /*
8527 : * Return the address of the affected column.
8528 : */
8529 : static ObjectAddress
8530 32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8531 : {
8532 : HeapTuple tuple;
8533 : Form_pg_attribute attTup;
8534 : AttrNumber attnum;
8535 : Relation attrelation;
8536 : Oid attrdefoid;
8537 : ObjectAddress address;
8538 :
8539 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8540 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8541 32 : if (!HeapTupleIsValid(tuple))
8542 0 : ereport(ERROR,
8543 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8544 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8545 : colName, RelationGetRelationName(rel))));
8546 :
8547 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8548 32 : attnum = attTup->attnum;
8549 :
8550 32 : if (attnum <= 0)
8551 0 : ereport(ERROR,
8552 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8553 : errmsg("cannot alter system column \"%s\"",
8554 : colName)));
8555 :
8556 32 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8557 : {
8558 12 : if (!missing_ok)
8559 6 : ereport(ERROR,
8560 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8561 : errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8562 : colName, RelationGetRelationName(rel))));
8563 : else
8564 : {
8565 6 : ereport(NOTICE,
8566 : (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8567 : colName, RelationGetRelationName(rel))));
8568 6 : heap_freetuple(tuple);
8569 6 : table_close(attrelation, RowExclusiveLock);
8570 6 : return InvalidObjectAddress;
8571 : }
8572 : }
8573 :
8574 : /*
8575 : * Mark the column as no longer generated. (The atthasdef flag needs to
8576 : * get cleared too, but RemoveAttrDefault will handle that.)
8577 : */
8578 20 : attTup->attgenerated = '\0';
8579 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8580 :
8581 20 : InvokeObjectPostAlterHook(RelationRelationId,
8582 : RelationGetRelid(rel),
8583 : attnum);
8584 20 : heap_freetuple(tuple);
8585 :
8586 20 : table_close(attrelation, RowExclusiveLock);
8587 :
8588 : /*
8589 : * Drop the dependency records of the GENERATED expression, in particular
8590 : * its INTERNAL dependency on the column, which would otherwise cause
8591 : * dependency.c to refuse to perform the deletion.
8592 : */
8593 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8594 20 : if (!OidIsValid(attrdefoid))
8595 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8596 : RelationGetRelid(rel), attnum);
8597 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8598 :
8599 : /* Make above changes visible */
8600 20 : CommandCounterIncrement();
8601 :
8602 : /*
8603 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8604 : * safety, but at present we do not expect anything to depend on the
8605 : * default.
8606 : */
8607 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8608 : false, false);
8609 :
8610 20 : ObjectAddressSubSet(address, RelationRelationId,
8611 : RelationGetRelid(rel), attnum);
8612 20 : return address;
8613 : }
8614 :
8615 : /*
8616 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8617 : *
8618 : * Return value is the address of the modified column
8619 : */
8620 : static ObjectAddress
8621 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8622 : {
8623 164 : int newtarget = 0;
8624 : bool newtarget_default;
8625 : Relation attrelation;
8626 : HeapTuple tuple,
8627 : newtuple;
8628 : Form_pg_attribute attrtuple;
8629 : AttrNumber attnum;
8630 : ObjectAddress address;
8631 : Datum repl_val[Natts_pg_attribute];
8632 : bool repl_null[Natts_pg_attribute];
8633 : bool repl_repl[Natts_pg_attribute];
8634 :
8635 : /*
8636 : * We allow referencing columns by numbers only for indexes, since table
8637 : * column numbers could contain gaps if columns are later dropped.
8638 : */
8639 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8640 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8641 : !colName)
8642 0 : ereport(ERROR,
8643 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8644 : errmsg("cannot refer to non-index column by number")));
8645 :
8646 : /* -1 was used in previous versions for the default setting */
8647 164 : if (newValue && intVal(newValue) != -1)
8648 : {
8649 120 : newtarget = intVal(newValue);
8650 120 : newtarget_default = false;
8651 : }
8652 : else
8653 44 : newtarget_default = true;
8654 :
8655 164 : if (!newtarget_default)
8656 : {
8657 : /*
8658 : * Limit target to a sane range
8659 : */
8660 120 : if (newtarget < 0)
8661 : {
8662 0 : ereport(ERROR,
8663 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8664 : errmsg("statistics target %d is too low",
8665 : newtarget)));
8666 : }
8667 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8668 : {
8669 0 : newtarget = MAX_STATISTICS_TARGET;
8670 0 : ereport(WARNING,
8671 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8672 : errmsg("lowering statistics target to %d",
8673 : newtarget)));
8674 : }
8675 : }
8676 :
8677 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8678 :
8679 164 : if (colName)
8680 : {
8681 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8682 :
8683 100 : if (!HeapTupleIsValid(tuple))
8684 12 : ereport(ERROR,
8685 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8686 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8687 : colName, RelationGetRelationName(rel))));
8688 : }
8689 : else
8690 : {
8691 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8692 :
8693 64 : if (!HeapTupleIsValid(tuple))
8694 12 : ereport(ERROR,
8695 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8696 : errmsg("column number %d of relation \"%s\" does not exist",
8697 : colNum, RelationGetRelationName(rel))));
8698 : }
8699 :
8700 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8701 :
8702 140 : attnum = attrtuple->attnum;
8703 140 : if (attnum <= 0)
8704 0 : ereport(ERROR,
8705 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8706 : errmsg("cannot alter system column \"%s\"",
8707 : colName)));
8708 :
8709 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8710 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8711 : {
8712 52 : if (attnum > rel->rd_index->indnkeyatts)
8713 6 : ereport(ERROR,
8714 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8715 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8716 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8717 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8718 18 : ereport(ERROR,
8719 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8720 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8721 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8722 : errhint("Alter statistics on table column instead.")));
8723 : }
8724 :
8725 : /* Build new tuple. */
8726 116 : memset(repl_null, false, sizeof(repl_null));
8727 116 : memset(repl_repl, false, sizeof(repl_repl));
8728 116 : if (!newtarget_default)
8729 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8730 : else
8731 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8732 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8733 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8734 : repl_val, repl_null, repl_repl);
8735 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8736 :
8737 116 : InvokeObjectPostAlterHook(RelationRelationId,
8738 : RelationGetRelid(rel),
8739 : attrtuple->attnum);
8740 116 : ObjectAddressSubSet(address, RelationRelationId,
8741 : RelationGetRelid(rel), attnum);
8742 :
8743 116 : heap_freetuple(newtuple);
8744 :
8745 116 : ReleaseSysCache(tuple);
8746 :
8747 116 : table_close(attrelation, RowExclusiveLock);
8748 :
8749 116 : return address;
8750 : }
8751 :
8752 : /*
8753 : * Return value is the address of the modified column
8754 : */
8755 : static ObjectAddress
8756 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8757 : bool isReset, LOCKMODE lockmode)
8758 : {
8759 : Relation attrelation;
8760 : HeapTuple tuple,
8761 : newtuple;
8762 : Form_pg_attribute attrtuple;
8763 : AttrNumber attnum;
8764 : Datum datum,
8765 : newOptions;
8766 : bool isnull;
8767 : ObjectAddress address;
8768 : Datum repl_val[Natts_pg_attribute];
8769 : bool repl_null[Natts_pg_attribute];
8770 : bool repl_repl[Natts_pg_attribute];
8771 :
8772 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8773 :
8774 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8775 :
8776 32 : if (!HeapTupleIsValid(tuple))
8777 0 : ereport(ERROR,
8778 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8779 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8780 : colName, RelationGetRelationName(rel))));
8781 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8782 :
8783 32 : attnum = attrtuple->attnum;
8784 32 : if (attnum <= 0)
8785 0 : ereport(ERROR,
8786 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8787 : errmsg("cannot alter system column \"%s\"",
8788 : colName)));
8789 :
8790 : /* Generate new proposed attoptions (text array) */
8791 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8792 : &isnull);
8793 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8794 : castNode(List, options), NULL, NULL,
8795 : false, isReset);
8796 : /* Validate new options */
8797 32 : (void) attribute_reloptions(newOptions, true);
8798 :
8799 : /* Build new tuple. */
8800 32 : memset(repl_null, false, sizeof(repl_null));
8801 32 : memset(repl_repl, false, sizeof(repl_repl));
8802 32 : if (newOptions != (Datum) 0)
8803 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8804 : else
8805 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8806 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8807 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8808 : repl_val, repl_null, repl_repl);
8809 :
8810 : /* Update system catalog. */
8811 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8812 :
8813 32 : InvokeObjectPostAlterHook(RelationRelationId,
8814 : RelationGetRelid(rel),
8815 : attrtuple->attnum);
8816 32 : ObjectAddressSubSet(address, RelationRelationId,
8817 : RelationGetRelid(rel), attnum);
8818 :
8819 32 : heap_freetuple(newtuple);
8820 :
8821 32 : ReleaseSysCache(tuple);
8822 :
8823 32 : table_close(attrelation, RowExclusiveLock);
8824 :
8825 32 : return address;
8826 : }
8827 :
8828 : /*
8829 : * Helper function for ATExecSetStorage and ATExecSetCompression
8830 : *
8831 : * Set the attstorage and/or attcompression fields for index columns
8832 : * associated with the specified table column.
8833 : */
8834 : static void
8835 284 : SetIndexStorageProperties(Relation rel, Relation attrelation,
8836 : AttrNumber attnum,
8837 : bool setstorage, char newstorage,
8838 : bool setcompression, char newcompression,
8839 : LOCKMODE lockmode)
8840 : {
8841 : ListCell *lc;
8842 :
8843 356 : foreach(lc, RelationGetIndexList(rel))
8844 : {
8845 72 : Oid indexoid = lfirst_oid(lc);
8846 : Relation indrel;
8847 72 : AttrNumber indattnum = 0;
8848 : HeapTuple tuple;
8849 :
8850 72 : indrel = index_open(indexoid, lockmode);
8851 :
8852 120 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
8853 : {
8854 78 : if (indrel->rd_index->indkey.values[i] == attnum)
8855 : {
8856 30 : indattnum = i + 1;
8857 30 : break;
8858 : }
8859 : }
8860 :
8861 72 : if (indattnum == 0)
8862 : {
8863 42 : index_close(indrel, lockmode);
8864 42 : continue;
8865 : }
8866 :
8867 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8868 :
8869 30 : if (HeapTupleIsValid(tuple))
8870 : {
8871 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8872 :
8873 30 : if (setstorage)
8874 24 : attrtuple->attstorage = newstorage;
8875 :
8876 30 : if (setcompression)
8877 6 : attrtuple->attcompression = newcompression;
8878 :
8879 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8880 :
8881 30 : InvokeObjectPostAlterHook(RelationRelationId,
8882 : RelationGetRelid(rel),
8883 : attrtuple->attnum);
8884 :
8885 30 : heap_freetuple(tuple);
8886 : }
8887 :
8888 30 : index_close(indrel, lockmode);
8889 : }
8890 284 : }
8891 :
8892 : /*
8893 : * ALTER TABLE ALTER COLUMN SET STORAGE
8894 : *
8895 : * Return value is the address of the modified column
8896 : */
8897 : static ObjectAddress
8898 234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8899 : {
8900 : Relation attrelation;
8901 : HeapTuple tuple;
8902 : Form_pg_attribute attrtuple;
8903 : AttrNumber attnum;
8904 : ObjectAddress address;
8905 :
8906 234 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8907 :
8908 234 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8909 :
8910 234 : if (!HeapTupleIsValid(tuple))
8911 12 : ereport(ERROR,
8912 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8913 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8914 : colName, RelationGetRelationName(rel))));
8915 222 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8916 :
8917 222 : attnum = attrtuple->attnum;
8918 222 : if (attnum <= 0)
8919 0 : ereport(ERROR,
8920 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8921 : errmsg("cannot alter system column \"%s\"",
8922 : colName)));
8923 :
8924 222 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8925 :
8926 222 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8927 :
8928 222 : InvokeObjectPostAlterHook(RelationRelationId,
8929 : RelationGetRelid(rel),
8930 : attrtuple->attnum);
8931 :
8932 : /*
8933 : * Apply the change to indexes as well (only for simple index columns,
8934 : * matching behavior of index.c ConstructTupleDescriptor()).
8935 : */
8936 222 : SetIndexStorageProperties(rel, attrelation, attnum,
8937 222 : true, attrtuple->attstorage,
8938 : false, 0,
8939 : lockmode);
8940 :
8941 222 : heap_freetuple(tuple);
8942 :
8943 222 : table_close(attrelation, RowExclusiveLock);
8944 :
8945 222 : ObjectAddressSubSet(address, RelationRelationId,
8946 : RelationGetRelid(rel), attnum);
8947 222 : return address;
8948 : }
8949 :
8950 :
8951 : /*
8952 : * ALTER TABLE DROP COLUMN
8953 : *
8954 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8955 : * because we have to decide at runtime whether to recurse or not depending
8956 : * on whether attinhcount goes to zero or not. (We can't check this in a
8957 : * static pre-pass because it won't handle multiple inheritance situations
8958 : * correctly.)
8959 : */
8960 : static void
8961 1598 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8962 : AlterTableCmd *cmd, LOCKMODE lockmode,
8963 : AlterTableUtilityContext *context)
8964 : {
8965 1598 : if (rel->rd_rel->reloftype && !recursing)
8966 6 : ereport(ERROR,
8967 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8968 : errmsg("cannot drop column from typed table")));
8969 :
8970 1592 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8971 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8972 :
8973 1586 : if (recurse)
8974 1318 : cmd->recurse = true;
8975 1586 : }
8976 :
8977 : /*
8978 : * Drops column 'colName' from relation 'rel' and returns the address of the
8979 : * dropped column. The column is also dropped (or marked as no longer
8980 : * inherited from relation) from the relation's inheritance children, if any.
8981 : *
8982 : * In the recursive invocations for inheritance child relations, instead of
8983 : * dropping the column directly (if to be dropped at all), its object address
8984 : * is added to 'addrs', which must be non-NULL in such invocations. All
8985 : * columns are dropped at the same time after all the children have been
8986 : * checked recursively.
8987 : */
8988 : static ObjectAddress
8989 2142 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8990 : DropBehavior behavior,
8991 : bool recurse, bool recursing,
8992 : bool missing_ok, LOCKMODE lockmode,
8993 : ObjectAddresses *addrs)
8994 : {
8995 : HeapTuple tuple;
8996 : Form_pg_attribute targetatt;
8997 : AttrNumber attnum;
8998 : List *children;
8999 : ObjectAddress object;
9000 : bool is_expr;
9001 :
9002 : /* At top level, permission check was done in ATPrepCmd, else do it */
9003 2142 : if (recursing)
9004 556 : ATSimplePermissions(AT_DropColumn, rel,
9005 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9006 :
9007 : /* Initialize addrs on the first invocation */
9008 : Assert(!recursing || addrs != NULL);
9009 :
9010 : /* since this function recurses, it could be driven to stack overflow */
9011 2142 : check_stack_depth();
9012 :
9013 2142 : if (!recursing)
9014 1586 : addrs = new_object_addresses();
9015 :
9016 : /*
9017 : * get the number of the attribute
9018 : */
9019 2142 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9020 2142 : if (!HeapTupleIsValid(tuple))
9021 : {
9022 54 : if (!missing_ok)
9023 : {
9024 36 : ereport(ERROR,
9025 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9026 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9027 : colName, RelationGetRelationName(rel))));
9028 : }
9029 : else
9030 : {
9031 18 : ereport(NOTICE,
9032 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9033 : colName, RelationGetRelationName(rel))));
9034 18 : return InvalidObjectAddress;
9035 : }
9036 : }
9037 2088 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9038 :
9039 2088 : attnum = targetatt->attnum;
9040 :
9041 : /* Can't drop a system attribute */
9042 2088 : if (attnum <= 0)
9043 6 : ereport(ERROR,
9044 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9045 : errmsg("cannot drop system column \"%s\"",
9046 : colName)));
9047 :
9048 : /*
9049 : * Don't drop inherited columns, unless recursing (presumably from a drop
9050 : * of the parent column)
9051 : */
9052 2082 : if (targetatt->attinhcount > 0 && !recursing)
9053 48 : ereport(ERROR,
9054 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9055 : errmsg("cannot drop inherited column \"%s\"",
9056 : colName)));
9057 :
9058 : /*
9059 : * Don't drop columns used in the partition key, either. (If we let this
9060 : * go through, the key column's dependencies would cause a cascaded drop
9061 : * of the whole table, which is surely not what the user expected.)
9062 : */
9063 2034 : if (has_partition_attrs(rel,
9064 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9065 : &is_expr))
9066 30 : ereport(ERROR,
9067 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9068 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9069 : colName, RelationGetRelationName(rel))));
9070 :
9071 2004 : ReleaseSysCache(tuple);
9072 :
9073 : /*
9074 : * Propagate to children as appropriate. Unlike most other ALTER
9075 : * routines, we have to do this one level of recursion at a time; we can't
9076 : * use find_all_inheritors to do it in one pass.
9077 : */
9078 : children =
9079 2004 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9080 :
9081 2004 : if (children)
9082 : {
9083 : Relation attr_rel;
9084 : ListCell *child;
9085 :
9086 : /*
9087 : * In case of a partitioned table, the column must be dropped from the
9088 : * partitions as well.
9089 : */
9090 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9091 6 : ereport(ERROR,
9092 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9093 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9094 : errhint("Do not specify the ONLY keyword.")));
9095 :
9096 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9097 882 : foreach(child, children)
9098 : {
9099 592 : Oid childrelid = lfirst_oid(child);
9100 : Relation childrel;
9101 : Form_pg_attribute childatt;
9102 :
9103 : /* find_inheritance_children already got lock */
9104 592 : childrel = table_open(childrelid, NoLock);
9105 592 : CheckAlterTableIsSafe(childrel);
9106 :
9107 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9108 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9109 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9110 : colName, childrelid);
9111 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9112 :
9113 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9114 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9115 : childrelid, colName);
9116 :
9117 592 : if (recurse)
9118 : {
9119 : /*
9120 : * If the child column has other definition sources, just
9121 : * decrement its inheritance count; if not, recurse to delete
9122 : * it.
9123 : */
9124 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9125 : {
9126 : /* Time to delete this child column, too */
9127 556 : ATExecDropColumn(wqueue, childrel, colName,
9128 : behavior, true, true,
9129 : false, lockmode, addrs);
9130 : }
9131 : else
9132 : {
9133 : /* Child column must survive my deletion */
9134 12 : childatt->attinhcount--;
9135 :
9136 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9137 :
9138 : /* Make update visible */
9139 12 : CommandCounterIncrement();
9140 : }
9141 : }
9142 : else
9143 : {
9144 : /*
9145 : * If we were told to drop ONLY in this table (no recursion),
9146 : * we need to mark the inheritors' attributes as locally
9147 : * defined rather than inherited.
9148 : */
9149 24 : childatt->attinhcount--;
9150 24 : childatt->attislocal = true;
9151 :
9152 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9153 :
9154 : /* Make update visible */
9155 24 : CommandCounterIncrement();
9156 : }
9157 :
9158 586 : heap_freetuple(tuple);
9159 :
9160 586 : table_close(childrel, NoLock);
9161 : }
9162 290 : table_close(attr_rel, RowExclusiveLock);
9163 : }
9164 :
9165 : /* Add object to delete */
9166 1992 : object.classId = RelationRelationId;
9167 1992 : object.objectId = RelationGetRelid(rel);
9168 1992 : object.objectSubId = attnum;
9169 1992 : add_exact_object_address(&object, addrs);
9170 :
9171 1992 : if (!recursing)
9172 : {
9173 : /* Recursion has ended, drop everything that was collected */
9174 1442 : performMultipleDeletions(addrs, behavior, 0);
9175 1394 : free_object_addresses(addrs);
9176 : }
9177 :
9178 1944 : return object;
9179 : }
9180 :
9181 : /*
9182 : * Prepare to add a primary key on table, by adding not-null constraints
9183 : * on all columns.
9184 : */
9185 : static void
9186 14812 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9187 : bool recurse, LOCKMODE lockmode,
9188 : AlterTableUtilityContext *context)
9189 : {
9190 : ListCell *lc;
9191 : Constraint *pkconstr;
9192 :
9193 14812 : pkconstr = castNode(Constraint, cmd->def);
9194 14812 : if (pkconstr->contype != CONSTR_PRIMARY)
9195 8650 : return;
9196 :
9197 : /*
9198 : * If not recursing, we must ensure that all children have a NOT NULL
9199 : * constraint on the columns, and error out if not.
9200 : */
9201 6162 : if (!recurse)
9202 : {
9203 : List *children;
9204 :
9205 254 : children = find_inheritance_children(RelationGetRelid(rel),
9206 : lockmode);
9207 628 : foreach_oid(childrelid, children)
9208 : {
9209 260 : foreach(lc, pkconstr->keys)
9210 : {
9211 : HeapTuple tup;
9212 : Form_pg_attribute attrForm;
9213 134 : char *attname = strVal(lfirst(lc));
9214 :
9215 134 : tup = SearchSysCacheAttName(childrelid, attname);
9216 134 : if (!tup)
9217 0 : elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9218 : attname, childrelid);
9219 134 : attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9220 134 : if (!attrForm->attnotnull)
9221 6 : ereport(ERROR,
9222 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9223 : attname, get_rel_name(childrelid)));
9224 128 : ReleaseSysCache(tup);
9225 : }
9226 : }
9227 : }
9228 :
9229 : /* Insert not-null constraints in the queue for the PK columns */
9230 7106 : foreach(lc, pkconstr->keys)
9231 : {
9232 : AlterTableCmd *newcmd;
9233 : Constraint *nnconstr;
9234 :
9235 950 : nnconstr = makeNotNullConstraint(lfirst(lc));
9236 :
9237 950 : newcmd = makeNode(AlterTableCmd);
9238 950 : newcmd->subtype = AT_AddConstraint;
9239 950 : newcmd->recurse = true;
9240 950 : newcmd->def = (Node *) nnconstr;
9241 :
9242 950 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9243 : }
9244 : }
9245 :
9246 : /*
9247 : * ALTER TABLE ADD INDEX
9248 : *
9249 : * There is no such command in the grammar, but parse_utilcmd.c converts
9250 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9251 : * us schedule creation of the index at the appropriate time during ALTER.
9252 : *
9253 : * Return value is the address of the new index.
9254 : */
9255 : static ObjectAddress
9256 1588 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9257 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9258 : {
9259 : bool check_rights;
9260 : bool skip_build;
9261 : bool quiet;
9262 : ObjectAddress address;
9263 :
9264 : Assert(IsA(stmt, IndexStmt));
9265 : Assert(!stmt->concurrent);
9266 :
9267 : /* The IndexStmt has already been through transformIndexStmt */
9268 : Assert(stmt->transformed);
9269 :
9270 : /* suppress schema rights check when rebuilding existing index */
9271 1588 : check_rights = !is_rebuild;
9272 : /* skip index build if phase 3 will do it or we're reusing an old one */
9273 1588 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9274 : /* suppress notices when rebuilding existing index */
9275 1588 : quiet = is_rebuild;
9276 :
9277 1588 : address = DefineIndex(RelationGetRelid(rel),
9278 : stmt,
9279 : InvalidOid, /* no predefined OID */
9280 : InvalidOid, /* no parent index */
9281 : InvalidOid, /* no parent constraint */
9282 : -1, /* total_parts unknown */
9283 : true, /* is_alter_table */
9284 : check_rights,
9285 : false, /* check_not_in_use - we did it already */
9286 : skip_build,
9287 : quiet);
9288 :
9289 : /*
9290 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9291 : * new index instead of building from scratch. Restore associated fields.
9292 : * This may store InvalidSubTransactionId in both fields, in which case
9293 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9294 : * this after the CCI that made catalog rows visible to any rebuild. The
9295 : * DROP of the old edition of this index will have scheduled the storage
9296 : * for deletion at commit, so cancel that pending deletion.
9297 : */
9298 1418 : if (RelFileNumberIsValid(stmt->oldNumber))
9299 : {
9300 74 : Relation irel = index_open(address.objectId, NoLock);
9301 :
9302 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9303 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9304 74 : RelationPreserveStorage(irel->rd_locator, true);
9305 74 : index_close(irel, NoLock);
9306 : }
9307 :
9308 1418 : return address;
9309 : }
9310 :
9311 : /*
9312 : * ALTER TABLE ADD STATISTICS
9313 : *
9314 : * This is no such command in the grammar, but we use this internally to add
9315 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9316 : * column type change.
9317 : */
9318 : static ObjectAddress
9319 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9320 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9321 : {
9322 : ObjectAddress address;
9323 :
9324 : Assert(IsA(stmt, CreateStatsStmt));
9325 :
9326 : /* The CreateStatsStmt has already been through transformStatsStmt */
9327 : Assert(stmt->transformed);
9328 :
9329 14 : address = CreateStatistics(stmt);
9330 :
9331 14 : return address;
9332 : }
9333 :
9334 : /*
9335 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9336 : *
9337 : * Returns the address of the new constraint.
9338 : */
9339 : static ObjectAddress
9340 9540 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9341 : IndexStmt *stmt, LOCKMODE lockmode)
9342 : {
9343 9540 : Oid index_oid = stmt->indexOid;
9344 : Relation indexRel;
9345 : char *indexName;
9346 : IndexInfo *indexInfo;
9347 : char *constraintName;
9348 : char constraintType;
9349 : ObjectAddress address;
9350 : bits16 flags;
9351 :
9352 : Assert(IsA(stmt, IndexStmt));
9353 : Assert(OidIsValid(index_oid));
9354 : Assert(stmt->isconstraint);
9355 :
9356 : /*
9357 : * Doing this on partitioned tables is not a simple feature to implement,
9358 : * so let's punt for now.
9359 : */
9360 9540 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9361 6 : ereport(ERROR,
9362 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9363 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9364 :
9365 9534 : indexRel = index_open(index_oid, AccessShareLock);
9366 :
9367 9534 : indexName = pstrdup(RelationGetRelationName(indexRel));
9368 :
9369 9534 : indexInfo = BuildIndexInfo(indexRel);
9370 :
9371 : /* this should have been checked at parse time */
9372 9534 : if (!indexInfo->ii_Unique)
9373 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9374 :
9375 : /*
9376 : * Determine name to assign to constraint. We require a constraint to
9377 : * have the same name as the underlying index; therefore, use the index's
9378 : * existing name as the default constraint name, and if the user
9379 : * explicitly gives some other name for the constraint, rename the index
9380 : * to match.
9381 : */
9382 9534 : constraintName = stmt->idxname;
9383 9534 : if (constraintName == NULL)
9384 9508 : constraintName = indexName;
9385 26 : else if (strcmp(constraintName, indexName) != 0)
9386 : {
9387 20 : ereport(NOTICE,
9388 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9389 : indexName, constraintName)));
9390 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9391 : }
9392 :
9393 : /* Extra checks needed if making primary key */
9394 9534 : if (stmt->primary)
9395 5386 : index_check_primary_key(rel, indexInfo, true, stmt);
9396 :
9397 : /* Note we currently don't support EXCLUSION constraints here */
9398 9528 : if (stmt->primary)
9399 5380 : constraintType = CONSTRAINT_PRIMARY;
9400 : else
9401 4148 : constraintType = CONSTRAINT_UNIQUE;
9402 :
9403 : /* Create the catalog entries for the constraint */
9404 9528 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9405 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9406 19056 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9407 9528 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9408 9528 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9409 :
9410 9528 : address = index_constraint_create(rel,
9411 : index_oid,
9412 : InvalidOid,
9413 : indexInfo,
9414 : constraintName,
9415 : constraintType,
9416 : flags,
9417 : allowSystemTableMods,
9418 : false); /* is_internal */
9419 :
9420 9528 : index_close(indexRel, NoLock);
9421 :
9422 9528 : return address;
9423 : }
9424 :
9425 : /*
9426 : * ALTER TABLE ADD CONSTRAINT
9427 : *
9428 : * Return value is the address of the new constraint; if no constraint was
9429 : * added, InvalidObjectAddress is returned.
9430 : */
9431 : static ObjectAddress
9432 11854 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9433 : Constraint *newConstraint, bool recurse, bool is_readd,
9434 : LOCKMODE lockmode)
9435 : {
9436 11854 : ObjectAddress address = InvalidObjectAddress;
9437 :
9438 : Assert(IsA(newConstraint, Constraint));
9439 :
9440 : /*
9441 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9442 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9443 : * parse_utilcmd.c).
9444 : */
9445 11854 : switch (newConstraint->contype)
9446 : {
9447 9354 : case CONSTR_CHECK:
9448 : case CONSTR_NOTNULL:
9449 : address =
9450 9354 : ATAddCheckNNConstraint(wqueue, tab, rel,
9451 : newConstraint, recurse, false, is_readd,
9452 : lockmode);
9453 9240 : break;
9454 :
9455 2500 : case CONSTR_FOREIGN:
9456 :
9457 : /*
9458 : * Assign or validate constraint name
9459 : */
9460 2500 : if (newConstraint->conname)
9461 : {
9462 1172 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9463 : RelationGetRelid(rel),
9464 1172 : newConstraint->conname))
9465 0 : ereport(ERROR,
9466 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9467 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9468 : newConstraint->conname,
9469 : RelationGetRelationName(rel))));
9470 : }
9471 : else
9472 1328 : newConstraint->conname =
9473 1328 : ChooseConstraintName(RelationGetRelationName(rel),
9474 1328 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9475 : "fkey",
9476 1328 : RelationGetNamespace(rel),
9477 : NIL);
9478 :
9479 2500 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9480 : newConstraint,
9481 : recurse, false,
9482 : lockmode);
9483 1988 : break;
9484 :
9485 0 : default:
9486 0 : elog(ERROR, "unrecognized constraint type: %d",
9487 : (int) newConstraint->contype);
9488 : }
9489 :
9490 11228 : return address;
9491 : }
9492 :
9493 : /*
9494 : * Generate the column-name portion of the constraint name for a new foreign
9495 : * key given the list of column names that reference the referenced
9496 : * table. This will be passed to ChooseConstraintName along with the parent
9497 : * table name and the "fkey" suffix.
9498 : *
9499 : * We know that less than NAMEDATALEN characters will actually be used, so we
9500 : * can truncate the result once we've generated that many.
9501 : *
9502 : * XXX see also ChooseExtendedStatisticNameAddition and
9503 : * ChooseIndexNameAddition.
9504 : */
9505 : static char *
9506 2204 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9507 : {
9508 : char buf[NAMEDATALEN * 2];
9509 2204 : int buflen = 0;
9510 : ListCell *lc;
9511 :
9512 2204 : buf[0] = '\0';
9513 4968 : foreach(lc, colnames)
9514 : {
9515 2764 : const char *name = strVal(lfirst(lc));
9516 :
9517 2764 : if (buflen > 0)
9518 560 : buf[buflen++] = '_'; /* insert _ between names */
9519 :
9520 : /*
9521 : * At this point we have buflen <= NAMEDATALEN. name should be less
9522 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9523 : */
9524 2764 : strlcpy(buf + buflen, name, NAMEDATALEN);
9525 2764 : buflen += strlen(buf + buflen);
9526 2764 : if (buflen >= NAMEDATALEN)
9527 0 : break;
9528 : }
9529 2204 : return pstrdup(buf);
9530 : }
9531 :
9532 : /*
9533 : * Add a check or not-null constraint to a single table and its children.
9534 : * Returns the address of the constraint added to the parent relation,
9535 : * if one gets added, or InvalidObjectAddress otherwise.
9536 : *
9537 : * Subroutine for ATExecAddConstraint.
9538 : *
9539 : * We must recurse to child tables during execution, rather than using
9540 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9541 : * constraints *must* be given the same name, else they won't be seen as
9542 : * related later. If the user didn't explicitly specify a name, then
9543 : * AddRelationNewConstraints would normally assign different names to the
9544 : * child constraints. To fix that, we must capture the name assigned at
9545 : * the parent table and pass that down.
9546 : */
9547 : static ObjectAddress
9548 10108 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9549 : Constraint *constr, bool recurse, bool recursing,
9550 : bool is_readd, LOCKMODE lockmode)
9551 : {
9552 : List *newcons;
9553 : ListCell *lcon;
9554 : List *children;
9555 : ListCell *child;
9556 10108 : ObjectAddress address = InvalidObjectAddress;
9557 :
9558 : /* Guard against stack overflow due to overly deep inheritance tree. */
9559 10108 : check_stack_depth();
9560 :
9561 : /* At top level, permission check was done in ATPrepCmd, else do it */
9562 10108 : if (recursing)
9563 620 : ATSimplePermissions(AT_AddConstraint, rel,
9564 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9565 :
9566 : /*
9567 : * Call AddRelationNewConstraints to do the work, making sure it works on
9568 : * a copy of the Constraint so transformExpr can't modify the original. It
9569 : * returns a list of cooked constraints.
9570 : *
9571 : * If the constraint ends up getting merged with a pre-existing one, it's
9572 : * omitted from the returned list, which is what we want: we do not need
9573 : * to do any validation work. That can only happen at child tables,
9574 : * though, since we disallow merging at the top level.
9575 : */
9576 10108 : newcons = AddRelationNewConstraints(rel, NIL,
9577 10108 : list_make1(copyObject(constr)),
9578 10108 : recursing || is_readd, /* allow_merge */
9579 10108 : !recursing, /* is_local */
9580 : is_readd, /* is_internal */
9581 10108 : NULL); /* queryString not available
9582 : * here */
9583 :
9584 : /* we don't expect more than one constraint here */
9585 : Assert(list_length(newcons) <= 1);
9586 :
9587 : /* Add each to-be-validated constraint to Phase 3's queue */
9588 19418 : foreach(lcon, newcons)
9589 : {
9590 9418 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9591 :
9592 9418 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9593 : {
9594 : NewConstraint *newcon;
9595 :
9596 802 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9597 802 : newcon->name = ccon->name;
9598 802 : newcon->contype = ccon->contype;
9599 802 : newcon->qual = ccon->expr;
9600 :
9601 802 : tab->constraints = lappend(tab->constraints, newcon);
9602 : }
9603 :
9604 : /* Save the actually assigned name if it was defaulted */
9605 9418 : if (constr->conname == NULL)
9606 8122 : constr->conname = ccon->name;
9607 :
9608 : /*
9609 : * If adding a not-null constraint, set the pg_attribute flag and tell
9610 : * phase 3 to verify existing rows, if needed.
9611 : */
9612 9418 : if (constr->contype == CONSTR_NOTNULL)
9613 8240 : set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9614 :
9615 9418 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9616 : }
9617 :
9618 : /* At this point we must have a locked-down name to use */
9619 : Assert(newcons == NIL || constr->conname != NULL);
9620 :
9621 : /* Advance command counter in case same table is visited multiple times */
9622 10000 : CommandCounterIncrement();
9623 :
9624 : /*
9625 : * If the constraint got merged with an existing constraint, we're done.
9626 : * We mustn't recurse to child tables in this case, because they've
9627 : * already got the constraint, and visiting them again would lead to an
9628 : * incorrect value for coninhcount.
9629 : */
9630 10000 : if (newcons == NIL)
9631 582 : return address;
9632 :
9633 : /*
9634 : * If adding a NO INHERIT constraint, no need to find our children.
9635 : */
9636 9418 : if (constr->is_no_inherit)
9637 72 : return address;
9638 :
9639 : /*
9640 : * Propagate to children as appropriate. Unlike most other ALTER
9641 : * routines, we have to do this one level of recursion at a time; we can't
9642 : * use find_all_inheritors to do it in one pass.
9643 : */
9644 : children =
9645 9346 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9646 :
9647 : /*
9648 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9649 : * constraint creation only if there are no children currently. Error out
9650 : * otherwise.
9651 : */
9652 9346 : if (!recurse && children != NIL)
9653 6 : ereport(ERROR,
9654 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9655 : errmsg("constraint must be added to child tables too")));
9656 :
9657 : /*
9658 : * Recurse to create the constraint on each child.
9659 : */
9660 9936 : foreach(child, children)
9661 : {
9662 620 : Oid childrelid = lfirst_oid(child);
9663 : Relation childrel;
9664 : AlteredTableInfo *childtab;
9665 :
9666 : /* find_inheritance_children already got lock */
9667 620 : childrel = table_open(childrelid, NoLock);
9668 620 : CheckAlterTableIsSafe(childrel);
9669 :
9670 : /* Find or create work queue entry for this table */
9671 620 : childtab = ATGetQueueEntry(wqueue, childrel);
9672 :
9673 : /* Recurse to this child */
9674 620 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9675 : constr, recurse, true, is_readd, lockmode);
9676 :
9677 596 : table_close(childrel, NoLock);
9678 : }
9679 :
9680 9316 : return address;
9681 : }
9682 :
9683 : /*
9684 : * Add a foreign-key constraint to a single table; return the new constraint's
9685 : * address.
9686 : *
9687 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9688 : * lock on the rel, and have done appropriate validity checks for it.
9689 : * We do permissions checks here, however.
9690 : *
9691 : * When the referenced or referencing tables (or both) are partitioned,
9692 : * multiple pg_constraint rows are required -- one for each partitioned table
9693 : * and each partition on each side (fortunately, not one for every combination
9694 : * thereof). We also need action triggers on each leaf partition on the
9695 : * referenced side, and check triggers on each leaf partition on the
9696 : * referencing side.
9697 : */
9698 : static ObjectAddress
9699 2500 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9700 : Constraint *fkconstraint,
9701 : bool recurse, bool recursing, LOCKMODE lockmode)
9702 : {
9703 : Relation pkrel;
9704 2500 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9705 2500 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9706 2500 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9707 2500 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9708 2500 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9709 2500 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9710 2500 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9711 2500 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9712 2500 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9713 2500 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9714 2500 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9715 : bool with_period;
9716 : bool pk_has_without_overlaps;
9717 : int i;
9718 : int numfks,
9719 : numpks,
9720 : numfkdelsetcols;
9721 : Oid indexOid;
9722 : bool old_check_ok;
9723 : ObjectAddress address;
9724 2500 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9725 :
9726 : /*
9727 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9728 : * delete rows out from under us.
9729 : */
9730 2500 : if (OidIsValid(fkconstraint->old_pktable_oid))
9731 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9732 : else
9733 2428 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9734 :
9735 : /*
9736 : * Validity checks (permission checks wait till we have the column
9737 : * numbers)
9738 : */
9739 2500 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9740 : {
9741 372 : if (!recurse)
9742 6 : ereport(ERROR,
9743 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9744 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9745 : RelationGetRelationName(rel),
9746 : RelationGetRelationName(pkrel))));
9747 366 : if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9748 6 : ereport(ERROR,
9749 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9750 : errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9751 : RelationGetRelationName(rel),
9752 : RelationGetRelationName(pkrel)),
9753 : errdetail("This feature is not yet supported on partitioned tables.")));
9754 : }
9755 :
9756 2488 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9757 326 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9758 0 : ereport(ERROR,
9759 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9760 : errmsg("referenced relation \"%s\" is not a table",
9761 : RelationGetRelationName(pkrel))));
9762 :
9763 2488 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9764 2 : ereport(ERROR,
9765 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9766 : errmsg("permission denied: \"%s\" is a system catalog",
9767 : RelationGetRelationName(pkrel))));
9768 :
9769 : /*
9770 : * References from permanent or unlogged tables to temp tables, and from
9771 : * permanent tables to unlogged tables, are disallowed because the
9772 : * referenced data can vanish out from under us. References from temp
9773 : * tables to any other table type are also disallowed, because other
9774 : * backends might need to run the RI triggers on the perm table, but they
9775 : * can't reliably see tuples in the local buffers of other backends.
9776 : */
9777 2486 : switch (rel->rd_rel->relpersistence)
9778 : {
9779 2196 : case RELPERSISTENCE_PERMANENT:
9780 2196 : if (!RelationIsPermanent(pkrel))
9781 0 : ereport(ERROR,
9782 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9783 : errmsg("constraints on permanent tables may reference only permanent tables")));
9784 2196 : break;
9785 12 : case RELPERSISTENCE_UNLOGGED:
9786 12 : if (!RelationIsPermanent(pkrel)
9787 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9788 0 : ereport(ERROR,
9789 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9790 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9791 12 : break;
9792 278 : case RELPERSISTENCE_TEMP:
9793 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9794 0 : ereport(ERROR,
9795 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9796 : errmsg("constraints on temporary tables may reference only temporary tables")));
9797 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9798 0 : ereport(ERROR,
9799 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9800 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
9801 278 : break;
9802 : }
9803 :
9804 : /*
9805 : * Look up the referencing attributes to make sure they exist, and record
9806 : * their attnums and type and collation OIDs.
9807 : */
9808 2486 : numfks = transformColumnNameList(RelationGetRelid(rel),
9809 : fkconstraint->fk_attrs,
9810 : fkattnum, fktypoid, fkcolloid);
9811 2456 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9812 2456 : if (with_period && !fkconstraint->fk_with_period)
9813 24 : ereport(ERROR,
9814 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9815 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9816 :
9817 2432 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9818 : fkconstraint->fk_del_set_cols,
9819 : fkdelsetcols, NULL, NULL);
9820 2426 : validateFkOnDeleteSetColumns(numfks, fkattnum,
9821 : numfkdelsetcols, fkdelsetcols,
9822 : fkconstraint->fk_del_set_cols);
9823 :
9824 : /*
9825 : * If the attribute list for the referenced table was omitted, lookup the
9826 : * definition of the primary key and use it. Otherwise, validate the
9827 : * supplied attribute list. In either case, discover the index OID and
9828 : * index opclasses, and the attnums and type and collation OIDs of the
9829 : * attributes.
9830 : */
9831 2420 : if (fkconstraint->pk_attrs == NIL)
9832 : {
9833 1112 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9834 : &fkconstraint->pk_attrs,
9835 : pkattnum, pktypoid, pkcolloid,
9836 : opclasses, &pk_has_without_overlaps);
9837 :
9838 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9839 1112 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9840 24 : ereport(ERROR,
9841 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9842 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9843 : }
9844 : else
9845 : {
9846 1308 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
9847 : fkconstraint->pk_attrs,
9848 : pkattnum, pktypoid, pkcolloid);
9849 :
9850 : /* Since we got pk_attrs, one should be a period. */
9851 1278 : if (with_period && !fkconstraint->pk_with_period)
9852 24 : ereport(ERROR,
9853 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9854 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9855 :
9856 : /* Look for an index matching the column list */
9857 1254 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9858 : with_period, opclasses, &pk_has_without_overlaps);
9859 : }
9860 :
9861 : /*
9862 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9863 : * must use PERIOD.
9864 : */
9865 2306 : if (pk_has_without_overlaps && !with_period)
9866 12 : ereport(ERROR,
9867 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9868 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9869 :
9870 : /*
9871 : * Now we can check permissions.
9872 : */
9873 2294 : checkFkeyPermissions(pkrel, pkattnum, numpks);
9874 :
9875 : /*
9876 : * Check some things for generated columns.
9877 : */
9878 5388 : for (i = 0; i < numfks; i++)
9879 : {
9880 3106 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9881 :
9882 3106 : if (attgenerated)
9883 : {
9884 : /*
9885 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
9886 : */
9887 30 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9888 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9889 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9890 6 : ereport(ERROR,
9891 : (errcode(ERRCODE_SYNTAX_ERROR),
9892 : errmsg("invalid %s action for foreign key constraint containing generated column",
9893 : "ON UPDATE")));
9894 24 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9895 18 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9896 6 : ereport(ERROR,
9897 : (errcode(ERRCODE_SYNTAX_ERROR),
9898 : errmsg("invalid %s action for foreign key constraint containing generated column",
9899 : "ON DELETE")));
9900 : }
9901 : }
9902 :
9903 : /*
9904 : * Some actions are currently unsupported for foreign keys using PERIOD.
9905 : */
9906 2282 : if (fkconstraint->fk_with_period)
9907 : {
9908 262 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
9909 244 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9910 226 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
9911 54 : ereport(ERROR,
9912 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9913 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
9914 : "ON UPDATE"));
9915 :
9916 208 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
9917 208 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9918 208 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9919 0 : ereport(ERROR,
9920 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9921 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
9922 : "ON DELETE"));
9923 : }
9924 :
9925 : /*
9926 : * Look up the equality operators to use in the constraint.
9927 : *
9928 : * Note that we have to be careful about the difference between the actual
9929 : * PK column type and the opclass' declared input type, which might be
9930 : * only binary-compatible with it. The declared opcintype is the right
9931 : * thing to probe pg_amop with.
9932 : */
9933 2228 : if (numfks != numpks)
9934 0 : ereport(ERROR,
9935 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9936 : errmsg("number of referencing and referenced columns for foreign key disagree")));
9937 :
9938 : /*
9939 : * On the strength of a previous constraint, we might avoid scanning
9940 : * tables to validate this one. See below.
9941 : */
9942 2228 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9943 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9944 :
9945 4830 : for (i = 0; i < numpks; i++)
9946 : {
9947 2842 : Oid pktype = pktypoid[i];
9948 2842 : Oid fktype = fktypoid[i];
9949 : Oid fktyped;
9950 2842 : Oid pkcoll = pkcolloid[i];
9951 2842 : Oid fkcoll = fkcolloid[i];
9952 : HeapTuple cla_ht;
9953 : Form_pg_opclass cla_tup;
9954 : Oid amid;
9955 : Oid opfamily;
9956 : Oid opcintype;
9957 : Oid pfeqop;
9958 : Oid ppeqop;
9959 : Oid ffeqop;
9960 : int16 eqstrategy;
9961 : Oid pfeqop_right;
9962 :
9963 : /* We need several fields out of the pg_opclass entry */
9964 2842 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9965 2842 : if (!HeapTupleIsValid(cla_ht))
9966 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9967 2842 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9968 2842 : amid = cla_tup->opcmethod;
9969 2842 : opfamily = cla_tup->opcfamily;
9970 2842 : opcintype = cla_tup->opcintype;
9971 2842 : ReleaseSysCache(cla_ht);
9972 :
9973 2842 : if (with_period)
9974 : {
9975 : StrategyNumber rtstrategy;
9976 444 : bool for_overlaps = with_period && i == numpks - 1;
9977 :
9978 : /*
9979 : * GiST indexes are required to support temporal foreign keys
9980 : * because they combine equals and overlaps.
9981 : */
9982 444 : if (amid != GIST_AM_OID)
9983 0 : elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
9984 :
9985 444 : rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
9986 :
9987 : /*
9988 : * An opclass can use whatever strategy numbers it wants, so we
9989 : * ask the opclass what number it actually uses instead of our RT*
9990 : * constants.
9991 : */
9992 444 : eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
9993 444 : if (eqstrategy == InvalidStrategy)
9994 : {
9995 : HeapTuple tuple;
9996 :
9997 0 : tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9998 0 : if (!HeapTupleIsValid(tuple))
9999 0 : elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10000 :
10001 0 : ereport(ERROR,
10002 : errcode(ERRCODE_UNDEFINED_OBJECT),
10003 : for_overlaps
10004 : ? errmsg("could not identify an overlaps operator for foreign key")
10005 : : errmsg("could not identify an equality operator for foreign key"),
10006 : errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10007 : rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10008 : }
10009 : }
10010 : else
10011 : {
10012 : /*
10013 : * Check it's a btree; currently this can never fail since no
10014 : * other index AMs support unique indexes. If we ever did have
10015 : * other types of unique indexes, we'd need a way to determine
10016 : * which operator strategy number is equality. (We could use
10017 : * something like GistTranslateStratnum.)
10018 : */
10019 2398 : if (amid != BTREE_AM_OID)
10020 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
10021 2398 : eqstrategy = BTEqualStrategyNumber;
10022 : }
10023 :
10024 : /*
10025 : * There had better be a primary equality operator for the index.
10026 : * We'll use it for PK = PK comparisons.
10027 : */
10028 2842 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10029 : eqstrategy);
10030 :
10031 2842 : if (!OidIsValid(ppeqop))
10032 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10033 : eqstrategy, opcintype, opcintype, opfamily);
10034 :
10035 : /*
10036 : * Are there equality operators that take exactly the FK type? Assume
10037 : * we should look through any domain here.
10038 : */
10039 2842 : fktyped = getBaseType(fktype);
10040 :
10041 2842 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10042 : eqstrategy);
10043 2842 : if (OidIsValid(pfeqop))
10044 : {
10045 2142 : pfeqop_right = fktyped;
10046 2142 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10047 : eqstrategy);
10048 : }
10049 : else
10050 : {
10051 : /* keep compiler quiet */
10052 700 : pfeqop_right = InvalidOid;
10053 700 : ffeqop = InvalidOid;
10054 : }
10055 :
10056 2842 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10057 : {
10058 : /*
10059 : * Otherwise, look for an implicit cast from the FK type to the
10060 : * opcintype, and if found, use the primary equality operator.
10061 : * This is a bit tricky because opcintype might be a polymorphic
10062 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10063 : * whether the two actual column types can be concurrently cast to
10064 : * that type. (Otherwise, we'd fail to reject combinations such
10065 : * as int[] and point[].)
10066 : */
10067 : Oid input_typeids[2];
10068 : Oid target_typeids[2];
10069 :
10070 700 : input_typeids[0] = pktype;
10071 700 : input_typeids[1] = fktype;
10072 700 : target_typeids[0] = opcintype;
10073 700 : target_typeids[1] = opcintype;
10074 700 : if (can_coerce_type(2, input_typeids, target_typeids,
10075 : COERCION_IMPLICIT))
10076 : {
10077 472 : pfeqop = ffeqop = ppeqop;
10078 472 : pfeqop_right = opcintype;
10079 : }
10080 : }
10081 :
10082 2842 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10083 228 : ereport(ERROR,
10084 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10085 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10086 : fkconstraint->conname),
10087 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10088 : "are of incompatible types: %s and %s.",
10089 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10090 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10091 : format_type_be(fktype),
10092 : format_type_be(pktype))));
10093 :
10094 : /*
10095 : * This shouldn't be possible, but better check to make sure we have a
10096 : * consistent state for the check below.
10097 : */
10098 2614 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10099 0 : elog(ERROR, "key columns are not both collatable");
10100 :
10101 2614 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10102 : {
10103 : bool pkcolldet;
10104 : bool fkcolldet;
10105 :
10106 86 : pkcolldet = get_collation_isdeterministic(pkcoll);
10107 86 : fkcolldet = get_collation_isdeterministic(fkcoll);
10108 :
10109 : /*
10110 : * SQL requires that both collations are the same. This is
10111 : * because we need a consistent notion of equality on both
10112 : * columns. We relax this by allowing different collations if
10113 : * they are both deterministic. (This is also for backward
10114 : * compatibility, because PostgreSQL has always allowed this.)
10115 : */
10116 86 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10117 12 : ereport(ERROR,
10118 : (errcode(ERRCODE_COLLATION_MISMATCH),
10119 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10120 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10121 : "have incompatible collations: \"%s\" and \"%s\". "
10122 : "If either collation is nondeterministic, then both collations have to be the same.",
10123 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10124 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10125 : get_collation_name(fkcoll),
10126 : get_collation_name(pkcoll))));
10127 : }
10128 :
10129 2602 : if (old_check_ok)
10130 : {
10131 : /*
10132 : * When a pfeqop changes, revalidate the constraint. We could
10133 : * permit intra-opfamily changes, but that adds subtle complexity
10134 : * without any concrete benefit for core types. We need not
10135 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10136 : */
10137 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10138 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10139 : old_pfeqop_item);
10140 : }
10141 2602 : if (old_check_ok)
10142 : {
10143 : Oid old_fktype;
10144 : Oid new_fktype;
10145 : CoercionPathType old_pathtype;
10146 : CoercionPathType new_pathtype;
10147 : Oid old_castfunc;
10148 : Oid new_castfunc;
10149 : Oid old_fkcoll;
10150 : Oid new_fkcoll;
10151 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10152 : fkattnum[i] - 1);
10153 :
10154 : /*
10155 : * Identify coercion pathways from each of the old and new FK-side
10156 : * column types to the right (foreign) operand type of the pfeqop.
10157 : * We may assume that pg_constraint.conkey is not changing.
10158 : */
10159 6 : old_fktype = attr->atttypid;
10160 6 : new_fktype = fktype;
10161 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10162 : &old_castfunc);
10163 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10164 : &new_castfunc);
10165 :
10166 6 : old_fkcoll = attr->attcollation;
10167 6 : new_fkcoll = fkcoll;
10168 :
10169 : /*
10170 : * Upon a change to the cast from the FK column to its pfeqop
10171 : * operand, revalidate the constraint. For this evaluation, a
10172 : * binary coercion cast is equivalent to no cast at all. While
10173 : * type implementors should design implicit casts with an eye
10174 : * toward consistency of operations like equality, we cannot
10175 : * assume here that they have done so.
10176 : *
10177 : * A function with a polymorphic argument could change behavior
10178 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10179 : * when the cast destination is polymorphic, we only avoid
10180 : * revalidation if the input type has not changed at all. Given
10181 : * just the core data types and operator classes, this requirement
10182 : * prevents no would-be optimizations.
10183 : *
10184 : * If the cast converts from a base type to a domain thereon, then
10185 : * that domain type must be the opcintype of the unique index.
10186 : * Necessarily, the primary key column must then be of the domain
10187 : * type. Since the constraint was previously valid, all values on
10188 : * the foreign side necessarily exist on the primary side and in
10189 : * turn conform to the domain. Consequently, we need not treat
10190 : * domains specially here.
10191 : *
10192 : * If the collation changes, revalidation is required, unless both
10193 : * collations are deterministic, because those share the same
10194 : * notion of equality (because texteq reduces to bitwise
10195 : * equality).
10196 : *
10197 : * We need not directly consider the PK type. It's necessarily
10198 : * binary coercible to the opcintype of the unique index column,
10199 : * and ri_triggers.c will only deal with PK datums in terms of
10200 : * that opcintype. Changing the opcintype also changes pfeqop.
10201 : */
10202 6 : old_check_ok = (new_pathtype == old_pathtype &&
10203 6 : new_castfunc == old_castfunc &&
10204 6 : (!IsPolymorphicType(pfeqop_right) ||
10205 12 : new_fktype == old_fktype) &&
10206 0 : (new_fkcoll == old_fkcoll ||
10207 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10208 : }
10209 :
10210 2602 : pfeqoperators[i] = pfeqop;
10211 2602 : ppeqoperators[i] = ppeqop;
10212 2602 : ffeqoperators[i] = ffeqop;
10213 : }
10214 :
10215 : /*
10216 : * For FKs with PERIOD we need additional operators to check whether the
10217 : * referencing row's range is contained by the aggregated ranges of the
10218 : * referenced row(s). For rangetypes and multirangetypes this is
10219 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10220 : * support for now. FKs will look these up at "runtime", but we should
10221 : * make sure the lookup works here, even if we don't use the values.
10222 : */
10223 1988 : if (with_period)
10224 : {
10225 : Oid periodoperoid;
10226 : Oid aggedperiodoperoid;
10227 :
10228 190 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10229 : }
10230 :
10231 : /* First, create the constraint catalog entry itself. */
10232 1988 : address = addFkConstraint(addFkBothSides,
10233 : fkconstraint->conname, fkconstraint, rel, pkrel,
10234 : indexOid,
10235 : InvalidOid, /* no parent constraint */
10236 : numfks,
10237 : pkattnum,
10238 : fkattnum,
10239 : pfeqoperators,
10240 : ppeqoperators,
10241 : ffeqoperators,
10242 : numfkdelsetcols,
10243 : fkdelsetcols,
10244 : false,
10245 : with_period);
10246 :
10247 : /* Next process the action triggers at the referenced side and recurse */
10248 1988 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10249 : indexOid,
10250 : address.objectId,
10251 : numfks,
10252 : pkattnum,
10253 : fkattnum,
10254 : pfeqoperators,
10255 : ppeqoperators,
10256 : ffeqoperators,
10257 : numfkdelsetcols,
10258 : fkdelsetcols,
10259 : old_check_ok,
10260 : InvalidOid, InvalidOid,
10261 : with_period);
10262 :
10263 : /* Lastly create the check triggers at the referencing side and recurse */
10264 1988 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10265 : indexOid,
10266 : address.objectId,
10267 : numfks,
10268 : pkattnum,
10269 : fkattnum,
10270 : pfeqoperators,
10271 : ppeqoperators,
10272 : ffeqoperators,
10273 : numfkdelsetcols,
10274 : fkdelsetcols,
10275 : old_check_ok,
10276 : lockmode,
10277 : InvalidOid, InvalidOid,
10278 : with_period);
10279 :
10280 : /*
10281 : * Done. Close pk table, but keep lock until we've committed.
10282 : */
10283 1988 : table_close(pkrel, NoLock);
10284 :
10285 1988 : return address;
10286 : }
10287 :
10288 : /*
10289 : * validateFkOnDeleteSetColumns
10290 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10291 : * column lists are valid.
10292 : */
10293 : void
10294 2426 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10295 : int numfksetcols, const int16 *fksetcolsattnums,
10296 : List *fksetcols)
10297 : {
10298 2450 : for (int i = 0; i < numfksetcols; i++)
10299 : {
10300 30 : int16 setcol_attnum = fksetcolsattnums[i];
10301 30 : bool seen = false;
10302 :
10303 54 : for (int j = 0; j < numfks; j++)
10304 : {
10305 48 : if (fkattnums[j] == setcol_attnum)
10306 : {
10307 24 : seen = true;
10308 24 : break;
10309 : }
10310 : }
10311 :
10312 30 : if (!seen)
10313 : {
10314 6 : char *col = strVal(list_nth(fksetcols, i));
10315 :
10316 6 : ereport(ERROR,
10317 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10318 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10319 : }
10320 : }
10321 2420 : }
10322 :
10323 : /*
10324 : * addFkConstraint
10325 : * Install pg_constraint entries to implement a foreign key constraint.
10326 : * Caller must separately invoke addFkRecurseReferenced and
10327 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10328 : * and (for partitioned tables) recurse to partitions.
10329 : *
10330 : * fkside: the side of the FK (or both) to create. Caller should
10331 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10332 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10333 : * addFkBothSides.
10334 : * constraintname: the base name for the constraint being added,
10335 : * copied to fkconstraint->conname if the latter is not set
10336 : * fkconstraint: the constraint being added
10337 : * rel: the root referencing relation
10338 : * pkrel: the referenced relation; might be a partition, if recursing
10339 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10340 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10341 : * top-level constraint
10342 : * numfks: the number of columns in the foreign key
10343 : * pkattnum: the attnum array of referenced attributes
10344 : * fkattnum: the attnum array of referencing attributes
10345 : * pf/pp/ffeqoperators: OID array of operators between columns
10346 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10347 : * (...) clause
10348 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10349 : * NULL/DEFAULT clause
10350 : * with_period: true if this is a temporal FK
10351 : */
10352 : static ObjectAddress
10353 3640 : addFkConstraint(addFkConstraintSides fkside,
10354 : char *constraintname, Constraint *fkconstraint,
10355 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10356 : int numfks, int16 *pkattnum,
10357 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10358 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10359 : bool is_internal, bool with_period)
10360 : {
10361 : ObjectAddress address;
10362 : Oid constrOid;
10363 : char *conname;
10364 : bool conislocal;
10365 : int16 coninhcount;
10366 : bool connoinherit;
10367 :
10368 : /*
10369 : * Verify relkind for each referenced partition. At the top level, this
10370 : * is redundant with a previous check, but we need it when recursing.
10371 : */
10372 3640 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10373 784 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10374 0 : ereport(ERROR,
10375 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10376 : errmsg("referenced relation \"%s\" is not a table",
10377 : RelationGetRelationName(pkrel))));
10378 :
10379 : /*
10380 : * Caller supplies us with a constraint name; however, it may be used in
10381 : * this partition, so come up with a different one in that case.
10382 : */
10383 3640 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10384 : RelationGetRelid(rel),
10385 : constraintname))
10386 876 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10387 876 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10388 : "fkey",
10389 876 : RelationGetNamespace(rel), NIL);
10390 : else
10391 2764 : conname = constraintname;
10392 :
10393 3640 : if (fkconstraint->conname == NULL)
10394 400 : fkconstraint->conname = pstrdup(conname);
10395 :
10396 3640 : if (OidIsValid(parentConstr))
10397 : {
10398 1652 : conislocal = false;
10399 1652 : coninhcount = 1;
10400 1652 : connoinherit = false;
10401 : }
10402 : else
10403 : {
10404 1988 : conislocal = true;
10405 1988 : coninhcount = 0;
10406 :
10407 : /*
10408 : * always inherit for partitioned tables, never for legacy inheritance
10409 : */
10410 1988 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10411 : }
10412 :
10413 : /*
10414 : * Record the FK constraint in pg_constraint.
10415 : */
10416 3640 : constrOid = CreateConstraintEntry(conname,
10417 3640 : RelationGetNamespace(rel),
10418 : CONSTRAINT_FOREIGN,
10419 3640 : fkconstraint->deferrable,
10420 3640 : fkconstraint->initdeferred,
10421 3640 : fkconstraint->initially_valid,
10422 : parentConstr,
10423 : RelationGetRelid(rel),
10424 : fkattnum,
10425 : numfks,
10426 : numfks,
10427 : InvalidOid, /* not a domain constraint */
10428 : indexOid,
10429 : RelationGetRelid(pkrel),
10430 : pkattnum,
10431 : pfeqoperators,
10432 : ppeqoperators,
10433 : ffeqoperators,
10434 : numfks,
10435 3640 : fkconstraint->fk_upd_action,
10436 3640 : fkconstraint->fk_del_action,
10437 : fkdelsetcols,
10438 : numfkdelsetcols,
10439 3640 : fkconstraint->fk_matchtype,
10440 : NULL, /* no exclusion constraint */
10441 : NULL, /* no check constraint */
10442 : NULL,
10443 : conislocal, /* islocal */
10444 : coninhcount, /* inhcount */
10445 : connoinherit, /* conNoInherit */
10446 : with_period, /* conPeriod */
10447 : is_internal); /* is_internal */
10448 :
10449 3640 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10450 :
10451 : /*
10452 : * In partitioning cases, create the dependency entries for this
10453 : * constraint. (For non-partitioned cases, relevant entries were created
10454 : * by CreateConstraintEntry.)
10455 : *
10456 : * On the referenced side, we need the constraint to have an internal
10457 : * dependency on its parent constraint; this means that this constraint
10458 : * cannot be dropped on its own -- only through the parent constraint. It
10459 : * also means the containing partition cannot be dropped on its own, but
10460 : * it can be detached, at which point this dependency is removed (after
10461 : * verifying that no rows are referenced via this FK.)
10462 : *
10463 : * When processing the referencing side, we link the constraint via the
10464 : * special partitioning dependencies: the parent constraint is the primary
10465 : * dependent, and the partition on which the foreign key exists is the
10466 : * secondary dependency. That way, this constraint is dropped if either
10467 : * of these objects is.
10468 : *
10469 : * Note that this is only necessary for the subsidiary pg_constraint rows
10470 : * in partitions; the topmost row doesn't need any of this.
10471 : */
10472 3640 : if (OidIsValid(parentConstr))
10473 : {
10474 : ObjectAddress referenced;
10475 :
10476 1652 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10477 :
10478 : Assert(fkside != addFkBothSides);
10479 1652 : if (fkside == addFkReferencedSide)
10480 870 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10481 : else
10482 : {
10483 782 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10484 782 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10485 782 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10486 : }
10487 : }
10488 :
10489 : /* make new constraint visible, in case we add more */
10490 3640 : CommandCounterIncrement();
10491 :
10492 3640 : return address;
10493 : }
10494 :
10495 : /*
10496 : * addFkRecurseReferenced
10497 : * Recursive helper for the referenced side of foreign key creation,
10498 : * which creates the action triggers and recurses
10499 : *
10500 : * If the referenced relation is a plain relation, create the necessary action
10501 : * triggers that implement the constraint. If the referenced relation is a
10502 : * partitioned table, then we create a pg_constraint row referencing the parent
10503 : * of the referencing side for it and recurse on this routine for each
10504 : * partition.
10505 : *
10506 : * fkconstraint: the constraint being added
10507 : * rel: the root referencing relation
10508 : * pkrel: the referenced relation; might be a partition, if recursing
10509 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10510 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10511 : * top-level constraint
10512 : * numfks: the number of columns in the foreign key
10513 : * pkattnum: the attnum array of referenced attributes
10514 : * fkattnum: the attnum array of referencing attributes
10515 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10516 : * NULL/DEFAULT (...) clause
10517 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10518 : * NULL/DEFAULT clause
10519 : * pf/pp/ffeqoperators: OID array of operators between columns
10520 : * old_check_ok: true if this constraint replaces an existing one that
10521 : * was already validated (thus this one doesn't need validation)
10522 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10523 : * partition, the OIDs of the parent action triggers for DELETE and
10524 : * UPDATE respectively.
10525 : * with_period: true if this is a temporal FK
10526 : */
10527 : static void
10528 2930 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10529 : Relation pkrel, Oid indexOid, Oid parentConstr,
10530 : int numfks,
10531 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10532 : Oid *ppeqoperators, Oid *ffeqoperators,
10533 : int numfkdelsetcols, int16 *fkdelsetcols,
10534 : bool old_check_ok,
10535 : Oid parentDelTrigger, Oid parentUpdTrigger,
10536 : bool with_period)
10537 : {
10538 : Oid deleteTriggerOid,
10539 : updateTriggerOid;
10540 :
10541 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10542 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10543 :
10544 : /*
10545 : * Create the action triggers that enforce the constraint.
10546 : */
10547 2930 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10548 : fkconstraint,
10549 : parentConstr, indexOid,
10550 : parentDelTrigger, parentUpdTrigger,
10551 : &deleteTriggerOid, &updateTriggerOid);
10552 :
10553 : /*
10554 : * If the referenced table is partitioned, recurse on ourselves to handle
10555 : * each partition. We need one pg_constraint row created for each
10556 : * partition in addition to the pg_constraint row for the parent table.
10557 : */
10558 2930 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10559 : {
10560 462 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10561 :
10562 1206 : for (int i = 0; i < pd->nparts; i++)
10563 : {
10564 : Relation partRel;
10565 : AttrMap *map;
10566 : AttrNumber *mapped_pkattnum;
10567 : Oid partIndexId;
10568 : ObjectAddress address;
10569 :
10570 : /* XXX would it be better to acquire these locks beforehand? */
10571 744 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10572 :
10573 : /*
10574 : * Map the attribute numbers in the referenced side of the FK
10575 : * definition to match the partition's column layout.
10576 : */
10577 744 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10578 : RelationGetDescr(pkrel),
10579 : false);
10580 744 : if (map)
10581 : {
10582 58 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10583 128 : for (int j = 0; j < numfks; j++)
10584 70 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10585 : }
10586 : else
10587 686 : mapped_pkattnum = pkattnum;
10588 :
10589 : /* Determine the index to use at this level */
10590 744 : partIndexId = index_get_partition(partRel, indexOid);
10591 744 : if (!OidIsValid(partIndexId))
10592 0 : elog(ERROR, "index for %u not found in partition %s",
10593 : indexOid, RelationGetRelationName(partRel));
10594 :
10595 : /* Create entry at this level ... */
10596 744 : address = addFkConstraint(addFkReferencedSide,
10597 : fkconstraint->conname, fkconstraint, rel,
10598 : partRel, partIndexId, parentConstr,
10599 : numfks, mapped_pkattnum,
10600 : fkattnum, pfeqoperators, ppeqoperators,
10601 : ffeqoperators, numfkdelsetcols,
10602 : fkdelsetcols, true, with_period);
10603 : /* ... and recurse to our children */
10604 744 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10605 : partIndexId, address.objectId, numfks,
10606 : mapped_pkattnum, fkattnum,
10607 : pfeqoperators, ppeqoperators, ffeqoperators,
10608 : numfkdelsetcols, fkdelsetcols,
10609 : old_check_ok,
10610 : deleteTriggerOid, updateTriggerOid,
10611 : with_period);
10612 :
10613 : /* Done -- clean up (but keep the lock) */
10614 744 : table_close(partRel, NoLock);
10615 744 : if (map)
10616 : {
10617 58 : pfree(mapped_pkattnum);
10618 58 : free_attrmap(map);
10619 : }
10620 : }
10621 : }
10622 2930 : }
10623 :
10624 : /*
10625 : * addFkRecurseReferencing
10626 : * Recursive helper for the referencing side of foreign key creation,
10627 : * which creates the check triggers and recurses
10628 : *
10629 : * If the referencing relation is a plain relation, create the necessary check
10630 : * triggers that implement the constraint, and set up for Phase 3 constraint
10631 : * verification. If the referencing relation is a partitioned table, then
10632 : * we create a pg_constraint row for it and recurse on this routine for each
10633 : * partition.
10634 : *
10635 : * We assume that the referenced relation is locked against concurrent
10636 : * deletions. If it's a partitioned relation, every partition must be so
10637 : * locked.
10638 : *
10639 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10640 : * of an ALTER TABLE sequence.
10641 : * fkconstraint: the constraint being added
10642 : * rel: the referencing relation; might be a partition, if recursing
10643 : * pkrel: the root referenced relation
10644 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10645 : * parentConstr: the OID of the parent constraint (there is always one)
10646 : * numfks: the number of columns in the foreign key
10647 : * pkattnum: the attnum array of referenced attributes
10648 : * fkattnum: the attnum array of referencing attributes
10649 : * pf/pp/ffeqoperators: OID array of operators between columns
10650 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10651 : * (...) clause
10652 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10653 : * NULL/DEFAULT clause
10654 : * old_check_ok: true if this constraint replaces an existing one that
10655 : * was already validated (thus this one doesn't need validation)
10656 : * lockmode: the lockmode to acquire on partitions when recursing
10657 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10658 : * a partition, the OIDs of the parent check triggers for INSERT and
10659 : * UPDATE respectively.
10660 : * with_period: true if this is a temporal FK
10661 : */
10662 : static void
10663 2770 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10664 : Relation pkrel, Oid indexOid, Oid parentConstr,
10665 : int numfks, int16 *pkattnum, int16 *fkattnum,
10666 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10667 : int numfkdelsetcols, int16 *fkdelsetcols,
10668 : bool old_check_ok, LOCKMODE lockmode,
10669 : Oid parentInsTrigger, Oid parentUpdTrigger,
10670 : bool with_period)
10671 : {
10672 : Oid insertTriggerOid,
10673 : updateTriggerOid;
10674 :
10675 : Assert(OidIsValid(parentConstr));
10676 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10677 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10678 :
10679 2770 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10680 0 : ereport(ERROR,
10681 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10682 : errmsg("foreign key constraints are not supported on foreign tables")));
10683 :
10684 : /*
10685 : * Add the check triggers to it and, if necessary, schedule it to be
10686 : * checked in Phase 3.
10687 : *
10688 : * If the relation is partitioned, drill down to do it to its partitions.
10689 : */
10690 2770 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10691 : RelationGetRelid(pkrel),
10692 : fkconstraint,
10693 : parentConstr,
10694 : indexOid,
10695 : parentInsTrigger, parentUpdTrigger,
10696 : &insertTriggerOid, &updateTriggerOid);
10697 :
10698 2770 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10699 : {
10700 : /*
10701 : * Tell Phase 3 to check that the constraint is satisfied by existing
10702 : * rows. We can skip this during table creation, when requested
10703 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10704 : * and when we're recreating a constraint following a SET DATA TYPE
10705 : * operation that did not impugn its validity.
10706 : */
10707 2322 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10708 : {
10709 : NewConstraint *newcon;
10710 : AlteredTableInfo *tab;
10711 :
10712 816 : tab = ATGetQueueEntry(wqueue, rel);
10713 :
10714 816 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10715 816 : newcon->name = get_constraint_name(parentConstr);
10716 816 : newcon->contype = CONSTR_FOREIGN;
10717 816 : newcon->refrelid = RelationGetRelid(pkrel);
10718 816 : newcon->refindid = indexOid;
10719 816 : newcon->conid = parentConstr;
10720 816 : newcon->conwithperiod = fkconstraint->fk_with_period;
10721 816 : newcon->qual = (Node *) fkconstraint;
10722 :
10723 816 : tab->constraints = lappend(tab->constraints, newcon);
10724 : }
10725 : }
10726 448 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10727 : {
10728 448 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10729 : Relation trigrel;
10730 :
10731 : /*
10732 : * Triggers of the foreign keys will be manipulated a bunch of times
10733 : * in the loop below. To avoid repeatedly opening/closing the trigger
10734 : * catalog relation, we open it here and pass it to the subroutines
10735 : * called below.
10736 : */
10737 448 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10738 :
10739 : /*
10740 : * Recurse to take appropriate action on each partition; either we
10741 : * find an existing constraint to reparent to ours, or we create a new
10742 : * one.
10743 : */
10744 842 : for (int i = 0; i < pd->nparts; i++)
10745 : {
10746 400 : Oid partitionId = pd->oids[i];
10747 400 : Relation partition = table_open(partitionId, lockmode);
10748 : List *partFKs;
10749 : AttrMap *attmap;
10750 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10751 : bool attached;
10752 : ObjectAddress address;
10753 : ListCell *cell;
10754 :
10755 400 : CheckAlterTableIsSafe(partition);
10756 :
10757 394 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10758 : RelationGetDescr(rel),
10759 : false);
10760 1022 : for (int j = 0; j < numfks; j++)
10761 628 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10762 :
10763 : /* Check whether an existing constraint can be repurposed */
10764 394 : partFKs = copyObject(RelationGetFKeyList(partition));
10765 394 : attached = false;
10766 412 : foreach(cell, partFKs)
10767 : {
10768 : ForeignKeyCacheInfo *fk;
10769 :
10770 30 : fk = lfirst_node(ForeignKeyCacheInfo, cell);
10771 30 : if (tryAttachPartitionForeignKey(fk,
10772 : partitionId,
10773 : parentConstr,
10774 : numfks,
10775 : mapped_fkattnum,
10776 : pkattnum,
10777 : pfeqoperators,
10778 : insertTriggerOid,
10779 : updateTriggerOid,
10780 : trigrel))
10781 : {
10782 12 : attached = true;
10783 12 : break;
10784 : }
10785 : }
10786 394 : if (attached)
10787 : {
10788 12 : table_close(partition, NoLock);
10789 12 : continue;
10790 : }
10791 :
10792 : /*
10793 : * No luck finding a good constraint to reuse; create our own.
10794 : */
10795 382 : address = addFkConstraint(addFkReferencingSide,
10796 : fkconstraint->conname, fkconstraint,
10797 : partition, pkrel, indexOid, parentConstr,
10798 : numfks, pkattnum,
10799 : mapped_fkattnum, pfeqoperators,
10800 : ppeqoperators, ffeqoperators,
10801 : numfkdelsetcols, fkdelsetcols, true,
10802 : with_period);
10803 :
10804 : /* call ourselves to finalize the creation and we're done */
10805 382 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10806 : indexOid,
10807 : address.objectId,
10808 : numfks,
10809 : pkattnum,
10810 : mapped_fkattnum,
10811 : pfeqoperators,
10812 : ppeqoperators,
10813 : ffeqoperators,
10814 : numfkdelsetcols,
10815 : fkdelsetcols,
10816 : old_check_ok,
10817 : lockmode,
10818 : insertTriggerOid,
10819 : updateTriggerOid,
10820 : with_period);
10821 :
10822 382 : table_close(partition, NoLock);
10823 : }
10824 :
10825 442 : table_close(trigrel, RowExclusiveLock);
10826 : }
10827 2764 : }
10828 :
10829 : /*
10830 : * CloneForeignKeyConstraints
10831 : * Clone foreign keys from a partitioned table to a newly acquired
10832 : * partition.
10833 : *
10834 : * partitionRel is a partition of parentRel, so we can be certain that it has
10835 : * the same columns with the same datatypes. The columns may be in different
10836 : * order, though.
10837 : *
10838 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10839 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10840 : * PARTITION OF).
10841 : */
10842 : static void
10843 9250 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10844 : Relation partitionRel)
10845 : {
10846 : /* This only works for declarative partitioning */
10847 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10848 :
10849 : /*
10850 : * Clone constraints for which the parent is on the referenced side.
10851 : */
10852 9250 : CloneFkReferenced(parentRel, partitionRel);
10853 :
10854 : /*
10855 : * Now clone constraints where the parent is on the referencing side.
10856 : */
10857 9250 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10858 9238 : }
10859 :
10860 : /*
10861 : * CloneFkReferenced
10862 : * Subroutine for CloneForeignKeyConstraints
10863 : *
10864 : * Find all the FKs that have the parent relation on the referenced side;
10865 : * clone those constraints to the given partition. This is to be called
10866 : * when the partition is being created or attached.
10867 : *
10868 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10869 : *
10870 : * This recurses to partitions, if the relation being attached is partitioned.
10871 : * Recursion is done by calling addFkRecurseReferenced.
10872 : */
10873 : static void
10874 9250 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10875 : {
10876 : Relation pg_constraint;
10877 : AttrMap *attmap;
10878 : ListCell *cell;
10879 : SysScanDesc scan;
10880 : ScanKeyData key[2];
10881 : HeapTuple tuple;
10882 9250 : List *clone = NIL;
10883 : Relation trigrel;
10884 :
10885 : /*
10886 : * Search for any constraints where this partition's parent is in the
10887 : * referenced side. However, we must not clone any constraint whose
10888 : * parent constraint is also going to be cloned, to avoid duplicates. So
10889 : * do it in two steps: first construct the list of constraints to clone,
10890 : * then go over that list cloning those whose parents are not in the list.
10891 : * (We must not rely on the parent being seen first, since the catalog
10892 : * scan could return children first.)
10893 : */
10894 9250 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10895 9250 : ScanKeyInit(&key[0],
10896 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10897 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10898 9250 : ScanKeyInit(&key[1],
10899 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
10900 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10901 : /* This is a seqscan, as we don't have a usable index ... */
10902 9250 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
10903 : NULL, 2, key);
10904 9556 : while ((tuple = systable_getnext(scan)) != NULL)
10905 : {
10906 306 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10907 :
10908 306 : clone = lappend_oid(clone, constrForm->oid);
10909 : }
10910 9250 : systable_endscan(scan);
10911 9250 : table_close(pg_constraint, RowShareLock);
10912 :
10913 : /*
10914 : * Triggers of the foreign keys will be manipulated a bunch of times in
10915 : * the loop below. To avoid repeatedly opening/closing the trigger
10916 : * catalog relation, we open it here and pass it to the subroutines called
10917 : * below.
10918 : */
10919 9250 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10920 :
10921 9250 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10922 : RelationGetDescr(parentRel),
10923 : false);
10924 9556 : foreach(cell, clone)
10925 : {
10926 306 : Oid constrOid = lfirst_oid(cell);
10927 : Form_pg_constraint constrForm;
10928 : Relation fkRel;
10929 : Oid indexOid;
10930 : Oid partIndexId;
10931 : int numfks;
10932 : AttrNumber conkey[INDEX_MAX_KEYS];
10933 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10934 : AttrNumber confkey[INDEX_MAX_KEYS];
10935 : Oid conpfeqop[INDEX_MAX_KEYS];
10936 : Oid conppeqop[INDEX_MAX_KEYS];
10937 : Oid conffeqop[INDEX_MAX_KEYS];
10938 : int numfkdelsetcols;
10939 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10940 : Constraint *fkconstraint;
10941 : ObjectAddress address;
10942 : Oid deleteTriggerOid,
10943 : updateTriggerOid;
10944 :
10945 306 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10946 306 : if (!HeapTupleIsValid(tuple))
10947 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10948 306 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10949 :
10950 : /*
10951 : * As explained above: don't try to clone a constraint for which we're
10952 : * going to clone the parent.
10953 : */
10954 306 : if (list_member_oid(clone, constrForm->conparentid))
10955 : {
10956 126 : ReleaseSysCache(tuple);
10957 180 : continue;
10958 : }
10959 :
10960 : /*
10961 : * Don't clone self-referencing foreign keys, which can be in the
10962 : * partitioned table or in the partition-to-be.
10963 : */
10964 180 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10965 138 : constrForm->conrelid == RelationGetRelid(partitionRel))
10966 : {
10967 54 : ReleaseSysCache(tuple);
10968 54 : continue;
10969 : }
10970 :
10971 : /* We need the same lock level that CreateTrigger will acquire */
10972 126 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
10973 :
10974 126 : indexOid = constrForm->conindid;
10975 126 : DeconstructFkConstraintRow(tuple,
10976 : &numfks,
10977 : conkey,
10978 : confkey,
10979 : conpfeqop,
10980 : conppeqop,
10981 : conffeqop,
10982 : &numfkdelsetcols,
10983 : confdelsetcols);
10984 :
10985 258 : for (int i = 0; i < numfks; i++)
10986 132 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10987 :
10988 126 : fkconstraint = makeNode(Constraint);
10989 126 : fkconstraint->contype = CONSTRAINT_FOREIGN;
10990 126 : fkconstraint->conname = NameStr(constrForm->conname);
10991 126 : fkconstraint->deferrable = constrForm->condeferrable;
10992 126 : fkconstraint->initdeferred = constrForm->condeferred;
10993 126 : fkconstraint->location = -1;
10994 126 : fkconstraint->pktable = NULL;
10995 : /* ->fk_attrs determined below */
10996 126 : fkconstraint->pk_attrs = NIL;
10997 126 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
10998 126 : fkconstraint->fk_upd_action = constrForm->confupdtype;
10999 126 : fkconstraint->fk_del_action = constrForm->confdeltype;
11000 126 : fkconstraint->fk_del_set_cols = NIL;
11001 126 : fkconstraint->old_conpfeqop = NIL;
11002 126 : fkconstraint->old_pktable_oid = InvalidOid;
11003 126 : fkconstraint->skip_validation = false;
11004 126 : fkconstraint->initially_valid = true;
11005 :
11006 : /* set up colnames that are used to generate the constraint name */
11007 258 : for (int i = 0; i < numfks; i++)
11008 : {
11009 : Form_pg_attribute att;
11010 :
11011 132 : att = TupleDescAttr(RelationGetDescr(fkRel),
11012 : conkey[i] - 1);
11013 132 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11014 132 : makeString(NameStr(att->attname)));
11015 : }
11016 :
11017 : /*
11018 : * Add the new foreign key constraint pointing to the new partition.
11019 : * Because this new partition appears in the referenced side of the
11020 : * constraint, we don't need to set up for Phase 3 check.
11021 : */
11022 126 : partIndexId = index_get_partition(partitionRel, indexOid);
11023 126 : if (!OidIsValid(partIndexId))
11024 0 : elog(ERROR, "index for %u not found in partition %s",
11025 : indexOid, RelationGetRelationName(partitionRel));
11026 :
11027 : /*
11028 : * Get the "action" triggers belonging to the constraint to pass as
11029 : * parent OIDs for similar triggers that will be created on the
11030 : * partition in addFkRecurseReferenced().
11031 : */
11032 126 : GetForeignKeyActionTriggers(trigrel, constrOid,
11033 : constrForm->confrelid, constrForm->conrelid,
11034 : &deleteTriggerOid, &updateTriggerOid);
11035 :
11036 : /* Add this constraint ... */
11037 126 : address = addFkConstraint(addFkReferencedSide,
11038 : fkconstraint->conname, fkconstraint, fkRel,
11039 : partitionRel, partIndexId, constrOid,
11040 : numfks, mapped_confkey,
11041 : conkey, conpfeqop, conppeqop, conffeqop,
11042 : numfkdelsetcols, confdelsetcols, false,
11043 126 : constrForm->conperiod);
11044 : /* ... and recurse */
11045 126 : addFkRecurseReferenced(fkconstraint,
11046 : fkRel,
11047 : partitionRel,
11048 : partIndexId,
11049 : address.objectId,
11050 : numfks,
11051 : mapped_confkey,
11052 : conkey,
11053 : conpfeqop,
11054 : conppeqop,
11055 : conffeqop,
11056 : numfkdelsetcols,
11057 : confdelsetcols,
11058 : true,
11059 : deleteTriggerOid,
11060 : updateTriggerOid,
11061 126 : constrForm->conperiod);
11062 :
11063 126 : table_close(fkRel, NoLock);
11064 126 : ReleaseSysCache(tuple);
11065 : }
11066 :
11067 9250 : table_close(trigrel, RowExclusiveLock);
11068 9250 : }
11069 :
11070 : /*
11071 : * CloneFkReferencing
11072 : * Subroutine for CloneForeignKeyConstraints
11073 : *
11074 : * For each FK constraint of the parent relation in the given list, find an
11075 : * equivalent constraint in its partition relation that can be reparented;
11076 : * if one cannot be found, create a new constraint in the partition as its
11077 : * child.
11078 : *
11079 : * If wqueue is given, it is used to set up phase-3 verification for each
11080 : * cloned constraint; omit it if such verification is not needed
11081 : * (example: the partition is being created anew).
11082 : */
11083 : static void
11084 9250 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11085 : {
11086 : AttrMap *attmap;
11087 : List *partFKs;
11088 9250 : List *clone = NIL;
11089 : ListCell *cell;
11090 : Relation trigrel;
11091 :
11092 : /* obtain a list of constraints that we need to clone */
11093 10320 : foreach(cell, RelationGetFKeyList(parentRel))
11094 : {
11095 1076 : ForeignKeyCacheInfo *fk = lfirst(cell);
11096 :
11097 : /*
11098 : * Refuse to attach a table as partition that this partitioned table
11099 : * already has a foreign key to. This isn't useful schema, which is
11100 : * proven by the fact that there have been no user complaints that
11101 : * it's already impossible to achieve this in the opposite direction,
11102 : * i.e., creating a foreign key that references a partition. This
11103 : * restriction allows us to dodge some complexities around
11104 : * pg_constraint and pg_trigger row creations that would be needed
11105 : * during ATTACH/DETACH for this kind of relationship.
11106 : */
11107 1076 : if (fk->confrelid == RelationGetRelid(partRel))
11108 6 : ereport(ERROR,
11109 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11110 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11111 : RelationGetRelationName(partRel),
11112 : get_constraint_name(fk->conoid))));
11113 :
11114 1070 : clone = lappend_oid(clone, fk->conoid);
11115 : }
11116 :
11117 : /*
11118 : * Silently do nothing if there's nothing to do. In particular, this
11119 : * avoids throwing a spurious error for foreign tables.
11120 : */
11121 9244 : if (clone == NIL)
11122 8790 : return;
11123 :
11124 454 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11125 0 : ereport(ERROR,
11126 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11127 : errmsg("foreign key constraints are not supported on foreign tables")));
11128 :
11129 : /*
11130 : * Triggers of the foreign keys will be manipulated a bunch of times in
11131 : * the loop below. To avoid repeatedly opening/closing the trigger
11132 : * catalog relation, we open it here and pass it to the subroutines called
11133 : * below.
11134 : */
11135 454 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11136 :
11137 : /*
11138 : * The constraint key may differ, if the columns in the partition are
11139 : * different. This map is used to convert them.
11140 : */
11141 454 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11142 : RelationGetDescr(parentRel),
11143 : false);
11144 :
11145 454 : partFKs = copyObject(RelationGetFKeyList(partRel));
11146 :
11147 1518 : foreach(cell, clone)
11148 : {
11149 1070 : Oid parentConstrOid = lfirst_oid(cell);
11150 : Form_pg_constraint constrForm;
11151 : Relation pkrel;
11152 : HeapTuple tuple;
11153 : int numfks;
11154 : AttrNumber conkey[INDEX_MAX_KEYS];
11155 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11156 : AttrNumber confkey[INDEX_MAX_KEYS];
11157 : Oid conpfeqop[INDEX_MAX_KEYS];
11158 : Oid conppeqop[INDEX_MAX_KEYS];
11159 : Oid conffeqop[INDEX_MAX_KEYS];
11160 : int numfkdelsetcols;
11161 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11162 : Constraint *fkconstraint;
11163 : bool attached;
11164 : Oid indexOid;
11165 : ObjectAddress address;
11166 : ListCell *lc;
11167 : Oid insertTriggerOid,
11168 : updateTriggerOid;
11169 : bool with_period;
11170 :
11171 1070 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11172 1070 : if (!HeapTupleIsValid(tuple))
11173 0 : elog(ERROR, "cache lookup failed for constraint %u",
11174 : parentConstrOid);
11175 1070 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11176 :
11177 : /* Don't clone constraints whose parents are being cloned */
11178 1070 : if (list_member_oid(clone, constrForm->conparentid))
11179 : {
11180 592 : ReleaseSysCache(tuple);
11181 670 : continue;
11182 : }
11183 :
11184 : /*
11185 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11186 : * relation, that means to lock all partitions.
11187 : */
11188 478 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11189 478 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11190 220 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11191 : ShareRowExclusiveLock, NULL);
11192 :
11193 478 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11194 : conpfeqop, conppeqop, conffeqop,
11195 : &numfkdelsetcols, confdelsetcols);
11196 1130 : for (int i = 0; i < numfks; i++)
11197 652 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11198 :
11199 : /*
11200 : * Get the "check" triggers belonging to the constraint to pass as
11201 : * parent OIDs for similar triggers that will be created on the
11202 : * partition in addFkRecurseReferencing(). They are also passed to
11203 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11204 : * the partition's existing "check" triggers, that is, if the
11205 : * corresponding constraints is deemed attachable to the parent
11206 : * constraint.
11207 : */
11208 478 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11209 : constrForm->confrelid, constrForm->conrelid,
11210 : &insertTriggerOid, &updateTriggerOid);
11211 :
11212 : /*
11213 : * Before creating a new constraint, see whether any existing FKs are
11214 : * fit for the purpose. If one is, attach the parent constraint to
11215 : * it, and don't clone anything. This way we avoid the expensive
11216 : * verification step and don't end up with a duplicate FK, and we
11217 : * don't need to recurse to partitions for this constraint.
11218 : */
11219 478 : attached = false;
11220 682 : foreach(lc, partFKs)
11221 : {
11222 282 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11223 :
11224 282 : if (tryAttachPartitionForeignKey(fk,
11225 : RelationGetRelid(partRel),
11226 : parentConstrOid,
11227 : numfks,
11228 : mapped_conkey,
11229 : confkey,
11230 : conpfeqop,
11231 : insertTriggerOid,
11232 : updateTriggerOid,
11233 : trigrel))
11234 : {
11235 78 : attached = true;
11236 78 : table_close(pkrel, NoLock);
11237 78 : break;
11238 : }
11239 : }
11240 478 : if (attached)
11241 : {
11242 78 : ReleaseSysCache(tuple);
11243 78 : continue;
11244 : }
11245 :
11246 : /* No dice. Set up to create our own constraint */
11247 400 : fkconstraint = makeNode(Constraint);
11248 400 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11249 : /* ->conname determined below */
11250 400 : fkconstraint->deferrable = constrForm->condeferrable;
11251 400 : fkconstraint->initdeferred = constrForm->condeferred;
11252 400 : fkconstraint->location = -1;
11253 400 : fkconstraint->pktable = NULL;
11254 : /* ->fk_attrs determined below */
11255 400 : fkconstraint->pk_attrs = NIL;
11256 400 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11257 400 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11258 400 : fkconstraint->fk_del_action = constrForm->confdeltype;
11259 400 : fkconstraint->fk_del_set_cols = NIL;
11260 400 : fkconstraint->old_conpfeqop = NIL;
11261 400 : fkconstraint->old_pktable_oid = InvalidOid;
11262 400 : fkconstraint->skip_validation = false;
11263 400 : fkconstraint->initially_valid = constrForm->convalidated;
11264 914 : for (int i = 0; i < numfks; i++)
11265 : {
11266 : Form_pg_attribute att;
11267 :
11268 514 : att = TupleDescAttr(RelationGetDescr(partRel),
11269 : mapped_conkey[i] - 1);
11270 514 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11271 514 : makeString(NameStr(att->attname)));
11272 : }
11273 :
11274 400 : indexOid = constrForm->conindid;
11275 400 : with_period = constrForm->conperiod;
11276 :
11277 : /* Create the pg_constraint entry at this level */
11278 400 : address = addFkConstraint(addFkReferencingSide,
11279 400 : NameStr(constrForm->conname), fkconstraint,
11280 : partRel, pkrel, indexOid, parentConstrOid,
11281 : numfks, confkey,
11282 : mapped_conkey, conpfeqop,
11283 : conppeqop, conffeqop,
11284 : numfkdelsetcols, confdelsetcols,
11285 : false, with_period);
11286 :
11287 : /* Done with the cloned constraint's tuple */
11288 400 : ReleaseSysCache(tuple);
11289 :
11290 : /* Create the check triggers, and recurse to partitions, if any */
11291 400 : addFkRecurseReferencing(wqueue,
11292 : fkconstraint,
11293 : partRel,
11294 : pkrel,
11295 : indexOid,
11296 : address.objectId,
11297 : numfks,
11298 : confkey,
11299 : mapped_conkey,
11300 : conpfeqop,
11301 : conppeqop,
11302 : conffeqop,
11303 : numfkdelsetcols,
11304 : confdelsetcols,
11305 : false, /* no old check exists */
11306 : AccessExclusiveLock,
11307 : insertTriggerOid,
11308 : updateTriggerOid,
11309 : with_period);
11310 394 : table_close(pkrel, NoLock);
11311 : }
11312 :
11313 448 : table_close(trigrel, RowExclusiveLock);
11314 : }
11315 :
11316 : /*
11317 : * When the parent of a partition receives [the referencing side of] a foreign
11318 : * key, we must propagate that foreign key to the partition. However, the
11319 : * partition might already have an equivalent foreign key; this routine
11320 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11321 : * by the other parameters. If they are equivalent, create the link between
11322 : * the two constraints and return true.
11323 : *
11324 : * If the given FK does not match the one defined by rest of the params,
11325 : * return false.
11326 : */
11327 : static bool
11328 312 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11329 : Oid partRelid,
11330 : Oid parentConstrOid,
11331 : int numfks,
11332 : AttrNumber *mapped_conkey,
11333 : AttrNumber *confkey,
11334 : Oid *conpfeqop,
11335 : Oid parentInsTrigger,
11336 : Oid parentUpdTrigger,
11337 : Relation trigrel)
11338 : {
11339 : HeapTuple parentConstrTup;
11340 : Form_pg_constraint parentConstr;
11341 : HeapTuple partcontup;
11342 : Form_pg_constraint partConstr;
11343 : ScanKeyData key;
11344 : SysScanDesc scan;
11345 : HeapTuple trigtup;
11346 : Oid insertTriggerOid,
11347 : updateTriggerOid;
11348 :
11349 312 : parentConstrTup = SearchSysCache1(CONSTROID,
11350 : ObjectIdGetDatum(parentConstrOid));
11351 312 : if (!HeapTupleIsValid(parentConstrTup))
11352 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11353 312 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11354 :
11355 : /*
11356 : * Do some quick & easy initial checks. If any of these fail, we cannot
11357 : * use this constraint.
11358 : */
11359 312 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11360 : {
11361 120 : ReleaseSysCache(parentConstrTup);
11362 120 : return false;
11363 : }
11364 546 : for (int i = 0; i < numfks; i++)
11365 : {
11366 354 : if (fk->conkey[i] != mapped_conkey[i] ||
11367 354 : fk->confkey[i] != confkey[i] ||
11368 354 : fk->conpfeqop[i] != conpfeqop[i])
11369 : {
11370 0 : ReleaseSysCache(parentConstrTup);
11371 0 : return false;
11372 : }
11373 : }
11374 :
11375 : /*
11376 : * Looks good so far; do some more extensive checks. Presumably the check
11377 : * for 'convalidated' could be dropped, since we don't really care about
11378 : * that, but let's be careful for now.
11379 : */
11380 192 : partcontup = SearchSysCache1(CONSTROID,
11381 : ObjectIdGetDatum(fk->conoid));
11382 192 : if (!HeapTupleIsValid(partcontup))
11383 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11384 192 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11385 192 : if (OidIsValid(partConstr->conparentid) ||
11386 168 : !partConstr->convalidated ||
11387 168 : partConstr->condeferrable != parentConstr->condeferrable ||
11388 140 : partConstr->condeferred != parentConstr->condeferred ||
11389 140 : partConstr->confupdtype != parentConstr->confupdtype ||
11390 104 : partConstr->confdeltype != parentConstr->confdeltype ||
11391 104 : partConstr->confmatchtype != parentConstr->confmatchtype)
11392 : {
11393 102 : ReleaseSysCache(parentConstrTup);
11394 102 : ReleaseSysCache(partcontup);
11395 102 : return false;
11396 : }
11397 :
11398 90 : ReleaseSysCache(partcontup);
11399 90 : ReleaseSysCache(parentConstrTup);
11400 :
11401 : /*
11402 : * Looks good! Attach this constraint. The action triggers in the new
11403 : * partition become redundant -- the parent table already has equivalent
11404 : * ones, and those will be able to reach the partition. Remove the ones
11405 : * in the partition. We identify them because they have our constraint
11406 : * OID, as well as being on the referenced rel.
11407 : */
11408 90 : ScanKeyInit(&key,
11409 : Anum_pg_trigger_tgconstraint,
11410 : BTEqualStrategyNumber, F_OIDEQ,
11411 : ObjectIdGetDatum(fk->conoid));
11412 90 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11413 : NULL, 1, &key);
11414 450 : while ((trigtup = systable_getnext(scan)) != NULL)
11415 : {
11416 360 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11417 : ObjectAddress trigger;
11418 :
11419 360 : if (trgform->tgconstrrelid != fk->conrelid)
11420 180 : continue;
11421 180 : if (trgform->tgrelid != fk->confrelid)
11422 0 : continue;
11423 :
11424 : /*
11425 : * The constraint is originally set up to contain this trigger as an
11426 : * implementation object, so there's a dependency record that links
11427 : * the two; however, since the trigger is no longer needed, we remove
11428 : * the dependency link in order to be able to drop the trigger while
11429 : * keeping the constraint intact.
11430 : */
11431 180 : deleteDependencyRecordsFor(TriggerRelationId,
11432 : trgform->oid,
11433 : false);
11434 : /* make dependency deletion visible to performDeletion */
11435 180 : CommandCounterIncrement();
11436 180 : ObjectAddressSet(trigger, TriggerRelationId,
11437 : trgform->oid);
11438 180 : performDeletion(&trigger, DROP_RESTRICT, 0);
11439 : /* make trigger drop visible, in case the loop iterates */
11440 180 : CommandCounterIncrement();
11441 : }
11442 :
11443 90 : systable_endscan(scan);
11444 :
11445 90 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11446 :
11447 : /*
11448 : * Like the constraint, attach partition's "check" triggers to the
11449 : * corresponding parent triggers.
11450 : */
11451 90 : GetForeignKeyCheckTriggers(trigrel,
11452 : fk->conoid, fk->confrelid, fk->conrelid,
11453 : &insertTriggerOid, &updateTriggerOid);
11454 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11455 90 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11456 : partRelid);
11457 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11458 90 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11459 : partRelid);
11460 :
11461 : /*
11462 : * If the referenced table is partitioned, then the partition we're
11463 : * attaching now has extra pg_constraint rows and action triggers that are
11464 : * no longer needed. Remove those.
11465 : */
11466 90 : if (get_rel_relkind(fk->confrelid) == RELKIND_PARTITIONED_TABLE)
11467 : {
11468 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11469 : ObjectAddresses *objs;
11470 : HeapTuple consttup;
11471 :
11472 24 : ScanKeyInit(&key,
11473 : Anum_pg_constraint_conrelid,
11474 : BTEqualStrategyNumber, F_OIDEQ,
11475 : ObjectIdGetDatum(fk->conrelid));
11476 :
11477 24 : scan = systable_beginscan(pg_constraint,
11478 : ConstraintRelidTypidNameIndexId,
11479 : true, NULL, 1, &key);
11480 24 : objs = new_object_addresses();
11481 240 : while ((consttup = systable_getnext(scan)) != NULL)
11482 : {
11483 216 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11484 :
11485 216 : if (conform->conparentid != fk->conoid)
11486 168 : continue;
11487 : else
11488 : {
11489 : ObjectAddress addr;
11490 : SysScanDesc scan2;
11491 : ScanKeyData key2;
11492 : int n PG_USED_FOR_ASSERTS_ONLY;
11493 :
11494 48 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11495 48 : add_exact_object_address(&addr, objs);
11496 :
11497 : /*
11498 : * First we must delete the dependency record that binds the
11499 : * constraint records together.
11500 : */
11501 48 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11502 : conform->oid,
11503 : DEPENDENCY_INTERNAL,
11504 : ConstraintRelationId,
11505 : fk->conoid);
11506 : Assert(n == 1); /* actually only one is expected */
11507 :
11508 : /*
11509 : * Now search for the triggers for this constraint and set
11510 : * them up for deletion too
11511 : */
11512 48 : ScanKeyInit(&key2,
11513 : Anum_pg_trigger_tgconstraint,
11514 : BTEqualStrategyNumber, F_OIDEQ,
11515 : ObjectIdGetDatum(conform->oid));
11516 48 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11517 : true, NULL, 1, &key2);
11518 144 : while ((trigtup = systable_getnext(scan2)) != NULL)
11519 : {
11520 96 : ObjectAddressSet(addr, TriggerRelationId,
11521 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11522 96 : add_exact_object_address(&addr, objs);
11523 : }
11524 48 : systable_endscan(scan2);
11525 : }
11526 : }
11527 : /* make the dependency deletions visible */
11528 24 : CommandCounterIncrement();
11529 24 : performMultipleDeletions(objs, DROP_RESTRICT,
11530 : PERFORM_DELETION_INTERNAL);
11531 24 : systable_endscan(scan);
11532 :
11533 24 : table_close(pg_constraint, RowShareLock);
11534 : }
11535 :
11536 90 : CommandCounterIncrement();
11537 90 : return true;
11538 : }
11539 :
11540 : /*
11541 : * GetForeignKeyActionTriggers
11542 : * Returns delete and update "action" triggers of the given relation
11543 : * belonging to the given constraint
11544 : */
11545 : static void
11546 126 : GetForeignKeyActionTriggers(Relation trigrel,
11547 : Oid conoid, Oid confrelid, Oid conrelid,
11548 : Oid *deleteTriggerOid,
11549 : Oid *updateTriggerOid)
11550 : {
11551 : ScanKeyData key;
11552 : SysScanDesc scan;
11553 : HeapTuple trigtup;
11554 :
11555 126 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11556 126 : ScanKeyInit(&key,
11557 : Anum_pg_trigger_tgconstraint,
11558 : BTEqualStrategyNumber, F_OIDEQ,
11559 : ObjectIdGetDatum(conoid));
11560 :
11561 126 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11562 : NULL, 1, &key);
11563 252 : while ((trigtup = systable_getnext(scan)) != NULL)
11564 : {
11565 252 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11566 :
11567 252 : if (trgform->tgconstrrelid != conrelid)
11568 0 : continue;
11569 252 : if (trgform->tgrelid != confrelid)
11570 0 : continue;
11571 : /* Only ever look at "action" triggers on the PK side. */
11572 252 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11573 0 : continue;
11574 252 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11575 : {
11576 : Assert(*deleteTriggerOid == InvalidOid);
11577 126 : *deleteTriggerOid = trgform->oid;
11578 : }
11579 126 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11580 : {
11581 : Assert(*updateTriggerOid == InvalidOid);
11582 126 : *updateTriggerOid = trgform->oid;
11583 : }
11584 : #ifndef USE_ASSERT_CHECKING
11585 : /* In an assert-enabled build, continue looking to find duplicates */
11586 252 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11587 126 : break;
11588 : #endif
11589 : }
11590 :
11591 126 : if (!OidIsValid(*deleteTriggerOid))
11592 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11593 : conoid);
11594 126 : if (!OidIsValid(*updateTriggerOid))
11595 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11596 : conoid);
11597 :
11598 126 : systable_endscan(scan);
11599 126 : }
11600 :
11601 : /*
11602 : * GetForeignKeyCheckTriggers
11603 : * Returns insert and update "check" triggers of the given relation
11604 : * belonging to the given constraint
11605 : */
11606 : static void
11607 640 : GetForeignKeyCheckTriggers(Relation trigrel,
11608 : Oid conoid, Oid confrelid, Oid conrelid,
11609 : Oid *insertTriggerOid,
11610 : Oid *updateTriggerOid)
11611 : {
11612 : ScanKeyData key;
11613 : SysScanDesc scan;
11614 : HeapTuple trigtup;
11615 :
11616 640 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11617 640 : ScanKeyInit(&key,
11618 : Anum_pg_trigger_tgconstraint,
11619 : BTEqualStrategyNumber, F_OIDEQ,
11620 : ObjectIdGetDatum(conoid));
11621 :
11622 640 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11623 : NULL, 1, &key);
11624 2112 : while ((trigtup = systable_getnext(scan)) != NULL)
11625 : {
11626 2112 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11627 :
11628 2112 : if (trgform->tgconstrrelid != confrelid)
11629 748 : continue;
11630 1364 : if (trgform->tgrelid != conrelid)
11631 0 : continue;
11632 : /* Only ever look at "check" triggers on the FK side. */
11633 1364 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11634 84 : continue;
11635 1280 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11636 : {
11637 : Assert(*insertTriggerOid == InvalidOid);
11638 640 : *insertTriggerOid = trgform->oid;
11639 : }
11640 640 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11641 : {
11642 : Assert(*updateTriggerOid == InvalidOid);
11643 640 : *updateTriggerOid = trgform->oid;
11644 : }
11645 : #ifndef USE_ASSERT_CHECKING
11646 : /* In an assert-enabled build, continue looking to find duplicates. */
11647 1280 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11648 640 : break;
11649 : #endif
11650 : }
11651 :
11652 640 : if (!OidIsValid(*insertTriggerOid))
11653 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11654 : conoid);
11655 640 : if (!OidIsValid(*updateTriggerOid))
11656 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11657 : conoid);
11658 :
11659 640 : systable_endscan(scan);
11660 640 : }
11661 :
11662 : /*
11663 : * ALTER TABLE ALTER CONSTRAINT
11664 : *
11665 : * Update the attributes of a constraint.
11666 : *
11667 : * Currently only works for Foreign Key constraints.
11668 : *
11669 : * If the constraint is modified, returns its address; otherwise, return
11670 : * InvalidObjectAddress.
11671 : */
11672 : static ObjectAddress
11673 126 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11674 : bool recursing, LOCKMODE lockmode)
11675 : {
11676 : Constraint *cmdcon;
11677 : Relation conrel;
11678 : Relation tgrel;
11679 : SysScanDesc scan;
11680 : ScanKeyData skey[3];
11681 : HeapTuple contuple;
11682 : Form_pg_constraint currcon;
11683 : ObjectAddress address;
11684 126 : List *otherrelids = NIL;
11685 : ListCell *lc;
11686 :
11687 126 : cmdcon = castNode(Constraint, cmd->def);
11688 :
11689 126 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11690 126 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11691 :
11692 : /*
11693 : * Find and check the target constraint
11694 : */
11695 126 : ScanKeyInit(&skey[0],
11696 : Anum_pg_constraint_conrelid,
11697 : BTEqualStrategyNumber, F_OIDEQ,
11698 : ObjectIdGetDatum(RelationGetRelid(rel)));
11699 126 : ScanKeyInit(&skey[1],
11700 : Anum_pg_constraint_contypid,
11701 : BTEqualStrategyNumber, F_OIDEQ,
11702 : ObjectIdGetDatum(InvalidOid));
11703 126 : ScanKeyInit(&skey[2],
11704 : Anum_pg_constraint_conname,
11705 : BTEqualStrategyNumber, F_NAMEEQ,
11706 126 : CStringGetDatum(cmdcon->conname));
11707 126 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11708 : true, NULL, 3, skey);
11709 :
11710 : /* There can be at most one matching row */
11711 126 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11712 0 : ereport(ERROR,
11713 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11714 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11715 : cmdcon->conname, RelationGetRelationName(rel))));
11716 :
11717 126 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11718 126 : if (currcon->contype != CONSTRAINT_FOREIGN)
11719 0 : ereport(ERROR,
11720 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11721 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11722 : cmdcon->conname, RelationGetRelationName(rel))));
11723 :
11724 : /*
11725 : * If it's not the topmost constraint, raise an error.
11726 : *
11727 : * Altering a non-topmost constraint leaves some triggers untouched, since
11728 : * they are not directly connected to this constraint; also, pg_dump would
11729 : * ignore the deferrability status of the individual constraint, since it
11730 : * only dumps topmost constraints. Avoid these problems by refusing this
11731 : * operation and telling the user to alter the parent constraint instead.
11732 : */
11733 126 : if (OidIsValid(currcon->conparentid))
11734 : {
11735 : HeapTuple tp;
11736 12 : Oid parent = currcon->conparentid;
11737 12 : char *ancestorname = NULL;
11738 12 : char *ancestortable = NULL;
11739 :
11740 : /* Loop to find the topmost constraint */
11741 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11742 : {
11743 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11744 :
11745 : /* If no parent, this is the constraint we want */
11746 24 : if (!OidIsValid(contup->conparentid))
11747 : {
11748 12 : ancestorname = pstrdup(NameStr(contup->conname));
11749 12 : ancestortable = get_rel_name(contup->conrelid);
11750 12 : ReleaseSysCache(tp);
11751 12 : break;
11752 : }
11753 :
11754 12 : parent = contup->conparentid;
11755 12 : ReleaseSysCache(tp);
11756 : }
11757 :
11758 12 : ereport(ERROR,
11759 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11760 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11761 : cmdcon->conname, RelationGetRelationName(rel)),
11762 : ancestorname && ancestortable ?
11763 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11764 : cmdcon->conname, ancestorname, ancestortable) : 0,
11765 : errhint("You may alter the constraint it derives from instead.")));
11766 : }
11767 :
11768 : /*
11769 : * Do the actual catalog work. We can skip changing if already in the
11770 : * desired state, but not if a partitioned table: partitions need to be
11771 : * processed regardless, in case they had the constraint locally changed.
11772 : */
11773 114 : address = InvalidObjectAddress;
11774 114 : if (currcon->condeferrable != cmdcon->deferrable ||
11775 6 : currcon->condeferred != cmdcon->initdeferred ||
11776 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11777 : {
11778 114 : if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11779 : &otherrelids, lockmode))
11780 114 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11781 : }
11782 :
11783 : /*
11784 : * ATExecAlterConstrRecurse already invalidated relcache for the relations
11785 : * having the constraint itself; here we also invalidate for relations
11786 : * that have any triggers that are part of the constraint.
11787 : */
11788 258 : foreach(lc, otherrelids)
11789 144 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11790 :
11791 114 : systable_endscan(scan);
11792 :
11793 114 : table_close(tgrel, RowExclusiveLock);
11794 114 : table_close(conrel, RowExclusiveLock);
11795 :
11796 114 : return address;
11797 : }
11798 :
11799 : /*
11800 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11801 : * constraint is altered.
11802 : *
11803 : * *otherrelids is appended OIDs of relations containing affected triggers.
11804 : *
11805 : * Note that we must recurse even when the values are correct, in case
11806 : * indirect descendants have had their constraints altered locally.
11807 : * (This could be avoided if we forbade altering constraints in partitions
11808 : * but existing releases don't do that.)
11809 : */
11810 : static bool
11811 180 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11812 : Relation rel, HeapTuple contuple, List **otherrelids,
11813 : LOCKMODE lockmode)
11814 : {
11815 : Form_pg_constraint currcon;
11816 : Oid conoid;
11817 : Oid refrelid;
11818 180 : bool changed = false;
11819 :
11820 : /* since this function recurses, it could be driven to stack overflow */
11821 180 : check_stack_depth();
11822 :
11823 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11824 180 : conoid = currcon->oid;
11825 180 : refrelid = currcon->confrelid;
11826 :
11827 : /*
11828 : * Update pg_constraint with the flags from cmdcon.
11829 : *
11830 : * If called to modify a constraint that's already in the desired state,
11831 : * silently do nothing.
11832 : */
11833 180 : if (currcon->condeferrable != cmdcon->deferrable ||
11834 6 : currcon->condeferred != cmdcon->initdeferred)
11835 : {
11836 : HeapTuple copyTuple;
11837 : Form_pg_constraint copy_con;
11838 : HeapTuple tgtuple;
11839 : ScanKeyData tgkey;
11840 : SysScanDesc tgscan;
11841 :
11842 180 : copyTuple = heap_copytuple(contuple);
11843 180 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11844 180 : copy_con->condeferrable = cmdcon->deferrable;
11845 180 : copy_con->condeferred = cmdcon->initdeferred;
11846 180 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11847 :
11848 180 : InvokeObjectPostAlterHook(ConstraintRelationId,
11849 : conoid, 0);
11850 :
11851 180 : heap_freetuple(copyTuple);
11852 180 : changed = true;
11853 :
11854 : /* Make new constraint flags visible to others */
11855 180 : CacheInvalidateRelcache(rel);
11856 :
11857 : /*
11858 : * Now we need to update the multiple entries in pg_trigger that
11859 : * implement the constraint.
11860 : */
11861 180 : ScanKeyInit(&tgkey,
11862 : Anum_pg_trigger_tgconstraint,
11863 : BTEqualStrategyNumber, F_OIDEQ,
11864 : ObjectIdGetDatum(conoid));
11865 180 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11866 : NULL, 1, &tgkey);
11867 768 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11868 : {
11869 588 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11870 : Form_pg_trigger copy_tg;
11871 : HeapTuple tgCopyTuple;
11872 :
11873 : /*
11874 : * Remember OIDs of other relation(s) involved in FK constraint.
11875 : * (Note: it's likely that we could skip forcing a relcache inval
11876 : * for other rels that don't have a trigger whose properties
11877 : * change, but let's be conservative.)
11878 : */
11879 588 : if (tgform->tgrelid != RelationGetRelid(rel))
11880 288 : *otherrelids = list_append_unique_oid(*otherrelids,
11881 : tgform->tgrelid);
11882 :
11883 : /*
11884 : * Update deferrability of RI_FKey_noaction_del,
11885 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11886 : * triggers, but not others; see createForeignKeyActionTriggers
11887 : * and CreateFKCheckTrigger.
11888 : */
11889 588 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11890 474 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11891 342 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11892 192 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11893 42 : continue;
11894 :
11895 546 : tgCopyTuple = heap_copytuple(tgtuple);
11896 546 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11897 :
11898 546 : copy_tg->tgdeferrable = cmdcon->deferrable;
11899 546 : copy_tg->tginitdeferred = cmdcon->initdeferred;
11900 546 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11901 :
11902 546 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11903 :
11904 546 : heap_freetuple(tgCopyTuple);
11905 : }
11906 :
11907 180 : systable_endscan(tgscan);
11908 : }
11909 :
11910 : /*
11911 : * If the table at either end of the constraint is partitioned, we need to
11912 : * recurse and handle every constraint that is a child of this one.
11913 : *
11914 : * (This assumes that the recurse flag is forcibly set for partitioned
11915 : * tables, and not set for legacy inheritance, though we don't check for
11916 : * that here.)
11917 : */
11918 336 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11919 156 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11920 : {
11921 : ScanKeyData pkey;
11922 : SysScanDesc pscan;
11923 : HeapTuple childtup;
11924 :
11925 42 : ScanKeyInit(&pkey,
11926 : Anum_pg_constraint_conparentid,
11927 : BTEqualStrategyNumber, F_OIDEQ,
11928 : ObjectIdGetDatum(conoid));
11929 :
11930 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11931 : true, NULL, 1, &pkey);
11932 :
11933 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11934 : {
11935 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11936 : Relation childrel;
11937 :
11938 66 : childrel = table_open(childcon->conrelid, lockmode);
11939 66 : ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11940 : otherrelids, lockmode);
11941 66 : table_close(childrel, NoLock);
11942 : }
11943 :
11944 42 : systable_endscan(pscan);
11945 : }
11946 :
11947 180 : return changed;
11948 : }
11949 :
11950 : /*
11951 : * ALTER TABLE VALIDATE CONSTRAINT
11952 : *
11953 : * XXX The reason we handle recursion here rather than at Phase 1 is because
11954 : * there's no good way to skip recursing when handling foreign keys: there is
11955 : * no need to lock children in that case, yet we wouldn't be able to avoid
11956 : * doing so at that level.
11957 : *
11958 : * Return value is the address of the validated constraint. If the constraint
11959 : * was already validated, InvalidObjectAddress is returned.
11960 : */
11961 : static ObjectAddress
11962 436 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11963 : bool recurse, bool recursing, LOCKMODE lockmode)
11964 : {
11965 : Relation conrel;
11966 : SysScanDesc scan;
11967 : ScanKeyData skey[3];
11968 : HeapTuple tuple;
11969 : Form_pg_constraint con;
11970 : ObjectAddress address;
11971 :
11972 436 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11973 :
11974 : /*
11975 : * Find and check the target constraint
11976 : */
11977 436 : ScanKeyInit(&skey[0],
11978 : Anum_pg_constraint_conrelid,
11979 : BTEqualStrategyNumber, F_OIDEQ,
11980 : ObjectIdGetDatum(RelationGetRelid(rel)));
11981 436 : ScanKeyInit(&skey[1],
11982 : Anum_pg_constraint_contypid,
11983 : BTEqualStrategyNumber, F_OIDEQ,
11984 : ObjectIdGetDatum(InvalidOid));
11985 436 : ScanKeyInit(&skey[2],
11986 : Anum_pg_constraint_conname,
11987 : BTEqualStrategyNumber, F_NAMEEQ,
11988 : CStringGetDatum(constrName));
11989 436 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11990 : true, NULL, 3, skey);
11991 :
11992 : /* There can be at most one matching row */
11993 436 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11994 0 : ereport(ERROR,
11995 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11996 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11997 : constrName, RelationGetRelationName(rel))));
11998 :
11999 436 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12000 436 : if (con->contype != CONSTRAINT_FOREIGN &&
12001 132 : con->contype != CONSTRAINT_CHECK)
12002 0 : ereport(ERROR,
12003 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12004 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12005 : constrName, RelationGetRelationName(rel))));
12006 :
12007 436 : if (!con->convalidated)
12008 : {
12009 : AlteredTableInfo *tab;
12010 : HeapTuple copyTuple;
12011 : Form_pg_constraint copy_con;
12012 :
12013 418 : if (con->contype == CONSTRAINT_FOREIGN)
12014 : {
12015 : NewConstraint *newcon;
12016 : Constraint *fkconstraint;
12017 :
12018 : /* Queue validation for phase 3 */
12019 298 : fkconstraint = makeNode(Constraint);
12020 : /* for now this is all we need */
12021 298 : fkconstraint->conname = constrName;
12022 :
12023 298 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12024 298 : newcon->name = constrName;
12025 298 : newcon->contype = CONSTR_FOREIGN;
12026 298 : newcon->refrelid = con->confrelid;
12027 298 : newcon->refindid = con->conindid;
12028 298 : newcon->conid = con->oid;
12029 298 : newcon->qual = (Node *) fkconstraint;
12030 :
12031 : /* Find or create work queue entry for this table */
12032 298 : tab = ATGetQueueEntry(wqueue, rel);
12033 298 : tab->constraints = lappend(tab->constraints, newcon);
12034 :
12035 : /*
12036 : * We disallow creating invalid foreign keys to or from
12037 : * partitioned tables, so ignoring the recursion bit is okay.
12038 : */
12039 : }
12040 120 : else if (con->contype == CONSTRAINT_CHECK)
12041 : {
12042 120 : List *children = NIL;
12043 : ListCell *child;
12044 : NewConstraint *newcon;
12045 : Datum val;
12046 : char *conbin;
12047 :
12048 : /*
12049 : * If we're recursing, the parent has already done this, so skip
12050 : * it. Also, if the constraint is a NO INHERIT constraint, we
12051 : * shouldn't try to look for it in the children.
12052 : */
12053 120 : if (!recursing && !con->connoinherit)
12054 66 : children = find_all_inheritors(RelationGetRelid(rel),
12055 : lockmode, NULL);
12056 :
12057 : /*
12058 : * For CHECK constraints, we must ensure that we only mark the
12059 : * constraint as validated on the parent if it's already validated
12060 : * on the children.
12061 : *
12062 : * We recurse before validating on the parent, to reduce risk of
12063 : * deadlocks.
12064 : */
12065 234 : foreach(child, children)
12066 : {
12067 114 : Oid childoid = lfirst_oid(child);
12068 : Relation childrel;
12069 :
12070 114 : if (childoid == RelationGetRelid(rel))
12071 66 : continue;
12072 :
12073 : /*
12074 : * If we are told not to recurse, there had better not be any
12075 : * child tables, because we can't mark the constraint on the
12076 : * parent valid unless it is valid for all child tables.
12077 : */
12078 48 : if (!recurse)
12079 0 : ereport(ERROR,
12080 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12081 : errmsg("constraint must be validated on child tables too")));
12082 :
12083 : /* find_all_inheritors already got lock */
12084 48 : childrel = table_open(childoid, NoLock);
12085 :
12086 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12087 : true, lockmode);
12088 48 : table_close(childrel, NoLock);
12089 : }
12090 :
12091 : /* Queue validation for phase 3 */
12092 120 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12093 120 : newcon->name = constrName;
12094 120 : newcon->contype = CONSTR_CHECK;
12095 120 : newcon->refrelid = InvalidOid;
12096 120 : newcon->refindid = InvalidOid;
12097 120 : newcon->conid = con->oid;
12098 :
12099 120 : val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12100 : Anum_pg_constraint_conbin);
12101 120 : conbin = TextDatumGetCString(val);
12102 120 : newcon->qual = (Node *) stringToNode(conbin);
12103 :
12104 : /* Find or create work queue entry for this table */
12105 120 : tab = ATGetQueueEntry(wqueue, rel);
12106 120 : tab->constraints = lappend(tab->constraints, newcon);
12107 :
12108 : /*
12109 : * Invalidate relcache so that others see the new validated
12110 : * constraint.
12111 : */
12112 120 : CacheInvalidateRelcache(rel);
12113 : }
12114 :
12115 : /*
12116 : * Now update the catalog, while we have the door open.
12117 : */
12118 418 : copyTuple = heap_copytuple(tuple);
12119 418 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12120 418 : copy_con->convalidated = true;
12121 418 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12122 :
12123 418 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12124 :
12125 418 : heap_freetuple(copyTuple);
12126 :
12127 418 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12128 : }
12129 : else
12130 18 : address = InvalidObjectAddress; /* already validated */
12131 :
12132 436 : systable_endscan(scan);
12133 :
12134 436 : table_close(conrel, RowExclusiveLock);
12135 :
12136 436 : return address;
12137 : }
12138 :
12139 :
12140 : /*
12141 : * transformColumnNameList - transform list of column names
12142 : *
12143 : * Lookup each name and return its attnum and, optionally, type and collation
12144 : * OIDs
12145 : *
12146 : * Note: the name of this function suggests that it's general-purpose,
12147 : * but actually it's only used to look up names appearing in foreign-key
12148 : * clauses. The error messages would need work to use it in other cases,
12149 : * and perhaps the validity checks as well.
12150 : */
12151 : static int
12152 6226 : transformColumnNameList(Oid relId, List *colList,
12153 : int16 *attnums, Oid *atttypids, Oid *attcollids)
12154 : {
12155 : ListCell *l;
12156 : int attnum;
12157 :
12158 6226 : attnum = 0;
12159 11394 : foreach(l, colList)
12160 : {
12161 5234 : char *attname = strVal(lfirst(l));
12162 : HeapTuple atttuple;
12163 : Form_pg_attribute attform;
12164 :
12165 5234 : atttuple = SearchSysCacheAttName(relId, attname);
12166 5234 : if (!HeapTupleIsValid(atttuple))
12167 54 : ereport(ERROR,
12168 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12169 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12170 : attname)));
12171 5180 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12172 5180 : if (attform->attnum < 0)
12173 12 : ereport(ERROR,
12174 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12175 : errmsg("system columns cannot be used in foreign keys")));
12176 5168 : if (attnum >= INDEX_MAX_KEYS)
12177 0 : ereport(ERROR,
12178 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12179 : errmsg("cannot have more than %d keys in a foreign key",
12180 : INDEX_MAX_KEYS)));
12181 5168 : attnums[attnum] = attform->attnum;
12182 5168 : if (atttypids != NULL)
12183 5138 : atttypids[attnum] = attform->atttypid;
12184 5168 : if (attcollids != NULL)
12185 5138 : attcollids[attnum] = attform->attcollation;
12186 5168 : ReleaseSysCache(atttuple);
12187 5168 : attnum++;
12188 : }
12189 :
12190 6160 : return attnum;
12191 : }
12192 :
12193 : /*
12194 : * transformFkeyGetPrimaryKey -
12195 : *
12196 : * Look up the names, attnums, types, and collations of the primary key attributes
12197 : * for the pkrel. Also return the index OID and index opclasses of the
12198 : * index supporting the primary key. Also return whether the index has
12199 : * WITHOUT OVERLAPS.
12200 : *
12201 : * All parameters except pkrel are output parameters. Also, the function
12202 : * return value is the number of attributes in the primary key.
12203 : *
12204 : * Used when the column list in the REFERENCES specification is omitted.
12205 : */
12206 : static int
12207 1112 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12208 : List **attnamelist,
12209 : int16 *attnums, Oid *atttypids, Oid *attcollids,
12210 : Oid *opclasses, bool *pk_has_without_overlaps)
12211 : {
12212 : List *indexoidlist;
12213 : ListCell *indexoidscan;
12214 1112 : HeapTuple indexTuple = NULL;
12215 1112 : Form_pg_index indexStruct = NULL;
12216 : Datum indclassDatum;
12217 : oidvector *indclass;
12218 : int i;
12219 :
12220 : /*
12221 : * Get the list of index OIDs for the table from the relcache, and look up
12222 : * each one in the pg_index syscache until we find one marked primary key
12223 : * (hopefully there isn't more than one such). Insist it's valid, too.
12224 : */
12225 1112 : *indexOid = InvalidOid;
12226 :
12227 1112 : indexoidlist = RelationGetIndexList(pkrel);
12228 :
12229 1118 : foreach(indexoidscan, indexoidlist)
12230 : {
12231 1118 : Oid indexoid = lfirst_oid(indexoidscan);
12232 :
12233 1118 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12234 1118 : if (!HeapTupleIsValid(indexTuple))
12235 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12236 1118 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12237 1118 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12238 : {
12239 : /*
12240 : * Refuse to use a deferrable primary key. This is per SQL spec,
12241 : * and there would be a lot of interesting semantic problems if we
12242 : * tried to allow it.
12243 : */
12244 1112 : if (!indexStruct->indimmediate)
12245 0 : ereport(ERROR,
12246 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12247 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12248 : RelationGetRelationName(pkrel))));
12249 :
12250 1112 : *indexOid = indexoid;
12251 1112 : break;
12252 : }
12253 6 : ReleaseSysCache(indexTuple);
12254 : }
12255 :
12256 1112 : list_free(indexoidlist);
12257 :
12258 : /*
12259 : * Check that we found it
12260 : */
12261 1112 : if (!OidIsValid(*indexOid))
12262 0 : ereport(ERROR,
12263 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12264 : errmsg("there is no primary key for referenced table \"%s\"",
12265 : RelationGetRelationName(pkrel))));
12266 :
12267 : /* Must get indclass the hard way */
12268 1112 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12269 : Anum_pg_index_indclass);
12270 1112 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12271 :
12272 : /*
12273 : * Now build the list of PK attributes from the indkey definition (we
12274 : * assume a primary key cannot have expressional elements)
12275 : */
12276 1112 : *attnamelist = NIL;
12277 2612 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12278 : {
12279 1500 : int pkattno = indexStruct->indkey.values[i];
12280 :
12281 1500 : attnums[i] = pkattno;
12282 1500 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12283 1500 : attcollids[i] = attnumCollationId(pkrel, pkattno);
12284 1500 : opclasses[i] = indclass->values[i];
12285 1500 : *attnamelist = lappend(*attnamelist,
12286 1500 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12287 : }
12288 :
12289 1112 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12290 :
12291 1112 : ReleaseSysCache(indexTuple);
12292 :
12293 1112 : return i;
12294 : }
12295 :
12296 : /*
12297 : * transformFkeyCheckAttrs -
12298 : *
12299 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12300 : * reference as part of a foreign key constraint.
12301 : *
12302 : * Returns the OID of the unique index supporting the constraint and
12303 : * populates the caller-provided 'opclasses' array with the opclasses
12304 : * associated with the index columns. Also sets whether the index
12305 : * uses WITHOUT OVERLAPS.
12306 : *
12307 : * Raises an ERROR on validation failure.
12308 : */
12309 : static Oid
12310 1254 : transformFkeyCheckAttrs(Relation pkrel,
12311 : int numattrs, int16 *attnums,
12312 : bool with_period, Oid *opclasses,
12313 : bool *pk_has_without_overlaps)
12314 : {
12315 1254 : Oid indexoid = InvalidOid;
12316 1254 : bool found = false;
12317 1254 : bool found_deferrable = false;
12318 : List *indexoidlist;
12319 : ListCell *indexoidscan;
12320 : int i,
12321 : j;
12322 :
12323 : /*
12324 : * Reject duplicate appearances of columns in the referenced-columns list.
12325 : * Such a case is forbidden by the SQL standard, and even if we thought it
12326 : * useful to allow it, there would be ambiguity about how to match the
12327 : * list to unique indexes (in particular, it'd be unclear which index
12328 : * opclass goes with which FK column).
12329 : */
12330 2944 : for (i = 0; i < numattrs; i++)
12331 : {
12332 2244 : for (j = i + 1; j < numattrs; j++)
12333 : {
12334 554 : if (attnums[i] == attnums[j])
12335 24 : ereport(ERROR,
12336 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12337 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12338 : }
12339 : }
12340 :
12341 : /*
12342 : * Get the list of index OIDs for the table from the relcache, and look up
12343 : * each one in the pg_index syscache, and match unique indexes to the list
12344 : * of attnums we are given.
12345 : */
12346 1230 : indexoidlist = RelationGetIndexList(pkrel);
12347 :
12348 1410 : foreach(indexoidscan, indexoidlist)
12349 : {
12350 : HeapTuple indexTuple;
12351 : Form_pg_index indexStruct;
12352 :
12353 1398 : indexoid = lfirst_oid(indexoidscan);
12354 1398 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12355 1398 : if (!HeapTupleIsValid(indexTuple))
12356 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12357 1398 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12358 :
12359 : /*
12360 : * Must have the right number of columns; must be unique (or if
12361 : * temporal then exclusion instead) and not a partial index; forget it
12362 : * if there are any expressions, too. Invalid indexes are out as well.
12363 : */
12364 2688 : if (indexStruct->indnkeyatts == numattrs &&
12365 1290 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12366 2552 : indexStruct->indisvalid &&
12367 2552 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12368 1276 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12369 : {
12370 : Datum indclassDatum;
12371 : oidvector *indclass;
12372 :
12373 : /* Must get indclass the hard way */
12374 1276 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12375 : Anum_pg_index_indclass);
12376 1276 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12377 :
12378 : /*
12379 : * The given attnum list may match the index columns in any order.
12380 : * Check for a match, and extract the appropriate opclasses while
12381 : * we're at it.
12382 : *
12383 : * We know that attnums[] is duplicate-free per the test at the
12384 : * start of this function, and we checked above that the number of
12385 : * index columns agrees, so if we find a match for each attnums[]
12386 : * entry then we must have a one-to-one match in some order.
12387 : */
12388 2954 : for (i = 0; i < numattrs; i++)
12389 : {
12390 1736 : found = false;
12391 2324 : for (j = 0; j < numattrs; j++)
12392 : {
12393 2266 : if (attnums[i] == indexStruct->indkey.values[j])
12394 : {
12395 1678 : opclasses[i] = indclass->values[j];
12396 1678 : found = true;
12397 1678 : break;
12398 : }
12399 : }
12400 1736 : if (!found)
12401 58 : break;
12402 : }
12403 : /* The last attribute in the index must be the PERIOD FK part */
12404 1276 : if (found && with_period)
12405 : {
12406 130 : int16 periodattnum = attnums[numattrs - 1];
12407 :
12408 130 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12409 : }
12410 :
12411 : /*
12412 : * Refuse to use a deferrable unique/primary key. This is per SQL
12413 : * spec, and there would be a lot of interesting semantic problems
12414 : * if we tried to allow it.
12415 : */
12416 1276 : if (found && !indexStruct->indimmediate)
12417 : {
12418 : /*
12419 : * Remember that we found an otherwise matching index, so that
12420 : * we can generate a more appropriate error message.
12421 : */
12422 0 : found_deferrable = true;
12423 0 : found = false;
12424 : }
12425 :
12426 : /* We need to know whether the index has WITHOUT OVERLAPS */
12427 1276 : if (found)
12428 1218 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12429 : }
12430 1398 : ReleaseSysCache(indexTuple);
12431 1398 : if (found)
12432 1218 : break;
12433 : }
12434 :
12435 1230 : if (!found)
12436 : {
12437 12 : if (found_deferrable)
12438 0 : ereport(ERROR,
12439 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12440 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12441 : RelationGetRelationName(pkrel))));
12442 : else
12443 12 : ereport(ERROR,
12444 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12445 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12446 : RelationGetRelationName(pkrel))));
12447 : }
12448 :
12449 1218 : list_free(indexoidlist);
12450 :
12451 1218 : return indexoid;
12452 : }
12453 :
12454 : /*
12455 : * findFkeyCast -
12456 : *
12457 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12458 : * Caller has equal regard for binary coercibility and for an exact match.
12459 : */
12460 : static CoercionPathType
12461 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12462 : {
12463 : CoercionPathType ret;
12464 :
12465 12 : if (targetTypeId == sourceTypeId)
12466 : {
12467 12 : ret = COERCION_PATH_RELABELTYPE;
12468 12 : *funcid = InvalidOid;
12469 : }
12470 : else
12471 : {
12472 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12473 : COERCION_IMPLICIT, funcid);
12474 0 : if (ret == COERCION_PATH_NONE)
12475 : /* A previously-relied-upon cast is now gone. */
12476 0 : elog(ERROR, "could not find cast from %u to %u",
12477 : sourceTypeId, targetTypeId);
12478 : }
12479 :
12480 12 : return ret;
12481 : }
12482 :
12483 : /*
12484 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12485 : *
12486 : * Note: we have already checked that the user owns the referencing table,
12487 : * else we'd have failed much earlier; no additional checks are needed for it.
12488 : */
12489 : static void
12490 2294 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12491 : {
12492 2294 : Oid roleid = GetUserId();
12493 : AclResult aclresult;
12494 : int i;
12495 :
12496 : /* Okay if we have relation-level REFERENCES permission */
12497 2294 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12498 : ACL_REFERENCES);
12499 2294 : if (aclresult == ACLCHECK_OK)
12500 2294 : return;
12501 : /* Else we must have REFERENCES on each column */
12502 0 : for (i = 0; i < natts; i++)
12503 : {
12504 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12505 : roleid, ACL_REFERENCES);
12506 0 : if (aclresult != ACLCHECK_OK)
12507 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12508 0 : RelationGetRelationName(rel));
12509 : }
12510 : }
12511 :
12512 : /*
12513 : * Scan the existing rows in a table to verify they meet a proposed FK
12514 : * constraint.
12515 : *
12516 : * Caller must have opened and locked both relations appropriately.
12517 : */
12518 : static void
12519 1108 : validateForeignKeyConstraint(char *conname,
12520 : Relation rel,
12521 : Relation pkrel,
12522 : Oid pkindOid,
12523 : Oid constraintOid,
12524 : bool hasperiod)
12525 : {
12526 : TupleTableSlot *slot;
12527 : TableScanDesc scan;
12528 1108 : Trigger trig = {0};
12529 : Snapshot snapshot;
12530 : MemoryContext oldcxt;
12531 : MemoryContext perTupCxt;
12532 :
12533 1108 : ereport(DEBUG1,
12534 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12535 :
12536 : /*
12537 : * Build a trigger call structure; we'll need it either way.
12538 : */
12539 1108 : trig.tgoid = InvalidOid;
12540 1108 : trig.tgname = conname;
12541 1108 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12542 1108 : trig.tgisinternal = true;
12543 1108 : trig.tgconstrrelid = RelationGetRelid(pkrel);
12544 1108 : trig.tgconstrindid = pkindOid;
12545 1108 : trig.tgconstraint = constraintOid;
12546 1108 : trig.tgdeferrable = false;
12547 1108 : trig.tginitdeferred = false;
12548 : /* we needn't fill in remaining fields */
12549 :
12550 : /*
12551 : * See if we can do it with a single LEFT JOIN query. A false result
12552 : * indicates we must proceed with the fire-the-trigger method. We can't do
12553 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12554 : * left joins.
12555 : */
12556 1108 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12557 888 : return;
12558 :
12559 : /*
12560 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12561 : * if that tuple had just been inserted. If any of those fail, it should
12562 : * ereport(ERROR) and that's that.
12563 : */
12564 158 : snapshot = RegisterSnapshot(GetLatestSnapshot());
12565 158 : slot = table_slot_create(rel, NULL);
12566 158 : scan = table_beginscan(rel, snapshot, 0, NULL);
12567 :
12568 158 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12569 : "validateForeignKeyConstraint",
12570 : ALLOCSET_SMALL_SIZES);
12571 158 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12572 :
12573 242 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12574 : {
12575 102 : LOCAL_FCINFO(fcinfo, 0);
12576 102 : TriggerData trigdata = {0};
12577 :
12578 102 : CHECK_FOR_INTERRUPTS();
12579 :
12580 : /*
12581 : * Make a call to the trigger function
12582 : *
12583 : * No parameters are passed, but we do set a context
12584 : */
12585 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12586 :
12587 : /*
12588 : * We assume RI_FKey_check_ins won't look at flinfo...
12589 : */
12590 102 : trigdata.type = T_TriggerData;
12591 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12592 102 : trigdata.tg_relation = rel;
12593 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12594 102 : trigdata.tg_trigslot = slot;
12595 102 : trigdata.tg_trigger = &trig;
12596 :
12597 102 : fcinfo->context = (Node *) &trigdata;
12598 :
12599 102 : RI_FKey_check_ins(fcinfo);
12600 :
12601 84 : MemoryContextReset(perTupCxt);
12602 : }
12603 :
12604 140 : MemoryContextSwitchTo(oldcxt);
12605 140 : MemoryContextDelete(perTupCxt);
12606 140 : table_endscan(scan);
12607 140 : UnregisterSnapshot(snapshot);
12608 140 : ExecDropSingleTupleTableSlot(slot);
12609 : }
12610 :
12611 : /*
12612 : * CreateFKCheckTrigger
12613 : * Creates the insert (on_insert=true) or update "check" trigger that
12614 : * implements a given foreign key
12615 : *
12616 : * Returns the OID of the so created trigger.
12617 : */
12618 : static Oid
12619 5540 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12620 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12621 : bool on_insert)
12622 : {
12623 : ObjectAddress trigAddress;
12624 : CreateTrigStmt *fk_trigger;
12625 :
12626 : /*
12627 : * Note: for a self-referential FK (referencing and referenced tables are
12628 : * the same), it is important that the ON UPDATE action fires before the
12629 : * CHECK action, since both triggers will fire on the same row during an
12630 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12631 : * state of the row. Triggers fire in name order, so we ensure this by
12632 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12633 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12634 : */
12635 5540 : fk_trigger = makeNode(CreateTrigStmt);
12636 5540 : fk_trigger->replace = false;
12637 5540 : fk_trigger->isconstraint = true;
12638 5540 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
12639 5540 : fk_trigger->relation = NULL;
12640 :
12641 : /* Either ON INSERT or ON UPDATE */
12642 5540 : if (on_insert)
12643 : {
12644 2770 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12645 2770 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12646 : }
12647 : else
12648 : {
12649 2770 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12650 2770 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12651 : }
12652 :
12653 5540 : fk_trigger->args = NIL;
12654 5540 : fk_trigger->row = true;
12655 5540 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12656 5540 : fk_trigger->columns = NIL;
12657 5540 : fk_trigger->whenClause = NULL;
12658 5540 : fk_trigger->transitionRels = NIL;
12659 5540 : fk_trigger->deferrable = fkconstraint->deferrable;
12660 5540 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12661 5540 : fk_trigger->constrrel = NULL;
12662 :
12663 5540 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12664 : constraintOid, indexOid, InvalidOid,
12665 : parentTrigOid, NULL, true, false);
12666 :
12667 : /* Make changes-so-far visible */
12668 5540 : CommandCounterIncrement();
12669 :
12670 5540 : return trigAddress.objectId;
12671 : }
12672 :
12673 : /*
12674 : * createForeignKeyActionTriggers
12675 : * Create the referenced-side "action" triggers that implement a foreign
12676 : * key.
12677 : *
12678 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
12679 : * *updateTrigOid.
12680 : */
12681 : static void
12682 2930 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12683 : Oid constraintOid, Oid indexOid,
12684 : Oid parentDelTrigger, Oid parentUpdTrigger,
12685 : Oid *deleteTrigOid, Oid *updateTrigOid)
12686 : {
12687 : CreateTrigStmt *fk_trigger;
12688 : ObjectAddress trigAddress;
12689 :
12690 : /*
12691 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12692 : * DELETE action on the referenced table.
12693 : */
12694 2930 : fk_trigger = makeNode(CreateTrigStmt);
12695 2930 : fk_trigger->replace = false;
12696 2930 : fk_trigger->isconstraint = true;
12697 2930 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12698 2930 : fk_trigger->relation = NULL;
12699 2930 : fk_trigger->args = NIL;
12700 2930 : fk_trigger->row = true;
12701 2930 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12702 2930 : fk_trigger->events = TRIGGER_TYPE_DELETE;
12703 2930 : fk_trigger->columns = NIL;
12704 2930 : fk_trigger->whenClause = NULL;
12705 2930 : fk_trigger->transitionRels = NIL;
12706 2930 : fk_trigger->constrrel = NULL;
12707 :
12708 2930 : switch (fkconstraint->fk_del_action)
12709 : {
12710 2262 : case FKCONSTR_ACTION_NOACTION:
12711 2262 : fk_trigger->deferrable = fkconstraint->deferrable;
12712 2262 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12713 2262 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12714 2262 : break;
12715 82 : case FKCONSTR_ACTION_RESTRICT:
12716 82 : fk_trigger->deferrable = false;
12717 82 : fk_trigger->initdeferred = false;
12718 82 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12719 82 : break;
12720 428 : case FKCONSTR_ACTION_CASCADE:
12721 428 : fk_trigger->deferrable = false;
12722 428 : fk_trigger->initdeferred = false;
12723 428 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12724 428 : break;
12725 98 : case FKCONSTR_ACTION_SETNULL:
12726 98 : fk_trigger->deferrable = false;
12727 98 : fk_trigger->initdeferred = false;
12728 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12729 98 : break;
12730 60 : case FKCONSTR_ACTION_SETDEFAULT:
12731 60 : fk_trigger->deferrable = false;
12732 60 : fk_trigger->initdeferred = false;
12733 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12734 60 : break;
12735 0 : default:
12736 0 : elog(ERROR, "unrecognized FK action type: %d",
12737 : (int) fkconstraint->fk_del_action);
12738 : break;
12739 : }
12740 :
12741 2930 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12742 : RelationGetRelid(rel),
12743 : constraintOid, indexOid, InvalidOid,
12744 : parentDelTrigger, NULL, true, false);
12745 2930 : if (deleteTrigOid)
12746 2930 : *deleteTrigOid = trigAddress.objectId;
12747 :
12748 : /* Make changes-so-far visible */
12749 2930 : CommandCounterIncrement();
12750 :
12751 : /*
12752 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12753 : * UPDATE action on the referenced table.
12754 : */
12755 2930 : fk_trigger = makeNode(CreateTrigStmt);
12756 2930 : fk_trigger->replace = false;
12757 2930 : fk_trigger->isconstraint = true;
12758 2930 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12759 2930 : fk_trigger->relation = NULL;
12760 2930 : fk_trigger->args = NIL;
12761 2930 : fk_trigger->row = true;
12762 2930 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12763 2930 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12764 2930 : fk_trigger->columns = NIL;
12765 2930 : fk_trigger->whenClause = NULL;
12766 2930 : fk_trigger->transitionRels = NIL;
12767 2930 : fk_trigger->constrrel = NULL;
12768 :
12769 2930 : switch (fkconstraint->fk_upd_action)
12770 : {
12771 2508 : case FKCONSTR_ACTION_NOACTION:
12772 2508 : fk_trigger->deferrable = fkconstraint->deferrable;
12773 2508 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12774 2508 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12775 2508 : break;
12776 42 : case FKCONSTR_ACTION_RESTRICT:
12777 42 : fk_trigger->deferrable = false;
12778 42 : fk_trigger->initdeferred = false;
12779 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12780 42 : break;
12781 276 : case FKCONSTR_ACTION_CASCADE:
12782 276 : fk_trigger->deferrable = false;
12783 276 : fk_trigger->initdeferred = false;
12784 276 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12785 276 : break;
12786 62 : case FKCONSTR_ACTION_SETNULL:
12787 62 : fk_trigger->deferrable = false;
12788 62 : fk_trigger->initdeferred = false;
12789 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12790 62 : break;
12791 42 : case FKCONSTR_ACTION_SETDEFAULT:
12792 42 : fk_trigger->deferrable = false;
12793 42 : fk_trigger->initdeferred = false;
12794 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12795 42 : break;
12796 0 : default:
12797 0 : elog(ERROR, "unrecognized FK action type: %d",
12798 : (int) fkconstraint->fk_upd_action);
12799 : break;
12800 : }
12801 :
12802 2930 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12803 : RelationGetRelid(rel),
12804 : constraintOid, indexOid, InvalidOid,
12805 : parentUpdTrigger, NULL, true, false);
12806 2930 : if (updateTrigOid)
12807 2930 : *updateTrigOid = trigAddress.objectId;
12808 2930 : }
12809 :
12810 : /*
12811 : * createForeignKeyCheckTriggers
12812 : * Create the referencing-side "check" triggers that implement a foreign
12813 : * key.
12814 : *
12815 : * Returns the OIDs of the so created triggers in *insertTrigOid and
12816 : * *updateTrigOid.
12817 : */
12818 : static void
12819 2770 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12820 : Constraint *fkconstraint, Oid constraintOid,
12821 : Oid indexOid,
12822 : Oid parentInsTrigger, Oid parentUpdTrigger,
12823 : Oid *insertTrigOid, Oid *updateTrigOid)
12824 : {
12825 2770 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12826 : constraintOid, indexOid,
12827 : parentInsTrigger, true);
12828 2770 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12829 : constraintOid, indexOid,
12830 : parentUpdTrigger, false);
12831 2770 : }
12832 :
12833 : /*
12834 : * ALTER TABLE DROP CONSTRAINT
12835 : *
12836 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12837 : */
12838 : static void
12839 816 : ATExecDropConstraint(Relation rel, const char *constrName,
12840 : DropBehavior behavior, bool recurse,
12841 : bool missing_ok, LOCKMODE lockmode)
12842 : {
12843 : Relation conrel;
12844 : SysScanDesc scan;
12845 : ScanKeyData skey[3];
12846 : HeapTuple tuple;
12847 816 : bool found = false;
12848 :
12849 816 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12850 :
12851 : /*
12852 : * Find and drop the target constraint
12853 : */
12854 816 : ScanKeyInit(&skey[0],
12855 : Anum_pg_constraint_conrelid,
12856 : BTEqualStrategyNumber, F_OIDEQ,
12857 : ObjectIdGetDatum(RelationGetRelid(rel)));
12858 816 : ScanKeyInit(&skey[1],
12859 : Anum_pg_constraint_contypid,
12860 : BTEqualStrategyNumber, F_OIDEQ,
12861 : ObjectIdGetDatum(InvalidOid));
12862 816 : ScanKeyInit(&skey[2],
12863 : Anum_pg_constraint_conname,
12864 : BTEqualStrategyNumber, F_NAMEEQ,
12865 : CStringGetDatum(constrName));
12866 816 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12867 : true, NULL, 3, skey);
12868 :
12869 : /* There can be at most one matching row */
12870 816 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12871 : {
12872 780 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
12873 : missing_ok, lockmode);
12874 594 : found = true;
12875 : }
12876 :
12877 630 : systable_endscan(scan);
12878 :
12879 630 : if (!found)
12880 : {
12881 36 : if (!missing_ok)
12882 24 : ereport(ERROR,
12883 : errcode(ERRCODE_UNDEFINED_OBJECT),
12884 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12885 : constrName, RelationGetRelationName(rel)));
12886 : else
12887 12 : ereport(NOTICE,
12888 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12889 : constrName, RelationGetRelationName(rel)));
12890 : }
12891 :
12892 606 : table_close(conrel, RowExclusiveLock);
12893 606 : }
12894 :
12895 : /*
12896 : * Remove a constraint, using its pg_constraint tuple
12897 : *
12898 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12899 : * DROP NOT NULL.
12900 : *
12901 : * Returns the address of the constraint being removed.
12902 : */
12903 : static ObjectAddress
12904 1192 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12905 : bool recurse, bool recursing, bool missing_ok,
12906 : LOCKMODE lockmode)
12907 : {
12908 : Relation conrel;
12909 : Form_pg_constraint con;
12910 : ObjectAddress conobj;
12911 : List *children;
12912 1192 : bool is_no_inherit_constraint = false;
12913 : char *constrName;
12914 1192 : char *colname = NULL;
12915 :
12916 : /* Guard against stack overflow due to overly deep inheritance tree. */
12917 1192 : check_stack_depth();
12918 :
12919 : /* At top level, permission check was done in ATPrepCmd, else do it */
12920 1192 : if (recursing)
12921 210 : ATSimplePermissions(AT_DropConstraint, rel,
12922 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
12923 :
12924 1186 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12925 :
12926 1186 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12927 1186 : constrName = NameStr(con->conname);
12928 :
12929 : /* Don't allow drop of inherited constraints */
12930 1186 : if (con->coninhcount > 0 && !recursing)
12931 156 : ereport(ERROR,
12932 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12933 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12934 : constrName, RelationGetRelationName(rel))));
12935 :
12936 : /*
12937 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
12938 : *
12939 : * While doing that, we're in a good position to disallow dropping a not-
12940 : * null constraint underneath a primary key, a replica identity index, or
12941 : * a generated identity column.
12942 : */
12943 1030 : if (con->contype == CONSTRAINT_NOTNULL)
12944 : {
12945 272 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
12946 272 : AttrNumber attnum = extractNotNullColumn(constraintTup);
12947 : Bitmapset *pkattrs;
12948 : Bitmapset *irattrs;
12949 : HeapTuple atttup;
12950 : Form_pg_attribute attForm;
12951 :
12952 : /* save column name for recursion step */
12953 272 : colname = get_attname(RelationGetRelid(rel), attnum, false);
12954 :
12955 : /*
12956 : * Disallow if it's in the primary key. For partitioned tables we
12957 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
12958 : * return NULL if the primary key is invalid; but we still need to
12959 : * protect not-null constraints under such a constraint, so check the
12960 : * slow way.
12961 : */
12962 272 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
12963 :
12964 272 : if (pkattrs == NULL &&
12965 242 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12966 : {
12967 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
12968 :
12969 18 : if (OidIsValid(pkindex))
12970 : {
12971 0 : Relation pk = relation_open(pkindex, AccessShareLock);
12972 :
12973 0 : pkattrs = NULL;
12974 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
12975 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
12976 :
12977 0 : relation_close(pk, AccessShareLock);
12978 : }
12979 : }
12980 :
12981 302 : if (pkattrs &&
12982 30 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
12983 24 : ereport(ERROR,
12984 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12985 : errmsg("column \"%s\" is in a primary key",
12986 : get_attname(RelationGetRelid(rel), attnum, false)));
12987 :
12988 : /* Disallow if it's in the replica identity */
12989 248 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
12990 248 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
12991 12 : ereport(ERROR,
12992 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12993 : errmsg("column \"%s\" is in index used as replica identity",
12994 : get_attname(RelationGetRelid(rel), attnum, false)));
12995 :
12996 : /* Disallow if it's a GENERATED AS IDENTITY column */
12997 236 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
12998 236 : if (!HeapTupleIsValid(atttup))
12999 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13000 : attnum, RelationGetRelid(rel));
13001 236 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13002 236 : if (attForm->attidentity != '\0')
13003 0 : ereport(ERROR,
13004 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13005 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13006 : get_attname(RelationGetRelid(rel), attnum,
13007 : false),
13008 : RelationGetRelationName(rel)));
13009 :
13010 : /* All good -- reset attnotnull if needed */
13011 236 : if (attForm->attnotnull)
13012 : {
13013 236 : attForm->attnotnull = false;
13014 236 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13015 : }
13016 :
13017 236 : table_close(attrel, RowExclusiveLock);
13018 : }
13019 :
13020 994 : is_no_inherit_constraint = con->connoinherit;
13021 :
13022 : /*
13023 : * If it's a foreign-key constraint, we'd better lock the referenced table
13024 : * and check that that's not in use, just as we've already done for the
13025 : * constrained table (else we might, eg, be dropping a trigger that has
13026 : * unfired events). But we can/must skip that in the self-referential
13027 : * case.
13028 : */
13029 994 : if (con->contype == CONSTRAINT_FOREIGN &&
13030 216 : con->confrelid != RelationGetRelid(rel))
13031 : {
13032 : Relation frel;
13033 :
13034 : /* Must match lock taken by RemoveTriggerById: */
13035 216 : frel = table_open(con->confrelid, AccessExclusiveLock);
13036 216 : CheckAlterTableIsSafe(frel);
13037 210 : table_close(frel, NoLock);
13038 : }
13039 :
13040 : /*
13041 : * Perform the actual constraint deletion
13042 : */
13043 988 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13044 988 : performDeletion(&conobj, behavior, 0);
13045 :
13046 : /*
13047 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13048 : * are dropped via the dependency mechanism, so we're done here.
13049 : */
13050 952 : if (con->contype != CONSTRAINT_CHECK &&
13051 634 : con->contype != CONSTRAINT_NOTNULL &&
13052 398 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13053 : {
13054 90 : table_close(conrel, RowExclusiveLock);
13055 90 : return conobj;
13056 : }
13057 :
13058 : /*
13059 : * Propagate to children as appropriate. Unlike most other ALTER
13060 : * routines, we have to do this one level of recursion at a time; we can't
13061 : * use find_all_inheritors to do it in one pass.
13062 : */
13063 862 : if (!is_no_inherit_constraint)
13064 548 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13065 : else
13066 314 : children = NIL;
13067 :
13068 2090 : foreach_oid(childrelid, children)
13069 : {
13070 : Relation childrel;
13071 : HeapTuple tuple;
13072 : Form_pg_constraint childcon;
13073 :
13074 : /* find_inheritance_children already got lock */
13075 378 : childrel = table_open(childrelid, NoLock);
13076 378 : CheckAlterTableIsSafe(childrel);
13077 :
13078 : /*
13079 : * We search for not-null constraints by column name, and others by
13080 : * constraint name.
13081 : */
13082 378 : if (con->contype == CONSTRAINT_NOTNULL)
13083 : {
13084 142 : tuple = findNotNullConstraint(childrelid, colname);
13085 142 : if (!HeapTupleIsValid(tuple))
13086 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13087 : colname, RelationGetRelid(childrel));
13088 : }
13089 : else
13090 : {
13091 : SysScanDesc scan;
13092 : ScanKeyData skey[3];
13093 :
13094 236 : ScanKeyInit(&skey[0],
13095 : Anum_pg_constraint_conrelid,
13096 : BTEqualStrategyNumber, F_OIDEQ,
13097 : ObjectIdGetDatum(childrelid));
13098 236 : ScanKeyInit(&skey[1],
13099 : Anum_pg_constraint_contypid,
13100 : BTEqualStrategyNumber, F_OIDEQ,
13101 : ObjectIdGetDatum(InvalidOid));
13102 236 : ScanKeyInit(&skey[2],
13103 : Anum_pg_constraint_conname,
13104 : BTEqualStrategyNumber, F_NAMEEQ,
13105 : CStringGetDatum(constrName));
13106 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13107 : true, NULL, 3, skey);
13108 : /* There can only be one, so no need to loop */
13109 236 : tuple = systable_getnext(scan);
13110 236 : if (!HeapTupleIsValid(tuple))
13111 0 : ereport(ERROR,
13112 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13113 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13114 : constrName,
13115 : RelationGetRelationName(childrel))));
13116 236 : tuple = heap_copytuple(tuple);
13117 236 : systable_endscan(scan);
13118 : }
13119 :
13120 378 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13121 :
13122 : /* Right now only CHECK and not-null constraints can be inherited */
13123 378 : if (childcon->contype != CONSTRAINT_CHECK &&
13124 142 : childcon->contype != CONSTRAINT_NOTNULL)
13125 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13126 :
13127 378 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13128 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13129 : childrelid, NameStr(childcon->conname));
13130 :
13131 378 : if (recurse)
13132 : {
13133 : /*
13134 : * If the child constraint has other definition sources, just
13135 : * decrement its inheritance count; if not, recurse to delete it.
13136 : */
13137 276 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13138 : {
13139 : /* Time to delete this child constraint, too */
13140 210 : dropconstraint_internal(childrel, tuple, behavior,
13141 : recurse, true, missing_ok,
13142 : lockmode);
13143 : }
13144 : else
13145 : {
13146 : /* Child constraint must survive my deletion */
13147 66 : childcon->coninhcount--;
13148 66 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13149 :
13150 : /* Make update visible */
13151 66 : CommandCounterIncrement();
13152 : }
13153 : }
13154 : else
13155 : {
13156 : /*
13157 : * If we were told to drop ONLY in this table (no recursion) and
13158 : * there are no further parents for this constraint, we need to
13159 : * mark the inheritors' constraints as locally defined rather than
13160 : * inherited.
13161 : */
13162 102 : childcon->coninhcount--;
13163 102 : if (childcon->coninhcount == 0)
13164 102 : childcon->conislocal = true;
13165 :
13166 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13167 :
13168 : /* Make update visible */
13169 102 : CommandCounterIncrement();
13170 : }
13171 :
13172 372 : heap_freetuple(tuple);
13173 :
13174 372 : table_close(childrel, NoLock);
13175 : }
13176 :
13177 856 : table_close(conrel, RowExclusiveLock);
13178 :
13179 856 : return conobj;
13180 : }
13181 :
13182 : /*
13183 : * ALTER COLUMN TYPE
13184 : *
13185 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13186 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13187 : * transformed (and must be, because we rely on some transformed fields).
13188 : *
13189 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13190 : * table will be done "in parallel" during phase 3, so all the USING
13191 : * expressions should be parsed assuming the original column types. Also,
13192 : * this allows a USING expression to refer to a field that will be dropped.
13193 : *
13194 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13195 : * the first two execution steps in phase 2; they must not see the effects
13196 : * of any other subcommand types, since the USING expressions are parsed
13197 : * against the unmodified table's state.
13198 : */
13199 : static void
13200 1150 : ATPrepAlterColumnType(List **wqueue,
13201 : AlteredTableInfo *tab, Relation rel,
13202 : bool recurse, bool recursing,
13203 : AlterTableCmd *cmd, LOCKMODE lockmode,
13204 : AlterTableUtilityContext *context)
13205 : {
13206 1150 : char *colName = cmd->name;
13207 1150 : ColumnDef *def = (ColumnDef *) cmd->def;
13208 1150 : TypeName *typeName = def->typeName;
13209 1150 : Node *transform = def->cooked_default;
13210 : HeapTuple tuple;
13211 : Form_pg_attribute attTup;
13212 : AttrNumber attnum;
13213 : Oid targettype;
13214 : int32 targettypmod;
13215 : Oid targetcollid;
13216 : NewColumnValue *newval;
13217 1150 : ParseState *pstate = make_parsestate(NULL);
13218 : AclResult aclresult;
13219 : bool is_expr;
13220 :
13221 1150 : if (rel->rd_rel->reloftype && !recursing)
13222 6 : ereport(ERROR,
13223 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13224 : errmsg("cannot alter column type of typed table")));
13225 :
13226 : /* lookup the attribute so we can check inheritance status */
13227 1144 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13228 1144 : if (!HeapTupleIsValid(tuple))
13229 0 : ereport(ERROR,
13230 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13231 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13232 : colName, RelationGetRelationName(rel))));
13233 1144 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13234 1144 : attnum = attTup->attnum;
13235 :
13236 : /* Can't alter a system attribute */
13237 1144 : if (attnum <= 0)
13238 0 : ereport(ERROR,
13239 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13240 : errmsg("cannot alter system column \"%s\"",
13241 : colName)));
13242 :
13243 : /*
13244 : * Cannot specify USING when altering type of a generated column, because
13245 : * that would violate the generation expression.
13246 : */
13247 1144 : if (attTup->attgenerated && def->cooked_default)
13248 6 : ereport(ERROR,
13249 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13250 : errmsg("cannot specify USING when altering type of generated column"),
13251 : errdetail("Column \"%s\" is a generated column.", colName)));
13252 :
13253 : /*
13254 : * Don't alter inherited columns. At outer level, there had better not be
13255 : * any inherited definition; when recursing, we assume this was checked at
13256 : * the parent level (see below).
13257 : */
13258 1138 : if (attTup->attinhcount > 0 && !recursing)
13259 6 : ereport(ERROR,
13260 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13261 : errmsg("cannot alter inherited column \"%s\"",
13262 : colName)));
13263 :
13264 : /* Don't alter columns used in the partition key */
13265 1132 : if (has_partition_attrs(rel,
13266 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13267 : &is_expr))
13268 18 : ereport(ERROR,
13269 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13270 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13271 : colName, RelationGetRelationName(rel))));
13272 :
13273 : /* Look up the target type */
13274 1114 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13275 :
13276 1114 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13277 1114 : if (aclresult != ACLCHECK_OK)
13278 12 : aclcheck_error_type(aclresult, targettype);
13279 :
13280 : /* And the collation */
13281 1102 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13282 :
13283 : /* make sure datatype is legal for a column */
13284 1102 : CheckAttributeType(colName, targettype, targetcollid,
13285 1102 : list_make1_oid(rel->rd_rel->reltype),
13286 : 0);
13287 :
13288 1096 : if (tab->relkind == RELKIND_RELATION ||
13289 196 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13290 : {
13291 : /*
13292 : * Set up an expression to transform the old data value to the new
13293 : * type. If a USING option was given, use the expression as
13294 : * transformed by transformAlterTableStmt, else just take the old
13295 : * value and try to coerce it. We do this first so that type
13296 : * incompatibility can be detected before we waste effort, and because
13297 : * we need the expression to be parsed against the original table row
13298 : * type.
13299 : */
13300 960 : if (!transform)
13301 : {
13302 738 : transform = (Node *) makeVar(1, attnum,
13303 : attTup->atttypid, attTup->atttypmod,
13304 : attTup->attcollation,
13305 : 0);
13306 : }
13307 :
13308 960 : transform = coerce_to_target_type(pstate,
13309 : transform, exprType(transform),
13310 : targettype, targettypmod,
13311 : COERCION_ASSIGNMENT,
13312 : COERCE_IMPLICIT_CAST,
13313 : -1);
13314 960 : if (transform == NULL)
13315 : {
13316 : /* error text depends on whether USING was specified or not */
13317 24 : if (def->cooked_default != NULL)
13318 6 : ereport(ERROR,
13319 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13320 : errmsg("result of USING clause for column \"%s\""
13321 : " cannot be cast automatically to type %s",
13322 : colName, format_type_be(targettype)),
13323 : errhint("You might need to add an explicit cast.")));
13324 : else
13325 18 : ereport(ERROR,
13326 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13327 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13328 : colName, format_type_be(targettype)),
13329 : !attTup->attgenerated ?
13330 : /* translator: USING is SQL, don't translate it */
13331 : errhint("You might need to specify \"USING %s::%s\".",
13332 : quote_identifier(colName),
13333 : format_type_with_typemod(targettype,
13334 : targettypmod)) : 0));
13335 : }
13336 :
13337 : /* Fix collations after all else */
13338 936 : assign_expr_collations(pstate, transform);
13339 :
13340 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13341 936 : transform = (Node *) expression_planner((Expr *) transform);
13342 :
13343 : /*
13344 : * Add a work queue item to make ATRewriteTable update the column
13345 : * contents.
13346 : */
13347 936 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13348 936 : newval->attnum = attnum;
13349 936 : newval->expr = (Expr *) transform;
13350 936 : newval->is_generated = false;
13351 :
13352 936 : tab->newvals = lappend(tab->newvals, newval);
13353 936 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13354 752 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13355 : }
13356 136 : else if (transform)
13357 12 : ereport(ERROR,
13358 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13359 : errmsg("\"%s\" is not a table",
13360 : RelationGetRelationName(rel))));
13361 :
13362 1060 : if (!RELKIND_HAS_STORAGE(tab->relkind))
13363 : {
13364 : /*
13365 : * For relations without storage, do this check now. Regular tables
13366 : * will check it later when the table is being rewritten.
13367 : */
13368 184 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13369 : }
13370 :
13371 1030 : ReleaseSysCache(tuple);
13372 :
13373 : /*
13374 : * Recurse manually by queueing a new command for each child, if
13375 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13376 : * remap attribute numbers in the USING expression, if any.
13377 : *
13378 : * If we are told not to recurse, there had better not be any child
13379 : * tables; else the alter would put them out of step.
13380 : */
13381 1030 : if (recurse)
13382 : {
13383 772 : Oid relid = RelationGetRelid(rel);
13384 : List *child_oids,
13385 : *child_numparents;
13386 : ListCell *lo,
13387 : *li;
13388 :
13389 772 : child_oids = find_all_inheritors(relid, lockmode,
13390 : &child_numparents);
13391 :
13392 : /*
13393 : * find_all_inheritors does the recursive search of the inheritance
13394 : * hierarchy, so all we have to do is process all of the relids in the
13395 : * list that it returns.
13396 : */
13397 1752 : forboth(lo, child_oids, li, child_numparents)
13398 : {
13399 1004 : Oid childrelid = lfirst_oid(lo);
13400 1004 : int numparents = lfirst_int(li);
13401 : Relation childrel;
13402 : HeapTuple childtuple;
13403 : Form_pg_attribute childattTup;
13404 :
13405 1004 : if (childrelid == relid)
13406 772 : continue;
13407 :
13408 : /* find_all_inheritors already got lock */
13409 232 : childrel = relation_open(childrelid, NoLock);
13410 232 : CheckAlterTableIsSafe(childrel);
13411 :
13412 : /*
13413 : * Verify that the child doesn't have any inherited definitions of
13414 : * this column that came from outside this inheritance hierarchy.
13415 : * (renameatt makes a similar test, though in a different way
13416 : * because of its different recursion mechanism.)
13417 : */
13418 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13419 : colName);
13420 232 : if (!HeapTupleIsValid(childtuple))
13421 0 : ereport(ERROR,
13422 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13423 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13424 : colName, RelationGetRelationName(childrel))));
13425 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13426 :
13427 232 : if (childattTup->attinhcount > numparents)
13428 6 : ereport(ERROR,
13429 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13430 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13431 : colName, RelationGetRelationName(childrel))));
13432 :
13433 226 : ReleaseSysCache(childtuple);
13434 :
13435 : /*
13436 : * Remap the attribute numbers. If no USING expression was
13437 : * specified, there is no need for this step.
13438 : */
13439 226 : if (def->cooked_default)
13440 : {
13441 : AttrMap *attmap;
13442 : bool found_whole_row;
13443 :
13444 : /* create a copy to scribble on */
13445 78 : cmd = copyObject(cmd);
13446 :
13447 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13448 : RelationGetDescr(rel),
13449 : false);
13450 156 : ((ColumnDef *) cmd->def)->cooked_default =
13451 78 : map_variable_attnos(def->cooked_default,
13452 : 1, 0,
13453 : attmap,
13454 : InvalidOid, &found_whole_row);
13455 78 : if (found_whole_row)
13456 6 : ereport(ERROR,
13457 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13458 : errmsg("cannot convert whole-row table reference"),
13459 : errdetail("USING expression contains a whole-row table reference.")));
13460 72 : pfree(attmap);
13461 : }
13462 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13463 208 : relation_close(childrel, NoLock);
13464 : }
13465 : }
13466 308 : else if (!recursing &&
13467 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13468 0 : ereport(ERROR,
13469 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13470 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13471 : colName)));
13472 :
13473 1006 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13474 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13475 1000 : }
13476 :
13477 : /*
13478 : * When the data type of a column is changed, a rewrite might not be required
13479 : * if the new type is sufficiently identical to the old one, and the USING
13480 : * clause isn't trying to insert some other value. It's safe to skip the
13481 : * rewrite in these cases:
13482 : *
13483 : * - the old type is binary coercible to the new type
13484 : * - the new type is an unconstrained domain over the old type
13485 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13486 : *
13487 : * In the case of a constrained domain, we could get by with scanning the
13488 : * table and checking the constraint rather than actually rewriting it, but we
13489 : * don't currently try to do that.
13490 : */
13491 : static bool
13492 1042 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13493 : {
13494 : Assert(expr != NULL);
13495 :
13496 : for (;;)
13497 : {
13498 : /* only one varno, so no need to check that */
13499 1042 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13500 184 : return false;
13501 858 : else if (IsA(expr, RelabelType))
13502 94 : expr = (Node *) ((RelabelType *) expr)->arg;
13503 764 : else if (IsA(expr, CoerceToDomain))
13504 : {
13505 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
13506 :
13507 0 : if (DomainHasConstraints(d->resulttype))
13508 0 : return true;
13509 0 : expr = (Node *) d->arg;
13510 : }
13511 764 : else if (IsA(expr, FuncExpr))
13512 : {
13513 564 : FuncExpr *f = (FuncExpr *) expr;
13514 :
13515 564 : switch (f->funcid)
13516 : {
13517 18 : case F_TIMESTAMPTZ_TIMESTAMP:
13518 : case F_TIMESTAMP_TIMESTAMPTZ:
13519 18 : if (TimestampTimestampTzRequiresRewrite())
13520 6 : return true;
13521 : else
13522 12 : expr = linitial(f->args);
13523 12 : break;
13524 546 : default:
13525 546 : return true;
13526 : }
13527 : }
13528 : else
13529 200 : return true;
13530 : }
13531 : }
13532 :
13533 : /*
13534 : * ALTER COLUMN .. SET DATA TYPE
13535 : *
13536 : * Return the address of the modified column.
13537 : */
13538 : static ObjectAddress
13539 970 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13540 : AlterTableCmd *cmd, LOCKMODE lockmode)
13541 : {
13542 970 : char *colName = cmd->name;
13543 970 : ColumnDef *def = (ColumnDef *) cmd->def;
13544 970 : TypeName *typeName = def->typeName;
13545 : HeapTuple heapTup;
13546 : Form_pg_attribute attTup,
13547 : attOldTup;
13548 : AttrNumber attnum;
13549 : HeapTuple typeTuple;
13550 : Form_pg_type tform;
13551 : Oid targettype;
13552 : int32 targettypmod;
13553 : Oid targetcollid;
13554 : Node *defaultexpr;
13555 : Relation attrelation;
13556 : Relation depRel;
13557 : ScanKeyData key[3];
13558 : SysScanDesc scan;
13559 : HeapTuple depTup;
13560 : ObjectAddress address;
13561 :
13562 : /*
13563 : * Clear all the missing values if we're rewriting the table, since this
13564 : * renders them pointless.
13565 : */
13566 970 : if (tab->rewrite)
13567 : {
13568 : Relation newrel;
13569 :
13570 698 : newrel = table_open(RelationGetRelid(rel), NoLock);
13571 698 : RelationClearMissing(newrel);
13572 698 : relation_close(newrel, NoLock);
13573 : /* make sure we don't conflict with later attribute modifications */
13574 698 : CommandCounterIncrement();
13575 : }
13576 :
13577 970 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13578 :
13579 : /* Look up the target column */
13580 970 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13581 970 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13582 0 : ereport(ERROR,
13583 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13584 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13585 : colName, RelationGetRelationName(rel))));
13586 970 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13587 970 : attnum = attTup->attnum;
13588 970 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13589 :
13590 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13591 970 : if (attTup->atttypid != attOldTup->atttypid ||
13592 970 : attTup->atttypmod != attOldTup->atttypmod)
13593 0 : ereport(ERROR,
13594 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13595 : errmsg("cannot alter type of column \"%s\" twice",
13596 : colName)));
13597 :
13598 : /* Look up the target type (should not fail, since prep found it) */
13599 970 : typeTuple = typenameType(NULL, typeName, &targettypmod);
13600 970 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
13601 970 : targettype = tform->oid;
13602 : /* And the collation */
13603 970 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13604 :
13605 : /*
13606 : * If there is a default expression for the column, get it and ensure we
13607 : * can coerce it to the new datatype. (We must do this before changing
13608 : * the column type, because build_column_default itself will try to
13609 : * coerce, and will not issue the error message we want if it fails.)
13610 : *
13611 : * We remove any implicit coercion steps at the top level of the old
13612 : * default expression; this has been agreed to satisfy the principle of
13613 : * least surprise. (The conversion to the new column type should act like
13614 : * it started from what the user sees as the stored expression, and the
13615 : * implicit coercions aren't going to be shown.)
13616 : */
13617 970 : if (attTup->atthasdef)
13618 : {
13619 56 : defaultexpr = build_column_default(rel, attnum);
13620 : Assert(defaultexpr);
13621 56 : defaultexpr = strip_implicit_coercions(defaultexpr);
13622 56 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13623 : defaultexpr, exprType(defaultexpr),
13624 : targettype, targettypmod,
13625 : COERCION_ASSIGNMENT,
13626 : COERCE_IMPLICIT_CAST,
13627 : -1);
13628 56 : if (defaultexpr == NULL)
13629 : {
13630 6 : if (attTup->attgenerated)
13631 0 : ereport(ERROR,
13632 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13633 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13634 : colName, format_type_be(targettype))));
13635 : else
13636 6 : ereport(ERROR,
13637 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13638 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13639 : colName, format_type_be(targettype))));
13640 : }
13641 : }
13642 : else
13643 914 : defaultexpr = NULL;
13644 :
13645 : /*
13646 : * Find everything that depends on the column (constraints, indexes, etc),
13647 : * and record enough information to let us recreate the objects.
13648 : *
13649 : * The actual recreation does not happen here, but only after we have
13650 : * performed all the individual ALTER TYPE operations. We have to save
13651 : * the info before executing ALTER TYPE, though, else the deparser will
13652 : * get confused.
13653 : */
13654 964 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13655 :
13656 : /*
13657 : * Now scan for dependencies of this column on other things. The only
13658 : * things we should find are the dependency on the column datatype and
13659 : * possibly a collation dependency. Those can be removed.
13660 : */
13661 940 : depRel = table_open(DependRelationId, RowExclusiveLock);
13662 :
13663 940 : ScanKeyInit(&key[0],
13664 : Anum_pg_depend_classid,
13665 : BTEqualStrategyNumber, F_OIDEQ,
13666 : ObjectIdGetDatum(RelationRelationId));
13667 940 : ScanKeyInit(&key[1],
13668 : Anum_pg_depend_objid,
13669 : BTEqualStrategyNumber, F_OIDEQ,
13670 : ObjectIdGetDatum(RelationGetRelid(rel)));
13671 940 : ScanKeyInit(&key[2],
13672 : Anum_pg_depend_objsubid,
13673 : BTEqualStrategyNumber, F_INT4EQ,
13674 : Int32GetDatum((int32) attnum));
13675 :
13676 940 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
13677 : NULL, 3, key);
13678 :
13679 944 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13680 : {
13681 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13682 : ObjectAddress foundObject;
13683 :
13684 4 : foundObject.classId = foundDep->refclassid;
13685 4 : foundObject.objectId = foundDep->refobjid;
13686 4 : foundObject.objectSubId = foundDep->refobjsubid;
13687 :
13688 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
13689 0 : elog(ERROR, "found unexpected dependency type '%c'",
13690 : foundDep->deptype);
13691 4 : if (!(foundDep->refclassid == TypeRelationId &&
13692 4 : foundDep->refobjid == attTup->atttypid) &&
13693 0 : !(foundDep->refclassid == CollationRelationId &&
13694 0 : foundDep->refobjid == attTup->attcollation))
13695 0 : elog(ERROR, "found unexpected dependency for column: %s",
13696 : getObjectDescription(&foundObject, false));
13697 :
13698 4 : CatalogTupleDelete(depRel, &depTup->t_self);
13699 : }
13700 :
13701 940 : systable_endscan(scan);
13702 :
13703 940 : table_close(depRel, RowExclusiveLock);
13704 :
13705 : /*
13706 : * Here we go --- change the recorded column type and collation. (Note
13707 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13708 : * fix up the missing value if any.
13709 : */
13710 940 : if (attTup->atthasmissing)
13711 : {
13712 : Datum missingval;
13713 : bool missingNull;
13714 :
13715 : /* if rewrite is true the missing value should already be cleared */
13716 : Assert(tab->rewrite == 0);
13717 :
13718 : /* Get the missing value datum */
13719 6 : missingval = heap_getattr(heapTup,
13720 : Anum_pg_attribute_attmissingval,
13721 : attrelation->rd_att,
13722 : &missingNull);
13723 :
13724 : /* if it's a null array there is nothing to do */
13725 :
13726 6 : if (!missingNull)
13727 : {
13728 : /*
13729 : * Get the datum out of the array and repack it in a new array
13730 : * built with the new type data. We assume that since the table
13731 : * doesn't need rewriting, the actual Datum doesn't need to be
13732 : * changed, only the array metadata.
13733 : */
13734 :
13735 6 : int one = 1;
13736 : bool isNull;
13737 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
13738 6 : bool nullsAtt[Natts_pg_attribute] = {0};
13739 6 : bool replacesAtt[Natts_pg_attribute] = {0};
13740 : HeapTuple newTup;
13741 :
13742 12 : missingval = array_get_element(missingval,
13743 : 1,
13744 : &one,
13745 : 0,
13746 6 : attTup->attlen,
13747 6 : attTup->attbyval,
13748 6 : attTup->attalign,
13749 : &isNull);
13750 6 : missingval = PointerGetDatum(construct_array(&missingval,
13751 : 1,
13752 : targettype,
13753 6 : tform->typlen,
13754 6 : tform->typbyval,
13755 6 : tform->typalign));
13756 :
13757 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13758 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13759 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13760 :
13761 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13762 : valuesAtt, nullsAtt, replacesAtt);
13763 6 : heap_freetuple(heapTup);
13764 6 : heapTup = newTup;
13765 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13766 : }
13767 : }
13768 :
13769 940 : attTup->atttypid = targettype;
13770 940 : attTup->atttypmod = targettypmod;
13771 940 : attTup->attcollation = targetcollid;
13772 940 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13773 0 : ereport(ERROR,
13774 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13775 : errmsg("too many array dimensions"));
13776 940 : attTup->attndims = list_length(typeName->arrayBounds);
13777 940 : attTup->attlen = tform->typlen;
13778 940 : attTup->attbyval = tform->typbyval;
13779 940 : attTup->attalign = tform->typalign;
13780 940 : attTup->attstorage = tform->typstorage;
13781 940 : attTup->attcompression = InvalidCompressionMethod;
13782 :
13783 940 : ReleaseSysCache(typeTuple);
13784 :
13785 940 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13786 :
13787 940 : table_close(attrelation, RowExclusiveLock);
13788 :
13789 : /* Install dependencies on new datatype and collation */
13790 940 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13791 940 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13792 :
13793 : /*
13794 : * Drop any pg_statistic entry for the column, since it's now wrong type
13795 : */
13796 940 : RemoveStatistics(RelationGetRelid(rel), attnum);
13797 :
13798 940 : InvokeObjectPostAlterHook(RelationRelationId,
13799 : RelationGetRelid(rel), attnum);
13800 :
13801 : /*
13802 : * Update the default, if present, by brute force --- remove and re-add
13803 : * the default. Probably unsafe to take shortcuts, since the new version
13804 : * may well have additional dependencies. (It's okay to do this now,
13805 : * rather than after other ALTER TYPE commands, since the default won't
13806 : * depend on other column types.)
13807 : */
13808 940 : if (defaultexpr)
13809 : {
13810 : /*
13811 : * If it's a GENERATED default, drop its dependency records, in
13812 : * particular its INTERNAL dependency on the column, which would
13813 : * otherwise cause dependency.c to refuse to perform the deletion.
13814 : */
13815 50 : if (attTup->attgenerated)
13816 : {
13817 6 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13818 :
13819 6 : if (!OidIsValid(attrdefoid))
13820 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13821 : RelationGetRelid(rel), attnum);
13822 6 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13823 : }
13824 :
13825 : /*
13826 : * Make updates-so-far visible, particularly the new pg_attribute row
13827 : * which will be updated again.
13828 : */
13829 50 : CommandCounterIncrement();
13830 :
13831 : /*
13832 : * We use RESTRICT here for safety, but at present we do not expect
13833 : * anything to depend on the default.
13834 : */
13835 50 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13836 : true);
13837 :
13838 50 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13839 : }
13840 :
13841 940 : ObjectAddressSubSet(address, RelationRelationId,
13842 : RelationGetRelid(rel), attnum);
13843 :
13844 : /* Cleanup */
13845 940 : heap_freetuple(heapTup);
13846 :
13847 940 : return address;
13848 : }
13849 :
13850 : /*
13851 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13852 : * that depends on the column (constraints, indexes, etc), and record enough
13853 : * information to let us recreate the objects.
13854 : */
13855 : static void
13856 1042 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13857 : Relation rel, AttrNumber attnum, const char *colName)
13858 : {
13859 : Relation depRel;
13860 : ScanKeyData key[3];
13861 : SysScanDesc scan;
13862 : HeapTuple depTup;
13863 :
13864 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13865 :
13866 1042 : depRel = table_open(DependRelationId, RowExclusiveLock);
13867 :
13868 1042 : ScanKeyInit(&key[0],
13869 : Anum_pg_depend_refclassid,
13870 : BTEqualStrategyNumber, F_OIDEQ,
13871 : ObjectIdGetDatum(RelationRelationId));
13872 1042 : ScanKeyInit(&key[1],
13873 : Anum_pg_depend_refobjid,
13874 : BTEqualStrategyNumber, F_OIDEQ,
13875 : ObjectIdGetDatum(RelationGetRelid(rel)));
13876 1042 : ScanKeyInit(&key[2],
13877 : Anum_pg_depend_refobjsubid,
13878 : BTEqualStrategyNumber, F_INT4EQ,
13879 : Int32GetDatum((int32) attnum));
13880 :
13881 1042 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13882 : NULL, 3, key);
13883 :
13884 2132 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13885 : {
13886 1114 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13887 : ObjectAddress foundObject;
13888 :
13889 1114 : foundObject.classId = foundDep->classid;
13890 1114 : foundObject.objectId = foundDep->objid;
13891 1114 : foundObject.objectSubId = foundDep->objsubid;
13892 :
13893 1114 : switch (foundObject.classId)
13894 : {
13895 274 : case RelationRelationId:
13896 : {
13897 274 : char relKind = get_rel_relkind(foundObject.objectId);
13898 :
13899 274 : if (relKind == RELKIND_INDEX ||
13900 : relKind == RELKIND_PARTITIONED_INDEX)
13901 : {
13902 : Assert(foundObject.objectSubId == 0);
13903 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
13904 : }
13905 38 : else if (relKind == RELKIND_SEQUENCE)
13906 : {
13907 : /*
13908 : * This must be a SERIAL column's sequence. We need
13909 : * not do anything to it.
13910 : */
13911 : Assert(foundObject.objectSubId == 0);
13912 : }
13913 : else
13914 : {
13915 : /* Not expecting any other direct dependencies... */
13916 0 : elog(ERROR, "unexpected object depending on column: %s",
13917 : getObjectDescription(&foundObject, false));
13918 : }
13919 274 : break;
13920 : }
13921 :
13922 674 : case ConstraintRelationId:
13923 : Assert(foundObject.objectSubId == 0);
13924 674 : RememberConstraintForRebuilding(foundObject.objectId, tab);
13925 674 : break;
13926 :
13927 0 : case ProcedureRelationId:
13928 :
13929 : /*
13930 : * A new-style SQL function can depend on a column, if that
13931 : * column is referenced in the parsed function body. Ideally
13932 : * we'd automatically update the function by deparsing and
13933 : * reparsing it, but that's risky and might well fail anyhow.
13934 : * FIXME someday.
13935 : *
13936 : * This is only a problem for AT_AlterColumnType, not
13937 : * AT_SetExpression.
13938 : */
13939 0 : if (subtype == AT_AlterColumnType)
13940 0 : ereport(ERROR,
13941 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13942 : errmsg("cannot alter type of a column used by a function or procedure"),
13943 : errdetail("%s depends on column \"%s\"",
13944 : getObjectDescription(&foundObject, false),
13945 : colName)));
13946 0 : break;
13947 :
13948 12 : case RewriteRelationId:
13949 :
13950 : /*
13951 : * View/rule bodies have pretty much the same issues as
13952 : * function bodies. FIXME someday.
13953 : */
13954 12 : if (subtype == AT_AlterColumnType)
13955 12 : ereport(ERROR,
13956 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13957 : errmsg("cannot alter type of a column used by a view or rule"),
13958 : errdetail("%s depends on column \"%s\"",
13959 : getObjectDescription(&foundObject, false),
13960 : colName)));
13961 0 : break;
13962 :
13963 0 : case TriggerRelationId:
13964 :
13965 : /*
13966 : * A trigger can depend on a column because the column is
13967 : * specified as an update target, or because the column is
13968 : * used in the trigger's WHEN condition. The first case would
13969 : * not require any extra work, but the second case would
13970 : * require updating the WHEN expression, which has the same
13971 : * issues as above. Since we can't easily tell which case
13972 : * applies, we punt for both. FIXME someday.
13973 : */
13974 0 : if (subtype == AT_AlterColumnType)
13975 0 : ereport(ERROR,
13976 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13977 : errmsg("cannot alter type of a column used in a trigger definition"),
13978 : errdetail("%s depends on column \"%s\"",
13979 : getObjectDescription(&foundObject, false),
13980 : colName)));
13981 0 : break;
13982 :
13983 0 : case PolicyRelationId:
13984 :
13985 : /*
13986 : * A policy can depend on a column because the column is
13987 : * specified in the policy's USING or WITH CHECK qual
13988 : * expressions. It might be possible to rewrite and recheck
13989 : * the policy expression, but punt for now. It's certainly
13990 : * easy enough to remove and recreate the policy; still, FIXME
13991 : * someday.
13992 : */
13993 0 : if (subtype == AT_AlterColumnType)
13994 0 : ereport(ERROR,
13995 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13996 : errmsg("cannot alter type of a column used in a policy definition"),
13997 : errdetail("%s depends on column \"%s\"",
13998 : getObjectDescription(&foundObject, false),
13999 : colName)));
14000 0 : break;
14001 :
14002 140 : case AttrDefaultRelationId:
14003 : {
14004 140 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14005 :
14006 140 : if (col.objectId == RelationGetRelid(rel) &&
14007 140 : col.objectSubId == attnum)
14008 : {
14009 : /*
14010 : * Ignore the column's own default expression. The
14011 : * caller deals with it.
14012 : */
14013 : }
14014 : else
14015 : {
14016 : /*
14017 : * This must be a reference from the expression of a
14018 : * generated column elsewhere in the same table.
14019 : * Changing the type/generated expression of a column
14020 : * that is used by a generated column is not allowed
14021 : * by SQL standard, so just punt for now. It might be
14022 : * doable with some thinking and effort.
14023 : */
14024 12 : if (subtype == AT_AlterColumnType)
14025 12 : ereport(ERROR,
14026 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14027 : errmsg("cannot alter type of a column used by a generated column"),
14028 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14029 : colName,
14030 : get_attname(col.objectId,
14031 : col.objectSubId,
14032 : false))));
14033 : }
14034 128 : break;
14035 : }
14036 :
14037 14 : case StatisticExtRelationId:
14038 :
14039 : /*
14040 : * Give the extended-stats machinery a chance to fix anything
14041 : * that this column type change would break.
14042 : */
14043 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14044 14 : break;
14045 :
14046 0 : case PublicationRelRelationId:
14047 :
14048 : /*
14049 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14050 : * clause. Same issues as above. FIXME someday.
14051 : */
14052 0 : if (subtype == AT_AlterColumnType)
14053 0 : ereport(ERROR,
14054 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14055 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
14056 : errdetail("%s depends on column \"%s\"",
14057 : getObjectDescription(&foundObject, false),
14058 : colName)));
14059 0 : break;
14060 :
14061 0 : default:
14062 :
14063 : /*
14064 : * We don't expect any other sorts of objects to depend on a
14065 : * column.
14066 : */
14067 0 : elog(ERROR, "unexpected object depending on column: %s",
14068 : getObjectDescription(&foundObject, false));
14069 : break;
14070 : }
14071 : }
14072 :
14073 1018 : systable_endscan(scan);
14074 1018 : table_close(depRel, NoLock);
14075 1018 : }
14076 :
14077 : /*
14078 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14079 : * needs to be reset.
14080 : */
14081 : static void
14082 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14083 : {
14084 444 : if (!get_index_isreplident(indoid))
14085 426 : return;
14086 :
14087 18 : if (tab->replicaIdentityIndex)
14088 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14089 :
14090 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14091 : }
14092 :
14093 : /*
14094 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14095 : */
14096 : static void
14097 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14098 : {
14099 444 : if (!get_index_isclustered(indoid))
14100 426 : return;
14101 :
14102 18 : if (tab->clusterOnIndex)
14103 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14104 :
14105 18 : tab->clusterOnIndex = get_rel_name(indoid);
14106 : }
14107 :
14108 : /*
14109 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14110 : * to be rebuilt (which we might already know).
14111 : */
14112 : static void
14113 686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14114 : {
14115 : /*
14116 : * This de-duplication check is critical for two independent reasons: we
14117 : * mustn't try to recreate the same constraint twice, and if a constraint
14118 : * depends on more than one column whose type is to be altered, we must
14119 : * capture its definition string before applying any of the column type
14120 : * changes. ruleutils.c will get confused if we ask again later.
14121 : */
14122 686 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14123 : {
14124 : /* OK, capture the constraint's existing definition string */
14125 596 : char *defstring = pg_get_constraintdef_command(conoid);
14126 : Oid indoid;
14127 :
14128 : /*
14129 : * It is critical to create not-null constraints ahead of primary key
14130 : * indexes; otherwise, the not-null constraint would be created by the
14131 : * primary key, and the constraint name would be wrong.
14132 : */
14133 596 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14134 : {
14135 198 : tab->changedConstraintOids = lcons_oid(conoid,
14136 : tab->changedConstraintOids);
14137 198 : tab->changedConstraintDefs = lcons(defstring,
14138 : tab->changedConstraintDefs);
14139 : }
14140 : else
14141 : {
14142 :
14143 398 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14144 : conoid);
14145 398 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14146 : defstring);
14147 : }
14148 :
14149 : /*
14150 : * For the index of a constraint, if any, remember if it is used for
14151 : * the table's replica identity or if it is a clustered index, so that
14152 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14153 : * those properties.
14154 : */
14155 596 : indoid = get_constraint_index(conoid);
14156 596 : if (OidIsValid(indoid))
14157 : {
14158 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
14159 228 : RememberClusterOnForRebuilding(indoid, tab);
14160 : }
14161 : }
14162 686 : }
14163 :
14164 : /*
14165 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14166 : * to be rebuilt (which we might already know).
14167 : */
14168 : static void
14169 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14170 : {
14171 : /*
14172 : * This de-duplication check is critical for two independent reasons: we
14173 : * mustn't try to recreate the same index twice, and if an index depends
14174 : * on more than one column whose type is to be altered, we must capture
14175 : * its definition string before applying any of the column type changes.
14176 : * ruleutils.c will get confused if we ask again later.
14177 : */
14178 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
14179 : {
14180 : /*
14181 : * Before adding it as an index-to-rebuild, we'd better see if it
14182 : * belongs to a constraint, and if so rebuild the constraint instead.
14183 : * Typically this check fails, because constraint indexes normally
14184 : * have only dependencies on their constraint. But it's possible for
14185 : * such an index to also have direct dependencies on table columns,
14186 : * for example with a partial exclusion constraint.
14187 : */
14188 228 : Oid conoid = get_index_constraint(indoid);
14189 :
14190 228 : if (OidIsValid(conoid))
14191 : {
14192 12 : RememberConstraintForRebuilding(conoid, tab);
14193 : }
14194 : else
14195 : {
14196 : /* OK, capture the index's existing definition string */
14197 216 : char *defstring = pg_get_indexdef_string(indoid);
14198 :
14199 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14200 : indoid);
14201 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14202 : defstring);
14203 :
14204 : /*
14205 : * Remember if this index is used for the table's replica identity
14206 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14207 : * can queue up commands necessary to restore those properties.
14208 : */
14209 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
14210 216 : RememberClusterOnForRebuilding(indoid, tab);
14211 : }
14212 : }
14213 236 : }
14214 :
14215 : /*
14216 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14217 : * needs to be rebuilt (which we might already know).
14218 : */
14219 : static void
14220 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14221 : {
14222 : /*
14223 : * This de-duplication check is critical for two independent reasons: we
14224 : * mustn't try to recreate the same statistics object twice, and if the
14225 : * statistics object depends on more than one column whose type is to be
14226 : * altered, we must capture its definition string before applying any of
14227 : * the type changes. ruleutils.c will get confused if we ask again later.
14228 : */
14229 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14230 : {
14231 : /* OK, capture the statistics object's existing definition string */
14232 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14233 :
14234 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14235 : stxoid);
14236 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14237 : defstring);
14238 : }
14239 14 : }
14240 :
14241 : /*
14242 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14243 : * operations for a particular relation. We have to drop and recreate all the
14244 : * indexes and constraints that depend on the altered columns. We do the
14245 : * actual dropping here, but re-creation is managed by adding work queue
14246 : * entries to do those steps later.
14247 : */
14248 : static void
14249 988 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14250 : {
14251 : ObjectAddress obj;
14252 : ObjectAddresses *objects;
14253 : ListCell *def_item;
14254 : ListCell *oid_item;
14255 :
14256 : /*
14257 : * Collect all the constraints and indexes to drop so we can process them
14258 : * in a single call. That way we don't have to worry about dependencies
14259 : * among them.
14260 : */
14261 988 : objects = new_object_addresses();
14262 :
14263 : /*
14264 : * Re-parse the index and constraint definitions, and attach them to the
14265 : * appropriate work queue entries. We do this before dropping because in
14266 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14267 : * lock on the table the constraint is attached to, and we need to get
14268 : * that before reparsing/dropping.
14269 : *
14270 : * We can't rely on the output of deparsing to tell us which relation to
14271 : * operate on, because concurrent activity might have made the name
14272 : * resolve differently. Instead, we've got to use the OID of the
14273 : * constraint or index we're processing to figure out which relation to
14274 : * operate on.
14275 : */
14276 1584 : forboth(oid_item, tab->changedConstraintOids,
14277 : def_item, tab->changedConstraintDefs)
14278 : {
14279 596 : Oid oldId = lfirst_oid(oid_item);
14280 : HeapTuple tup;
14281 : Form_pg_constraint con;
14282 : Oid relid;
14283 : Oid confrelid;
14284 : char contype;
14285 : bool conislocal;
14286 :
14287 596 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14288 596 : if (!HeapTupleIsValid(tup)) /* should not happen */
14289 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14290 596 : con = (Form_pg_constraint) GETSTRUCT(tup);
14291 596 : if (OidIsValid(con->conrelid))
14292 582 : relid = con->conrelid;
14293 : else
14294 : {
14295 : /* must be a domain constraint */
14296 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14297 14 : if (!OidIsValid(relid))
14298 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14299 : }
14300 596 : confrelid = con->confrelid;
14301 596 : contype = con->contype;
14302 596 : conislocal = con->conislocal;
14303 596 : ReleaseSysCache(tup);
14304 :
14305 596 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14306 596 : add_exact_object_address(&obj, objects);
14307 :
14308 : /*
14309 : * If the constraint is inherited (only), we don't want to inject a
14310 : * new definition here; it'll get recreated when
14311 : * ATAddCheckNNConstraint recurses from adding the parent table's
14312 : * constraint. But we had to carry the info this far so that we can
14313 : * drop the constraint below.
14314 : */
14315 596 : if (!conislocal)
14316 28 : continue;
14317 :
14318 : /*
14319 : * When rebuilding an FK constraint that references the table we're
14320 : * modifying, we might not yet have any lock on the FK's table, so get
14321 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14322 : * step, so there's no value in asking for anything weaker.
14323 : */
14324 568 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14325 36 : LockRelationOid(relid, AccessExclusiveLock);
14326 :
14327 568 : ATPostAlterTypeParse(oldId, relid, confrelid,
14328 568 : (char *) lfirst(def_item),
14329 568 : wqueue, lockmode, tab->rewrite);
14330 : }
14331 1204 : forboth(oid_item, tab->changedIndexOids,
14332 : def_item, tab->changedIndexDefs)
14333 : {
14334 216 : Oid oldId = lfirst_oid(oid_item);
14335 : Oid relid;
14336 :
14337 216 : relid = IndexGetRelation(oldId, false);
14338 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14339 216 : (char *) lfirst(def_item),
14340 216 : wqueue, lockmode, tab->rewrite);
14341 :
14342 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
14343 216 : add_exact_object_address(&obj, objects);
14344 : }
14345 :
14346 : /* add dependencies for new statistics */
14347 1002 : forboth(oid_item, tab->changedStatisticsOids,
14348 : def_item, tab->changedStatisticsDefs)
14349 : {
14350 14 : Oid oldId = lfirst_oid(oid_item);
14351 : Oid relid;
14352 :
14353 14 : relid = StatisticsGetRelation(oldId, false);
14354 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14355 14 : (char *) lfirst(def_item),
14356 14 : wqueue, lockmode, tab->rewrite);
14357 :
14358 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14359 14 : add_exact_object_address(&obj, objects);
14360 : }
14361 :
14362 : /*
14363 : * Queue up command to restore replica identity index marking
14364 : */
14365 988 : if (tab->replicaIdentityIndex)
14366 : {
14367 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14368 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14369 :
14370 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14371 18 : subcmd->name = tab->replicaIdentityIndex;
14372 18 : cmd->subtype = AT_ReplicaIdentity;
14373 18 : cmd->def = (Node *) subcmd;
14374 :
14375 : /* do it after indexes and constraints */
14376 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14377 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14378 : }
14379 :
14380 : /*
14381 : * Queue up command to restore marking of index used for cluster.
14382 : */
14383 988 : if (tab->clusterOnIndex)
14384 : {
14385 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14386 :
14387 18 : cmd->subtype = AT_ClusterOn;
14388 18 : cmd->name = tab->clusterOnIndex;
14389 :
14390 : /* do it after indexes and constraints */
14391 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14392 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14393 : }
14394 :
14395 : /*
14396 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14397 : * be depending on these objects.
14398 : */
14399 988 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14400 :
14401 988 : free_object_addresses(objects);
14402 :
14403 : /*
14404 : * The objects will get recreated during subsequent passes over the work
14405 : * queue.
14406 : */
14407 988 : }
14408 :
14409 : /*
14410 : * Parse the previously-saved definition string for a constraint, index or
14411 : * statistics object against the newly-established column data type(s), and
14412 : * queue up the resulting command parsetrees for execution.
14413 : *
14414 : * This might fail if, for example, you have a WHERE clause that uses an
14415 : * operator that's not available for the new column type.
14416 : */
14417 : static void
14418 798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14419 : List **wqueue, LOCKMODE lockmode, bool rewrite)
14420 : {
14421 : List *raw_parsetree_list;
14422 : List *querytree_list;
14423 : ListCell *list_item;
14424 : Relation rel;
14425 :
14426 : /*
14427 : * We expect that we will get only ALTER TABLE and CREATE INDEX
14428 : * statements. Hence, there is no need to pass them through
14429 : * parse_analyze_*() or the rewriter, but instead we need to pass them
14430 : * through parse_utilcmd.c to make them ready for execution.
14431 : */
14432 798 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14433 798 : querytree_list = NIL;
14434 1596 : foreach(list_item, raw_parsetree_list)
14435 : {
14436 798 : RawStmt *rs = lfirst_node(RawStmt, list_item);
14437 798 : Node *stmt = rs->stmt;
14438 :
14439 798 : if (IsA(stmt, IndexStmt))
14440 216 : querytree_list = lappend(querytree_list,
14441 216 : transformIndexStmt(oldRelId,
14442 : (IndexStmt *) stmt,
14443 : cmd));
14444 582 : else if (IsA(stmt, AlterTableStmt))
14445 : {
14446 : List *beforeStmts;
14447 : List *afterStmts;
14448 :
14449 554 : stmt = (Node *) transformAlterTableStmt(oldRelId,
14450 : (AlterTableStmt *) stmt,
14451 : cmd,
14452 : &beforeStmts,
14453 : &afterStmts);
14454 554 : querytree_list = list_concat(querytree_list, beforeStmts);
14455 554 : querytree_list = lappend(querytree_list, stmt);
14456 554 : querytree_list = list_concat(querytree_list, afterStmts);
14457 : }
14458 28 : else if (IsA(stmt, CreateStatsStmt))
14459 14 : querytree_list = lappend(querytree_list,
14460 14 : transformStatsStmt(oldRelId,
14461 : (CreateStatsStmt *) stmt,
14462 : cmd));
14463 : else
14464 14 : querytree_list = lappend(querytree_list, stmt);
14465 : }
14466 :
14467 : /* Caller should already have acquired whatever lock we need. */
14468 798 : rel = relation_open(oldRelId, NoLock);
14469 :
14470 : /*
14471 : * Attach each generated command to the proper place in the work queue.
14472 : * Note this could result in creation of entirely new work-queue entries.
14473 : *
14474 : * Also note that we have to tweak the command subtypes, because it turns
14475 : * out that re-creation of indexes and constraints has to act a bit
14476 : * differently from initial creation.
14477 : */
14478 1596 : foreach(list_item, querytree_list)
14479 : {
14480 798 : Node *stm = (Node *) lfirst(list_item);
14481 : AlteredTableInfo *tab;
14482 :
14483 798 : tab = ATGetQueueEntry(wqueue, rel);
14484 :
14485 798 : if (IsA(stm, IndexStmt))
14486 : {
14487 216 : IndexStmt *stmt = (IndexStmt *) stm;
14488 : AlterTableCmd *newcmd;
14489 :
14490 216 : if (!rewrite)
14491 56 : TryReuseIndex(oldId, stmt);
14492 216 : stmt->reset_default_tblspc = true;
14493 : /* keep the index's comment */
14494 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14495 :
14496 216 : newcmd = makeNode(AlterTableCmd);
14497 216 : newcmd->subtype = AT_ReAddIndex;
14498 216 : newcmd->def = (Node *) stmt;
14499 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
14500 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14501 : }
14502 582 : else if (IsA(stm, AlterTableStmt))
14503 : {
14504 554 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14505 : ListCell *lcmd;
14506 :
14507 1108 : foreach(lcmd, stmt->cmds)
14508 : {
14509 554 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14510 :
14511 554 : if (cmd->subtype == AT_AddIndex)
14512 : {
14513 : IndexStmt *indstmt;
14514 : Oid indoid;
14515 :
14516 228 : indstmt = castNode(IndexStmt, cmd->def);
14517 228 : indoid = get_constraint_index(oldId);
14518 :
14519 228 : if (!rewrite)
14520 48 : TryReuseIndex(indoid, indstmt);
14521 : /* keep any comment on the index */
14522 228 : indstmt->idxcomment = GetComment(indoid,
14523 : RelationRelationId, 0);
14524 228 : indstmt->reset_default_tblspc = true;
14525 :
14526 228 : cmd->subtype = AT_ReAddIndex;
14527 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
14528 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14529 :
14530 : /* recreate any comment on the constraint */
14531 228 : RebuildConstraintComment(tab,
14532 : AT_PASS_OLD_INDEX,
14533 : oldId,
14534 : rel,
14535 : NIL,
14536 228 : indstmt->idxname);
14537 : }
14538 326 : else if (cmd->subtype == AT_AddConstraint)
14539 : {
14540 326 : Constraint *con = castNode(Constraint, cmd->def);
14541 :
14542 326 : con->old_pktable_oid = refRelId;
14543 : /* rewriting neither side of a FK */
14544 326 : if (con->contype == CONSTR_FOREIGN &&
14545 72 : !rewrite && tab->rewrite == 0)
14546 6 : TryReuseForeignKey(oldId, con);
14547 326 : con->reset_default_tblspc = true;
14548 326 : cmd->subtype = AT_ReAddConstraint;
14549 326 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14550 326 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14551 :
14552 : /*
14553 : * Recreate any comment on the constraint. If we have
14554 : * recreated a primary key, then transformTableConstraint
14555 : * has added an unnamed not-null constraint here; skip
14556 : * this in that case.
14557 : */
14558 326 : if (con->conname)
14559 326 : RebuildConstraintComment(tab,
14560 : AT_PASS_OLD_CONSTR,
14561 : oldId,
14562 : rel,
14563 : NIL,
14564 326 : con->conname);
14565 : else
14566 : Assert(con->contype == CONSTR_NOTNULL);
14567 : }
14568 : else
14569 0 : elog(ERROR, "unexpected statement subtype: %d",
14570 : (int) cmd->subtype);
14571 : }
14572 : }
14573 28 : else if (IsA(stm, AlterDomainStmt))
14574 : {
14575 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14576 :
14577 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14578 : {
14579 14 : Constraint *con = castNode(Constraint, stmt->def);
14580 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14581 :
14582 14 : cmd->subtype = AT_ReAddDomainConstraint;
14583 14 : cmd->def = (Node *) stmt;
14584 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14585 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14586 :
14587 : /* recreate any comment on the constraint */
14588 14 : RebuildConstraintComment(tab,
14589 : AT_PASS_OLD_CONSTR,
14590 : oldId,
14591 : NULL,
14592 : stmt->typeName,
14593 14 : con->conname);
14594 : }
14595 : else
14596 0 : elog(ERROR, "unexpected statement subtype: %d",
14597 : (int) stmt->subtype);
14598 : }
14599 14 : else if (IsA(stm, CreateStatsStmt))
14600 : {
14601 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14602 : AlterTableCmd *newcmd;
14603 :
14604 : /* keep the statistics object's comment */
14605 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14606 :
14607 14 : newcmd = makeNode(AlterTableCmd);
14608 14 : newcmd->subtype = AT_ReAddStatistics;
14609 14 : newcmd->def = (Node *) stmt;
14610 14 : tab->subcmds[AT_PASS_MISC] =
14611 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14612 : }
14613 : else
14614 0 : elog(ERROR, "unexpected statement type: %d",
14615 : (int) nodeTag(stm));
14616 : }
14617 :
14618 798 : relation_close(rel, NoLock);
14619 798 : }
14620 :
14621 : /*
14622 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14623 : * for a table or domain constraint that is being rebuilt.
14624 : *
14625 : * objid is the OID of the constraint.
14626 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14627 : * as a string list) for a domain constraint.
14628 : * (We could dig that info, as well as the conname, out of the pg_constraint
14629 : * entry; but callers already have them so might as well pass them.)
14630 : */
14631 : static void
14632 568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14633 : Relation rel, List *domname,
14634 : const char *conname)
14635 : {
14636 : CommentStmt *cmd;
14637 : char *comment_str;
14638 : AlterTableCmd *newcmd;
14639 :
14640 : /* Look for comment for object wanted, and leave if none */
14641 568 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14642 568 : if (comment_str == NULL)
14643 478 : return;
14644 :
14645 : /* Build CommentStmt node, copying all input data for safety */
14646 90 : cmd = makeNode(CommentStmt);
14647 90 : if (rel)
14648 : {
14649 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
14650 78 : cmd->object = (Node *)
14651 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14652 : makeString(pstrdup(RelationGetRelationName(rel))),
14653 : makeString(pstrdup(conname)));
14654 : }
14655 : else
14656 : {
14657 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
14658 12 : cmd->object = (Node *)
14659 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
14660 : makeString(pstrdup(conname)));
14661 : }
14662 90 : cmd->comment = comment_str;
14663 :
14664 : /* Append it to list of commands */
14665 90 : newcmd = makeNode(AlterTableCmd);
14666 90 : newcmd->subtype = AT_ReAddComment;
14667 90 : newcmd->def = (Node *) cmd;
14668 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14669 : }
14670 :
14671 : /*
14672 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14673 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14674 : */
14675 : static void
14676 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14677 : {
14678 104 : if (CheckIndexCompatible(oldId,
14679 104 : stmt->accessMethod,
14680 104 : stmt->indexParams,
14681 104 : stmt->excludeOpNames,
14682 104 : stmt->iswithoutoverlaps))
14683 : {
14684 104 : Relation irel = index_open(oldId, NoLock);
14685 :
14686 : /* If it's a partitioned index, there is no storage to share. */
14687 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14688 : {
14689 74 : stmt->oldNumber = irel->rd_locator.relNumber;
14690 74 : stmt->oldCreateSubid = irel->rd_createSubid;
14691 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14692 : }
14693 104 : index_close(irel, NoLock);
14694 : }
14695 104 : }
14696 :
14697 : /*
14698 : * Subroutine for ATPostAlterTypeParse().
14699 : *
14700 : * Stash the old P-F equality operator into the Constraint node, for possible
14701 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14702 : * this constraint can be skipped.
14703 : */
14704 : static void
14705 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
14706 : {
14707 : HeapTuple tup;
14708 : Datum adatum;
14709 : ArrayType *arr;
14710 : Oid *rawarr;
14711 : int numkeys;
14712 : int i;
14713 :
14714 : Assert(con->contype == CONSTR_FOREIGN);
14715 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14716 :
14717 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14718 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
14719 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14720 :
14721 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14722 : Anum_pg_constraint_conpfeqop);
14723 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14724 6 : numkeys = ARR_DIMS(arr)[0];
14725 : /* test follows the one in ri_FetchConstraintInfo() */
14726 6 : if (ARR_NDIM(arr) != 1 ||
14727 6 : ARR_HASNULL(arr) ||
14728 6 : ARR_ELEMTYPE(arr) != OIDOID)
14729 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
14730 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14731 :
14732 : /* stash a List of the operator Oids in our Constraint node */
14733 12 : for (i = 0; i < numkeys; i++)
14734 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14735 :
14736 6 : ReleaseSysCache(tup);
14737 6 : }
14738 :
14739 : /*
14740 : * ALTER COLUMN .. OPTIONS ( ... )
14741 : *
14742 : * Returns the address of the modified column
14743 : */
14744 : static ObjectAddress
14745 164 : ATExecAlterColumnGenericOptions(Relation rel,
14746 : const char *colName,
14747 : List *options,
14748 : LOCKMODE lockmode)
14749 : {
14750 : Relation ftrel;
14751 : Relation attrel;
14752 : ForeignServer *server;
14753 : ForeignDataWrapper *fdw;
14754 : HeapTuple tuple;
14755 : HeapTuple newtuple;
14756 : bool isnull;
14757 : Datum repl_val[Natts_pg_attribute];
14758 : bool repl_null[Natts_pg_attribute];
14759 : bool repl_repl[Natts_pg_attribute];
14760 : Datum datum;
14761 : Form_pg_foreign_table fttableform;
14762 : Form_pg_attribute atttableform;
14763 : AttrNumber attnum;
14764 : ObjectAddress address;
14765 :
14766 164 : if (options == NIL)
14767 0 : return InvalidObjectAddress;
14768 :
14769 : /* First, determine FDW validator associated to the foreign table. */
14770 164 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14771 164 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14772 164 : if (!HeapTupleIsValid(tuple))
14773 0 : ereport(ERROR,
14774 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14775 : errmsg("foreign table \"%s\" does not exist",
14776 : RelationGetRelationName(rel))));
14777 164 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14778 164 : server = GetForeignServer(fttableform->ftserver);
14779 164 : fdw = GetForeignDataWrapper(server->fdwid);
14780 :
14781 164 : table_close(ftrel, AccessShareLock);
14782 164 : ReleaseSysCache(tuple);
14783 :
14784 164 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
14785 164 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14786 164 : if (!HeapTupleIsValid(tuple))
14787 0 : ereport(ERROR,
14788 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14789 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14790 : colName, RelationGetRelationName(rel))));
14791 :
14792 : /* Prevent them from altering a system attribute */
14793 164 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14794 164 : attnum = atttableform->attnum;
14795 164 : if (attnum <= 0)
14796 6 : ereport(ERROR,
14797 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14798 : errmsg("cannot alter system column \"%s\"", colName)));
14799 :
14800 :
14801 : /* Initialize buffers for new tuple values */
14802 158 : memset(repl_val, 0, sizeof(repl_val));
14803 158 : memset(repl_null, false, sizeof(repl_null));
14804 158 : memset(repl_repl, false, sizeof(repl_repl));
14805 :
14806 : /* Extract the current options */
14807 158 : datum = SysCacheGetAttr(ATTNAME,
14808 : tuple,
14809 : Anum_pg_attribute_attfdwoptions,
14810 : &isnull);
14811 158 : if (isnull)
14812 148 : datum = PointerGetDatum(NULL);
14813 :
14814 : /* Transform the options */
14815 158 : datum = transformGenericOptions(AttributeRelationId,
14816 : datum,
14817 : options,
14818 : fdw->fdwvalidator);
14819 :
14820 158 : if (PointerIsValid(DatumGetPointer(datum)))
14821 158 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14822 : else
14823 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14824 :
14825 158 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14826 :
14827 : /* Everything looks good - update the tuple */
14828 :
14829 158 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14830 : repl_val, repl_null, repl_repl);
14831 :
14832 158 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14833 :
14834 158 : InvokeObjectPostAlterHook(RelationRelationId,
14835 : RelationGetRelid(rel),
14836 : atttableform->attnum);
14837 158 : ObjectAddressSubSet(address, RelationRelationId,
14838 : RelationGetRelid(rel), attnum);
14839 :
14840 158 : ReleaseSysCache(tuple);
14841 :
14842 158 : table_close(attrel, RowExclusiveLock);
14843 :
14844 158 : heap_freetuple(newtuple);
14845 :
14846 158 : return address;
14847 : }
14848 :
14849 : /*
14850 : * ALTER TABLE OWNER
14851 : *
14852 : * recursing is true if we are recursing from a table to its indexes,
14853 : * sequences, or toast table. We don't allow the ownership of those things to
14854 : * be changed separately from the parent table. Also, we can skip permission
14855 : * checks (this is necessary not just an optimization, else we'd fail to
14856 : * handle toast tables properly).
14857 : *
14858 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14859 : * free-standing composite type.
14860 : */
14861 : void
14862 2020 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14863 : {
14864 : Relation target_rel;
14865 : Relation class_rel;
14866 : HeapTuple tuple;
14867 : Form_pg_class tuple_class;
14868 :
14869 : /*
14870 : * Get exclusive lock till end of transaction on the target table. Use
14871 : * relation_open so that we can work on indexes and sequences.
14872 : */
14873 2020 : target_rel = relation_open(relationOid, lockmode);
14874 :
14875 : /* Get its pg_class tuple, too */
14876 2020 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
14877 :
14878 2020 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14879 2020 : if (!HeapTupleIsValid(tuple))
14880 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
14881 2020 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14882 :
14883 : /* Can we change the ownership of this tuple? */
14884 2020 : switch (tuple_class->relkind)
14885 : {
14886 1754 : case RELKIND_RELATION:
14887 : case RELKIND_VIEW:
14888 : case RELKIND_MATVIEW:
14889 : case RELKIND_FOREIGN_TABLE:
14890 : case RELKIND_PARTITIONED_TABLE:
14891 : /* ok to change owner */
14892 1754 : break;
14893 96 : case RELKIND_INDEX:
14894 96 : if (!recursing)
14895 : {
14896 : /*
14897 : * Because ALTER INDEX OWNER used to be allowed, and in fact
14898 : * is generated by old versions of pg_dump, we give a warning
14899 : * and do nothing rather than erroring out. Also, to avoid
14900 : * unnecessary chatter while restoring those old dumps, say
14901 : * nothing at all if the command would be a no-op anyway.
14902 : */
14903 0 : if (tuple_class->relowner != newOwnerId)
14904 0 : ereport(WARNING,
14905 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14906 : errmsg("cannot change owner of index \"%s\"",
14907 : NameStr(tuple_class->relname)),
14908 : errhint("Change the ownership of the index's table instead.")));
14909 : /* quick hack to exit via the no-op path */
14910 0 : newOwnerId = tuple_class->relowner;
14911 : }
14912 96 : break;
14913 20 : case RELKIND_PARTITIONED_INDEX:
14914 20 : if (recursing)
14915 20 : break;
14916 0 : ereport(ERROR,
14917 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14918 : errmsg("cannot change owner of index \"%s\"",
14919 : NameStr(tuple_class->relname)),
14920 : errhint("Change the ownership of the index's table instead.")));
14921 : break;
14922 100 : case RELKIND_SEQUENCE:
14923 100 : if (!recursing &&
14924 52 : tuple_class->relowner != newOwnerId)
14925 : {
14926 : /* if it's an owned sequence, disallow changing it by itself */
14927 : Oid tableId;
14928 : int32 colId;
14929 :
14930 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14931 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
14932 0 : ereport(ERROR,
14933 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14934 : errmsg("cannot change owner of sequence \"%s\"",
14935 : NameStr(tuple_class->relname)),
14936 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
14937 : NameStr(tuple_class->relname),
14938 : get_rel_name(tableId))));
14939 : }
14940 100 : break;
14941 8 : case RELKIND_COMPOSITE_TYPE:
14942 8 : if (recursing)
14943 8 : break;
14944 0 : ereport(ERROR,
14945 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14946 : errmsg("\"%s\" is a composite type",
14947 : NameStr(tuple_class->relname)),
14948 : /* translator: %s is an SQL ALTER command */
14949 : errhint("Use %s instead.",
14950 : "ALTER TYPE")));
14951 : break;
14952 42 : case RELKIND_TOASTVALUE:
14953 42 : if (recursing)
14954 42 : break;
14955 : /* FALL THRU */
14956 : default:
14957 0 : ereport(ERROR,
14958 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14959 : errmsg("cannot change owner of relation \"%s\"",
14960 : NameStr(tuple_class->relname)),
14961 : errdetail_relkind_not_supported(tuple_class->relkind)));
14962 : }
14963 :
14964 : /*
14965 : * If the new owner is the same as the existing owner, consider the
14966 : * command to have succeeded. This is for dump restoration purposes.
14967 : */
14968 2020 : if (tuple_class->relowner != newOwnerId)
14969 : {
14970 : Datum repl_val[Natts_pg_class];
14971 : bool repl_null[Natts_pg_class];
14972 : bool repl_repl[Natts_pg_class];
14973 : Acl *newAcl;
14974 : Datum aclDatum;
14975 : bool isNull;
14976 : HeapTuple newtuple;
14977 :
14978 : /* skip permission checks when recursing to index or toast table */
14979 498 : if (!recursing)
14980 : {
14981 : /* Superusers can always do it */
14982 280 : if (!superuser())
14983 : {
14984 42 : Oid namespaceOid = tuple_class->relnamespace;
14985 : AclResult aclresult;
14986 :
14987 : /* Otherwise, must be owner of the existing object */
14988 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
14989 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
14990 0 : RelationGetRelationName(target_rel));
14991 :
14992 : /* Must be able to become new owner */
14993 42 : check_can_set_role(GetUserId(), newOwnerId);
14994 :
14995 : /* New owner must have CREATE privilege on namespace */
14996 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
14997 : ACL_CREATE);
14998 30 : if (aclresult != ACLCHECK_OK)
14999 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15000 0 : get_namespace_name(namespaceOid));
15001 : }
15002 : }
15003 :
15004 486 : memset(repl_null, false, sizeof(repl_null));
15005 486 : memset(repl_repl, false, sizeof(repl_repl));
15006 :
15007 486 : repl_repl[Anum_pg_class_relowner - 1] = true;
15008 486 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15009 :
15010 : /*
15011 : * Determine the modified ACL for the new owner. This is only
15012 : * necessary when the ACL is non-null.
15013 : */
15014 486 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15015 : Anum_pg_class_relacl,
15016 : &isNull);
15017 486 : if (!isNull)
15018 : {
15019 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15020 : tuple_class->relowner, newOwnerId);
15021 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
15022 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15023 : }
15024 :
15025 486 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15026 :
15027 486 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15028 :
15029 486 : heap_freetuple(newtuple);
15030 :
15031 : /*
15032 : * We must similarly update any per-column ACLs to reflect the new
15033 : * owner; for neatness reasons that's split out as a subroutine.
15034 : */
15035 486 : change_owner_fix_column_acls(relationOid,
15036 : tuple_class->relowner,
15037 : newOwnerId);
15038 :
15039 : /*
15040 : * Update owner dependency reference, if any. A composite type has
15041 : * none, because it's tracked for the pg_type entry instead of here;
15042 : * indexes and TOAST tables don't have their own entries either.
15043 : */
15044 486 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15045 478 : tuple_class->relkind != RELKIND_INDEX &&
15046 382 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15047 362 : tuple_class->relkind != RELKIND_TOASTVALUE)
15048 320 : changeDependencyOnOwner(RelationRelationId, relationOid,
15049 : newOwnerId);
15050 :
15051 : /*
15052 : * Also change the ownership of the table's row type, if it has one
15053 : */
15054 486 : if (OidIsValid(tuple_class->reltype))
15055 294 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15056 :
15057 : /*
15058 : * If we are operating on a table or materialized view, also change
15059 : * the ownership of any indexes and sequences that belong to the
15060 : * relation, as well as its toast table (if it has one).
15061 : */
15062 486 : if (tuple_class->relkind == RELKIND_RELATION ||
15063 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15064 224 : tuple_class->relkind == RELKIND_MATVIEW ||
15065 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
15066 : {
15067 : List *index_oid_list;
15068 : ListCell *i;
15069 :
15070 : /* Find all the indexes belonging to this relation */
15071 304 : index_oid_list = RelationGetIndexList(target_rel);
15072 :
15073 : /* For each index, recursively change its ownership */
15074 420 : foreach(i, index_oid_list)
15075 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15076 :
15077 304 : list_free(index_oid_list);
15078 : }
15079 :
15080 : /* If it has a toast table, recurse to change its ownership */
15081 486 : if (tuple_class->reltoastrelid != InvalidOid)
15082 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15083 : true, lockmode);
15084 :
15085 : /* If it has dependent sequences, recurse to change them too */
15086 486 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15087 : }
15088 :
15089 2008 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15090 :
15091 2008 : ReleaseSysCache(tuple);
15092 2008 : table_close(class_rel, RowExclusiveLock);
15093 2008 : relation_close(target_rel, NoLock);
15094 2008 : }
15095 :
15096 : /*
15097 : * change_owner_fix_column_acls
15098 : *
15099 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15100 : * and fix any non-null column ACLs to reflect the new owner.
15101 : */
15102 : static void
15103 486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15104 : {
15105 : Relation attRelation;
15106 : SysScanDesc scan;
15107 : ScanKeyData key[1];
15108 : HeapTuple attributeTuple;
15109 :
15110 486 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15111 486 : ScanKeyInit(&key[0],
15112 : Anum_pg_attribute_attrelid,
15113 : BTEqualStrategyNumber, F_OIDEQ,
15114 : ObjectIdGetDatum(relationOid));
15115 486 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15116 : true, NULL, 1, key);
15117 3372 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15118 : {
15119 2886 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15120 : Datum repl_val[Natts_pg_attribute];
15121 : bool repl_null[Natts_pg_attribute];
15122 : bool repl_repl[Natts_pg_attribute];
15123 : Acl *newAcl;
15124 : Datum aclDatum;
15125 : bool isNull;
15126 : HeapTuple newtuple;
15127 :
15128 : /* Ignore dropped columns */
15129 2886 : if (att->attisdropped)
15130 2884 : continue;
15131 :
15132 2886 : aclDatum = heap_getattr(attributeTuple,
15133 : Anum_pg_attribute_attacl,
15134 : RelationGetDescr(attRelation),
15135 : &isNull);
15136 : /* Null ACLs do not require changes */
15137 2886 : if (isNull)
15138 2884 : continue;
15139 :
15140 2 : memset(repl_null, false, sizeof(repl_null));
15141 2 : memset(repl_repl, false, sizeof(repl_repl));
15142 :
15143 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15144 : oldOwnerId, newOwnerId);
15145 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15146 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15147 :
15148 2 : newtuple = heap_modify_tuple(attributeTuple,
15149 : RelationGetDescr(attRelation),
15150 : repl_val, repl_null, repl_repl);
15151 :
15152 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15153 :
15154 2 : heap_freetuple(newtuple);
15155 : }
15156 486 : systable_endscan(scan);
15157 486 : table_close(attRelation, RowExclusiveLock);
15158 486 : }
15159 :
15160 : /*
15161 : * change_owner_recurse_to_sequences
15162 : *
15163 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15164 : * for sequences that are dependent on serial columns, and changes their
15165 : * ownership.
15166 : */
15167 : static void
15168 486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15169 : {
15170 : Relation depRel;
15171 : SysScanDesc scan;
15172 : ScanKeyData key[2];
15173 : HeapTuple tup;
15174 :
15175 : /*
15176 : * SERIAL sequences are those having an auto dependency on one of the
15177 : * table's columns (we don't care *which* column, exactly).
15178 : */
15179 486 : depRel = table_open(DependRelationId, AccessShareLock);
15180 :
15181 486 : ScanKeyInit(&key[0],
15182 : Anum_pg_depend_refclassid,
15183 : BTEqualStrategyNumber, F_OIDEQ,
15184 : ObjectIdGetDatum(RelationRelationId));
15185 486 : ScanKeyInit(&key[1],
15186 : Anum_pg_depend_refobjid,
15187 : BTEqualStrategyNumber, F_OIDEQ,
15188 : ObjectIdGetDatum(relationOid));
15189 : /* we leave refobjsubid unspecified */
15190 :
15191 486 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15192 : NULL, 2, key);
15193 :
15194 1374 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15195 : {
15196 888 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15197 : Relation seqRel;
15198 :
15199 : /* skip dependencies other than auto dependencies on columns */
15200 888 : if (depForm->refobjsubid == 0 ||
15201 352 : depForm->classid != RelationRelationId ||
15202 142 : depForm->objsubid != 0 ||
15203 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15204 746 : continue;
15205 :
15206 : /* Use relation_open just in case it's an index */
15207 142 : seqRel = relation_open(depForm->objid, lockmode);
15208 :
15209 : /* skip non-sequence relations */
15210 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15211 : {
15212 : /* No need to keep the lock */
15213 116 : relation_close(seqRel, lockmode);
15214 116 : continue;
15215 : }
15216 :
15217 : /* We don't need to close the sequence while we alter it. */
15218 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15219 :
15220 : /* Now we can close it. Keep the lock till end of transaction. */
15221 26 : relation_close(seqRel, NoLock);
15222 : }
15223 :
15224 486 : systable_endscan(scan);
15225 :
15226 486 : relation_close(depRel, AccessShareLock);
15227 486 : }
15228 :
15229 : /*
15230 : * ALTER TABLE CLUSTER ON
15231 : *
15232 : * The only thing we have to do is to change the indisclustered bits.
15233 : *
15234 : * Return the address of the new clustering index.
15235 : */
15236 : static ObjectAddress
15237 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15238 : {
15239 : Oid indexOid;
15240 : ObjectAddress address;
15241 :
15242 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15243 :
15244 64 : if (!OidIsValid(indexOid))
15245 0 : ereport(ERROR,
15246 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15247 : errmsg("index \"%s\" for table \"%s\" does not exist",
15248 : indexName, RelationGetRelationName(rel))));
15249 :
15250 : /* Check index is valid to cluster on */
15251 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15252 :
15253 : /* And do the work */
15254 64 : mark_index_clustered(rel, indexOid, false);
15255 :
15256 58 : ObjectAddressSet(address,
15257 : RelationRelationId, indexOid);
15258 :
15259 58 : return address;
15260 : }
15261 :
15262 : /*
15263 : * ALTER TABLE SET WITHOUT CLUSTER
15264 : *
15265 : * We have to find any indexes on the table that have indisclustered bit
15266 : * set and turn it off.
15267 : */
15268 : static void
15269 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15270 : {
15271 18 : mark_index_clustered(rel, InvalidOid, false);
15272 12 : }
15273 :
15274 : /*
15275 : * Preparation phase for SET ACCESS METHOD
15276 : *
15277 : * Check that the access method exists and determine whether a change is
15278 : * actually needed.
15279 : */
15280 : static void
15281 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15282 : {
15283 : Oid amoid;
15284 :
15285 : /*
15286 : * Look up the access method name and check that it differs from the
15287 : * table's current AM. If DEFAULT was specified for a partitioned table
15288 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15289 : */
15290 110 : if (amname != NULL)
15291 74 : amoid = get_table_am_oid(amname, false);
15292 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15293 18 : amoid = InvalidOid;
15294 : else
15295 18 : amoid = get_table_am_oid(default_table_access_method, false);
15296 :
15297 : /* if it's a match, phase 3 doesn't need to do anything */
15298 110 : if (rel->rd_rel->relam == amoid)
15299 12 : return;
15300 :
15301 : /* Save info for Phase 3 to do the real work */
15302 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15303 98 : tab->newAccessMethod = amoid;
15304 98 : tab->chgAccessMethod = true;
15305 : }
15306 :
15307 : /*
15308 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15309 : * storage that have an interest in preserving AM.
15310 : *
15311 : * Since these have no storage, setting the access method is a catalog only
15312 : * operation.
15313 : */
15314 : static void
15315 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15316 : {
15317 : Relation pg_class;
15318 : Oid oldAccessMethodId;
15319 : HeapTuple tuple;
15320 : Form_pg_class rd_rel;
15321 44 : Oid reloid = RelationGetRelid(rel);
15322 :
15323 : /*
15324 : * Shouldn't be called on relations having storage; these are processed in
15325 : * phase 3.
15326 : */
15327 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15328 :
15329 : /* Get a modifiable copy of the relation's pg_class row. */
15330 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15331 :
15332 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15333 44 : if (!HeapTupleIsValid(tuple))
15334 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15335 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15336 :
15337 : /* Update the pg_class row. */
15338 44 : oldAccessMethodId = rd_rel->relam;
15339 44 : rd_rel->relam = newAccessMethodId;
15340 :
15341 : /* Leave if no update required */
15342 44 : if (rd_rel->relam == oldAccessMethodId)
15343 : {
15344 0 : heap_freetuple(tuple);
15345 0 : table_close(pg_class, RowExclusiveLock);
15346 0 : return;
15347 : }
15348 :
15349 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15350 :
15351 : /*
15352 : * Update the dependency on the new access method. No dependency is added
15353 : * if the new access method is InvalidOid (default case). Be very careful
15354 : * that this has to compare the previous value stored in pg_class with the
15355 : * new one.
15356 : */
15357 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15358 20 : {
15359 : ObjectAddress relobj,
15360 : referenced;
15361 :
15362 : /*
15363 : * New access method is defined and there was no dependency
15364 : * previously, so record a new one.
15365 : */
15366 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15367 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15368 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15369 : }
15370 24 : else if (OidIsValid(oldAccessMethodId) &&
15371 24 : !OidIsValid(rd_rel->relam))
15372 : {
15373 : /*
15374 : * There was an access method defined, and no new one, so just remove
15375 : * the existing dependency.
15376 : */
15377 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15378 : AccessMethodRelationId,
15379 : DEPENDENCY_NORMAL);
15380 : }
15381 : else
15382 : {
15383 : Assert(OidIsValid(oldAccessMethodId) &&
15384 : OidIsValid(rd_rel->relam));
15385 :
15386 : /* Both are valid, so update the dependency */
15387 12 : changeDependencyFor(RelationRelationId, reloid,
15388 : AccessMethodRelationId,
15389 : oldAccessMethodId, rd_rel->relam);
15390 : }
15391 :
15392 : /* make the relam and dependency changes visible */
15393 44 : CommandCounterIncrement();
15394 :
15395 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15396 :
15397 44 : heap_freetuple(tuple);
15398 44 : table_close(pg_class, RowExclusiveLock);
15399 : }
15400 :
15401 : /*
15402 : * ALTER TABLE SET TABLESPACE
15403 : */
15404 : static void
15405 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15406 : {
15407 : Oid tablespaceId;
15408 :
15409 : /* Check that the tablespace exists */
15410 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
15411 :
15412 : /* Check permissions except when moving to database's default */
15413 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15414 : {
15415 : AclResult aclresult;
15416 :
15417 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15418 66 : if (aclresult != ACLCHECK_OK)
15419 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15420 : }
15421 :
15422 : /* Save info for Phase 3 to do the real work */
15423 158 : if (OidIsValid(tab->newTableSpace))
15424 0 : ereport(ERROR,
15425 : (errcode(ERRCODE_SYNTAX_ERROR),
15426 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
15427 :
15428 158 : tab->newTableSpace = tablespaceId;
15429 158 : }
15430 :
15431 : /*
15432 : * Set, reset, or replace reloptions.
15433 : */
15434 : static void
15435 946 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15436 : LOCKMODE lockmode)
15437 : {
15438 : Oid relid;
15439 : Relation pgclass;
15440 : HeapTuple tuple;
15441 : HeapTuple newtuple;
15442 : Datum datum;
15443 : bool isnull;
15444 : Datum newOptions;
15445 : Datum repl_val[Natts_pg_class];
15446 : bool repl_null[Natts_pg_class];
15447 : bool repl_repl[Natts_pg_class];
15448 946 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
15449 :
15450 946 : if (defList == NIL && operation != AT_ReplaceRelOptions)
15451 0 : return; /* nothing to do */
15452 :
15453 946 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
15454 :
15455 : /* Fetch heap tuple */
15456 946 : relid = RelationGetRelid(rel);
15457 946 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
15458 946 : if (!HeapTupleIsValid(tuple))
15459 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
15460 :
15461 946 : if (operation == AT_ReplaceRelOptions)
15462 : {
15463 : /*
15464 : * If we're supposed to replace the reloptions list, we just pretend
15465 : * there were none before.
15466 : */
15467 194 : datum = (Datum) 0;
15468 194 : isnull = true;
15469 : }
15470 : else
15471 : {
15472 : /* Get the old reloptions */
15473 752 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15474 : &isnull);
15475 : }
15476 :
15477 : /* Generate new proposed reloptions (text array) */
15478 946 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15479 : defList, NULL, validnsps, false,
15480 : operation == AT_ResetRelOptions);
15481 :
15482 : /* Validate */
15483 940 : switch (rel->rd_rel->relkind)
15484 : {
15485 524 : case RELKIND_RELATION:
15486 : case RELKIND_TOASTVALUE:
15487 : case RELKIND_MATVIEW:
15488 524 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15489 524 : break;
15490 6 : case RELKIND_PARTITIONED_TABLE:
15491 6 : (void) partitioned_table_reloptions(newOptions, true);
15492 0 : break;
15493 296 : case RELKIND_VIEW:
15494 296 : (void) view_reloptions(newOptions, true);
15495 278 : break;
15496 114 : case RELKIND_INDEX:
15497 : case RELKIND_PARTITIONED_INDEX:
15498 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15499 92 : break;
15500 0 : default:
15501 0 : ereport(ERROR,
15502 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15503 : errmsg("cannot set options for relation \"%s\"",
15504 : RelationGetRelationName(rel)),
15505 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15506 : break;
15507 : }
15508 :
15509 : /* Special-case validation of view options */
15510 894 : if (rel->rd_rel->relkind == RELKIND_VIEW)
15511 : {
15512 278 : Query *view_query = get_view_query(rel);
15513 278 : List *view_options = untransformRelOptions(newOptions);
15514 : ListCell *cell;
15515 278 : bool check_option = false;
15516 :
15517 380 : foreach(cell, view_options)
15518 : {
15519 102 : DefElem *defel = (DefElem *) lfirst(cell);
15520 :
15521 102 : if (strcmp(defel->defname, "check_option") == 0)
15522 24 : check_option = true;
15523 : }
15524 :
15525 : /*
15526 : * If the check option is specified, look to see if the view is
15527 : * actually auto-updatable or not.
15528 : */
15529 278 : if (check_option)
15530 : {
15531 : const char *view_updatable_error =
15532 24 : view_query_is_auto_updatable(view_query, true);
15533 :
15534 24 : if (view_updatable_error)
15535 0 : ereport(ERROR,
15536 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15537 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15538 : errhint("%s", _(view_updatable_error))));
15539 : }
15540 : }
15541 :
15542 : /*
15543 : * All we need do here is update the pg_class row; the new options will be
15544 : * propagated into relcaches during post-commit cache inval.
15545 : */
15546 894 : memset(repl_val, 0, sizeof(repl_val));
15547 894 : memset(repl_null, false, sizeof(repl_null));
15548 894 : memset(repl_repl, false, sizeof(repl_repl));
15549 :
15550 894 : if (newOptions != (Datum) 0)
15551 600 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15552 : else
15553 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
15554 :
15555 894 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15556 :
15557 894 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15558 : repl_val, repl_null, repl_repl);
15559 :
15560 894 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15561 894 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
15562 :
15563 894 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15564 :
15565 894 : heap_freetuple(newtuple);
15566 :
15567 894 : ReleaseSysCache(tuple);
15568 :
15569 : /* repeat the whole exercise for the toast table, if there's one */
15570 894 : if (OidIsValid(rel->rd_rel->reltoastrelid))
15571 : {
15572 : Relation toastrel;
15573 256 : Oid toastid = rel->rd_rel->reltoastrelid;
15574 :
15575 256 : toastrel = table_open(toastid, lockmode);
15576 :
15577 : /* Fetch heap tuple */
15578 256 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15579 256 : if (!HeapTupleIsValid(tuple))
15580 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15581 :
15582 256 : if (operation == AT_ReplaceRelOptions)
15583 : {
15584 : /*
15585 : * If we're supposed to replace the reloptions list, we just
15586 : * pretend there were none before.
15587 : */
15588 0 : datum = (Datum) 0;
15589 0 : isnull = true;
15590 : }
15591 : else
15592 : {
15593 : /* Get the old reloptions */
15594 256 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15595 : &isnull);
15596 : }
15597 :
15598 256 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15599 : defList, "toast", validnsps, false,
15600 : operation == AT_ResetRelOptions);
15601 :
15602 256 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15603 :
15604 256 : memset(repl_val, 0, sizeof(repl_val));
15605 256 : memset(repl_null, false, sizeof(repl_null));
15606 256 : memset(repl_repl, false, sizeof(repl_repl));
15607 :
15608 256 : if (newOptions != (Datum) 0)
15609 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15610 : else
15611 214 : repl_null[Anum_pg_class_reloptions - 1] = true;
15612 :
15613 256 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15614 :
15615 256 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15616 : repl_val, repl_null, repl_repl);
15617 :
15618 256 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15619 :
15620 256 : InvokeObjectPostAlterHookArg(RelationRelationId,
15621 : RelationGetRelid(toastrel), 0,
15622 : InvalidOid, true);
15623 :
15624 256 : heap_freetuple(newtuple);
15625 :
15626 256 : ReleaseSysCache(tuple);
15627 :
15628 256 : table_close(toastrel, NoLock);
15629 : }
15630 :
15631 894 : table_close(pgclass, RowExclusiveLock);
15632 : }
15633 :
15634 : /*
15635 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15636 : * rewriting to be done, so we just want to copy the data as fast as possible.
15637 : */
15638 : static void
15639 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15640 : {
15641 : Relation rel;
15642 : Oid reltoastrelid;
15643 : RelFileNumber newrelfilenumber;
15644 : RelFileLocator newrlocator;
15645 162 : List *reltoastidxids = NIL;
15646 : ListCell *lc;
15647 :
15648 : /*
15649 : * Need lock here in case we are recursing to toast table or index
15650 : */
15651 162 : rel = relation_open(tableOid, lockmode);
15652 :
15653 : /* Check first if relation can be moved to new tablespace */
15654 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15655 : {
15656 2 : InvokeObjectPostAlterHook(RelationRelationId,
15657 : RelationGetRelid(rel), 0);
15658 2 : relation_close(rel, NoLock);
15659 2 : return;
15660 : }
15661 :
15662 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
15663 : /* Fetch the list of indexes on toast relation if necessary */
15664 160 : if (OidIsValid(reltoastrelid))
15665 : {
15666 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15667 :
15668 20 : reltoastidxids = RelationGetIndexList(toastRel);
15669 20 : relation_close(toastRel, lockmode);
15670 : }
15671 :
15672 : /*
15673 : * Relfilenumbers are not unique in databases across tablespaces, so we
15674 : * need to allocate a new one in the new tablespace.
15675 : */
15676 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15677 160 : rel->rd_rel->relpersistence);
15678 :
15679 : /* Open old and new relation */
15680 160 : newrlocator = rel->rd_locator;
15681 160 : newrlocator.relNumber = newrelfilenumber;
15682 160 : newrlocator.spcOid = newTableSpace;
15683 :
15684 : /* hand off to AM to actually create new rel storage and copy the data */
15685 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
15686 : {
15687 62 : index_copy_data(rel, newrlocator);
15688 : }
15689 : else
15690 : {
15691 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15692 98 : table_relation_copy_data(rel, &newrlocator);
15693 : }
15694 :
15695 : /*
15696 : * Update the pg_class row.
15697 : *
15698 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15699 : * executed on pg_class or its indexes (the above copy wouldn't contain
15700 : * the updated pg_class entry), but that's forbidden with
15701 : * CheckRelationTableSpaceMove().
15702 : */
15703 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15704 :
15705 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15706 :
15707 160 : RelationAssumeNewRelfilelocator(rel);
15708 :
15709 160 : relation_close(rel, NoLock);
15710 :
15711 : /* Make sure the reltablespace change is visible */
15712 160 : CommandCounterIncrement();
15713 :
15714 : /* Move associated toast relation and/or indexes, too */
15715 160 : if (OidIsValid(reltoastrelid))
15716 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15717 180 : foreach(lc, reltoastidxids)
15718 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15719 :
15720 : /* Clean up */
15721 160 : list_free(reltoastidxids);
15722 : }
15723 :
15724 : /*
15725 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15726 : * storage that have an interest in preserving tablespace.
15727 : *
15728 : * Since these have no storage the tablespace can be updated with a simple
15729 : * metadata only operation to update the tablespace.
15730 : */
15731 : static void
15732 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15733 : {
15734 : /*
15735 : * Shouldn't be called on relations having storage; these are processed in
15736 : * phase 3.
15737 : */
15738 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15739 :
15740 : /* check if relation can be moved to its new tablespace */
15741 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15742 : {
15743 0 : InvokeObjectPostAlterHook(RelationRelationId,
15744 : RelationGetRelid(rel),
15745 : 0);
15746 0 : return;
15747 : }
15748 :
15749 : /* Update can be done, so change reltablespace */
15750 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15751 :
15752 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15753 :
15754 : /* Make sure the reltablespace change is visible */
15755 30 : CommandCounterIncrement();
15756 : }
15757 :
15758 : /*
15759 : * Alter Table ALL ... SET TABLESPACE
15760 : *
15761 : * Allows a user to move all objects of some type in a given tablespace in the
15762 : * current database to another tablespace. Objects can be chosen based on the
15763 : * owner of the object also, to allow users to move only their objects.
15764 : * The user must have CREATE rights on the new tablespace, as usual. The main
15765 : * permissions handling is done by the lower-level table move function.
15766 : *
15767 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
15768 : * lock can't be acquired then we ereport(ERROR).
15769 : */
15770 : Oid
15771 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15772 : {
15773 30 : List *relations = NIL;
15774 : ListCell *l;
15775 : ScanKeyData key[1];
15776 : Relation rel;
15777 : TableScanDesc scan;
15778 : HeapTuple tuple;
15779 : Oid orig_tablespaceoid;
15780 : Oid new_tablespaceoid;
15781 30 : List *role_oids = roleSpecsToIds(stmt->roles);
15782 :
15783 : /* Ensure we were not asked to move something we can't */
15784 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15785 12 : stmt->objtype != OBJECT_MATVIEW)
15786 0 : ereport(ERROR,
15787 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15788 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15789 :
15790 : /* Get the orig and new tablespace OIDs */
15791 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15792 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15793 :
15794 : /* Can't move shared relations in to or out of pg_global */
15795 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15796 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15797 : new_tablespaceoid == GLOBALTABLESPACE_OID)
15798 0 : ereport(ERROR,
15799 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15800 : errmsg("cannot move relations in to or out of pg_global tablespace")));
15801 :
15802 : /*
15803 : * Must have CREATE rights on the new tablespace, unless it is the
15804 : * database default tablespace (which all users implicitly have CREATE
15805 : * rights on).
15806 : */
15807 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15808 : {
15809 : AclResult aclresult;
15810 :
15811 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15812 : ACL_CREATE);
15813 0 : if (aclresult != ACLCHECK_OK)
15814 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
15815 0 : get_tablespace_name(new_tablespaceoid));
15816 : }
15817 :
15818 : /*
15819 : * Now that the checks are done, check if we should set either to
15820 : * InvalidOid because it is our database's default tablespace.
15821 : */
15822 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
15823 0 : orig_tablespaceoid = InvalidOid;
15824 :
15825 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
15826 30 : new_tablespaceoid = InvalidOid;
15827 :
15828 : /* no-op */
15829 30 : if (orig_tablespaceoid == new_tablespaceoid)
15830 0 : return new_tablespaceoid;
15831 :
15832 : /*
15833 : * Walk the list of objects in the tablespace and move them. This will
15834 : * only find objects in our database, of course.
15835 : */
15836 30 : ScanKeyInit(&key[0],
15837 : Anum_pg_class_reltablespace,
15838 : BTEqualStrategyNumber, F_OIDEQ,
15839 : ObjectIdGetDatum(orig_tablespaceoid));
15840 :
15841 30 : rel = table_open(RelationRelationId, AccessShareLock);
15842 30 : scan = table_beginscan_catalog(rel, 1, key);
15843 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15844 : {
15845 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15846 102 : Oid relOid = relForm->oid;
15847 :
15848 : /*
15849 : * Do not move objects in pg_catalog as part of this, if an admin
15850 : * really wishes to do so, they can issue the individual ALTER
15851 : * commands directly.
15852 : *
15853 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
15854 : * (TOAST will be moved with the main table).
15855 : */
15856 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
15857 204 : relForm->relisshared ||
15858 204 : isAnyTempNamespace(relForm->relnamespace) ||
15859 102 : IsToastNamespace(relForm->relnamespace))
15860 0 : continue;
15861 :
15862 : /* Only move the object type requested */
15863 102 : if ((stmt->objtype == OBJECT_TABLE &&
15864 60 : relForm->relkind != RELKIND_RELATION &&
15865 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15866 66 : (stmt->objtype == OBJECT_INDEX &&
15867 36 : relForm->relkind != RELKIND_INDEX &&
15868 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15869 60 : (stmt->objtype == OBJECT_MATVIEW &&
15870 6 : relForm->relkind != RELKIND_MATVIEW))
15871 42 : continue;
15872 :
15873 : /* Check if we are only moving objects owned by certain roles */
15874 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15875 0 : continue;
15876 :
15877 : /*
15878 : * Handle permissions-checking here since we are locking the tables
15879 : * and also to avoid doing a bunch of work only to fail part-way. Note
15880 : * that permissions will also be checked by AlterTableInternal().
15881 : *
15882 : * Caller must be considered an owner on the table to move it.
15883 : */
15884 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15885 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
15886 0 : NameStr(relForm->relname));
15887 :
15888 60 : if (stmt->nowait &&
15889 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15890 0 : ereport(ERROR,
15891 : (errcode(ERRCODE_OBJECT_IN_USE),
15892 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
15893 : get_namespace_name(relForm->relnamespace),
15894 : NameStr(relForm->relname))));
15895 : else
15896 60 : LockRelationOid(relOid, AccessExclusiveLock);
15897 :
15898 : /* Add to our list of objects to move */
15899 60 : relations = lappend_oid(relations, relOid);
15900 : }
15901 :
15902 30 : table_endscan(scan);
15903 30 : table_close(rel, AccessShareLock);
15904 :
15905 30 : if (relations == NIL)
15906 12 : ereport(NOTICE,
15907 : (errcode(ERRCODE_NO_DATA_FOUND),
15908 : errmsg("no matching relations in tablespace \"%s\" found",
15909 : orig_tablespaceoid == InvalidOid ? "(database default)" :
15910 : get_tablespace_name(orig_tablespaceoid))));
15911 :
15912 : /* Everything is locked, loop through and move all of the relations. */
15913 90 : foreach(l, relations)
15914 : {
15915 60 : List *cmds = NIL;
15916 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15917 :
15918 60 : cmd->subtype = AT_SetTableSpace;
15919 60 : cmd->name = stmt->new_tablespacename;
15920 :
15921 60 : cmds = lappend(cmds, cmd);
15922 :
15923 60 : EventTriggerAlterTableStart((Node *) stmt);
15924 : /* OID is set by AlterTableInternal */
15925 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
15926 60 : EventTriggerAlterTableEnd();
15927 : }
15928 :
15929 30 : return new_tablespaceoid;
15930 : }
15931 :
15932 : static void
15933 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
15934 : {
15935 : SMgrRelation dstrel;
15936 :
15937 : /*
15938 : * Since we copy the file directly without looking at the shared buffers,
15939 : * we'd better first flush out any pages of the source relation that are
15940 : * in shared buffers. We assume no new changes will be made while we are
15941 : * holding exclusive lock on the rel.
15942 : */
15943 62 : FlushRelationBuffers(rel);
15944 :
15945 : /*
15946 : * Create and copy all forks of the relation, and schedule unlinking of
15947 : * old physical files.
15948 : *
15949 : * NOTE: any conflict in relfilenumber value will be caught in
15950 : * RelationCreateStorage().
15951 : */
15952 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15953 :
15954 : /* copy main fork */
15955 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
15956 62 : rel->rd_rel->relpersistence);
15957 :
15958 : /* copy those extra forks that exist */
15959 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
15960 186 : forkNum <= MAX_FORKNUM; forkNum++)
15961 : {
15962 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
15963 : {
15964 0 : smgrcreate(dstrel, forkNum, false);
15965 :
15966 : /*
15967 : * WAL log creation if the relation is persistent, or this is the
15968 : * init fork of an unlogged relation.
15969 : */
15970 0 : if (RelationIsPermanent(rel) ||
15971 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
15972 : forkNum == INIT_FORKNUM))
15973 0 : log_smgrcreate(&newrlocator, forkNum);
15974 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
15975 0 : rel->rd_rel->relpersistence);
15976 : }
15977 : }
15978 :
15979 : /* drop old relation, and close new one */
15980 62 : RelationDropStorage(rel);
15981 62 : smgrclose(dstrel);
15982 62 : }
15983 :
15984 : /*
15985 : * ALTER TABLE ENABLE/DISABLE TRIGGER
15986 : *
15987 : * We just pass this off to trigger.c.
15988 : */
15989 : static void
15990 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
15991 : char fires_when, bool skip_system, bool recurse,
15992 : LOCKMODE lockmode)
15993 : {
15994 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
15995 : fires_when, skip_system, recurse,
15996 : lockmode);
15997 :
15998 340 : InvokeObjectPostAlterHook(RelationRelationId,
15999 : RelationGetRelid(rel), 0);
16000 340 : }
16001 :
16002 : /*
16003 : * ALTER TABLE ENABLE/DISABLE RULE
16004 : *
16005 : * We just pass this off to rewriteDefine.c.
16006 : */
16007 : static void
16008 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16009 : char fires_when, LOCKMODE lockmode)
16010 : {
16011 46 : EnableDisableRule(rel, rulename, fires_when);
16012 :
16013 46 : InvokeObjectPostAlterHook(RelationRelationId,
16014 : RelationGetRelid(rel), 0);
16015 46 : }
16016 :
16017 : /*
16018 : * ALTER TABLE INHERIT
16019 : *
16020 : * Add a parent to the child's parents. This verifies that all the columns and
16021 : * check constraints of the parent appear in the child and that they have the
16022 : * same data types and expressions.
16023 : */
16024 : static void
16025 334 : ATPrepAddInherit(Relation child_rel)
16026 : {
16027 334 : if (child_rel->rd_rel->reloftype)
16028 6 : ereport(ERROR,
16029 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16030 : errmsg("cannot change inheritance of typed table")));
16031 :
16032 328 : if (child_rel->rd_rel->relispartition)
16033 6 : ereport(ERROR,
16034 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16035 : errmsg("cannot change inheritance of a partition")));
16036 :
16037 322 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16038 6 : ereport(ERROR,
16039 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16040 : errmsg("cannot change inheritance of partitioned table")));
16041 316 : }
16042 :
16043 : /*
16044 : * Return the address of the new parent relation.
16045 : */
16046 : static ObjectAddress
16047 316 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16048 : {
16049 : Relation parent_rel;
16050 : List *children;
16051 : ObjectAddress address;
16052 : const char *trigger_name;
16053 :
16054 : /*
16055 : * A self-exclusive lock is needed here. See the similar case in
16056 : * MergeAttributes() for a full explanation.
16057 : */
16058 316 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16059 :
16060 : /*
16061 : * Must be owner of both parent and child -- child was checked by
16062 : * ATSimplePermissions call in ATPrepCmd
16063 : */
16064 316 : ATSimplePermissions(AT_AddInherit, parent_rel,
16065 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
16066 :
16067 : /* Permanent rels cannot inherit from temporary ones */
16068 316 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16069 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16070 0 : ereport(ERROR,
16071 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16072 : errmsg("cannot inherit from temporary relation \"%s\"",
16073 : RelationGetRelationName(parent_rel))));
16074 :
16075 : /* If parent rel is temp, it must belong to this session */
16076 316 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16077 6 : !parent_rel->rd_islocaltemp)
16078 0 : ereport(ERROR,
16079 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16080 : errmsg("cannot inherit from temporary relation of another session")));
16081 :
16082 : /* Ditto for the child */
16083 316 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16084 6 : !child_rel->rd_islocaltemp)
16085 0 : ereport(ERROR,
16086 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16087 : errmsg("cannot inherit to temporary relation of another session")));
16088 :
16089 : /* Prevent partitioned tables from becoming inheritance parents */
16090 316 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16091 6 : ereport(ERROR,
16092 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16093 : errmsg("cannot inherit from partitioned table \"%s\"",
16094 : parent->relname)));
16095 :
16096 : /* Likewise for partitions */
16097 310 : if (parent_rel->rd_rel->relispartition)
16098 6 : ereport(ERROR,
16099 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16100 : errmsg("cannot inherit from a partition")));
16101 :
16102 : /*
16103 : * Prevent circularity by seeing if proposed parent inherits from child.
16104 : * (In particular, this disallows making a rel inherit from itself.)
16105 : *
16106 : * This is not completely bulletproof because of race conditions: in
16107 : * multi-level inheritance trees, someone else could concurrently be
16108 : * making another inheritance link that closes the loop but does not join
16109 : * either of the rels we have locked. Preventing that seems to require
16110 : * exclusive locks on the entire inheritance tree, which is a cure worse
16111 : * than the disease. find_all_inheritors() will cope with circularity
16112 : * anyway, so don't sweat it too much.
16113 : *
16114 : * We use weakest lock we can on child's children, namely AccessShareLock.
16115 : */
16116 304 : children = find_all_inheritors(RelationGetRelid(child_rel),
16117 : AccessShareLock, NULL);
16118 :
16119 304 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16120 12 : ereport(ERROR,
16121 : (errcode(ERRCODE_DUPLICATE_TABLE),
16122 : errmsg("circular inheritance not allowed"),
16123 : errdetail("\"%s\" is already a child of \"%s\".",
16124 : parent->relname,
16125 : RelationGetRelationName(child_rel))));
16126 :
16127 : /*
16128 : * If child_rel has row-level triggers with transition tables, we
16129 : * currently don't allow it to become an inheritance child. See also
16130 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16131 : */
16132 292 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16133 292 : if (trigger_name != NULL)
16134 6 : ereport(ERROR,
16135 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16136 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16137 : trigger_name, RelationGetRelationName(child_rel)),
16138 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16139 :
16140 : /* OK to create inheritance */
16141 286 : CreateInheritance(child_rel, parent_rel, false);
16142 :
16143 226 : ObjectAddressSet(address, RelationRelationId,
16144 : RelationGetRelid(parent_rel));
16145 :
16146 : /* keep our lock on the parent relation until commit */
16147 226 : table_close(parent_rel, NoLock);
16148 :
16149 226 : return address;
16150 : }
16151 :
16152 : /*
16153 : * CreateInheritance
16154 : * Catalog manipulation portion of creating inheritance between a child
16155 : * table and a parent table.
16156 : *
16157 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16158 : */
16159 : static void
16160 2328 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16161 : {
16162 : Relation catalogRelation;
16163 : SysScanDesc scan;
16164 : ScanKeyData key;
16165 : HeapTuple inheritsTuple;
16166 : int32 inhseqno;
16167 :
16168 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16169 2328 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16170 :
16171 : /*
16172 : * Check for duplicates in the list of parents, and determine the highest
16173 : * inhseqno already present; we'll use the next one for the new parent.
16174 : * Also, if proposed child is a partition, it cannot already be
16175 : * inheriting.
16176 : *
16177 : * Note: we do not reject the case where the child already inherits from
16178 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16179 : */
16180 2328 : ScanKeyInit(&key,
16181 : Anum_pg_inherits_inhrelid,
16182 : BTEqualStrategyNumber, F_OIDEQ,
16183 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16184 2328 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16185 : true, NULL, 1, &key);
16186 :
16187 : /* inhseqno sequences start at 1 */
16188 2328 : inhseqno = 0;
16189 2374 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16190 : {
16191 52 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16192 :
16193 52 : if (inh->inhparent == RelationGetRelid(parent_rel))
16194 6 : ereport(ERROR,
16195 : (errcode(ERRCODE_DUPLICATE_TABLE),
16196 : errmsg("relation \"%s\" would be inherited from more than once",
16197 : RelationGetRelationName(parent_rel))));
16198 :
16199 46 : if (inh->inhseqno > inhseqno)
16200 46 : inhseqno = inh->inhseqno;
16201 : }
16202 2322 : systable_endscan(scan);
16203 :
16204 : /* Match up the columns and bump attinhcount as needed */
16205 2322 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16206 :
16207 : /* Match up the constraints and bump coninhcount as needed */
16208 2238 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16209 :
16210 : /*
16211 : * OK, it looks valid. Make the catalog entries that show inheritance.
16212 : */
16213 2196 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16214 : RelationGetRelid(parent_rel),
16215 : inhseqno + 1,
16216 : catalogRelation,
16217 2196 : parent_rel->rd_rel->relkind ==
16218 : RELKIND_PARTITIONED_TABLE);
16219 :
16220 : /* Now we're done with pg_inherits */
16221 2196 : table_close(catalogRelation, RowExclusiveLock);
16222 2196 : }
16223 :
16224 : /*
16225 : * Obtain the source-text form of the constraint expression for a check
16226 : * constraint, given its pg_constraint tuple
16227 : */
16228 : static char *
16229 160 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16230 : {
16231 : Form_pg_constraint con;
16232 : bool isnull;
16233 : Datum attr;
16234 : Datum expr;
16235 :
16236 160 : con = (Form_pg_constraint) GETSTRUCT(contup);
16237 160 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16238 160 : if (isnull)
16239 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16240 :
16241 160 : expr = DirectFunctionCall2(pg_get_expr, attr,
16242 : ObjectIdGetDatum(con->conrelid));
16243 160 : return TextDatumGetCString(expr);
16244 : }
16245 :
16246 : /*
16247 : * Determine whether two check constraints are functionally equivalent
16248 : *
16249 : * The test we apply is to see whether they reverse-compile to the same
16250 : * source string. This insulates us from issues like whether attributes
16251 : * have the same physical column numbers in parent and child relations.
16252 : */
16253 : static bool
16254 80 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16255 : {
16256 80 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16257 80 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16258 :
16259 80 : if (acon->condeferrable != bcon->condeferrable ||
16260 80 : acon->condeferred != bcon->condeferred ||
16261 80 : strcmp(decompile_conbin(a, tupleDesc),
16262 80 : decompile_conbin(b, tupleDesc)) != 0)
16263 6 : return false;
16264 : else
16265 74 : return true;
16266 : }
16267 :
16268 : /*
16269 : * Check columns in child table match up with columns in parent, and increment
16270 : * their attinhcount.
16271 : *
16272 : * Called by CreateInheritance
16273 : *
16274 : * Currently all parent columns must be found in child. Missing columns are an
16275 : * error. One day we might consider creating new columns like CREATE TABLE
16276 : * does. However, that is widely unpopular --- in the common use case of
16277 : * partitioned tables it's a foot-gun.
16278 : *
16279 : * The data type must match exactly. If the parent column is NOT NULL then
16280 : * the child must be as well. Defaults are not compared, however.
16281 : */
16282 : static void
16283 2322 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16284 : {
16285 : Relation attrrel;
16286 : TupleDesc parent_desc;
16287 :
16288 2322 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16289 2322 : parent_desc = RelationGetDescr(parent_rel);
16290 :
16291 7492 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16292 : {
16293 5254 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16294 5254 : char *parent_attname = NameStr(parent_att->attname);
16295 : HeapTuple tuple;
16296 :
16297 : /* Ignore dropped columns in the parent. */
16298 5254 : if (parent_att->attisdropped)
16299 296 : continue;
16300 :
16301 : /* Find same column in child (matching on column name). */
16302 4958 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16303 4958 : if (HeapTupleIsValid(tuple))
16304 : {
16305 4946 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16306 :
16307 4946 : if (parent_att->atttypid != child_att->atttypid ||
16308 4940 : parent_att->atttypmod != child_att->atttypmod)
16309 12 : ereport(ERROR,
16310 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16311 : errmsg("child table \"%s\" has different type for column \"%s\"",
16312 : RelationGetRelationName(child_rel), parent_attname)));
16313 :
16314 4934 : if (parent_att->attcollation != child_att->attcollation)
16315 6 : ereport(ERROR,
16316 : (errcode(ERRCODE_COLLATION_MISMATCH),
16317 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16318 : RelationGetRelationName(child_rel), parent_attname)));
16319 :
16320 : /*
16321 : * If the parent has a not-null constraint that's not NO INHERIT,
16322 : * make sure the child has one too.
16323 : *
16324 : * Other constraints are checked elsewhere.
16325 : */
16326 4928 : if (parent_att->attnotnull && !child_att->attnotnull)
16327 : {
16328 : HeapTuple contup;
16329 :
16330 30 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16331 30 : parent_att->attnum);
16332 30 : if (HeapTupleIsValid(contup) &&
16333 30 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16334 24 : ereport(ERROR,
16335 : errcode(ERRCODE_DATATYPE_MISMATCH),
16336 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16337 : parent_attname, RelationGetRelationName(child_rel)));
16338 : }
16339 :
16340 : /*
16341 : * Child column must be generated if and only if parent column is.
16342 : */
16343 4904 : if (parent_att->attgenerated && !child_att->attgenerated)
16344 18 : ereport(ERROR,
16345 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16346 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16347 4886 : if (child_att->attgenerated && !parent_att->attgenerated)
16348 12 : ereport(ERROR,
16349 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16350 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16351 :
16352 : /*
16353 : * Regular inheritance children are independent enough not to
16354 : * inherit identity columns. But partitions are integral part of
16355 : * a partitioned table and inherit identity column.
16356 : */
16357 4874 : if (ispartition)
16358 4350 : child_att->attidentity = parent_att->attidentity;
16359 :
16360 : /*
16361 : * OK, bump the child column's inheritance count. (If we fail
16362 : * later on, this change will just roll back.)
16363 : */
16364 4874 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
16365 : &child_att->attinhcount))
16366 0 : ereport(ERROR,
16367 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16368 : errmsg("too many inheritance parents"));
16369 :
16370 : /*
16371 : * In case of partitions, we must enforce that value of attislocal
16372 : * is same in all partitions. (Note: there are only inherited
16373 : * attributes in partitions)
16374 : */
16375 4874 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16376 : {
16377 : Assert(child_att->attinhcount == 1);
16378 4350 : child_att->attislocal = false;
16379 : }
16380 :
16381 4874 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16382 4874 : heap_freetuple(tuple);
16383 : }
16384 : else
16385 : {
16386 12 : ereport(ERROR,
16387 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16388 : errmsg("child table is missing column \"%s\"", parent_attname)));
16389 : }
16390 : }
16391 :
16392 2238 : table_close(attrrel, RowExclusiveLock);
16393 2238 : }
16394 :
16395 : /*
16396 : * Check constraints in child table match up with constraints in parent,
16397 : * and increment their coninhcount.
16398 : *
16399 : * Constraints that are marked ONLY in the parent are ignored.
16400 : *
16401 : * Called by CreateInheritance
16402 : *
16403 : * Currently all constraints in parent must be present in the child. One day we
16404 : * may consider adding new constraints like CREATE TABLE does.
16405 : *
16406 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
16407 : * constraints. As long as tables have more like 10 constraints it shouldn't be
16408 : * a problem though. Even 100 constraints ought not be the end of the world.
16409 : *
16410 : * XXX See MergeWithExistingConstraint too if you change this code.
16411 : */
16412 : static void
16413 2238 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16414 : {
16415 : Relation constraintrel;
16416 : SysScanDesc parent_scan;
16417 : ScanKeyData parent_key;
16418 : HeapTuple parent_tuple;
16419 2238 : Oid parent_relid = RelationGetRelid(parent_rel);
16420 : AttrMap *attmap;
16421 :
16422 2238 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16423 :
16424 : /* Outer loop scans through the parent's constraint definitions */
16425 2238 : ScanKeyInit(&parent_key,
16426 : Anum_pg_constraint_conrelid,
16427 : BTEqualStrategyNumber, F_OIDEQ,
16428 : ObjectIdGetDatum(parent_relid));
16429 2238 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16430 : true, NULL, 1, &parent_key);
16431 :
16432 2238 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16433 : RelationGetDescr(child_rel),
16434 : true);
16435 :
16436 3898 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16437 : {
16438 1702 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16439 : SysScanDesc child_scan;
16440 : ScanKeyData child_key;
16441 : HeapTuple child_tuple;
16442 : AttrNumber parent_attno;
16443 1702 : bool found = false;
16444 :
16445 1702 : if (parent_con->contype != CONSTRAINT_CHECK &&
16446 1578 : parent_con->contype != CONSTRAINT_NOTNULL)
16447 814 : continue;
16448 :
16449 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16450 920 : if (parent_con->connoinherit)
16451 32 : continue;
16452 :
16453 888 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16454 784 : parent_attno = extractNotNullColumn(parent_tuple);
16455 : else
16456 104 : parent_attno = InvalidAttrNumber;
16457 :
16458 : /* Search for a child constraint matching this one */
16459 888 : ScanKeyInit(&child_key,
16460 : Anum_pg_constraint_conrelid,
16461 : BTEqualStrategyNumber, F_OIDEQ,
16462 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16463 888 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16464 : true, NULL, 1, &child_key);
16465 :
16466 1534 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16467 : {
16468 1510 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16469 : HeapTuple child_copy;
16470 :
16471 1510 : if (child_con->contype != parent_con->contype)
16472 352 : continue;
16473 :
16474 : /*
16475 : * CHECK constraint are matched by constraint name, NOT NULL ones
16476 : * by attribute number.
16477 : */
16478 1158 : if (child_con->contype == CONSTRAINT_CHECK)
16479 : {
16480 110 : if (strcmp(NameStr(parent_con->conname),
16481 110 : NameStr(child_con->conname)) != 0)
16482 30 : continue;
16483 : }
16484 1048 : else if (child_con->contype == CONSTRAINT_NOTNULL)
16485 : {
16486 : Form_pg_attribute parent_attr;
16487 : Form_pg_attribute child_attr;
16488 : AttrNumber child_attno;
16489 :
16490 1048 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
16491 1048 : child_attno = extractNotNullColumn(child_tuple);
16492 1048 : if (parent_attno != attmap->attnums[child_attno - 1])
16493 264 : continue;
16494 :
16495 784 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
16496 : /* there shouldn't be constraints on dropped columns */
16497 784 : if (parent_attr->attisdropped || child_attr->attisdropped)
16498 0 : elog(ERROR, "found not-null constraint on dropped columns");
16499 : }
16500 :
16501 864 : if (child_con->contype == CONSTRAINT_CHECK &&
16502 80 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16503 6 : ereport(ERROR,
16504 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16505 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16506 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16507 :
16508 : /*
16509 : * If the child constraint is "no inherit" then cannot merge
16510 : */
16511 858 : if (child_con->connoinherit)
16512 12 : ereport(ERROR,
16513 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16514 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16515 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16516 :
16517 : /*
16518 : * If the child constraint is "not valid" then cannot merge with a
16519 : * valid parent constraint
16520 : */
16521 846 : if (parent_con->convalidated && !child_con->convalidated)
16522 0 : ereport(ERROR,
16523 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16524 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16525 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16526 :
16527 : /*
16528 : * OK, bump the child constraint's inheritance count. (If we fail
16529 : * later on, this change will just roll back.)
16530 : */
16531 846 : child_copy = heap_copytuple(child_tuple);
16532 846 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16533 :
16534 846 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
16535 : &child_con->coninhcount))
16536 0 : ereport(ERROR,
16537 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16538 : errmsg("too many inheritance parents"));
16539 :
16540 : /*
16541 : * In case of partitions, an inherited constraint must be
16542 : * inherited only once since it cannot have multiple parents and
16543 : * it is never considered local.
16544 : */
16545 846 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16546 : {
16547 : Assert(child_con->coninhcount == 1);
16548 740 : child_con->conislocal = false;
16549 : }
16550 :
16551 846 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16552 846 : heap_freetuple(child_copy);
16553 :
16554 846 : found = true;
16555 846 : break;
16556 : }
16557 :
16558 870 : systable_endscan(child_scan);
16559 :
16560 870 : if (!found)
16561 : {
16562 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16563 0 : ereport(ERROR,
16564 : errcode(ERRCODE_DATATYPE_MISMATCH),
16565 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16566 : get_attname(parent_relid,
16567 : extractNotNullColumn(parent_tuple),
16568 : false),
16569 : RelationGetRelationName(child_rel)));
16570 :
16571 24 : ereport(ERROR,
16572 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16573 : errmsg("child table is missing constraint \"%s\"",
16574 : NameStr(parent_con->conname))));
16575 : }
16576 : }
16577 :
16578 2196 : systable_endscan(parent_scan);
16579 2196 : table_close(constraintrel, RowExclusiveLock);
16580 2196 : }
16581 :
16582 : /*
16583 : * ALTER TABLE NO INHERIT
16584 : *
16585 : * Return value is the address of the relation that is no longer parent.
16586 : */
16587 : static ObjectAddress
16588 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16589 : {
16590 : ObjectAddress address;
16591 : Relation parent_rel;
16592 :
16593 86 : if (rel->rd_rel->relispartition)
16594 0 : ereport(ERROR,
16595 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16596 : errmsg("cannot change inheritance of a partition")));
16597 :
16598 : /*
16599 : * AccessShareLock on the parent is probably enough, seeing that DROP
16600 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
16601 : * be inspecting the parent's schema.
16602 : */
16603 86 : parent_rel = table_openrv(parent, AccessShareLock);
16604 :
16605 : /*
16606 : * We don't bother to check ownership of the parent table --- ownership of
16607 : * the child is presumed enough rights.
16608 : */
16609 :
16610 : /* Off to RemoveInheritance() where most of the work happens */
16611 86 : RemoveInheritance(rel, parent_rel, false);
16612 :
16613 80 : ObjectAddressSet(address, RelationRelationId,
16614 : RelationGetRelid(parent_rel));
16615 :
16616 : /* keep our lock on the parent relation until commit */
16617 80 : table_close(parent_rel, NoLock);
16618 :
16619 80 : return address;
16620 : }
16621 :
16622 : /*
16623 : * MarkInheritDetached
16624 : *
16625 : * Set inhdetachpending for a partition, for ATExecDetachPartition
16626 : * in concurrent mode. While at it, verify that no other partition is
16627 : * already pending detach.
16628 : */
16629 : static void
16630 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16631 : {
16632 : Relation catalogRelation;
16633 : SysScanDesc scan;
16634 : ScanKeyData key;
16635 : HeapTuple inheritsTuple;
16636 146 : bool found = false;
16637 :
16638 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16639 :
16640 : /*
16641 : * Find pg_inherits entries by inhparent. (We need to scan them all in
16642 : * order to verify that no other partition is pending detach.)
16643 : */
16644 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16645 146 : ScanKeyInit(&key,
16646 : Anum_pg_inherits_inhparent,
16647 : BTEqualStrategyNumber, F_OIDEQ,
16648 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16649 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16650 : true, NULL, 1, &key);
16651 :
16652 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16653 : {
16654 : Form_pg_inherits inhForm;
16655 :
16656 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16657 286 : if (inhForm->inhdetachpending)
16658 2 : ereport(ERROR,
16659 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16660 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16661 : get_rel_name(inhForm->inhrelid),
16662 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16663 : RelationGetRelationName(parent_rel)),
16664 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16665 :
16666 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16667 : {
16668 : HeapTuple newtup;
16669 :
16670 144 : newtup = heap_copytuple(inheritsTuple);
16671 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16672 :
16673 144 : CatalogTupleUpdate(catalogRelation,
16674 : &inheritsTuple->t_self,
16675 : newtup);
16676 144 : found = true;
16677 144 : heap_freetuple(newtup);
16678 : /* keep looking, to ensure we catch others pending detach */
16679 : }
16680 : }
16681 :
16682 : /* Done */
16683 144 : systable_endscan(scan);
16684 144 : table_close(catalogRelation, RowExclusiveLock);
16685 :
16686 144 : if (!found)
16687 0 : ereport(ERROR,
16688 : (errcode(ERRCODE_UNDEFINED_TABLE),
16689 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16690 : RelationGetRelationName(child_rel),
16691 : RelationGetRelationName(parent_rel))));
16692 144 : }
16693 :
16694 : /*
16695 : * RemoveInheritance
16696 : *
16697 : * Drop a parent from the child's parents. This just adjusts the attinhcount
16698 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16699 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16700 : *
16701 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16702 : * up attislocal stays true, which means if a child is ever removed from a
16703 : * parent then its columns will never be automatically dropped which may
16704 : * surprise. But at least we'll never surprise by dropping columns someone
16705 : * isn't expecting to be dropped which would actually mean data loss.
16706 : *
16707 : * coninhcount and conislocal for inherited constraints are adjusted in
16708 : * exactly the same way.
16709 : *
16710 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16711 : */
16712 : static void
16713 566 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16714 : {
16715 : Relation catalogRelation;
16716 : SysScanDesc scan;
16717 : ScanKeyData key[3];
16718 : HeapTuple attributeTuple,
16719 : constraintTuple;
16720 : AttrMap *attmap;
16721 : List *connames;
16722 : List *nncolumns;
16723 : bool found;
16724 : bool is_partitioning;
16725 :
16726 566 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16727 :
16728 566 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16729 : RelationGetRelid(parent_rel),
16730 : expect_detached,
16731 566 : RelationGetRelationName(child_rel));
16732 566 : if (!found)
16733 : {
16734 24 : if (is_partitioning)
16735 18 : ereport(ERROR,
16736 : (errcode(ERRCODE_UNDEFINED_TABLE),
16737 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16738 : RelationGetRelationName(child_rel),
16739 : RelationGetRelationName(parent_rel))));
16740 : else
16741 6 : ereport(ERROR,
16742 : (errcode(ERRCODE_UNDEFINED_TABLE),
16743 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16744 : RelationGetRelationName(parent_rel),
16745 : RelationGetRelationName(child_rel))));
16746 : }
16747 :
16748 : /*
16749 : * Search through child columns looking for ones matching parent rel
16750 : */
16751 542 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16752 542 : ScanKeyInit(&key[0],
16753 : Anum_pg_attribute_attrelid,
16754 : BTEqualStrategyNumber, F_OIDEQ,
16755 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16756 542 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16757 : true, NULL, 1, key);
16758 4844 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16759 : {
16760 4302 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16761 :
16762 : /* Ignore if dropped or not inherited */
16763 4302 : if (att->attisdropped)
16764 6 : continue;
16765 4296 : if (att->attinhcount <= 0)
16766 3282 : continue;
16767 :
16768 1014 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16769 1014 : NameStr(att->attname)))
16770 : {
16771 : /* Decrement inhcount and possibly set islocal to true */
16772 960 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
16773 960 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16774 :
16775 960 : copy_att->attinhcount--;
16776 960 : if (copy_att->attinhcount == 0)
16777 930 : copy_att->attislocal = true;
16778 :
16779 960 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16780 960 : heap_freetuple(copyTuple);
16781 : }
16782 : }
16783 542 : systable_endscan(scan);
16784 542 : table_close(catalogRelation, RowExclusiveLock);
16785 :
16786 : /*
16787 : * Likewise, find inherited check and not-null constraints and disinherit
16788 : * them. To do this, we first need a list of the names of the parent's
16789 : * check constraints. (We cheat a bit by only checking for name matches,
16790 : * assuming that the expressions will match.)
16791 : *
16792 : * For NOT NULL columns, we store column numbers to match, mapping them in
16793 : * to the child rel's attribute numbers.
16794 : */
16795 542 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
16796 : RelationGetDescr(parent_rel),
16797 : false);
16798 :
16799 542 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16800 542 : ScanKeyInit(&key[0],
16801 : Anum_pg_constraint_conrelid,
16802 : BTEqualStrategyNumber, F_OIDEQ,
16803 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16804 542 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16805 : true, NULL, 1, key);
16806 :
16807 542 : connames = NIL;
16808 542 : nncolumns = NIL;
16809 :
16810 1010 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16811 : {
16812 468 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16813 :
16814 468 : if (con->connoinherit)
16815 98 : continue;
16816 :
16817 370 : if (con->contype == CONSTRAINT_CHECK)
16818 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
16819 370 : if (con->contype == CONSTRAINT_NOTNULL)
16820 : {
16821 196 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
16822 :
16823 196 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
16824 : }
16825 : }
16826 :
16827 542 : systable_endscan(scan);
16828 :
16829 : /* Now scan the child's constraints to find matches */
16830 542 : ScanKeyInit(&key[0],
16831 : Anum_pg_constraint_conrelid,
16832 : BTEqualStrategyNumber, F_OIDEQ,
16833 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16834 542 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16835 : true, NULL, 1, key);
16836 :
16837 1194 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16838 : {
16839 652 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16840 652 : bool match = false;
16841 :
16842 : /*
16843 : * Match CHECK constraints by name, not-null constraints by column
16844 : * number, and ignore all others.
16845 : */
16846 652 : if (con->contype == CONSTRAINT_CHECK)
16847 : {
16848 350 : foreach_ptr(char, chkname, connames)
16849 : {
16850 18 : if (con->contype == CONSTRAINT_CHECK &&
16851 18 : strcmp(NameStr(con->conname), chkname) == 0)
16852 : {
16853 12 : match = true;
16854 12 : connames = foreach_delete_current(connames, chkname);
16855 12 : break;
16856 : }
16857 : }
16858 : }
16859 480 : else if (con->contype == CONSTRAINT_NOTNULL)
16860 : {
16861 256 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16862 :
16863 518 : foreach_int(prevattno, nncolumns)
16864 : {
16865 202 : if (prevattno == child_attno)
16866 : {
16867 196 : match = true;
16868 196 : nncolumns = foreach_delete_current(nncolumns, prevattno);
16869 196 : break;
16870 : }
16871 : }
16872 : }
16873 : else
16874 224 : continue;
16875 :
16876 428 : if (match)
16877 : {
16878 : /* Decrement inhcount and possibly set islocal to true */
16879 208 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
16880 208 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16881 :
16882 208 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
16883 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16884 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
16885 :
16886 208 : copy_con->coninhcount--;
16887 208 : if (copy_con->coninhcount == 0)
16888 190 : copy_con->conislocal = true;
16889 :
16890 208 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16891 208 : heap_freetuple(copyTuple);
16892 : }
16893 : }
16894 :
16895 : /* We should have matched all constraints */
16896 542 : if (connames != NIL || nncolumns != NIL)
16897 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
16898 : list_length(connames) + list_length(nncolumns),
16899 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
16900 :
16901 542 : systable_endscan(scan);
16902 542 : table_close(catalogRelation, RowExclusiveLock);
16903 :
16904 542 : drop_parent_dependency(RelationGetRelid(child_rel),
16905 : RelationRelationId,
16906 : RelationGetRelid(parent_rel),
16907 : child_dependency_type(is_partitioning));
16908 :
16909 : /*
16910 : * Post alter hook of this inherits. Since object_access_hook doesn't take
16911 : * multiple object identifiers, we relay oid of parent relation using
16912 : * auxiliary_id argument.
16913 : */
16914 542 : InvokeObjectPostAlterHookArg(InheritsRelationId,
16915 : RelationGetRelid(child_rel), 0,
16916 : RelationGetRelid(parent_rel), false);
16917 542 : }
16918 :
16919 : /*
16920 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16921 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16922 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16923 : * be TypeRelationId). There's no convenient way to do this, so go trawling
16924 : * through pg_depend.
16925 : */
16926 : static void
16927 554 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
16928 : DependencyType deptype)
16929 : {
16930 : Relation catalogRelation;
16931 : SysScanDesc scan;
16932 : ScanKeyData key[3];
16933 : HeapTuple depTuple;
16934 :
16935 554 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16936 :
16937 554 : ScanKeyInit(&key[0],
16938 : Anum_pg_depend_classid,
16939 : BTEqualStrategyNumber, F_OIDEQ,
16940 : ObjectIdGetDatum(RelationRelationId));
16941 554 : ScanKeyInit(&key[1],
16942 : Anum_pg_depend_objid,
16943 : BTEqualStrategyNumber, F_OIDEQ,
16944 : ObjectIdGetDatum(relid));
16945 554 : ScanKeyInit(&key[2],
16946 : Anum_pg_depend_objsubid,
16947 : BTEqualStrategyNumber, F_INT4EQ,
16948 : Int32GetDatum(0));
16949 :
16950 554 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
16951 : NULL, 3, key);
16952 :
16953 1722 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
16954 : {
16955 1168 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
16956 :
16957 1168 : if (dep->refclassid == refclassid &&
16958 596 : dep->refobjid == refobjid &&
16959 554 : dep->refobjsubid == 0 &&
16960 554 : dep->deptype == deptype)
16961 554 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
16962 : }
16963 :
16964 554 : systable_endscan(scan);
16965 554 : table_close(catalogRelation, RowExclusiveLock);
16966 554 : }
16967 :
16968 : /*
16969 : * ALTER TABLE OF
16970 : *
16971 : * Attach a table to a composite type, as though it had been created with CREATE
16972 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16973 : * subject table must not have inheritance parents. These restrictions ensure
16974 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16975 : *
16976 : * The address of the type is returned.
16977 : */
16978 : static ObjectAddress
16979 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
16980 : {
16981 66 : Oid relid = RelationGetRelid(rel);
16982 : Type typetuple;
16983 : Form_pg_type typeform;
16984 : Oid typeid;
16985 : Relation inheritsRelation,
16986 : relationRelation;
16987 : SysScanDesc scan;
16988 : ScanKeyData key;
16989 : AttrNumber table_attno,
16990 : type_attno;
16991 : TupleDesc typeTupleDesc,
16992 : tableTupleDesc;
16993 : ObjectAddress tableobj,
16994 : typeobj;
16995 : HeapTuple classtuple;
16996 :
16997 : /* Validate the type. */
16998 66 : typetuple = typenameType(NULL, ofTypename, NULL);
16999 66 : check_of_type(typetuple);
17000 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17001 66 : typeid = typeform->oid;
17002 :
17003 : /* Fail if the table has any inheritance parents. */
17004 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17005 66 : ScanKeyInit(&key,
17006 : Anum_pg_inherits_inhrelid,
17007 : BTEqualStrategyNumber, F_OIDEQ,
17008 : ObjectIdGetDatum(relid));
17009 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17010 : true, NULL, 1, &key);
17011 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17012 6 : ereport(ERROR,
17013 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17014 : errmsg("typed tables cannot inherit")));
17015 60 : systable_endscan(scan);
17016 60 : table_close(inheritsRelation, AccessShareLock);
17017 :
17018 : /*
17019 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17020 : * require that the order also match. However, attnotnull need not match.
17021 : */
17022 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17023 60 : tableTupleDesc = RelationGetDescr(rel);
17024 60 : table_attno = 1;
17025 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17026 : {
17027 : Form_pg_attribute type_attr,
17028 : table_attr;
17029 : const char *type_attname,
17030 : *table_attname;
17031 :
17032 : /* Get the next non-dropped type attribute. */
17033 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17034 154 : if (type_attr->attisdropped)
17035 44 : continue;
17036 110 : type_attname = NameStr(type_attr->attname);
17037 :
17038 : /* Get the next non-dropped table attribute. */
17039 : do
17040 : {
17041 122 : if (table_attno > tableTupleDesc->natts)
17042 6 : ereport(ERROR,
17043 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17044 : errmsg("table is missing column \"%s\"",
17045 : type_attname)));
17046 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17047 116 : table_attno++;
17048 116 : } while (table_attr->attisdropped);
17049 104 : table_attname = NameStr(table_attr->attname);
17050 :
17051 : /* Compare name. */
17052 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17053 6 : ereport(ERROR,
17054 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17055 : errmsg("table has column \"%s\" where type requires \"%s\"",
17056 : table_attname, type_attname)));
17057 :
17058 : /* Compare type. */
17059 98 : if (table_attr->atttypid != type_attr->atttypid ||
17060 92 : table_attr->atttypmod != type_attr->atttypmod ||
17061 86 : table_attr->attcollation != type_attr->attcollation)
17062 12 : ereport(ERROR,
17063 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17064 : errmsg("table \"%s\" has different type for column \"%s\"",
17065 : RelationGetRelationName(rel), type_attname)));
17066 : }
17067 36 : ReleaseTupleDesc(typeTupleDesc);
17068 :
17069 : /* Any remaining columns at the end of the table had better be dropped. */
17070 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17071 : {
17072 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17073 : table_attno - 1);
17074 :
17075 6 : if (!table_attr->attisdropped)
17076 6 : ereport(ERROR,
17077 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17078 : errmsg("table has extra column \"%s\"",
17079 : NameStr(table_attr->attname))));
17080 : }
17081 :
17082 : /* If the table was already typed, drop the existing dependency. */
17083 30 : if (rel->rd_rel->reloftype)
17084 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17085 : DEPENDENCY_NORMAL);
17086 :
17087 : /* Record a dependency on the new type. */
17088 30 : tableobj.classId = RelationRelationId;
17089 30 : tableobj.objectId = relid;
17090 30 : tableobj.objectSubId = 0;
17091 30 : typeobj.classId = TypeRelationId;
17092 30 : typeobj.objectId = typeid;
17093 30 : typeobj.objectSubId = 0;
17094 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17095 :
17096 : /* Update pg_class.reloftype */
17097 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17098 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17099 30 : if (!HeapTupleIsValid(classtuple))
17100 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17101 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17102 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17103 :
17104 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17105 :
17106 30 : heap_freetuple(classtuple);
17107 30 : table_close(relationRelation, RowExclusiveLock);
17108 :
17109 30 : ReleaseSysCache(typetuple);
17110 :
17111 30 : return typeobj;
17112 : }
17113 :
17114 : /*
17115 : * ALTER TABLE NOT OF
17116 : *
17117 : * Detach a typed table from its originating type. Just clear reloftype and
17118 : * remove the dependency.
17119 : */
17120 : static void
17121 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17122 : {
17123 6 : Oid relid = RelationGetRelid(rel);
17124 : Relation relationRelation;
17125 : HeapTuple tuple;
17126 :
17127 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17128 0 : ereport(ERROR,
17129 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17130 : errmsg("\"%s\" is not a typed table",
17131 : RelationGetRelationName(rel))));
17132 :
17133 : /*
17134 : * We don't bother to check ownership of the type --- ownership of the
17135 : * table is presumed enough rights. No lock required on the type, either.
17136 : */
17137 :
17138 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17139 : DEPENDENCY_NORMAL);
17140 :
17141 : /* Clear pg_class.reloftype */
17142 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17143 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17144 6 : if (!HeapTupleIsValid(tuple))
17145 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17146 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17147 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17148 :
17149 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17150 :
17151 6 : heap_freetuple(tuple);
17152 6 : table_close(relationRelation, RowExclusiveLock);
17153 6 : }
17154 :
17155 : /*
17156 : * relation_mark_replica_identity: Update a table's replica identity
17157 : *
17158 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17159 : * index. Otherwise, it must be InvalidOid.
17160 : *
17161 : * Caller had better hold an exclusive lock on the relation, as the results
17162 : * of running two of these concurrently wouldn't be pretty.
17163 : */
17164 : static void
17165 406 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17166 : bool is_internal)
17167 : {
17168 : Relation pg_index;
17169 : Relation pg_class;
17170 : HeapTuple pg_class_tuple;
17171 : HeapTuple pg_index_tuple;
17172 : Form_pg_class pg_class_form;
17173 : Form_pg_index pg_index_form;
17174 : ListCell *index;
17175 :
17176 : /*
17177 : * Check whether relreplident has changed, and update it if so.
17178 : */
17179 406 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17180 406 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17181 : ObjectIdGetDatum(RelationGetRelid(rel)));
17182 406 : if (!HeapTupleIsValid(pg_class_tuple))
17183 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17184 : RelationGetRelationName(rel));
17185 406 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17186 406 : if (pg_class_form->relreplident != ri_type)
17187 : {
17188 356 : pg_class_form->relreplident = ri_type;
17189 356 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17190 : }
17191 406 : table_close(pg_class, RowExclusiveLock);
17192 406 : heap_freetuple(pg_class_tuple);
17193 :
17194 : /*
17195 : * Update the per-index indisreplident flags correctly.
17196 : */
17197 406 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17198 1084 : foreach(index, RelationGetIndexList(rel))
17199 : {
17200 678 : Oid thisIndexOid = lfirst_oid(index);
17201 678 : bool dirty = false;
17202 :
17203 678 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17204 : ObjectIdGetDatum(thisIndexOid));
17205 678 : if (!HeapTupleIsValid(pg_index_tuple))
17206 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17207 678 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17208 :
17209 678 : if (thisIndexOid == indexOid)
17210 : {
17211 : /* Set the bit if not already set. */
17212 220 : if (!pg_index_form->indisreplident)
17213 : {
17214 202 : dirty = true;
17215 202 : pg_index_form->indisreplident = true;
17216 : }
17217 : }
17218 : else
17219 : {
17220 : /* Unset the bit if set. */
17221 458 : if (pg_index_form->indisreplident)
17222 : {
17223 46 : dirty = true;
17224 46 : pg_index_form->indisreplident = false;
17225 : }
17226 : }
17227 :
17228 678 : if (dirty)
17229 : {
17230 248 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17231 248 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17232 : InvalidOid, is_internal);
17233 :
17234 : /*
17235 : * Invalidate the relcache for the table, so that after we commit
17236 : * all sessions will refresh the table's replica identity index
17237 : * before attempting any UPDATE or DELETE on the table. (If we
17238 : * changed the table's pg_class row above, then a relcache inval
17239 : * is already queued due to that; but we might not have.)
17240 : */
17241 248 : CacheInvalidateRelcache(rel);
17242 : }
17243 678 : heap_freetuple(pg_index_tuple);
17244 : }
17245 :
17246 406 : table_close(pg_index, RowExclusiveLock);
17247 406 : }
17248 :
17249 : /*
17250 : * ALTER TABLE <name> REPLICA IDENTITY ...
17251 : */
17252 : static void
17253 460 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17254 : {
17255 : Oid indexOid;
17256 : Relation indexRel;
17257 : int key;
17258 :
17259 460 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17260 : {
17261 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17262 6 : return;
17263 : }
17264 454 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17265 : {
17266 144 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17267 144 : return;
17268 : }
17269 310 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17270 : {
17271 36 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17272 36 : return;
17273 : }
17274 274 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17275 : {
17276 : /* fallthrough */ ;
17277 : }
17278 : else
17279 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17280 :
17281 : /* Check that the index exists */
17282 274 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17283 274 : if (!OidIsValid(indexOid))
17284 0 : ereport(ERROR,
17285 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17286 : errmsg("index \"%s\" for table \"%s\" does not exist",
17287 : stmt->name, RelationGetRelationName(rel))));
17288 :
17289 274 : indexRel = index_open(indexOid, ShareLock);
17290 :
17291 : /* Check that the index is on the relation we're altering. */
17292 274 : if (indexRel->rd_index == NULL ||
17293 274 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17294 6 : ereport(ERROR,
17295 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17296 : errmsg("\"%s\" is not an index for table \"%s\"",
17297 : RelationGetRelationName(indexRel),
17298 : RelationGetRelationName(rel))));
17299 : /* The AM must support uniqueness, and the index must in fact be unique. */
17300 268 : if (!indexRel->rd_indam->amcanunique ||
17301 256 : !indexRel->rd_index->indisunique)
17302 18 : ereport(ERROR,
17303 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17304 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17305 : RelationGetRelationName(indexRel))));
17306 : /* Deferred indexes are not guaranteed to be always unique. */
17307 250 : if (!indexRel->rd_index->indimmediate)
17308 12 : ereport(ERROR,
17309 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17310 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17311 : RelationGetRelationName(indexRel))));
17312 : /* Expression indexes aren't supported. */
17313 238 : if (RelationGetIndexExpressions(indexRel) != NIL)
17314 6 : ereport(ERROR,
17315 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17316 : errmsg("cannot use expression index \"%s\" as replica identity",
17317 : RelationGetRelationName(indexRel))));
17318 : /* Predicate indexes aren't supported. */
17319 232 : if (RelationGetIndexPredicate(indexRel) != NIL)
17320 6 : ereport(ERROR,
17321 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17322 : errmsg("cannot use partial index \"%s\" as replica identity",
17323 : RelationGetRelationName(indexRel))));
17324 :
17325 : /* Check index for nullable columns. */
17326 498 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17327 : {
17328 278 : int16 attno = indexRel->rd_index->indkey.values[key];
17329 : Form_pg_attribute attr;
17330 :
17331 : /*
17332 : * Reject any other system columns. (Going forward, we'll disallow
17333 : * indexes containing such columns in the first place, but they might
17334 : * exist in older branches.)
17335 : */
17336 278 : if (attno <= 0)
17337 0 : ereport(ERROR,
17338 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17339 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17340 : RelationGetRelationName(indexRel), attno)));
17341 :
17342 278 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17343 278 : if (!attr->attnotnull)
17344 6 : ereport(ERROR,
17345 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17346 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17347 : RelationGetRelationName(indexRel),
17348 : NameStr(attr->attname))));
17349 : }
17350 :
17351 : /* This index is suitable for use as a replica identity. Mark it. */
17352 220 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17353 :
17354 220 : index_close(indexRel, NoLock);
17355 : }
17356 :
17357 : /*
17358 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17359 : */
17360 : static void
17361 294 : ATExecSetRowSecurity(Relation rel, bool rls)
17362 : {
17363 : Relation pg_class;
17364 : Oid relid;
17365 : HeapTuple tuple;
17366 :
17367 294 : relid = RelationGetRelid(rel);
17368 :
17369 : /* Pull the record for this relation and update it */
17370 294 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17371 :
17372 294 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17373 :
17374 294 : if (!HeapTupleIsValid(tuple))
17375 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17376 :
17377 294 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17378 294 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17379 :
17380 294 : InvokeObjectPostAlterHook(RelationRelationId,
17381 : RelationGetRelid(rel), 0);
17382 :
17383 294 : table_close(pg_class, RowExclusiveLock);
17384 294 : heap_freetuple(tuple);
17385 294 : }
17386 :
17387 : /*
17388 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17389 : */
17390 : static void
17391 120 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17392 : {
17393 : Relation pg_class;
17394 : Oid relid;
17395 : HeapTuple tuple;
17396 :
17397 120 : relid = RelationGetRelid(rel);
17398 :
17399 120 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17400 :
17401 120 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17402 :
17403 120 : if (!HeapTupleIsValid(tuple))
17404 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17405 :
17406 120 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17407 120 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17408 :
17409 120 : InvokeObjectPostAlterHook(RelationRelationId,
17410 : RelationGetRelid(rel), 0);
17411 :
17412 120 : table_close(pg_class, RowExclusiveLock);
17413 120 : heap_freetuple(tuple);
17414 120 : }
17415 :
17416 : /*
17417 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
17418 : */
17419 : static void
17420 58 : ATExecGenericOptions(Relation rel, List *options)
17421 : {
17422 : Relation ftrel;
17423 : ForeignServer *server;
17424 : ForeignDataWrapper *fdw;
17425 : HeapTuple tuple;
17426 : bool isnull;
17427 : Datum repl_val[Natts_pg_foreign_table];
17428 : bool repl_null[Natts_pg_foreign_table];
17429 : bool repl_repl[Natts_pg_foreign_table];
17430 : Datum datum;
17431 : Form_pg_foreign_table tableform;
17432 :
17433 58 : if (options == NIL)
17434 0 : return;
17435 :
17436 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17437 :
17438 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17439 : ObjectIdGetDatum(rel->rd_id));
17440 58 : if (!HeapTupleIsValid(tuple))
17441 0 : ereport(ERROR,
17442 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17443 : errmsg("foreign table \"%s\" does not exist",
17444 : RelationGetRelationName(rel))));
17445 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17446 58 : server = GetForeignServer(tableform->ftserver);
17447 58 : fdw = GetForeignDataWrapper(server->fdwid);
17448 :
17449 58 : memset(repl_val, 0, sizeof(repl_val));
17450 58 : memset(repl_null, false, sizeof(repl_null));
17451 58 : memset(repl_repl, false, sizeof(repl_repl));
17452 :
17453 : /* Extract the current options */
17454 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
17455 : tuple,
17456 : Anum_pg_foreign_table_ftoptions,
17457 : &isnull);
17458 58 : if (isnull)
17459 4 : datum = PointerGetDatum(NULL);
17460 :
17461 : /* Transform the options */
17462 58 : datum = transformGenericOptions(ForeignTableRelationId,
17463 : datum,
17464 : options,
17465 : fdw->fdwvalidator);
17466 :
17467 56 : if (PointerIsValid(DatumGetPointer(datum)))
17468 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17469 : else
17470 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17471 :
17472 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17473 :
17474 : /* Everything looks good - update the tuple */
17475 :
17476 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17477 : repl_val, repl_null, repl_repl);
17478 :
17479 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17480 :
17481 : /*
17482 : * Invalidate relcache so that all sessions will refresh any cached plans
17483 : * that might depend on the old options.
17484 : */
17485 56 : CacheInvalidateRelcache(rel);
17486 :
17487 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
17488 : RelationGetRelid(rel), 0);
17489 :
17490 56 : table_close(ftrel, RowExclusiveLock);
17491 :
17492 56 : heap_freetuple(tuple);
17493 : }
17494 :
17495 : /*
17496 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
17497 : *
17498 : * Return value is the address of the modified column
17499 : */
17500 : static ObjectAddress
17501 68 : ATExecSetCompression(Relation rel,
17502 : const char *column,
17503 : Node *newValue,
17504 : LOCKMODE lockmode)
17505 : {
17506 : Relation attrel;
17507 : HeapTuple tuple;
17508 : Form_pg_attribute atttableform;
17509 : AttrNumber attnum;
17510 : char *compression;
17511 : char cmethod;
17512 : ObjectAddress address;
17513 :
17514 68 : compression = strVal(newValue);
17515 :
17516 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
17517 :
17518 : /* copy the cache entry so we can scribble on it below */
17519 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17520 68 : if (!HeapTupleIsValid(tuple))
17521 0 : ereport(ERROR,
17522 : (errcode(ERRCODE_UNDEFINED_COLUMN),
17523 : errmsg("column \"%s\" of relation \"%s\" does not exist",
17524 : column, RelationGetRelationName(rel))));
17525 :
17526 : /* prevent them from altering a system attribute */
17527 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17528 68 : attnum = atttableform->attnum;
17529 68 : if (attnum <= 0)
17530 0 : ereport(ERROR,
17531 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17532 : errmsg("cannot alter system column \"%s\"", column)));
17533 :
17534 : /*
17535 : * Check that column type is compressible, then get the attribute
17536 : * compression method code
17537 : */
17538 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17539 :
17540 : /* update pg_attribute entry */
17541 62 : atttableform->attcompression = cmethod;
17542 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17543 :
17544 62 : InvokeObjectPostAlterHook(RelationRelationId,
17545 : RelationGetRelid(rel),
17546 : attnum);
17547 :
17548 : /*
17549 : * Apply the change to indexes as well (only for simple index columns,
17550 : * matching behavior of index.c ConstructTupleDescriptor()).
17551 : */
17552 62 : SetIndexStorageProperties(rel, attrel, attnum,
17553 : false, 0,
17554 : true, cmethod,
17555 : lockmode);
17556 :
17557 62 : heap_freetuple(tuple);
17558 :
17559 62 : table_close(attrel, RowExclusiveLock);
17560 :
17561 : /* make changes visible */
17562 62 : CommandCounterIncrement();
17563 :
17564 62 : ObjectAddressSubSet(address, RelationRelationId,
17565 : RelationGetRelid(rel), attnum);
17566 62 : return address;
17567 : }
17568 :
17569 :
17570 : /*
17571 : * Preparation phase for SET LOGGED/UNLOGGED
17572 : *
17573 : * This verifies that we're not trying to change a temp table. Also,
17574 : * existing foreign key constraints are checked to avoid ending up with
17575 : * permanent tables referencing unlogged tables.
17576 : */
17577 : static void
17578 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
17579 : {
17580 : Relation pg_constraint;
17581 : HeapTuple tuple;
17582 : SysScanDesc scan;
17583 : ScanKeyData skey[1];
17584 :
17585 : /*
17586 : * Disallow changing status for a temp table. Also verify whether we can
17587 : * get away with doing nothing; in such cases we don't need to run the
17588 : * checks below, either.
17589 : */
17590 100 : switch (rel->rd_rel->relpersistence)
17591 : {
17592 0 : case RELPERSISTENCE_TEMP:
17593 0 : ereport(ERROR,
17594 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17595 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
17596 : RelationGetRelationName(rel)),
17597 : errtable(rel)));
17598 : break;
17599 56 : case RELPERSISTENCE_PERMANENT:
17600 56 : if (toLogged)
17601 : /* nothing to do */
17602 12 : return;
17603 50 : break;
17604 44 : case RELPERSISTENCE_UNLOGGED:
17605 44 : if (!toLogged)
17606 : /* nothing to do */
17607 6 : return;
17608 38 : break;
17609 : }
17610 :
17611 : /*
17612 : * Check that the table is not part of any publication when changing to
17613 : * UNLOGGED, as UNLOGGED tables can't be published.
17614 : */
17615 138 : if (!toLogged &&
17616 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
17617 0 : ereport(ERROR,
17618 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17619 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17620 : RelationGetRelationName(rel)),
17621 : errdetail("Unlogged relations cannot be replicated.")));
17622 :
17623 : /*
17624 : * Check existing foreign key constraints to preserve the invariant that
17625 : * permanent tables cannot reference unlogged ones. Self-referencing
17626 : * foreign keys can safely be ignored.
17627 : */
17628 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17629 :
17630 : /*
17631 : * Scan conrelid if changing to permanent, else confrelid. This also
17632 : * determines whether a useful index exists.
17633 : */
17634 88 : ScanKeyInit(&skey[0],
17635 : toLogged ? Anum_pg_constraint_conrelid :
17636 : Anum_pg_constraint_confrelid,
17637 : BTEqualStrategyNumber, F_OIDEQ,
17638 : ObjectIdGetDatum(RelationGetRelid(rel)));
17639 88 : scan = systable_beginscan(pg_constraint,
17640 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17641 : true, NULL, 1, skey);
17642 :
17643 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17644 : {
17645 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17646 :
17647 66 : if (con->contype == CONSTRAINT_FOREIGN)
17648 : {
17649 : Oid foreignrelid;
17650 : Relation foreignrel;
17651 :
17652 : /* the opposite end of what we used as scankey */
17653 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17654 :
17655 : /* ignore if self-referencing */
17656 30 : if (RelationGetRelid(rel) == foreignrelid)
17657 12 : continue;
17658 :
17659 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17660 :
17661 18 : if (toLogged)
17662 : {
17663 6 : if (!RelationIsPermanent(foreignrel))
17664 6 : ereport(ERROR,
17665 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17666 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17667 : RelationGetRelationName(rel),
17668 : RelationGetRelationName(foreignrel)),
17669 : errtableconstraint(rel, NameStr(con->conname))));
17670 : }
17671 : else
17672 : {
17673 12 : if (RelationIsPermanent(foreignrel))
17674 6 : ereport(ERROR,
17675 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17676 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17677 : RelationGetRelationName(rel),
17678 : RelationGetRelationName(foreignrel)),
17679 : errtableconstraint(rel, NameStr(con->conname))));
17680 : }
17681 :
17682 6 : relation_close(foreignrel, AccessShareLock);
17683 : }
17684 : }
17685 :
17686 76 : systable_endscan(scan);
17687 :
17688 76 : table_close(pg_constraint, AccessShareLock);
17689 :
17690 : /* force rewrite if necessary; see comment in ATRewriteTables */
17691 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
17692 76 : if (toLogged)
17693 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
17694 : else
17695 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
17696 76 : tab->chgPersistence = true;
17697 : }
17698 :
17699 : /*
17700 : * Execute ALTER TABLE SET SCHEMA
17701 : */
17702 : ObjectAddress
17703 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17704 : {
17705 : Relation rel;
17706 : Oid relid;
17707 : Oid oldNspOid;
17708 : Oid nspOid;
17709 : RangeVar *newrv;
17710 : ObjectAddresses *objsMoved;
17711 : ObjectAddress myself;
17712 :
17713 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17714 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
17715 : RangeVarCallbackForAlterRelation,
17716 : (void *) stmt);
17717 :
17718 102 : if (!OidIsValid(relid))
17719 : {
17720 12 : ereport(NOTICE,
17721 : (errmsg("relation \"%s\" does not exist, skipping",
17722 : stmt->relation->relname)));
17723 12 : return InvalidObjectAddress;
17724 : }
17725 :
17726 90 : rel = relation_open(relid, NoLock);
17727 :
17728 90 : oldNspOid = RelationGetNamespace(rel);
17729 :
17730 : /* If it's an owned sequence, disallow moving it by itself. */
17731 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17732 : {
17733 : Oid tableId;
17734 : int32 colId;
17735 :
17736 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17737 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17738 6 : ereport(ERROR,
17739 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17740 : errmsg("cannot move an owned sequence into another schema"),
17741 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
17742 : RelationGetRelationName(rel),
17743 : get_rel_name(tableId))));
17744 : }
17745 :
17746 : /* Get and lock schema OID and check its permissions. */
17747 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17748 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17749 :
17750 : /* common checks on switching namespaces */
17751 84 : CheckSetNamespace(oldNspOid, nspOid);
17752 :
17753 84 : objsMoved = new_object_addresses();
17754 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17755 84 : free_object_addresses(objsMoved);
17756 :
17757 84 : ObjectAddressSet(myself, RelationRelationId, relid);
17758 :
17759 84 : if (oldschema)
17760 84 : *oldschema = oldNspOid;
17761 :
17762 : /* close rel, but keep lock until commit */
17763 84 : relation_close(rel, NoLock);
17764 :
17765 84 : return myself;
17766 : }
17767 :
17768 : /*
17769 : * The guts of relocating a table or materialized view to another namespace:
17770 : * besides moving the relation itself, its dependent objects are relocated to
17771 : * the new schema.
17772 : */
17773 : void
17774 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17775 : ObjectAddresses *objsMoved)
17776 : {
17777 : Relation classRel;
17778 :
17779 : Assert(objsMoved != NULL);
17780 :
17781 : /* OK, modify the pg_class row and pg_depend entry */
17782 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
17783 :
17784 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17785 : nspOid, true, objsMoved);
17786 :
17787 : /* Fix the table's row type too, if it has one */
17788 86 : if (OidIsValid(rel->rd_rel->reltype))
17789 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
17790 : false, /* isImplicitArray */
17791 : false, /* ignoreDependent */
17792 : false, /* errorOnTableType */
17793 : objsMoved);
17794 :
17795 : /* Fix other dependent stuff */
17796 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17797 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17798 : objsMoved, AccessExclusiveLock);
17799 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17800 : false, objsMoved);
17801 :
17802 86 : table_close(classRel, RowExclusiveLock);
17803 86 : }
17804 :
17805 : /*
17806 : * The guts of relocating a relation to another namespace: fix the pg_class
17807 : * entry, and the pg_depend entry if any. Caller must already have
17808 : * opened and write-locked pg_class.
17809 : */
17810 : void
17811 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17812 : Oid oldNspOid, Oid newNspOid,
17813 : bool hasDependEntry,
17814 : ObjectAddresses *objsMoved)
17815 : {
17816 : HeapTuple classTup;
17817 : Form_pg_class classForm;
17818 : ObjectAddress thisobj;
17819 188 : bool already_done = false;
17820 :
17821 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
17822 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
17823 188 : if (!HeapTupleIsValid(classTup))
17824 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
17825 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
17826 :
17827 : Assert(classForm->relnamespace == oldNspOid);
17828 :
17829 188 : thisobj.classId = RelationRelationId;
17830 188 : thisobj.objectId = relOid;
17831 188 : thisobj.objectSubId = 0;
17832 :
17833 : /*
17834 : * If the object has already been moved, don't move it again. If it's
17835 : * already in the right place, don't move it, but still fire the object
17836 : * access hook.
17837 : */
17838 188 : already_done = object_address_present(&thisobj, objsMoved);
17839 188 : if (!already_done && oldNspOid != newNspOid)
17840 146 : {
17841 146 : ItemPointerData otid = classTup->t_self;
17842 :
17843 : /* check for duplicate name (more friendly than unique-index failure) */
17844 146 : if (get_relname_relid(NameStr(classForm->relname),
17845 : newNspOid) != InvalidOid)
17846 0 : ereport(ERROR,
17847 : (errcode(ERRCODE_DUPLICATE_TABLE),
17848 : errmsg("relation \"%s\" already exists in schema \"%s\"",
17849 : NameStr(classForm->relname),
17850 : get_namespace_name(newNspOid))));
17851 :
17852 : /* classTup is a copy, so OK to scribble on */
17853 146 : classForm->relnamespace = newNspOid;
17854 :
17855 146 : CatalogTupleUpdate(classRel, &otid, classTup);
17856 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
17857 :
17858 :
17859 : /* Update dependency on schema if caller said so */
17860 250 : if (hasDependEntry &&
17861 104 : changeDependencyFor(RelationRelationId,
17862 : relOid,
17863 : NamespaceRelationId,
17864 : oldNspOid,
17865 : newNspOid) != 1)
17866 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
17867 : NameStr(classForm->relname));
17868 : }
17869 : else
17870 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
17871 188 : if (!already_done)
17872 : {
17873 188 : add_exact_object_address(&thisobj, objsMoved);
17874 :
17875 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17876 : }
17877 :
17878 188 : heap_freetuple(classTup);
17879 188 : }
17880 :
17881 : /*
17882 : * Move all indexes for the specified relation to another namespace.
17883 : *
17884 : * Note: we assume adequate permission checking was done by the caller,
17885 : * and that the caller has a suitable lock on the owning relation.
17886 : */
17887 : static void
17888 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
17889 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17890 : {
17891 : List *indexList;
17892 : ListCell *l;
17893 :
17894 86 : indexList = RelationGetIndexList(rel);
17895 :
17896 132 : foreach(l, indexList)
17897 : {
17898 46 : Oid indexOid = lfirst_oid(l);
17899 : ObjectAddress thisobj;
17900 :
17901 46 : thisobj.classId = RelationRelationId;
17902 46 : thisobj.objectId = indexOid;
17903 46 : thisobj.objectSubId = 0;
17904 :
17905 : /*
17906 : * Note: currently, the index will not have its own dependency on the
17907 : * namespace, so we don't need to do changeDependencyFor(). There's no
17908 : * row type in pg_type, either.
17909 : *
17910 : * XXX this objsMoved test may be pointless -- surely we have a single
17911 : * dependency link from a relation to each index?
17912 : */
17913 46 : if (!object_address_present(&thisobj, objsMoved))
17914 : {
17915 46 : AlterRelationNamespaceInternal(classRel, indexOid,
17916 : oldNspOid, newNspOid,
17917 : false, objsMoved);
17918 46 : add_exact_object_address(&thisobj, objsMoved);
17919 : }
17920 : }
17921 :
17922 86 : list_free(indexList);
17923 86 : }
17924 :
17925 : /*
17926 : * Move all identity and SERIAL-column sequences of the specified relation to another
17927 : * namespace.
17928 : *
17929 : * Note: we assume adequate permission checking was done by the caller,
17930 : * and that the caller has a suitable lock on the owning relation.
17931 : */
17932 : static void
17933 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
17934 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
17935 : LOCKMODE lockmode)
17936 : {
17937 : Relation depRel;
17938 : SysScanDesc scan;
17939 : ScanKeyData key[2];
17940 : HeapTuple tup;
17941 :
17942 : /*
17943 : * SERIAL sequences are those having an auto dependency on one of the
17944 : * table's columns (we don't care *which* column, exactly).
17945 : */
17946 86 : depRel = table_open(DependRelationId, AccessShareLock);
17947 :
17948 86 : ScanKeyInit(&key[0],
17949 : Anum_pg_depend_refclassid,
17950 : BTEqualStrategyNumber, F_OIDEQ,
17951 : ObjectIdGetDatum(RelationRelationId));
17952 86 : ScanKeyInit(&key[1],
17953 : Anum_pg_depend_refobjid,
17954 : BTEqualStrategyNumber, F_OIDEQ,
17955 : ObjectIdGetDatum(RelationGetRelid(rel)));
17956 : /* we leave refobjsubid unspecified */
17957 :
17958 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
17959 : NULL, 2, key);
17960 :
17961 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
17962 : {
17963 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
17964 : Relation seqRel;
17965 :
17966 : /* skip dependencies other than auto dependencies on columns */
17967 530 : if (depForm->refobjsubid == 0 ||
17968 382 : depForm->classid != RelationRelationId ||
17969 42 : depForm->objsubid != 0 ||
17970 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
17971 488 : continue;
17972 :
17973 : /* Use relation_open just in case it's an index */
17974 42 : seqRel = relation_open(depForm->objid, lockmode);
17975 :
17976 : /* skip non-sequence relations */
17977 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17978 : {
17979 : /* No need to keep the lock */
17980 0 : relation_close(seqRel, lockmode);
17981 0 : continue;
17982 : }
17983 :
17984 : /* Fix the pg_class and pg_depend entries */
17985 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
17986 : oldNspOid, newNspOid,
17987 : true, objsMoved);
17988 :
17989 : /*
17990 : * Sequences used to have entries in pg_type, but no longer do. If we
17991 : * ever re-instate that, we'll need to move the pg_type entry to the
17992 : * new namespace, too (using AlterTypeNamespaceInternal).
17993 : */
17994 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
17995 :
17996 : /* Now we can close it. Keep the lock till end of transaction. */
17997 42 : relation_close(seqRel, NoLock);
17998 : }
17999 :
18000 86 : systable_endscan(scan);
18001 :
18002 86 : relation_close(depRel, AccessShareLock);
18003 86 : }
18004 :
18005 :
18006 : /*
18007 : * This code supports
18008 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18009 : *
18010 : * Because we only support this for TEMP tables, it's sufficient to remember
18011 : * the state in a backend-local data structure.
18012 : */
18013 :
18014 : /*
18015 : * Register a newly-created relation's ON COMMIT action.
18016 : */
18017 : void
18018 166 : register_on_commit_action(Oid relid, OnCommitAction action)
18019 : {
18020 : OnCommitItem *oc;
18021 : MemoryContext oldcxt;
18022 :
18023 : /*
18024 : * We needn't bother registering the relation unless there is an ON COMMIT
18025 : * action we need to take.
18026 : */
18027 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18028 24 : return;
18029 :
18030 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18031 :
18032 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18033 142 : oc->relid = relid;
18034 142 : oc->oncommit = action;
18035 142 : oc->creating_subid = GetCurrentSubTransactionId();
18036 142 : oc->deleting_subid = InvalidSubTransactionId;
18037 :
18038 : /*
18039 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18040 : * order of registration. That might not be essential but it seems
18041 : * reasonable.
18042 : */
18043 142 : on_commits = lcons(oc, on_commits);
18044 :
18045 142 : MemoryContextSwitchTo(oldcxt);
18046 : }
18047 :
18048 : /*
18049 : * Unregister any ON COMMIT action when a relation is deleted.
18050 : *
18051 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18052 : */
18053 : void
18054 45070 : remove_on_commit_action(Oid relid)
18055 : {
18056 : ListCell *l;
18057 :
18058 45204 : foreach(l, on_commits)
18059 : {
18060 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18061 :
18062 264 : if (oc->relid == relid)
18063 : {
18064 130 : oc->deleting_subid = GetCurrentSubTransactionId();
18065 130 : break;
18066 : }
18067 : }
18068 45070 : }
18069 :
18070 : /*
18071 : * Perform ON COMMIT actions.
18072 : *
18073 : * This is invoked just before actually committing, since it's possible
18074 : * to encounter errors.
18075 : */
18076 : void
18077 702208 : PreCommit_on_commit_actions(void)
18078 : {
18079 : ListCell *l;
18080 702208 : List *oids_to_truncate = NIL;
18081 702208 : List *oids_to_drop = NIL;
18082 :
18083 702924 : foreach(l, on_commits)
18084 : {
18085 716 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18086 :
18087 : /* Ignore entry if already dropped in this xact */
18088 716 : if (oc->deleting_subid != InvalidSubTransactionId)
18089 68 : continue;
18090 :
18091 648 : switch (oc->oncommit)
18092 : {
18093 0 : case ONCOMMIT_NOOP:
18094 : case ONCOMMIT_PRESERVE_ROWS:
18095 : /* Do nothing (there shouldn't be such entries, actually) */
18096 0 : break;
18097 598 : case ONCOMMIT_DELETE_ROWS:
18098 :
18099 : /*
18100 : * If this transaction hasn't accessed any temporary
18101 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18102 : * tables, as they must still be empty.
18103 : */
18104 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18105 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18106 598 : break;
18107 50 : case ONCOMMIT_DROP:
18108 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18109 50 : break;
18110 : }
18111 716 : }
18112 :
18113 : /*
18114 : * Truncate relations before dropping so that all dependencies between
18115 : * relations are removed after they are worked on. Doing it like this
18116 : * might be a waste as it is possible that a relation being truncated will
18117 : * be dropped anyway due to its parent being dropped, but this makes the
18118 : * code more robust because of not having to re-check that the relation
18119 : * exists at truncation time.
18120 : */
18121 702208 : if (oids_to_truncate != NIL)
18122 334 : heap_truncate(oids_to_truncate);
18123 :
18124 702202 : if (oids_to_drop != NIL)
18125 : {
18126 44 : ObjectAddresses *targetObjects = new_object_addresses();
18127 :
18128 94 : foreach(l, oids_to_drop)
18129 : {
18130 : ObjectAddress object;
18131 :
18132 50 : object.classId = RelationRelationId;
18133 50 : object.objectId = lfirst_oid(l);
18134 50 : object.objectSubId = 0;
18135 :
18136 : Assert(!object_address_present(&object, targetObjects));
18137 :
18138 50 : add_exact_object_address(&object, targetObjects);
18139 : }
18140 :
18141 : /*
18142 : * Object deletion might involve toast table access (to clean up
18143 : * toasted catalog entries), so ensure we have a valid snapshot.
18144 : */
18145 44 : PushActiveSnapshot(GetTransactionSnapshot());
18146 :
18147 : /*
18148 : * Since this is an automatic drop, rather than one directly initiated
18149 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18150 : */
18151 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18152 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18153 :
18154 44 : PopActiveSnapshot();
18155 :
18156 : #ifdef USE_ASSERT_CHECKING
18157 :
18158 : /*
18159 : * Note that table deletion will call remove_on_commit_action, so the
18160 : * entry should get marked as deleted.
18161 : */
18162 : foreach(l, on_commits)
18163 : {
18164 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18165 :
18166 : if (oc->oncommit != ONCOMMIT_DROP)
18167 : continue;
18168 :
18169 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18170 : }
18171 : #endif
18172 : }
18173 702202 : }
18174 :
18175 : /*
18176 : * Post-commit or post-abort cleanup for ON COMMIT management.
18177 : *
18178 : * All we do here is remove no-longer-needed OnCommitItem entries.
18179 : *
18180 : * During commit, remove entries that were deleted during this transaction;
18181 : * during abort, remove those created during this transaction.
18182 : */
18183 : void
18184 748334 : AtEOXact_on_commit_actions(bool isCommit)
18185 : {
18186 : ListCell *cur_item;
18187 :
18188 749080 : foreach(cur_item, on_commits)
18189 : {
18190 746 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18191 :
18192 848 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18193 102 : oc->creating_subid != InvalidSubTransactionId)
18194 : {
18195 : /* cur_item must be removed */
18196 142 : on_commits = foreach_delete_current(on_commits, cur_item);
18197 142 : pfree(oc);
18198 : }
18199 : else
18200 : {
18201 : /* cur_item must be preserved */
18202 604 : oc->creating_subid = InvalidSubTransactionId;
18203 604 : oc->deleting_subid = InvalidSubTransactionId;
18204 : }
18205 : }
18206 748334 : }
18207 :
18208 : /*
18209 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18210 : *
18211 : * During subabort, we can immediately remove entries created during this
18212 : * subtransaction. During subcommit, just relabel entries marked during
18213 : * this subtransaction as being the parent's responsibility.
18214 : */
18215 : void
18216 19988 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18217 : SubTransactionId parentSubid)
18218 : {
18219 : ListCell *cur_item;
18220 :
18221 19988 : foreach(cur_item, on_commits)
18222 : {
18223 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18224 :
18225 0 : if (!isCommit && oc->creating_subid == mySubid)
18226 : {
18227 : /* cur_item must be removed */
18228 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18229 0 : pfree(oc);
18230 : }
18231 : else
18232 : {
18233 : /* cur_item must be preserved */
18234 0 : if (oc->creating_subid == mySubid)
18235 0 : oc->creating_subid = parentSubid;
18236 0 : if (oc->deleting_subid == mySubid)
18237 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18238 : }
18239 : }
18240 19988 : }
18241 :
18242 : /*
18243 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18244 : * the relation to be locked only if (1) it's a plain or partitioned table,
18245 : * materialized view, or TOAST table and (2) the current user is the owner (or
18246 : * the superuser) or has been granted MAINTAIN. This meets the
18247 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18248 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18249 : */
18250 : void
18251 1000 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18252 : Oid relId, Oid oldRelId, void *arg)
18253 : {
18254 : char relkind;
18255 : AclResult aclresult;
18256 :
18257 : /* Nothing to do if the relation was not found. */
18258 1000 : if (!OidIsValid(relId))
18259 6 : return;
18260 :
18261 : /*
18262 : * If the relation does exist, check whether it's an index. But note that
18263 : * the relation might have been dropped between the time we did the name
18264 : * lookup and now. In that case, there's nothing to do.
18265 : */
18266 994 : relkind = get_rel_relkind(relId);
18267 994 : if (!relkind)
18268 0 : return;
18269 994 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18270 136 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18271 28 : ereport(ERROR,
18272 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18273 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18274 :
18275 : /* Check permissions */
18276 966 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18277 966 : if (aclresult != ACLCHECK_OK)
18278 30 : aclcheck_error(aclresult,
18279 30 : get_relkind_objtype(get_rel_relkind(relId)),
18280 30 : relation->relname);
18281 : }
18282 :
18283 : /*
18284 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18285 : */
18286 : static void
18287 2022 : RangeVarCallbackForTruncate(const RangeVar *relation,
18288 : Oid relId, Oid oldRelId, void *arg)
18289 : {
18290 : HeapTuple tuple;
18291 :
18292 : /* Nothing to do if the relation was not found. */
18293 2022 : if (!OidIsValid(relId))
18294 0 : return;
18295 :
18296 2022 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18297 2022 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18298 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18299 :
18300 2022 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18301 2016 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18302 :
18303 1984 : ReleaseSysCache(tuple);
18304 : }
18305 :
18306 : /*
18307 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18308 : * the owner of the relation, or superuser.
18309 : */
18310 : void
18311 15000 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18312 : Oid relId, Oid oldRelId, void *arg)
18313 : {
18314 : HeapTuple tuple;
18315 :
18316 : /* Nothing to do if the relation was not found. */
18317 15000 : if (!OidIsValid(relId))
18318 12 : return;
18319 :
18320 14988 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18321 14988 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18322 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18323 :
18324 14988 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18325 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18326 6 : relation->relname);
18327 :
18328 29844 : if (!allowSystemTableMods &&
18329 14862 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18330 2 : ereport(ERROR,
18331 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18332 : errmsg("permission denied: \"%s\" is a system catalog",
18333 : relation->relname)));
18334 :
18335 14980 : ReleaseSysCache(tuple);
18336 : }
18337 :
18338 : /*
18339 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18340 : * processing.
18341 : */
18342 : static void
18343 30408 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18344 : void *arg)
18345 : {
18346 30408 : Node *stmt = (Node *) arg;
18347 : ObjectType reltype;
18348 : HeapTuple tuple;
18349 : Form_pg_class classform;
18350 : AclResult aclresult;
18351 : char relkind;
18352 :
18353 30408 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18354 30408 : if (!HeapTupleIsValid(tuple))
18355 216 : return; /* concurrently dropped */
18356 30192 : classform = (Form_pg_class) GETSTRUCT(tuple);
18357 30192 : relkind = classform->relkind;
18358 :
18359 : /* Must own relation. */
18360 30192 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18361 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18362 :
18363 : /* No system table modifications unless explicitly allowed. */
18364 30132 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18365 28 : ereport(ERROR,
18366 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18367 : errmsg("permission denied: \"%s\" is a system catalog",
18368 : rv->relname)));
18369 :
18370 : /*
18371 : * Extract the specified relation type from the statement parse tree.
18372 : *
18373 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
18374 : * have CREATE rights on the containing namespace.
18375 : */
18376 30104 : if (IsA(stmt, RenameStmt))
18377 : {
18378 490 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18379 : GetUserId(), ACL_CREATE);
18380 490 : if (aclresult != ACLCHECK_OK)
18381 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
18382 0 : get_namespace_name(classform->relnamespace));
18383 490 : reltype = ((RenameStmt *) stmt)->renameType;
18384 : }
18385 29614 : else if (IsA(stmt, AlterObjectSchemaStmt))
18386 94 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18387 :
18388 29520 : else if (IsA(stmt, AlterTableStmt))
18389 29520 : reltype = ((AlterTableStmt *) stmt)->objtype;
18390 : else
18391 : {
18392 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18393 : reltype = OBJECT_TABLE; /* placate compiler */
18394 : }
18395 :
18396 : /*
18397 : * For compatibility with prior releases, we allow ALTER TABLE to be used
18398 : * with most other types of relations (but not composite types). We allow
18399 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
18400 : * otherwise. Otherwise, the user must select the correct form of the
18401 : * command for the relation at issue.
18402 : */
18403 30104 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18404 0 : ereport(ERROR,
18405 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18406 : errmsg("\"%s\" is not a sequence", rv->relname)));
18407 :
18408 30104 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18409 0 : ereport(ERROR,
18410 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18411 : errmsg("\"%s\" is not a view", rv->relname)));
18412 :
18413 30104 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18414 0 : ereport(ERROR,
18415 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18416 : errmsg("\"%s\" is not a materialized view", rv->relname)));
18417 :
18418 30104 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18419 0 : ereport(ERROR,
18420 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18421 : errmsg("\"%s\" is not a foreign table", rv->relname)));
18422 :
18423 30104 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18424 0 : ereport(ERROR,
18425 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18426 : errmsg("\"%s\" is not a composite type", rv->relname)));
18427 :
18428 30104 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18429 : relkind != RELKIND_PARTITIONED_INDEX
18430 36 : && !IsA(stmt, RenameStmt))
18431 6 : ereport(ERROR,
18432 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18433 : errmsg("\"%s\" is not an index", rv->relname)));
18434 :
18435 : /*
18436 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18437 : * TYPE for that.
18438 : */
18439 30098 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18440 0 : ereport(ERROR,
18441 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18442 : errmsg("\"%s\" is a composite type", rv->relname),
18443 : /* translator: %s is an SQL ALTER command */
18444 : errhint("Use %s instead.",
18445 : "ALTER TYPE")));
18446 :
18447 : /*
18448 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18449 : * to a different schema, such as indexes and TOAST tables.
18450 : */
18451 30098 : if (IsA(stmt, AlterObjectSchemaStmt))
18452 : {
18453 94 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18454 0 : ereport(ERROR,
18455 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18456 : errmsg("cannot change schema of index \"%s\"",
18457 : rv->relname),
18458 : errhint("Change the schema of the table instead.")));
18459 94 : else if (relkind == RELKIND_COMPOSITE_TYPE)
18460 0 : ereport(ERROR,
18461 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18462 : errmsg("cannot change schema of composite type \"%s\"",
18463 : rv->relname),
18464 : /* translator: %s is an SQL ALTER command */
18465 : errhint("Use %s instead.",
18466 : "ALTER TYPE")));
18467 94 : else if (relkind == RELKIND_TOASTVALUE)
18468 0 : ereport(ERROR,
18469 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18470 : errmsg("cannot change schema of TOAST table \"%s\"",
18471 : rv->relname),
18472 : errhint("Change the schema of the table instead.")));
18473 : }
18474 :
18475 30098 : ReleaseSysCache(tuple);
18476 : }
18477 :
18478 : /*
18479 : * Transform any expressions present in the partition key
18480 : *
18481 : * Returns a transformed PartitionSpec.
18482 : */
18483 : static PartitionSpec *
18484 4754 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18485 : {
18486 : PartitionSpec *newspec;
18487 : ParseState *pstate;
18488 : ParseNamespaceItem *nsitem;
18489 : ListCell *l;
18490 :
18491 4754 : newspec = makeNode(PartitionSpec);
18492 :
18493 4754 : newspec->strategy = partspec->strategy;
18494 4754 : newspec->partParams = NIL;
18495 4754 : newspec->location = partspec->location;
18496 :
18497 : /* Check valid number of columns for strategy */
18498 7172 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18499 2418 : list_length(partspec->partParams) != 1)
18500 6 : ereport(ERROR,
18501 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18502 : errmsg("cannot use \"list\" partition strategy with more than one column")));
18503 :
18504 : /*
18505 : * Create a dummy ParseState and insert the target relation as its sole
18506 : * rangetable entry. We need a ParseState for transformExpr.
18507 : */
18508 4748 : pstate = make_parsestate(NULL);
18509 4748 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18510 : NULL, false, true);
18511 4748 : addNSItemToQuery(pstate, nsitem, true, true, true);
18512 :
18513 : /* take care of any partition expressions */
18514 9904 : foreach(l, partspec->partParams)
18515 : {
18516 5180 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
18517 :
18518 5180 : if (pelem->expr)
18519 : {
18520 : /* Copy, to avoid scribbling on the input */
18521 298 : pelem = copyObject(pelem);
18522 :
18523 : /* Now do parse transformation of the expression */
18524 298 : pelem->expr = transformExpr(pstate, pelem->expr,
18525 : EXPR_KIND_PARTITION_EXPRESSION);
18526 :
18527 : /* we have to fix its collations too */
18528 274 : assign_expr_collations(pstate, pelem->expr);
18529 : }
18530 :
18531 5156 : newspec->partParams = lappend(newspec->partParams, pelem);
18532 : }
18533 :
18534 4724 : return newspec;
18535 : }
18536 :
18537 : /*
18538 : * Compute per-partition-column information from a list of PartitionElems.
18539 : * Expressions in the PartitionElems must be parse-analyzed already.
18540 : */
18541 : static void
18542 4724 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18543 : List **partexprs, Oid *partopclass, Oid *partcollation,
18544 : PartitionStrategy strategy)
18545 : {
18546 : int attn;
18547 : ListCell *lc;
18548 : Oid am_oid;
18549 :
18550 4724 : attn = 0;
18551 9796 : foreach(lc, partParams)
18552 : {
18553 5156 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18554 : Oid atttype;
18555 : Oid attcollation;
18556 :
18557 5156 : if (pelem->name != NULL)
18558 : {
18559 : /* Simple attribute reference */
18560 : HeapTuple atttuple;
18561 : Form_pg_attribute attform;
18562 :
18563 4882 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18564 4882 : pelem->name);
18565 4882 : if (!HeapTupleIsValid(atttuple))
18566 12 : ereport(ERROR,
18567 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18568 : errmsg("column \"%s\" named in partition key does not exist",
18569 : pelem->name),
18570 : parser_errposition(pstate, pelem->location)));
18571 4870 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18572 :
18573 4870 : if (attform->attnum <= 0)
18574 6 : ereport(ERROR,
18575 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18576 : errmsg("cannot use system column \"%s\" in partition key",
18577 : pelem->name),
18578 : parser_errposition(pstate, pelem->location)));
18579 :
18580 : /*
18581 : * Generated columns cannot work: They are computed after BEFORE
18582 : * triggers, but partition routing is done before all triggers.
18583 : */
18584 4864 : if (attform->attgenerated)
18585 6 : ereport(ERROR,
18586 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18587 : errmsg("cannot use generated column in partition key"),
18588 : errdetail("Column \"%s\" is a generated column.",
18589 : pelem->name),
18590 : parser_errposition(pstate, pelem->location)));
18591 :
18592 4858 : partattrs[attn] = attform->attnum;
18593 4858 : atttype = attform->atttypid;
18594 4858 : attcollation = attform->attcollation;
18595 4858 : ReleaseSysCache(atttuple);
18596 : }
18597 : else
18598 : {
18599 : /* Expression */
18600 274 : Node *expr = pelem->expr;
18601 : char partattname[16];
18602 :
18603 : Assert(expr != NULL);
18604 274 : atttype = exprType(expr);
18605 274 : attcollation = exprCollation(expr);
18606 :
18607 : /*
18608 : * The expression must be of a storable type (e.g., not RECORD).
18609 : * The test is the same as for whether a table column is of a safe
18610 : * type (which is why we needn't check for the non-expression
18611 : * case).
18612 : */
18613 274 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18614 274 : CheckAttributeType(partattname,
18615 : atttype, attcollation,
18616 : NIL, CHKATYPE_IS_PARTKEY);
18617 :
18618 : /*
18619 : * Strip any top-level COLLATE clause. This ensures that we treat
18620 : * "x COLLATE y" and "(x COLLATE y)" alike.
18621 : */
18622 262 : while (IsA(expr, CollateExpr))
18623 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
18624 :
18625 262 : if (IsA(expr, Var) &&
18626 12 : ((Var *) expr)->varattno > 0)
18627 : {
18628 : /*
18629 : * User wrote "(column)" or "(column COLLATE something)".
18630 : * Treat it like simple attribute anyway.
18631 : */
18632 6 : partattrs[attn] = ((Var *) expr)->varattno;
18633 : }
18634 : else
18635 : {
18636 256 : Bitmapset *expr_attrs = NULL;
18637 : int i;
18638 :
18639 256 : partattrs[attn] = 0; /* marks the column as expression */
18640 256 : *partexprs = lappend(*partexprs, expr);
18641 :
18642 : /*
18643 : * transformPartitionSpec() should have already rejected
18644 : * subqueries, aggregates, window functions, and SRFs, based
18645 : * on the EXPR_KIND_ for partition expressions.
18646 : */
18647 :
18648 : /*
18649 : * Cannot allow system column references, since that would
18650 : * make partition routing impossible: their values won't be
18651 : * known yet when we need to do that.
18652 : */
18653 256 : pull_varattnos(expr, 1, &expr_attrs);
18654 2048 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18655 : {
18656 1792 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18657 : expr_attrs))
18658 0 : ereport(ERROR,
18659 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18660 : errmsg("partition key expressions cannot contain system column references")));
18661 : }
18662 :
18663 : /*
18664 : * Generated columns cannot work: They are computed after
18665 : * BEFORE triggers, but partition routing is done before all
18666 : * triggers.
18667 : */
18668 256 : i = -1;
18669 564 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
18670 : {
18671 314 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18672 :
18673 314 : if (attno > 0 &&
18674 308 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18675 6 : ereport(ERROR,
18676 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18677 : errmsg("cannot use generated column in partition key"),
18678 : errdetail("Column \"%s\" is a generated column.",
18679 : get_attname(RelationGetRelid(rel), attno, false)),
18680 : parser_errposition(pstate, pelem->location)));
18681 : }
18682 :
18683 : /*
18684 : * Preprocess the expression before checking for mutability.
18685 : * This is essential for the reasons described in
18686 : * contain_mutable_functions_after_planning. However, we call
18687 : * expression_planner for ourselves rather than using that
18688 : * function, because if constant-folding reduces the
18689 : * expression to a constant, we'd like to know that so we can
18690 : * complain below.
18691 : *
18692 : * Like contain_mutable_functions_after_planning, assume that
18693 : * expression_planner won't scribble on its input, so this
18694 : * won't affect the partexprs entry we saved above.
18695 : */
18696 250 : expr = (Node *) expression_planner((Expr *) expr);
18697 :
18698 : /*
18699 : * Partition expressions cannot contain mutable functions,
18700 : * because a given row must always map to the same partition
18701 : * as long as there is no change in the partition boundary
18702 : * structure.
18703 : */
18704 250 : if (contain_mutable_functions(expr))
18705 6 : ereport(ERROR,
18706 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18707 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
18708 :
18709 : /*
18710 : * While it is not exactly *wrong* for a partition expression
18711 : * to be a constant, it seems better to reject such keys.
18712 : */
18713 244 : if (IsA(expr, Const))
18714 12 : ereport(ERROR,
18715 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18716 : errmsg("cannot use constant expression as partition key")));
18717 : }
18718 : }
18719 :
18720 : /*
18721 : * Apply collation override if any
18722 : */
18723 5096 : if (pelem->collation)
18724 54 : attcollation = get_collation_oid(pelem->collation, false);
18725 :
18726 : /*
18727 : * Check we have a collation iff it's a collatable type. The only
18728 : * expected failures here are (1) COLLATE applied to a noncollatable
18729 : * type, or (2) partition expression had an unresolved collation. But
18730 : * we might as well code this to be a complete consistency check.
18731 : */
18732 5096 : if (type_is_collatable(atttype))
18733 : {
18734 596 : if (!OidIsValid(attcollation))
18735 0 : ereport(ERROR,
18736 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
18737 : errmsg("could not determine which collation to use for partition expression"),
18738 : errhint("Use the COLLATE clause to set the collation explicitly.")));
18739 : }
18740 : else
18741 : {
18742 4500 : if (OidIsValid(attcollation))
18743 0 : ereport(ERROR,
18744 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18745 : errmsg("collations are not supported by type %s",
18746 : format_type_be(atttype))));
18747 : }
18748 :
18749 5096 : partcollation[attn] = attcollation;
18750 :
18751 : /*
18752 : * Identify the appropriate operator class. For list and range
18753 : * partitioning, we use a btree operator class; hash partitioning uses
18754 : * a hash operator class.
18755 : */
18756 5096 : if (strategy == PARTITION_STRATEGY_HASH)
18757 294 : am_oid = HASH_AM_OID;
18758 : else
18759 4802 : am_oid = BTREE_AM_OID;
18760 :
18761 5096 : if (!pelem->opclass)
18762 : {
18763 4964 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18764 :
18765 4964 : if (!OidIsValid(partopclass[attn]))
18766 : {
18767 12 : if (strategy == PARTITION_STRATEGY_HASH)
18768 0 : ereport(ERROR,
18769 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18770 : errmsg("data type %s has no default operator class for access method \"%s\"",
18771 : format_type_be(atttype), "hash"),
18772 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18773 : else
18774 12 : ereport(ERROR,
18775 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18776 : errmsg("data type %s has no default operator class for access method \"%s\"",
18777 : format_type_be(atttype), "btree"),
18778 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18779 : }
18780 : }
18781 : else
18782 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
18783 : atttype,
18784 : am_oid == HASH_AM_OID ? "hash" : "btree",
18785 : am_oid);
18786 :
18787 5072 : attn++;
18788 : }
18789 4640 : }
18790 :
18791 : /*
18792 : * PartConstraintImpliedByRelConstraint
18793 : * Do scanrel's existing constraints imply the partition constraint?
18794 : *
18795 : * "Existing constraints" include its check constraints and column-level
18796 : * not-null constraints. partConstraint describes the partition constraint,
18797 : * in implicit-AND form.
18798 : */
18799 : bool
18800 3020 : PartConstraintImpliedByRelConstraint(Relation scanrel,
18801 : List *partConstraint)
18802 : {
18803 3020 : List *existConstraint = NIL;
18804 3020 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18805 : int i;
18806 :
18807 3020 : if (constr && constr->has_not_null)
18808 : {
18809 754 : int natts = scanrel->rd_att->natts;
18810 :
18811 2440 : for (i = 1; i <= natts; i++)
18812 : {
18813 1686 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18814 :
18815 1686 : if (att->attnotnull && !att->attisdropped)
18816 : {
18817 1058 : NullTest *ntest = makeNode(NullTest);
18818 :
18819 1058 : ntest->arg = (Expr *) makeVar(1,
18820 : i,
18821 : att->atttypid,
18822 : att->atttypmod,
18823 : att->attcollation,
18824 : 0);
18825 1058 : ntest->nulltesttype = IS_NOT_NULL;
18826 :
18827 : /*
18828 : * argisrow=false is correct even for a composite column,
18829 : * because attnotnull does not represent a SQL-spec IS NOT
18830 : * NULL test in such a case, just IS DISTINCT FROM NULL.
18831 : */
18832 1058 : ntest->argisrow = false;
18833 1058 : ntest->location = -1;
18834 1058 : existConstraint = lappend(existConstraint, ntest);
18835 : }
18836 : }
18837 : }
18838 :
18839 3020 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18840 : }
18841 :
18842 : /*
18843 : * ConstraintImpliedByRelConstraint
18844 : * Do scanrel's existing constraints imply the given constraint?
18845 : *
18846 : * testConstraint is the constraint to validate. provenConstraint is a
18847 : * caller-provided list of conditions which this function may assume
18848 : * to be true. Both provenConstraint and testConstraint must be in
18849 : * implicit-AND form, must only contain immutable clauses, and must
18850 : * contain only Vars with varno = 1.
18851 : */
18852 : bool
18853 4162 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18854 : {
18855 4162 : List *existConstraint = list_copy(provenConstraint);
18856 4162 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18857 : int num_check,
18858 : i;
18859 :
18860 4162 : num_check = (constr != NULL) ? constr->num_check : 0;
18861 4678 : for (i = 0; i < num_check; i++)
18862 : {
18863 : Node *cexpr;
18864 :
18865 : /*
18866 : * If this constraint hasn't been fully validated yet, we must ignore
18867 : * it here.
18868 : */
18869 516 : if (!constr->check[i].ccvalid)
18870 6 : continue;
18871 :
18872 510 : cexpr = stringToNode(constr->check[i].ccbin);
18873 :
18874 : /*
18875 : * Run each expression through const-simplification and
18876 : * canonicalization. It is necessary, because we will be comparing it
18877 : * to similarly-processed partition constraint expressions, and may
18878 : * fail to detect valid matches without this.
18879 : */
18880 510 : cexpr = eval_const_expressions(NULL, cexpr);
18881 510 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18882 :
18883 510 : existConstraint = list_concat(existConstraint,
18884 510 : make_ands_implicit((Expr *) cexpr));
18885 : }
18886 :
18887 : /*
18888 : * Try to make the proof. Since we are comparing CHECK constraints, we
18889 : * need to use weak implication, i.e., we assume existConstraint is
18890 : * not-false and try to prove the same for testConstraint.
18891 : *
18892 : * Note that predicate_implied_by assumes its first argument is known
18893 : * immutable. That should always be true for both NOT NULL and partition
18894 : * constraints, so we don't test it here.
18895 : */
18896 4162 : return predicate_implied_by(testConstraint, existConstraint, true);
18897 : }
18898 :
18899 : /*
18900 : * QueuePartitionConstraintValidation
18901 : *
18902 : * Add an entry to wqueue to have the given partition constraint validated by
18903 : * Phase 3, for the given relation, and all its children.
18904 : *
18905 : * We first verify whether the given constraint is implied by pre-existing
18906 : * relation constraints; if it is, there's no need to scan the table to
18907 : * validate, so don't queue in that case.
18908 : */
18909 : static void
18910 2390 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
18911 : List *partConstraint,
18912 : bool validate_default)
18913 : {
18914 : /*
18915 : * Based on the table's existing constraints, determine whether or not we
18916 : * may skip scanning the table.
18917 : */
18918 2390 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
18919 : {
18920 90 : if (!validate_default)
18921 68 : ereport(DEBUG1,
18922 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18923 : RelationGetRelationName(scanrel))));
18924 : else
18925 22 : ereport(DEBUG1,
18926 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18927 : RelationGetRelationName(scanrel))));
18928 90 : return;
18929 : }
18930 :
18931 : /*
18932 : * Constraints proved insufficient. For plain relations, queue a
18933 : * validation item now; for partitioned tables, recurse to process each
18934 : * partition.
18935 : */
18936 2300 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
18937 : {
18938 : AlteredTableInfo *tab;
18939 :
18940 : /* Grab a work queue entry. */
18941 1912 : tab = ATGetQueueEntry(wqueue, scanrel);
18942 : Assert(tab->partition_constraint == NULL);
18943 1912 : tab->partition_constraint = (Expr *) linitial(partConstraint);
18944 1912 : tab->validate_default = validate_default;
18945 : }
18946 388 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18947 : {
18948 340 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
18949 : int i;
18950 :
18951 710 : for (i = 0; i < partdesc->nparts; i++)
18952 : {
18953 : Relation part_rel;
18954 : List *thisPartConstraint;
18955 :
18956 : /*
18957 : * This is the minimum lock we need to prevent deadlocks.
18958 : */
18959 370 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
18960 :
18961 : /*
18962 : * Adjust the constraint for scanrel so that it matches this
18963 : * partition's attribute numbers.
18964 : */
18965 : thisPartConstraint =
18966 370 : map_partition_varattnos(partConstraint, 1,
18967 : part_rel, scanrel);
18968 :
18969 370 : QueuePartitionConstraintValidation(wqueue, part_rel,
18970 : thisPartConstraint,
18971 : validate_default);
18972 370 : table_close(part_rel, NoLock); /* keep lock till commit */
18973 : }
18974 : }
18975 : }
18976 :
18977 : /*
18978 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
18979 : *
18980 : * Return the address of the newly attached partition.
18981 : */
18982 : static ObjectAddress
18983 2192 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
18984 : AlterTableUtilityContext *context)
18985 : {
18986 : Relation attachrel,
18987 : catalog;
18988 : List *attachrel_children;
18989 : List *partConstraint;
18990 : SysScanDesc scan;
18991 : ScanKeyData skey;
18992 : AttrNumber attno;
18993 : int natts;
18994 : TupleDesc tupleDesc;
18995 : ObjectAddress address;
18996 : const char *trigger_name;
18997 : Oid defaultPartOid;
18998 : List *partBoundConstraint;
18999 2192 : ParseState *pstate = make_parsestate(NULL);
19000 :
19001 2192 : pstate->p_sourcetext = context->queryString;
19002 :
19003 : /*
19004 : * We must lock the default partition if one exists, because attaching a
19005 : * new partition will change its partition constraint.
19006 : */
19007 : defaultPartOid =
19008 2192 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19009 2192 : if (OidIsValid(defaultPartOid))
19010 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19011 :
19012 2192 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19013 :
19014 : /*
19015 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19016 : * by FKs at this point also.
19017 : */
19018 :
19019 : /*
19020 : * Must be owner of both parent and source table -- parent was checked by
19021 : * ATSimplePermissions call in ATPrepCmd
19022 : */
19023 2186 : ATSimplePermissions(AT_AttachPartition, attachrel,
19024 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
19025 :
19026 : /* A partition can only have one parent */
19027 2180 : if (attachrel->rd_rel->relispartition)
19028 6 : ereport(ERROR,
19029 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19030 : errmsg("\"%s\" is already a partition",
19031 : RelationGetRelationName(attachrel))));
19032 :
19033 2174 : if (OidIsValid(attachrel->rd_rel->reloftype))
19034 6 : ereport(ERROR,
19035 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19036 : errmsg("cannot attach a typed table as partition")));
19037 :
19038 : /*
19039 : * Table being attached should not already be part of inheritance; either
19040 : * as a child table...
19041 : */
19042 2168 : catalog = table_open(InheritsRelationId, AccessShareLock);
19043 2168 : ScanKeyInit(&skey,
19044 : Anum_pg_inherits_inhrelid,
19045 : BTEqualStrategyNumber, F_OIDEQ,
19046 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19047 2168 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19048 : NULL, 1, &skey);
19049 2168 : if (HeapTupleIsValid(systable_getnext(scan)))
19050 6 : ereport(ERROR,
19051 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19052 : errmsg("cannot attach inheritance child as partition")));
19053 2162 : systable_endscan(scan);
19054 :
19055 : /* ...or as a parent table (except the case when it is partitioned) */
19056 2162 : ScanKeyInit(&skey,
19057 : Anum_pg_inherits_inhparent,
19058 : BTEqualStrategyNumber, F_OIDEQ,
19059 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19060 2162 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19061 : 1, &skey);
19062 2162 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19063 248 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19064 6 : ereport(ERROR,
19065 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19066 : errmsg("cannot attach inheritance parent as partition")));
19067 2156 : systable_endscan(scan);
19068 2156 : table_close(catalog, AccessShareLock);
19069 :
19070 : /*
19071 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19072 : * particular, this disallows making a rel a partition of itself.)
19073 : *
19074 : * We do that by checking if rel is a member of the list of attachrel's
19075 : * partitions provided the latter is partitioned at all. We want to avoid
19076 : * having to construct this list again, so we request the strongest lock
19077 : * on all partitions. We need the strongest lock, because we may decide
19078 : * to scan them if we find out that the table being attached (or its leaf
19079 : * partitions) may contain rows that violate the partition constraint. If
19080 : * the table has a constraint that would prevent such rows, which by
19081 : * definition is present in all the partitions, we need not scan the
19082 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19083 : * weaker lock now and the stronger one only when needed.
19084 : */
19085 2156 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19086 : AccessExclusiveLock, NULL);
19087 2156 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19088 12 : ereport(ERROR,
19089 : (errcode(ERRCODE_DUPLICATE_TABLE),
19090 : errmsg("circular inheritance not allowed"),
19091 : errdetail("\"%s\" is already a child of \"%s\".",
19092 : RelationGetRelationName(rel),
19093 : RelationGetRelationName(attachrel))));
19094 :
19095 : /* If the parent is permanent, so must be all of its partitions. */
19096 2144 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19097 2102 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19098 6 : ereport(ERROR,
19099 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19100 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19101 : RelationGetRelationName(rel))));
19102 :
19103 : /* Temp parent cannot have a partition that is itself not a temp */
19104 2138 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19105 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19106 18 : ereport(ERROR,
19107 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19108 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19109 : RelationGetRelationName(rel))));
19110 :
19111 : /* If the parent is temp, it must belong to this session */
19112 2120 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19113 24 : !rel->rd_islocaltemp)
19114 0 : ereport(ERROR,
19115 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19116 : errmsg("cannot attach as partition of temporary relation of another session")));
19117 :
19118 : /* Ditto for the partition */
19119 2120 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19120 24 : !attachrel->rd_islocaltemp)
19121 0 : ereport(ERROR,
19122 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19123 : errmsg("cannot attach temporary relation of another session as partition")));
19124 :
19125 : /*
19126 : * Check if attachrel has any identity columns or any columns that aren't
19127 : * in the parent.
19128 : */
19129 2120 : tupleDesc = RelationGetDescr(attachrel);
19130 2120 : natts = tupleDesc->natts;
19131 7254 : for (attno = 1; attno <= natts; attno++)
19132 : {
19133 5170 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19134 5170 : char *attributeName = NameStr(attribute->attname);
19135 :
19136 : /* Ignore dropped */
19137 5170 : if (attribute->attisdropped)
19138 580 : continue;
19139 :
19140 4590 : if (attribute->attidentity)
19141 18 : ereport(ERROR,
19142 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19143 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19144 : RelationGetRelationName(attachrel), attributeName),
19145 : errdetail("The new partition may not contain an identity column."));
19146 :
19147 : /* Try to find the column in parent (matching on column name) */
19148 4572 : if (!SearchSysCacheExists2(ATTNAME,
19149 : ObjectIdGetDatum(RelationGetRelid(rel)),
19150 : CStringGetDatum(attributeName)))
19151 18 : ereport(ERROR,
19152 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19153 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19154 : RelationGetRelationName(attachrel), attributeName,
19155 : RelationGetRelationName(rel)),
19156 : errdetail("The new partition may contain only the columns present in parent.")));
19157 : }
19158 :
19159 : /*
19160 : * If child_rel has row-level triggers with transition tables, we
19161 : * currently don't allow it to become a partition. See also prohibitions
19162 : * in ATExecAddInherit() and CreateTrigger().
19163 : */
19164 2084 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19165 2084 : if (trigger_name != NULL)
19166 6 : ereport(ERROR,
19167 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19168 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19169 : trigger_name, RelationGetRelationName(attachrel)),
19170 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19171 :
19172 : /*
19173 : * Check that the new partition's bound is valid and does not overlap any
19174 : * of existing partitions of the parent - note that it does not return on
19175 : * error.
19176 : */
19177 2078 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19178 : cmd->bound, pstate);
19179 :
19180 : /* OK to create inheritance. Rest of the checks performed there */
19181 2042 : CreateInheritance(attachrel, rel, true);
19182 :
19183 : /* Update the pg_class entry. */
19184 1970 : StorePartitionBound(attachrel, rel, cmd->bound);
19185 :
19186 : /* Ensure there exists a correct set of indexes in the partition. */
19187 1970 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19188 :
19189 : /* and triggers */
19190 1940 : CloneRowTriggersToPartition(rel, attachrel);
19191 :
19192 : /*
19193 : * Clone foreign key constraints. Callee is responsible for setting up
19194 : * for phase 3 constraint verification.
19195 : */
19196 1934 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19197 :
19198 : /*
19199 : * Generate partition constraint from the partition bound specification.
19200 : * If the parent itself is a partition, make sure to include its
19201 : * constraint as well.
19202 : */
19203 1922 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19204 :
19205 : /*
19206 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19207 : * since it's needed later to construct the constraint expression for
19208 : * validating against the default partition, if any.
19209 : */
19210 1922 : partConstraint = list_concat_copy(partBoundConstraint,
19211 1922 : RelationGetPartitionQual(rel));
19212 :
19213 : /* Skip validation if there are no constraints to validate. */
19214 1922 : if (partConstraint)
19215 : {
19216 : /*
19217 : * Run the partition quals through const-simplification similar to
19218 : * check constraints. We skip canonicalize_qual, though, because
19219 : * partition quals should be in canonical form already.
19220 : */
19221 : partConstraint =
19222 1874 : (List *) eval_const_expressions(NULL,
19223 : (Node *) partConstraint);
19224 :
19225 : /* XXX this sure looks wrong */
19226 1874 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19227 :
19228 : /*
19229 : * Adjust the generated constraint to match this partition's attribute
19230 : * numbers.
19231 : */
19232 1874 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19233 : rel);
19234 :
19235 : /* Validate partition constraints against the table being attached. */
19236 1874 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19237 : false);
19238 : }
19239 :
19240 : /*
19241 : * If we're attaching a partition other than the default partition and a
19242 : * default one exists, then that partition's partition constraint changes,
19243 : * so add an entry to the work queue to validate it, too. (We must not do
19244 : * this when the partition being attached is the default one; we already
19245 : * did it above!)
19246 : */
19247 1922 : if (OidIsValid(defaultPartOid))
19248 : {
19249 : Relation defaultrel;
19250 : List *defPartConstraint;
19251 :
19252 : Assert(!cmd->bound->is_default);
19253 :
19254 : /* we already hold a lock on the default partition */
19255 146 : defaultrel = table_open(defaultPartOid, NoLock);
19256 : defPartConstraint =
19257 146 : get_proposed_default_constraint(partBoundConstraint);
19258 :
19259 : /*
19260 : * Map the Vars in the constraint expression from rel's attnos to
19261 : * defaultrel's.
19262 : */
19263 : defPartConstraint =
19264 146 : map_partition_varattnos(defPartConstraint,
19265 : 1, defaultrel, rel);
19266 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19267 : defPartConstraint, true);
19268 :
19269 : /* keep our lock until commit. */
19270 146 : table_close(defaultrel, NoLock);
19271 : }
19272 :
19273 1922 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19274 :
19275 : /*
19276 : * If the partition we just attached is partitioned itself, invalidate
19277 : * relcache for all descendent partitions too to ensure that their
19278 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19279 : * the beginning of this function.
19280 : */
19281 1922 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19282 : {
19283 : ListCell *l;
19284 :
19285 960 : foreach(l, attachrel_children)
19286 : {
19287 644 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19288 : }
19289 : }
19290 :
19291 : /* keep our lock until commit */
19292 1922 : table_close(attachrel, NoLock);
19293 :
19294 1922 : return address;
19295 : }
19296 :
19297 : /*
19298 : * AttachPartitionEnsureIndexes
19299 : * subroutine for ATExecAttachPartition to create/match indexes
19300 : *
19301 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19302 : * PARTITION: every partition must have an index attached to each index on the
19303 : * partitioned table.
19304 : */
19305 : static void
19306 1970 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19307 : {
19308 : List *idxes;
19309 : List *attachRelIdxs;
19310 : Relation *attachrelIdxRels;
19311 : IndexInfo **attachInfos;
19312 : ListCell *cell;
19313 : MemoryContext cxt;
19314 : MemoryContext oldcxt;
19315 :
19316 1970 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19317 : "AttachPartitionEnsureIndexes",
19318 : ALLOCSET_DEFAULT_SIZES);
19319 1970 : oldcxt = MemoryContextSwitchTo(cxt);
19320 :
19321 1970 : idxes = RelationGetIndexList(rel);
19322 1970 : attachRelIdxs = RelationGetIndexList(attachrel);
19323 1970 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19324 1970 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19325 :
19326 : /* Build arrays of all existing indexes and their IndexInfos */
19327 4304 : foreach_oid(cldIdxId, attachRelIdxs)
19328 : {
19329 364 : int i = foreach_current_index(cldIdxId);
19330 :
19331 364 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19332 364 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19333 : }
19334 :
19335 : /*
19336 : * If we're attaching a foreign table, we must fail if any of the indexes
19337 : * is a constraint index; otherwise, there's nothing to do here. Do this
19338 : * before starting work, to avoid wasting the effort of building a few
19339 : * non-unique indexes before coming across a unique one.
19340 : */
19341 1970 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19342 : {
19343 86 : foreach(cell, idxes)
19344 : {
19345 36 : Oid idx = lfirst_oid(cell);
19346 36 : Relation idxRel = index_open(idx, AccessShareLock);
19347 :
19348 36 : if (idxRel->rd_index->indisunique ||
19349 24 : idxRel->rd_index->indisprimary)
19350 12 : ereport(ERROR,
19351 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19352 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19353 : RelationGetRelationName(attachrel),
19354 : RelationGetRelationName(rel)),
19355 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19356 : RelationGetRelationName(rel))));
19357 24 : index_close(idxRel, AccessShareLock);
19358 : }
19359 :
19360 50 : goto out;
19361 : }
19362 :
19363 : /*
19364 : * For each index on the partitioned table, find a matching one in the
19365 : * partition-to-be; if one is not found, create one.
19366 : */
19367 2324 : foreach(cell, idxes)
19368 : {
19369 434 : Oid idx = lfirst_oid(cell);
19370 434 : Relation idxRel = index_open(idx, AccessShareLock);
19371 : IndexInfo *info;
19372 : AttrMap *attmap;
19373 434 : bool found = false;
19374 : Oid constraintOid;
19375 :
19376 : /*
19377 : * Ignore indexes in the partitioned table other than partitioned
19378 : * indexes.
19379 : */
19380 434 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19381 : {
19382 0 : index_close(idxRel, AccessShareLock);
19383 0 : continue;
19384 : }
19385 :
19386 : /* construct an indexinfo to compare existing indexes against */
19387 434 : info = BuildIndexInfo(idxRel);
19388 434 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19389 : RelationGetDescr(rel),
19390 : false);
19391 434 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19392 :
19393 : /*
19394 : * Scan the list of existing indexes in the partition-to-be, and mark
19395 : * the first matching, valid, unattached one we find, if any, as
19396 : * partition of the parent index. If we find one, we're done.
19397 : */
19398 494 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19399 : {
19400 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19401 262 : Oid cldConstrOid = InvalidOid;
19402 :
19403 : /* does this index have a parent? if so, can't use it */
19404 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
19405 12 : continue;
19406 :
19407 : /* If this index is invalid, can't use it */
19408 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
19409 6 : continue;
19410 :
19411 244 : if (CompareIndexInfo(attachInfos[i], info,
19412 244 : attachrelIdxRels[i]->rd_indcollation,
19413 244 : idxRel->rd_indcollation,
19414 244 : attachrelIdxRels[i]->rd_opfamily,
19415 244 : idxRel->rd_opfamily,
19416 : attmap))
19417 : {
19418 : /*
19419 : * If this index is being created in the parent because of a
19420 : * constraint, then the child needs to have a constraint also,
19421 : * so look for one. If there is no such constraint, this
19422 : * index is no good, so keep looking.
19423 : */
19424 208 : if (OidIsValid(constraintOid))
19425 : {
19426 : cldConstrOid =
19427 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19428 : cldIdxId);
19429 : /* no dice */
19430 110 : if (!OidIsValid(cldConstrOid))
19431 6 : continue;
19432 :
19433 : /* Ensure they're both the same type of constraint */
19434 208 : if (get_constraint_type(constraintOid) !=
19435 104 : get_constraint_type(cldConstrOid))
19436 0 : continue;
19437 : }
19438 :
19439 : /* bingo. */
19440 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
19441 202 : if (OidIsValid(constraintOid))
19442 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19443 : RelationGetRelid(attachrel));
19444 202 : found = true;
19445 :
19446 202 : CommandCounterIncrement();
19447 202 : break;
19448 : }
19449 : }
19450 :
19451 : /*
19452 : * If no suitable index was found in the partition-to-be, create one
19453 : * now. Note that if this is a PK, not-null constraints must already
19454 : * exist.
19455 : */
19456 434 : if (!found)
19457 : {
19458 : IndexStmt *stmt;
19459 : Oid conOid;
19460 :
19461 232 : stmt = generateClonedIndexStmt(NULL,
19462 : idxRel, attmap,
19463 : &conOid);
19464 232 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19465 : RelationGetRelid(idxRel),
19466 : conOid,
19467 : -1,
19468 : true, false, false, false, false);
19469 : }
19470 :
19471 416 : index_close(idxRel, AccessShareLock);
19472 : }
19473 :
19474 1940 : out:
19475 : /* Clean up. */
19476 2292 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19477 352 : index_close(attachrelIdxRels[i], AccessShareLock);
19478 1940 : MemoryContextSwitchTo(oldcxt);
19479 1940 : MemoryContextDelete(cxt);
19480 1940 : }
19481 :
19482 : /*
19483 : * CloneRowTriggersToPartition
19484 : * subroutine for ATExecAttachPartition/DefineRelation to create row
19485 : * triggers on partitions
19486 : */
19487 : static void
19488 2348 : CloneRowTriggersToPartition(Relation parent, Relation partition)
19489 : {
19490 : Relation pg_trigger;
19491 : ScanKeyData key;
19492 : SysScanDesc scan;
19493 : HeapTuple tuple;
19494 : MemoryContext perTupCxt;
19495 :
19496 2348 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19497 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19498 2348 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19499 2348 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19500 : true, NULL, 1, &key);
19501 :
19502 2348 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19503 : "clone trig", ALLOCSET_SMALL_SIZES);
19504 :
19505 3856 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19506 : {
19507 1514 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19508 : CreateTrigStmt *trigStmt;
19509 1514 : Node *qual = NULL;
19510 : Datum value;
19511 : bool isnull;
19512 1514 : List *cols = NIL;
19513 1514 : List *trigargs = NIL;
19514 : MemoryContext oldcxt;
19515 :
19516 : /*
19517 : * Ignore statement-level triggers; those are not cloned.
19518 : */
19519 1514 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19520 1358 : continue;
19521 :
19522 : /*
19523 : * Don't clone internal triggers, because the constraint cloning code
19524 : * will.
19525 : */
19526 1514 : if (trigForm->tgisinternal)
19527 1358 : continue;
19528 :
19529 : /*
19530 : * Complain if we find an unexpected trigger type.
19531 : */
19532 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19533 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
19534 0 : elog(ERROR, "unexpected trigger \"%s\" found",
19535 : NameStr(trigForm->tgname));
19536 :
19537 : /* Use short-lived context for CREATE TRIGGER */
19538 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
19539 :
19540 : /*
19541 : * If there is a WHEN clause, generate a 'cooked' version of it that's
19542 : * appropriate for the partition.
19543 : */
19544 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19545 : RelationGetDescr(pg_trigger), &isnull);
19546 156 : if (!isnull)
19547 : {
19548 6 : qual = stringToNode(TextDatumGetCString(value));
19549 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19550 : partition, parent);
19551 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19552 : partition, parent);
19553 : }
19554 :
19555 : /*
19556 : * If there is a column list, transform it to a list of column names.
19557 : * Note we don't need to map this list in any way ...
19558 : */
19559 156 : if (trigForm->tgattr.dim1 > 0)
19560 : {
19561 : int i;
19562 :
19563 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
19564 : {
19565 : Form_pg_attribute col;
19566 :
19567 6 : col = TupleDescAttr(parent->rd_att,
19568 : trigForm->tgattr.values[i] - 1);
19569 6 : cols = lappend(cols,
19570 6 : makeString(pstrdup(NameStr(col->attname))));
19571 : }
19572 : }
19573 :
19574 : /* Reconstruct trigger arguments list. */
19575 156 : if (trigForm->tgnargs > 0)
19576 : {
19577 : char *p;
19578 :
19579 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19580 : RelationGetDescr(pg_trigger), &isnull);
19581 12 : if (isnull)
19582 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19583 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
19584 :
19585 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19586 :
19587 36 : for (int i = 0; i < trigForm->tgnargs; i++)
19588 : {
19589 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
19590 24 : p += strlen(p) + 1;
19591 : }
19592 : }
19593 :
19594 156 : trigStmt = makeNode(CreateTrigStmt);
19595 156 : trigStmt->replace = false;
19596 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19597 156 : trigStmt->trigname = NameStr(trigForm->tgname);
19598 156 : trigStmt->relation = NULL;
19599 156 : trigStmt->funcname = NULL; /* passed separately */
19600 156 : trigStmt->args = trigargs;
19601 156 : trigStmt->row = true;
19602 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19603 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19604 156 : trigStmt->columns = cols;
19605 156 : trigStmt->whenClause = NULL; /* passed separately */
19606 156 : trigStmt->transitionRels = NIL; /* not supported at present */
19607 156 : trigStmt->deferrable = trigForm->tgdeferrable;
19608 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
19609 156 : trigStmt->constrrel = NULL; /* passed separately */
19610 :
19611 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19612 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19613 : trigForm->tgfoid, trigForm->oid, qual,
19614 156 : false, true, trigForm->tgenabled);
19615 :
19616 150 : MemoryContextSwitchTo(oldcxt);
19617 150 : MemoryContextReset(perTupCxt);
19618 : }
19619 :
19620 2342 : MemoryContextDelete(perTupCxt);
19621 :
19622 2342 : systable_endscan(scan);
19623 2342 : table_close(pg_trigger, RowExclusiveLock);
19624 2342 : }
19625 :
19626 : /*
19627 : * ALTER TABLE DETACH PARTITION
19628 : *
19629 : * Return the address of the relation that is no longer a partition of rel.
19630 : *
19631 : * If concurrent mode is requested, we run in two transactions. A side-
19632 : * effect is that this command cannot run in a multi-part ALTER TABLE.
19633 : * Currently, that's enforced by the grammar.
19634 : *
19635 : * The strategy for concurrency is to first modify the partition's
19636 : * pg_inherit catalog row to make it visible to everyone that the
19637 : * partition is detached, lock the partition against writes, and commit
19638 : * the transaction; anyone who requests the partition descriptor from
19639 : * that point onwards has to ignore such a partition. In a second
19640 : * transaction, we wait until all transactions that could have seen the
19641 : * partition as attached are gone, then we remove the rest of partition
19642 : * metadata (pg_inherits and pg_class.relpartbounds).
19643 : */
19644 : static ObjectAddress
19645 546 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19646 : RangeVar *name, bool concurrent)
19647 : {
19648 : Relation partRel;
19649 : ObjectAddress address;
19650 : Oid defaultPartOid;
19651 :
19652 : /*
19653 : * We must lock the default partition, because detaching this partition
19654 : * will change its partition constraint.
19655 : */
19656 : defaultPartOid =
19657 546 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19658 546 : if (OidIsValid(defaultPartOid))
19659 : {
19660 : /*
19661 : * Concurrent detaching when a default partition exists is not
19662 : * supported. The main problem is that the default partition
19663 : * constraint would change. And there's a definitional problem: what
19664 : * should happen to the tuples that are being inserted that belong to
19665 : * the partition being detached? Putting them on the partition being
19666 : * detached would be wrong, since they'd become "lost" after the
19667 : * detaching completes but we cannot put them in the default partition
19668 : * either until we alter its partition constraint.
19669 : *
19670 : * I think we could solve this problem if we effected the constraint
19671 : * change before committing the first transaction. But the lock would
19672 : * have to remain AEL and it would cause concurrent query planning to
19673 : * be blocked, so changing it that way would be even worse.
19674 : */
19675 106 : if (concurrent)
19676 12 : ereport(ERROR,
19677 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19678 : errmsg("cannot detach partitions concurrently when a default partition exists")));
19679 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19680 : }
19681 :
19682 : /*
19683 : * In concurrent mode, the partition is locked with share-update-exclusive
19684 : * in the first transaction. This allows concurrent transactions to be
19685 : * doing DML to the partition.
19686 : */
19687 534 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19688 : AccessExclusiveLock);
19689 :
19690 : /*
19691 : * Check inheritance conditions and either delete the pg_inherits row (in
19692 : * non-concurrent mode) or just set the inhdetachpending flag.
19693 : */
19694 522 : if (!concurrent)
19695 376 : RemoveInheritance(partRel, rel, false);
19696 : else
19697 146 : MarkInheritDetached(partRel, rel);
19698 :
19699 : /*
19700 : * Ensure that foreign keys still hold after this detach. This keeps
19701 : * locks on the referencing tables, which prevents concurrent transactions
19702 : * from adding rows that we wouldn't see. For this to work in concurrent
19703 : * mode, it is critical that the partition appears as no longer attached
19704 : * for the RI queries as soon as the first transaction commits.
19705 : */
19706 502 : ATDetachCheckNoForeignKeyRefs(partRel);
19707 :
19708 : /*
19709 : * Concurrent mode has to work harder; first we add a new constraint to
19710 : * the partition that matches the partition constraint. Then we close our
19711 : * existing transaction, and in a new one wait for all processes to catch
19712 : * up on the catalog updates we've done so far; at that point we can
19713 : * complete the operation.
19714 : */
19715 468 : if (concurrent)
19716 : {
19717 : Oid partrelid,
19718 : parentrelid;
19719 : LOCKTAG tag;
19720 : char *parentrelname;
19721 : char *partrelname;
19722 :
19723 : /*
19724 : * Add a new constraint to the partition being detached, which
19725 : * supplants the partition constraint (unless there is one already).
19726 : */
19727 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
19728 :
19729 : /*
19730 : * We're almost done now; the only traces that remain are the
19731 : * pg_inherits tuple and the partition's relpartbounds. Before we can
19732 : * remove those, we need to wait until all transactions that know that
19733 : * this is a partition are gone.
19734 : */
19735 :
19736 : /*
19737 : * Remember relation OIDs to re-acquire them later; and relation names
19738 : * too, for error messages if something is dropped in between.
19739 : */
19740 140 : partrelid = RelationGetRelid(partRel);
19741 140 : parentrelid = RelationGetRelid(rel);
19742 140 : parentrelname = MemoryContextStrdup(PortalContext,
19743 140 : RelationGetRelationName(rel));
19744 140 : partrelname = MemoryContextStrdup(PortalContext,
19745 140 : RelationGetRelationName(partRel));
19746 :
19747 : /* Invalidate relcache entries for the parent -- must be before close */
19748 140 : CacheInvalidateRelcache(rel);
19749 :
19750 140 : table_close(partRel, NoLock);
19751 140 : table_close(rel, NoLock);
19752 140 : tab->rel = NULL;
19753 :
19754 : /* Make updated catalog entry visible */
19755 140 : PopActiveSnapshot();
19756 140 : CommitTransactionCommand();
19757 :
19758 140 : StartTransactionCommand();
19759 :
19760 : /*
19761 : * Now wait. This ensures that all queries that were planned
19762 : * including the partition are finished before we remove the rest of
19763 : * catalog entries. We don't need or indeed want to acquire this
19764 : * lock, though -- that would block later queries.
19765 : *
19766 : * We don't need to concern ourselves with waiting for a lock on the
19767 : * partition itself, since we will acquire AccessExclusiveLock below.
19768 : */
19769 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19770 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19771 :
19772 : /*
19773 : * Now acquire locks in both relations again. Note they may have been
19774 : * removed in the meantime, so care is required.
19775 : */
19776 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19777 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
19778 :
19779 : /* If the relations aren't there, something bad happened; bail out */
19780 90 : if (rel == NULL)
19781 : {
19782 0 : if (partRel != NULL) /* shouldn't happen */
19783 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19784 : partrelname);
19785 0 : ereport(ERROR,
19786 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19787 : errmsg("partitioned table \"%s\" was removed concurrently",
19788 : parentrelname)));
19789 : }
19790 90 : if (partRel == NULL)
19791 0 : ereport(ERROR,
19792 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19793 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
19794 :
19795 90 : tab->rel = rel;
19796 : }
19797 :
19798 : /* Do the final part of detaching */
19799 418 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19800 :
19801 416 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19802 :
19803 : /* keep our lock until commit */
19804 416 : table_close(partRel, NoLock);
19805 :
19806 416 : return address;
19807 : }
19808 :
19809 : /*
19810 : * Second part of ALTER TABLE .. DETACH.
19811 : *
19812 : * This is separate so that it can be run independently when the second
19813 : * transaction of the concurrent algorithm fails (crash or abort).
19814 : */
19815 : static void
19816 432 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19817 : Oid defaultPartOid)
19818 : {
19819 : Relation classRel;
19820 : List *fks;
19821 : ListCell *cell;
19822 : List *indexes;
19823 : Datum new_val[Natts_pg_class];
19824 : bool new_null[Natts_pg_class],
19825 : new_repl[Natts_pg_class];
19826 : HeapTuple tuple,
19827 : newtuple;
19828 432 : Relation trigrel = NULL;
19829 :
19830 432 : if (concurrent)
19831 : {
19832 : /*
19833 : * We can remove the pg_inherits row now. (In the non-concurrent case,
19834 : * this was already done).
19835 : */
19836 104 : RemoveInheritance(partRel, rel, true);
19837 : }
19838 :
19839 : /* Drop any triggers that were cloned on creation/attach. */
19840 432 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19841 :
19842 : /*
19843 : * Detach any foreign keys that are inherited. This includes creating
19844 : * additional action triggers.
19845 : */
19846 432 : fks = copyObject(RelationGetFKeyList(partRel));
19847 432 : if (fks != NIL)
19848 60 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19849 522 : foreach(cell, fks)
19850 : {
19851 90 : ForeignKeyCacheInfo *fk = lfirst(cell);
19852 : HeapTuple contup;
19853 : Form_pg_constraint conform;
19854 : Oid insertTriggerOid,
19855 : updateTriggerOid;
19856 :
19857 90 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19858 90 : if (!HeapTupleIsValid(contup))
19859 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19860 90 : conform = (Form_pg_constraint) GETSTRUCT(contup);
19861 :
19862 : /* consider only the inherited foreign keys */
19863 90 : if (conform->contype != CONSTRAINT_FOREIGN ||
19864 90 : !OidIsValid(conform->conparentid))
19865 : {
19866 18 : ReleaseSysCache(contup);
19867 18 : continue;
19868 : }
19869 :
19870 : /*
19871 : * The constraint on this table must be marked no longer a child of
19872 : * the parent's constraint, as do its check triggers.
19873 : */
19874 72 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19875 :
19876 : /*
19877 : * Also, look up the partition's "check" triggers corresponding to the
19878 : * constraint being detached and detach them from the parent triggers.
19879 : */
19880 72 : GetForeignKeyCheckTriggers(trigrel,
19881 : fk->conoid, fk->confrelid, fk->conrelid,
19882 : &insertTriggerOid, &updateTriggerOid);
19883 : Assert(OidIsValid(insertTriggerOid));
19884 72 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19885 : RelationGetRelid(partRel));
19886 : Assert(OidIsValid(updateTriggerOid));
19887 72 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19888 : RelationGetRelid(partRel));
19889 :
19890 : /*
19891 : * Lastly, create the action triggers on the referenced table, using
19892 : * addFkRecurseReferenced, which requires some elaborate setup (so put
19893 : * it in a separate block). While at it, if the table is partitioned,
19894 : * that function will recurse to create the pg_constraint rows and
19895 : * action triggers for each partition.
19896 : *
19897 : * Note there's no need to do addFkConstraint() here, because the
19898 : * pg_constraint row already exists.
19899 : */
19900 : {
19901 : Constraint *fkconstraint;
19902 : int numfks;
19903 : AttrNumber conkey[INDEX_MAX_KEYS];
19904 : AttrNumber confkey[INDEX_MAX_KEYS];
19905 : Oid conpfeqop[INDEX_MAX_KEYS];
19906 : Oid conppeqop[INDEX_MAX_KEYS];
19907 : Oid conffeqop[INDEX_MAX_KEYS];
19908 : int numfkdelsetcols;
19909 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
19910 : Relation refdRel;
19911 :
19912 72 : DeconstructFkConstraintRow(contup,
19913 : &numfks,
19914 : conkey,
19915 : confkey,
19916 : conpfeqop,
19917 : conppeqop,
19918 : conffeqop,
19919 : &numfkdelsetcols,
19920 : confdelsetcols);
19921 :
19922 : /* Create a synthetic node we'll use throughout */
19923 72 : fkconstraint = makeNode(Constraint);
19924 72 : fkconstraint->contype = CONSTRAINT_FOREIGN;
19925 72 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
19926 72 : fkconstraint->deferrable = conform->condeferrable;
19927 72 : fkconstraint->initdeferred = conform->condeferred;
19928 72 : fkconstraint->skip_validation = true;
19929 72 : fkconstraint->initially_valid = true;
19930 : /* a few irrelevant fields omitted here */
19931 72 : fkconstraint->pktable = NULL;
19932 72 : fkconstraint->fk_attrs = NIL;
19933 72 : fkconstraint->pk_attrs = NIL;
19934 72 : fkconstraint->fk_matchtype = conform->confmatchtype;
19935 72 : fkconstraint->fk_upd_action = conform->confupdtype;
19936 72 : fkconstraint->fk_del_action = conform->confdeltype;
19937 72 : fkconstraint->fk_del_set_cols = NIL;
19938 72 : fkconstraint->old_conpfeqop = NIL;
19939 72 : fkconstraint->old_pktable_oid = InvalidOid;
19940 72 : fkconstraint->location = -1;
19941 :
19942 : /* set up colnames, used to generate the constraint name */
19943 192 : for (int i = 0; i < numfks; i++)
19944 : {
19945 : Form_pg_attribute att;
19946 :
19947 120 : att = TupleDescAttr(RelationGetDescr(partRel),
19948 : conkey[i] - 1);
19949 :
19950 120 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
19951 120 : makeString(NameStr(att->attname)));
19952 : }
19953 :
19954 72 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
19955 :
19956 72 : addFkRecurseReferenced(fkconstraint, partRel,
19957 : refdRel,
19958 : conform->conindid,
19959 : fk->conoid,
19960 : numfks,
19961 : confkey,
19962 : conkey,
19963 : conpfeqop,
19964 : conppeqop,
19965 : conffeqop,
19966 : numfkdelsetcols,
19967 : confdelsetcols,
19968 : true,
19969 : InvalidOid, InvalidOid,
19970 72 : conform->conperiod);
19971 72 : table_close(refdRel, NoLock); /* keep lock till end of xact */
19972 : }
19973 :
19974 72 : ReleaseSysCache(contup);
19975 : }
19976 432 : list_free_deep(fks);
19977 432 : if (trigrel)
19978 60 : table_close(trigrel, RowExclusiveLock);
19979 :
19980 : /*
19981 : * Any sub-constraints that are in the referenced-side of a larger
19982 : * constraint have to be removed. This partition is no longer part of the
19983 : * key space of the constraint.
19984 : */
19985 468 : foreach(cell, GetParentedForeignKeyRefs(partRel))
19986 : {
19987 38 : Oid constrOid = lfirst_oid(cell);
19988 : ObjectAddress constraint;
19989 :
19990 38 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19991 38 : deleteDependencyRecordsForClass(ConstraintRelationId,
19992 : constrOid,
19993 : ConstraintRelationId,
19994 : DEPENDENCY_INTERNAL);
19995 38 : CommandCounterIncrement();
19996 :
19997 38 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
19998 38 : performDeletion(&constraint, DROP_RESTRICT, 0);
19999 : }
20000 :
20001 : /* Now we can detach indexes */
20002 430 : indexes = RelationGetIndexList(partRel);
20003 612 : foreach(cell, indexes)
20004 : {
20005 182 : Oid idxid = lfirst_oid(cell);
20006 : Oid parentidx;
20007 : Relation idx;
20008 : Oid constrOid;
20009 : Oid parentConstrOid;
20010 :
20011 182 : if (!has_superclass(idxid))
20012 12 : continue;
20013 :
20014 170 : parentidx = get_partition_parent(idxid, false);
20015 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20016 :
20017 170 : idx = index_open(idxid, AccessExclusiveLock);
20018 170 : IndexSetParentIndex(idx, InvalidOid);
20019 :
20020 : /*
20021 : * If there's a constraint associated with the index, detach it too.
20022 : * Careful: it is possible for a constraint index in a partition to be
20023 : * the child of a non-constraint index, so verify whether the parent
20024 : * index does actually have a constraint.
20025 : */
20026 170 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20027 : idxid);
20028 170 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
20029 : parentidx);
20030 170 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20031 72 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20032 :
20033 170 : index_close(idx, NoLock);
20034 : }
20035 :
20036 : /* Update pg_class tuple */
20037 430 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20038 430 : tuple = SearchSysCacheCopy1(RELOID,
20039 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20040 430 : if (!HeapTupleIsValid(tuple))
20041 0 : elog(ERROR, "cache lookup failed for relation %u",
20042 : RelationGetRelid(partRel));
20043 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20044 :
20045 : /* Clear relpartbound and reset relispartition */
20046 430 : memset(new_val, 0, sizeof(new_val));
20047 430 : memset(new_null, false, sizeof(new_null));
20048 430 : memset(new_repl, false, sizeof(new_repl));
20049 430 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20050 430 : new_null[Anum_pg_class_relpartbound - 1] = true;
20051 430 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20052 430 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20053 : new_val, new_null, new_repl);
20054 :
20055 430 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20056 430 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20057 430 : heap_freetuple(newtuple);
20058 430 : table_close(classRel, RowExclusiveLock);
20059 :
20060 : /*
20061 : * Drop identity property from all identity columns of partition.
20062 : */
20063 1228 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20064 : {
20065 798 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20066 :
20067 798 : if (!attr->attisdropped && attr->attidentity)
20068 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20069 : AccessExclusiveLock, true, true);
20070 : }
20071 :
20072 430 : if (OidIsValid(defaultPartOid))
20073 : {
20074 : /*
20075 : * If the relation being detached is the default partition itself,
20076 : * remove it from the parent's pg_partitioned_table entry.
20077 : *
20078 : * If not, we must invalidate default partition's relcache entry, as
20079 : * in StorePartitionBound: its partition constraint depends on every
20080 : * other partition's partition constraint.
20081 : */
20082 46 : if (RelationGetRelid(partRel) == defaultPartOid)
20083 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20084 : else
20085 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20086 : }
20087 :
20088 : /*
20089 : * Invalidate the parent's relcache so that the partition is no longer
20090 : * included in its partition descriptor.
20091 : */
20092 430 : CacheInvalidateRelcache(rel);
20093 :
20094 : /*
20095 : * If the partition we just detached is partitioned itself, invalidate
20096 : * relcache for all descendent partitions too to ensure that their
20097 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20098 : * doing so, using the same lockmode as what partRel has been locked with
20099 : * by the caller.
20100 : */
20101 430 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20102 : {
20103 : List *children;
20104 :
20105 56 : children = find_all_inheritors(RelationGetRelid(partRel),
20106 : AccessExclusiveLock, NULL);
20107 180 : foreach(cell, children)
20108 : {
20109 124 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20110 : }
20111 : }
20112 430 : }
20113 :
20114 : /*
20115 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20116 : *
20117 : * To use when a DETACH PARTITION command previously did not run to
20118 : * completion; this completes the detaching process.
20119 : */
20120 : static ObjectAddress
20121 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20122 : {
20123 : Relation partRel;
20124 : ObjectAddress address;
20125 14 : Snapshot snap = GetActiveSnapshot();
20126 :
20127 14 : partRel = table_openrv(name, AccessExclusiveLock);
20128 :
20129 : /*
20130 : * Wait until existing snapshots are gone. This is important if the
20131 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20132 : * user could immediately run DETACH FINALIZE without actually waiting for
20133 : * existing transactions. We must not complete the detach action until
20134 : * all such queries are complete (otherwise we would present them with an
20135 : * inconsistent view of catalogs).
20136 : */
20137 14 : WaitForOlderSnapshots(snap->xmin, false);
20138 :
20139 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20140 :
20141 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20142 :
20143 14 : table_close(partRel, NoLock);
20144 :
20145 14 : return address;
20146 : }
20147 :
20148 : /*
20149 : * DetachAddConstraintIfNeeded
20150 : * Subroutine for ATExecDetachPartition. Create a constraint that
20151 : * takes the place of the partition constraint, but avoid creating
20152 : * a dupe if a constraint already exists which implies the needed
20153 : * constraint.
20154 : */
20155 : static void
20156 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20157 : {
20158 : List *constraintExpr;
20159 :
20160 140 : constraintExpr = RelationGetPartitionQual(partRel);
20161 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20162 :
20163 : /*
20164 : * Avoid adding a new constraint if the needed constraint is implied by an
20165 : * existing constraint
20166 : */
20167 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20168 : {
20169 : AlteredTableInfo *tab;
20170 : Constraint *n;
20171 :
20172 134 : tab = ATGetQueueEntry(wqueue, partRel);
20173 :
20174 : /* Add constraint on partition, equivalent to the partition constraint */
20175 134 : n = makeNode(Constraint);
20176 134 : n->contype = CONSTR_CHECK;
20177 134 : n->conname = NULL;
20178 134 : n->location = -1;
20179 134 : n->is_no_inherit = false;
20180 134 : n->raw_expr = NULL;
20181 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20182 134 : n->initially_valid = true;
20183 134 : n->skip_validation = true;
20184 : /* It's a re-add, since it nominally already exists */
20185 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20186 : true, false, true, ShareUpdateExclusiveLock);
20187 : }
20188 140 : }
20189 :
20190 : /*
20191 : * DropClonedTriggersFromPartition
20192 : * subroutine for ATExecDetachPartition to remove any triggers that were
20193 : * cloned to the partition when it was created-as-partition or attached.
20194 : * This undoes what CloneRowTriggersToPartition did.
20195 : */
20196 : static void
20197 432 : DropClonedTriggersFromPartition(Oid partitionId)
20198 : {
20199 : ScanKeyData skey;
20200 : SysScanDesc scan;
20201 : HeapTuple trigtup;
20202 : Relation tgrel;
20203 : ObjectAddresses *objects;
20204 :
20205 432 : objects = new_object_addresses();
20206 :
20207 : /*
20208 : * Scan pg_trigger to search for all triggers on this rel.
20209 : */
20210 432 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20211 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20212 432 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20213 432 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20214 : true, NULL, 1, &skey);
20215 706 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20216 : {
20217 274 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20218 : ObjectAddress trig;
20219 :
20220 : /* Ignore triggers that weren't cloned */
20221 274 : if (!OidIsValid(pg_trigger->tgparentid))
20222 256 : continue;
20223 :
20224 : /*
20225 : * Ignore internal triggers that are implementation objects of foreign
20226 : * keys, because these will be detached when the foreign keys
20227 : * themselves are.
20228 : */
20229 238 : if (OidIsValid(pg_trigger->tgconstrrelid))
20230 220 : continue;
20231 :
20232 : /*
20233 : * This is ugly, but necessary: remove the dependency markings on the
20234 : * trigger so that it can be removed.
20235 : */
20236 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20237 : TriggerRelationId,
20238 : DEPENDENCY_PARTITION_PRI);
20239 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20240 : RelationRelationId,
20241 : DEPENDENCY_PARTITION_SEC);
20242 :
20243 : /* remember this trigger to remove it below */
20244 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20245 18 : add_exact_object_address(&trig, objects);
20246 : }
20247 :
20248 : /* make the dependency removal visible to the deletion below */
20249 432 : CommandCounterIncrement();
20250 432 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20251 :
20252 : /* done */
20253 432 : free_object_addresses(objects);
20254 432 : systable_endscan(scan);
20255 432 : table_close(tgrel, RowExclusiveLock);
20256 432 : }
20257 :
20258 : /*
20259 : * Before acquiring lock on an index, acquire the same lock on the owning
20260 : * table.
20261 : */
20262 : struct AttachIndexCallbackState
20263 : {
20264 : Oid partitionOid;
20265 : Oid parentTblOid;
20266 : bool lockedParentTbl;
20267 : };
20268 :
20269 : static void
20270 414 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20271 : void *arg)
20272 : {
20273 : struct AttachIndexCallbackState *state;
20274 : Form_pg_class classform;
20275 : HeapTuple tuple;
20276 :
20277 414 : state = (struct AttachIndexCallbackState *) arg;
20278 :
20279 414 : if (!state->lockedParentTbl)
20280 : {
20281 392 : LockRelationOid(state->parentTblOid, AccessShareLock);
20282 392 : state->lockedParentTbl = true;
20283 : }
20284 :
20285 : /*
20286 : * If we previously locked some other heap, and the name we're looking up
20287 : * no longer refers to an index on that relation, release the now-useless
20288 : * lock. XXX maybe we should do *after* we verify whether the index does
20289 : * not actually belong to the same relation ...
20290 : */
20291 414 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20292 : {
20293 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20294 0 : state->partitionOid = InvalidOid;
20295 : }
20296 :
20297 : /* Didn't find a relation, so no need for locking or permission checks. */
20298 414 : if (!OidIsValid(relOid))
20299 6 : return;
20300 :
20301 408 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20302 408 : if (!HeapTupleIsValid(tuple))
20303 0 : return; /* concurrently dropped, so nothing to do */
20304 408 : classform = (Form_pg_class) GETSTRUCT(tuple);
20305 408 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20306 318 : classform->relkind != RELKIND_INDEX)
20307 6 : ereport(ERROR,
20308 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20309 : errmsg("\"%s\" is not an index", rv->relname)));
20310 402 : ReleaseSysCache(tuple);
20311 :
20312 : /*
20313 : * Since we need only examine the heap's tupledesc, an access share lock
20314 : * on it (preventing any DDL) is sufficient.
20315 : */
20316 402 : state->partitionOid = IndexGetRelation(relOid, false);
20317 402 : LockRelationOid(state->partitionOid, AccessShareLock);
20318 : }
20319 :
20320 : /*
20321 : * ALTER INDEX i1 ATTACH PARTITION i2
20322 : */
20323 : static ObjectAddress
20324 392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20325 : {
20326 : Relation partIdx;
20327 : Relation partTbl;
20328 : Relation parentTbl;
20329 : ObjectAddress address;
20330 : Oid partIdxId;
20331 : Oid currParent;
20332 : struct AttachIndexCallbackState state;
20333 :
20334 : /*
20335 : * We need to obtain lock on the index 'name' to modify it, but we also
20336 : * need to read its owning table's tuple descriptor -- so we need to lock
20337 : * both. To avoid deadlocks, obtain lock on the table before doing so on
20338 : * the index. Furthermore, we need to examine the parent table of the
20339 : * partition, so lock that one too.
20340 : */
20341 392 : state.partitionOid = InvalidOid;
20342 392 : state.parentTblOid = parentIdx->rd_index->indrelid;
20343 392 : state.lockedParentTbl = false;
20344 : partIdxId =
20345 392 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20346 : RangeVarCallbackForAttachIndex,
20347 : (void *) &state);
20348 : /* Not there? */
20349 380 : if (!OidIsValid(partIdxId))
20350 0 : ereport(ERROR,
20351 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20352 : errmsg("index \"%s\" does not exist", name->relname)));
20353 :
20354 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20355 380 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
20356 :
20357 : /* we already hold locks on both tables, so this is safe: */
20358 380 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20359 380 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20360 :
20361 380 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20362 :
20363 : /* Silently do nothing if already in the right state */
20364 760 : currParent = partIdx->rd_rel->relispartition ?
20365 380 : get_partition_parent(partIdxId, false) : InvalidOid;
20366 380 : if (currParent != RelationGetRelid(parentIdx))
20367 : {
20368 : IndexInfo *childInfo;
20369 : IndexInfo *parentInfo;
20370 : AttrMap *attmap;
20371 : bool found;
20372 : int i;
20373 : PartitionDesc partDesc;
20374 : Oid constraintOid,
20375 356 : cldConstrId = InvalidOid;
20376 :
20377 : /*
20378 : * If this partition already has an index attached, refuse the
20379 : * operation.
20380 : */
20381 356 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20382 :
20383 350 : if (OidIsValid(currParent))
20384 0 : ereport(ERROR,
20385 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20386 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20387 : RelationGetRelationName(partIdx),
20388 : RelationGetRelationName(parentIdx)),
20389 : errdetail("Index \"%s\" is already attached to another index.",
20390 : RelationGetRelationName(partIdx))));
20391 :
20392 : /* Make sure it indexes a partition of the other index's table */
20393 350 : partDesc = RelationGetPartitionDesc(parentTbl, true);
20394 350 : found = false;
20395 552 : for (i = 0; i < partDesc->nparts; i++)
20396 : {
20397 546 : if (partDesc->oids[i] == state.partitionOid)
20398 : {
20399 344 : found = true;
20400 344 : break;
20401 : }
20402 : }
20403 350 : if (!found)
20404 6 : ereport(ERROR,
20405 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20406 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20407 : RelationGetRelationName(partIdx),
20408 : RelationGetRelationName(parentIdx)),
20409 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20410 : RelationGetRelationName(partIdx),
20411 : RelationGetRelationName(parentTbl))));
20412 :
20413 : /* Ensure the indexes are compatible */
20414 344 : childInfo = BuildIndexInfo(partIdx);
20415 344 : parentInfo = BuildIndexInfo(parentIdx);
20416 344 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20417 : RelationGetDescr(parentTbl),
20418 : false);
20419 344 : if (!CompareIndexInfo(childInfo, parentInfo,
20420 344 : partIdx->rd_indcollation,
20421 344 : parentIdx->rd_indcollation,
20422 344 : partIdx->rd_opfamily,
20423 344 : parentIdx->rd_opfamily,
20424 : attmap))
20425 42 : ereport(ERROR,
20426 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20427 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20428 : RelationGetRelationName(partIdx),
20429 : RelationGetRelationName(parentIdx)),
20430 : errdetail("The index definitions do not match.")));
20431 :
20432 : /*
20433 : * If there is a constraint in the parent, make sure there is one in
20434 : * the child too.
20435 : */
20436 302 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20437 : RelationGetRelid(parentIdx));
20438 :
20439 302 : if (OidIsValid(constraintOid))
20440 : {
20441 122 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20442 : partIdxId);
20443 122 : if (!OidIsValid(cldConstrId))
20444 6 : ereport(ERROR,
20445 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20446 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20447 : RelationGetRelationName(partIdx),
20448 : RelationGetRelationName(parentIdx)),
20449 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20450 : RelationGetRelationName(parentIdx),
20451 : RelationGetRelationName(parentTbl),
20452 : RelationGetRelationName(partIdx))));
20453 : }
20454 :
20455 : /*
20456 : * If it's a primary key, make sure the columns in the partition are
20457 : * NOT NULL.
20458 : */
20459 296 : if (parentIdx->rd_index->indisprimary)
20460 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
20461 :
20462 : /* All good -- do it */
20463 296 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20464 296 : if (OidIsValid(constraintOid))
20465 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
20466 : RelationGetRelid(partTbl));
20467 :
20468 296 : free_attrmap(attmap);
20469 :
20470 296 : validatePartitionedIndex(parentIdx, parentTbl);
20471 : }
20472 :
20473 320 : relation_close(parentTbl, AccessShareLock);
20474 : /* keep these locks till commit */
20475 320 : relation_close(partTbl, NoLock);
20476 320 : relation_close(partIdx, NoLock);
20477 :
20478 320 : return address;
20479 : }
20480 :
20481 : /*
20482 : * Verify whether the given partition already contains an index attached
20483 : * to the given partitioned index. If so, raise an error.
20484 : */
20485 : static void
20486 356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20487 : {
20488 : Oid existingIdx;
20489 :
20490 356 : existingIdx = index_get_partition(partitionTbl,
20491 : RelationGetRelid(parentIdx));
20492 356 : if (OidIsValid(existingIdx))
20493 6 : ereport(ERROR,
20494 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20495 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20496 : RelationGetRelationName(partIdx),
20497 : RelationGetRelationName(parentIdx)),
20498 : errdetail("Another index is already attached for partition \"%s\".",
20499 : RelationGetRelationName(partitionTbl))));
20500 350 : }
20501 :
20502 : /*
20503 : * Verify whether the set of attached partition indexes to a parent index on
20504 : * a partitioned table is complete. If it is, mark the parent index valid.
20505 : *
20506 : * This should be called each time a partition index is attached.
20507 : */
20508 : static void
20509 338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20510 : {
20511 : Relation inheritsRel;
20512 : SysScanDesc scan;
20513 : ScanKeyData key;
20514 338 : int tuples = 0;
20515 : HeapTuple inhTup;
20516 338 : bool updated = false;
20517 :
20518 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20519 :
20520 : /*
20521 : * Scan pg_inherits for this parent index. Count each valid index we find
20522 : * (verifying the pg_index entry for each), and if we reach the total
20523 : * amount we expect, we can mark this parent index as valid.
20524 : */
20525 338 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20526 338 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20527 : BTEqualStrategyNumber, F_OIDEQ,
20528 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20529 338 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20530 : NULL, 1, &key);
20531 880 : while ((inhTup = systable_getnext(scan)) != NULL)
20532 : {
20533 542 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20534 : HeapTuple indTup;
20535 : Form_pg_index indexForm;
20536 :
20537 542 : indTup = SearchSysCache1(INDEXRELID,
20538 : ObjectIdGetDatum(inhForm->inhrelid));
20539 542 : if (!HeapTupleIsValid(indTup))
20540 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20541 542 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20542 542 : if (indexForm->indisvalid)
20543 484 : tuples += 1;
20544 542 : ReleaseSysCache(indTup);
20545 : }
20546 :
20547 : /* Done with pg_inherits */
20548 338 : systable_endscan(scan);
20549 338 : table_close(inheritsRel, AccessShareLock);
20550 :
20551 : /*
20552 : * If we found as many inherited indexes as the partitioned table has
20553 : * partitions, we're good; update pg_index to set indisvalid.
20554 : */
20555 338 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20556 : {
20557 : Relation idxRel;
20558 : HeapTuple indTup;
20559 : Form_pg_index indexForm;
20560 :
20561 168 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
20562 168 : indTup = SearchSysCacheCopy1(INDEXRELID,
20563 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20564 168 : if (!HeapTupleIsValid(indTup))
20565 0 : elog(ERROR, "cache lookup failed for index %u",
20566 : RelationGetRelid(partedIdx));
20567 168 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20568 :
20569 168 : indexForm->indisvalid = true;
20570 168 : updated = true;
20571 :
20572 168 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20573 :
20574 168 : table_close(idxRel, RowExclusiveLock);
20575 168 : heap_freetuple(indTup);
20576 : }
20577 :
20578 : /*
20579 : * If this index is in turn a partition of a larger index, validating it
20580 : * might cause the parent to become valid also. Try that.
20581 : */
20582 338 : if (updated && partedIdx->rd_rel->relispartition)
20583 : {
20584 : Oid parentIdxId,
20585 : parentTblId;
20586 : Relation parentIdx,
20587 : parentTbl;
20588 :
20589 : /* make sure we see the validation we just did */
20590 42 : CommandCounterIncrement();
20591 :
20592 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20593 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20594 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20595 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20596 : Assert(!parentIdx->rd_index->indisvalid);
20597 :
20598 42 : validatePartitionedIndex(parentIdx, parentTbl);
20599 :
20600 42 : relation_close(parentIdx, AccessExclusiveLock);
20601 42 : relation_close(parentTbl, AccessExclusiveLock);
20602 : }
20603 338 : }
20604 :
20605 : /*
20606 : * When attaching an index as a partition of a partitioned index which is a
20607 : * primary key, verify that all the columns in the partition are marked NOT
20608 : * NULL.
20609 : */
20610 : static void
20611 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20612 : {
20613 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20614 : {
20615 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20616 : iinfo->ii_IndexAttrNumbers[i] - 1);
20617 :
20618 94 : if (!att->attnotnull)
20619 0 : ereport(ERROR,
20620 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20621 : errmsg("invalid primary key definition"),
20622 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20623 : NameStr(att->attname),
20624 : RelationGetRelationName(partition)));
20625 : }
20626 92 : }
20627 :
20628 : /*
20629 : * Return an OID list of constraints that reference the given relation
20630 : * that are marked as having a parent constraints.
20631 : */
20632 : static List *
20633 934 : GetParentedForeignKeyRefs(Relation partition)
20634 : {
20635 : Relation pg_constraint;
20636 : HeapTuple tuple;
20637 : SysScanDesc scan;
20638 : ScanKeyData key[2];
20639 934 : List *constraints = NIL;
20640 :
20641 : /*
20642 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
20643 : * scan.
20644 : */
20645 1338 : if (RelationGetIndexList(partition) == NIL ||
20646 404 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
20647 : INDEX_ATTR_BITMAP_KEY)))
20648 686 : return NIL;
20649 :
20650 : /* Search for constraints referencing this table */
20651 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20652 248 : ScanKeyInit(&key[0],
20653 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20654 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20655 248 : ScanKeyInit(&key[1],
20656 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
20657 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20658 :
20659 : /* XXX This is a seqscan, as we don't have a usable index */
20660 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20661 372 : while ((tuple = systable_getnext(scan)) != NULL)
20662 : {
20663 124 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20664 :
20665 : /*
20666 : * We only need to process constraints that are part of larger ones.
20667 : */
20668 124 : if (!OidIsValid(constrForm->conparentid))
20669 0 : continue;
20670 :
20671 124 : constraints = lappend_oid(constraints, constrForm->oid);
20672 : }
20673 :
20674 248 : systable_endscan(scan);
20675 248 : table_close(pg_constraint, AccessShareLock);
20676 :
20677 248 : return constraints;
20678 : }
20679 :
20680 : /*
20681 : * During DETACH PARTITION, verify that any foreign keys pointing to the
20682 : * partitioned table would not become invalid. An error is raised if any
20683 : * referenced values exist.
20684 : */
20685 : static void
20686 502 : ATDetachCheckNoForeignKeyRefs(Relation partition)
20687 : {
20688 : List *constraints;
20689 : ListCell *cell;
20690 :
20691 502 : constraints = GetParentedForeignKeyRefs(partition);
20692 :
20693 554 : foreach(cell, constraints)
20694 : {
20695 86 : Oid constrOid = lfirst_oid(cell);
20696 : HeapTuple tuple;
20697 : Form_pg_constraint constrForm;
20698 : Relation rel;
20699 86 : Trigger trig = {0};
20700 :
20701 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20702 86 : if (!HeapTupleIsValid(tuple))
20703 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20704 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20705 :
20706 : Assert(OidIsValid(constrForm->conparentid));
20707 : Assert(constrForm->confrelid == RelationGetRelid(partition));
20708 :
20709 : /* prevent data changes into the referencing table until commit */
20710 86 : rel = table_open(constrForm->conrelid, ShareLock);
20711 :
20712 86 : trig.tgoid = InvalidOid;
20713 86 : trig.tgname = NameStr(constrForm->conname);
20714 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20715 86 : trig.tgisinternal = true;
20716 86 : trig.tgconstrrelid = RelationGetRelid(partition);
20717 86 : trig.tgconstrindid = constrForm->conindid;
20718 86 : trig.tgconstraint = constrForm->oid;
20719 86 : trig.tgdeferrable = false;
20720 86 : trig.tginitdeferred = false;
20721 : /* we needn't fill in remaining fields */
20722 :
20723 86 : RI_PartitionRemove_Check(&trig, rel, partition);
20724 :
20725 52 : ReleaseSysCache(tuple);
20726 :
20727 52 : table_close(rel, NoLock);
20728 : }
20729 468 : }
20730 :
20731 : /*
20732 : * resolve column compression specification to compression method.
20733 : */
20734 : static char
20735 230892 : GetAttributeCompression(Oid atttypid, const char *compression)
20736 : {
20737 : char cmethod;
20738 :
20739 230892 : if (compression == NULL || strcmp(compression, "default") == 0)
20740 230742 : return InvalidCompressionMethod;
20741 :
20742 : /*
20743 : * To specify a nondefault method, the column data type must be toastable.
20744 : * Note this says nothing about whether the column's attstorage setting
20745 : * permits compression; we intentionally allow attstorage and
20746 : * attcompression to be independent. But with a non-toastable type,
20747 : * attstorage could not be set to a value that would permit compression.
20748 : *
20749 : * We don't actually need to enforce this, since nothing bad would happen
20750 : * if attcompression were non-default; it would never be consulted. But
20751 : * it seems more user-friendly to complain about a certainly-useless
20752 : * attempt to set the property.
20753 : */
20754 150 : if (!TypeIsToastable(atttypid))
20755 6 : ereport(ERROR,
20756 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20757 : errmsg("column data type %s does not support compression",
20758 : format_type_be(atttypid))));
20759 :
20760 144 : cmethod = CompressionNameToMethod(compression);
20761 144 : if (!CompressionMethodIsValid(cmethod))
20762 12 : ereport(ERROR,
20763 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20764 : errmsg("invalid compression method \"%s\"", compression)));
20765 :
20766 132 : return cmethod;
20767 : }
20768 :
20769 : /*
20770 : * resolve column storage specification
20771 : */
20772 : static char
20773 242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
20774 : {
20775 242 : char cstorage = 0;
20776 :
20777 242 : if (pg_strcasecmp(storagemode, "plain") == 0)
20778 50 : cstorage = TYPSTORAGE_PLAIN;
20779 192 : else if (pg_strcasecmp(storagemode, "external") == 0)
20780 156 : cstorage = TYPSTORAGE_EXTERNAL;
20781 36 : else if (pg_strcasecmp(storagemode, "extended") == 0)
20782 16 : cstorage = TYPSTORAGE_EXTENDED;
20783 20 : else if (pg_strcasecmp(storagemode, "main") == 0)
20784 14 : cstorage = TYPSTORAGE_MAIN;
20785 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
20786 6 : cstorage = get_typstorage(atttypid);
20787 : else
20788 0 : ereport(ERROR,
20789 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20790 : errmsg("invalid storage type \"%s\"",
20791 : storagemode)));
20792 :
20793 : /*
20794 : * safety check: do not allow toasted storage modes unless column datatype
20795 : * is TOAST-aware.
20796 : */
20797 242 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20798 6 : ereport(ERROR,
20799 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20800 : errmsg("column data type %s can only have storage PLAIN",
20801 : format_type_be(atttypid))));
20802 :
20803 236 : return cstorage;
20804 : }
|