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-2025, 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, bool is_enforced);
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, ATAlterConstraint *cmdcon,
393 : bool recurse, LOCKMODE lockmode);
394 : static bool ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
395 : Relation tgrel, Relation rel, HeapTuple contuple,
396 : bool recurse, List **otherrelids, LOCKMODE lockmode);
397 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
398 : bool deferrable, bool initdeferred,
399 : List **otherrelids);
400 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
401 : Relation rel, char *constrName,
402 : bool recurse, bool recursing, LOCKMODE lockmode);
403 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
404 : HeapTuple contuple, LOCKMODE lockmode);
405 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
406 : char *constrName, HeapTuple contuple,
407 : bool recurse, bool recursing, LOCKMODE lockmode);
408 : static int transformColumnNameList(Oid relId, List *colList,
409 : int16 *attnums, Oid *atttypids, Oid *attcollids);
410 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
411 : List **attnamelist,
412 : int16 *attnums, Oid *atttypids, Oid *attcollids,
413 : Oid *opclasses, bool *pk_has_without_overlaps);
414 : static Oid transformFkeyCheckAttrs(Relation pkrel,
415 : int numattrs, int16 *attnums,
416 : bool with_period, Oid *opclasses,
417 : bool *pk_has_without_overlaps);
418 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
419 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
420 : Oid *funcid);
421 : static void validateForeignKeyConstraint(char *conname,
422 : Relation rel, Relation pkrel,
423 : Oid pkindOid, Oid constraintOid, bool hasperiod);
424 : static void CheckAlterTableIsSafe(Relation rel);
425 : static void ATController(AlterTableStmt *parsetree,
426 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
427 : AlterTableUtilityContext *context);
428 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
429 : bool recurse, bool recursing, LOCKMODE lockmode,
430 : AlterTableUtilityContext *context);
431 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
432 : AlterTableUtilityContext *context);
433 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
434 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
435 : AlterTableUtilityContext *context);
436 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
437 : Relation rel, AlterTableCmd *cmd,
438 : bool recurse, LOCKMODE lockmode,
439 : AlterTablePass cur_pass,
440 : AlterTableUtilityContext *context);
441 : static void ATRewriteTables(AlterTableStmt *parsetree,
442 : List **wqueue, LOCKMODE lockmode,
443 : AlterTableUtilityContext *context);
444 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
445 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
446 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
447 : static void ATSimpleRecursion(List **wqueue, Relation rel,
448 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
449 : AlterTableUtilityContext *context);
450 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
451 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
452 : LOCKMODE lockmode,
453 : AlterTableUtilityContext *context);
454 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
455 : DropBehavior behavior);
456 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
457 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
458 : AlterTableUtilityContext *context);
459 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
460 : Relation rel, AlterTableCmd **cmd,
461 : bool recurse, bool recursing,
462 : LOCKMODE lockmode, AlterTablePass cur_pass,
463 : AlterTableUtilityContext *context);
464 : static bool check_for_column_name_collision(Relation rel, const char *colname,
465 : bool if_not_exists);
466 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
467 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
468 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
469 : LOCKMODE lockmode);
470 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
471 : LOCKMODE lockmode);
472 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
473 : char *constrname, char *colName,
474 : bool recurse, bool recursing,
475 : LOCKMODE lockmode);
476 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
477 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
478 : List *testConstraint, List *provenConstraint);
479 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
480 : Node *newDefault, LOCKMODE lockmode);
481 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
482 : Node *newDefault);
483 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
484 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
485 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
486 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
487 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
488 : bool recurse, bool recursing);
489 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
490 : Node *newExpr, LOCKMODE lockmode);
491 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
492 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
493 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
494 : Node *newValue, LOCKMODE lockmode);
495 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
496 : Node *options, bool isReset, LOCKMODE lockmode);
497 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
498 : Node *newValue, LOCKMODE lockmode);
499 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
500 : AlterTableCmd *cmd, LOCKMODE lockmode,
501 : AlterTableUtilityContext *context);
502 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
503 : DropBehavior behavior,
504 : bool recurse, bool recursing,
505 : bool missing_ok, LOCKMODE lockmode,
506 : ObjectAddresses *addrs);
507 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
508 : bool recurse, LOCKMODE lockmode,
509 : AlterTableUtilityContext *context);
510 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
511 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
512 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
513 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
514 : static ObjectAddress ATExecAddConstraint(List **wqueue,
515 : AlteredTableInfo *tab, Relation rel,
516 : Constraint *newConstraint, bool recurse, bool is_readd,
517 : LOCKMODE lockmode);
518 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
519 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
520 : IndexStmt *stmt, LOCKMODE lockmode);
521 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
522 : AlteredTableInfo *tab, Relation rel,
523 : Constraint *constr,
524 : bool recurse, bool recursing, bool is_readd,
525 : LOCKMODE lockmode);
526 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
527 : Relation rel, Constraint *fkconstraint,
528 : bool recurse, bool recursing,
529 : LOCKMODE lockmode);
530 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
531 : int numfksetcols, const int16 *fksetcolsattnums,
532 : List *fksetcols);
533 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
534 : char *constraintname,
535 : Constraint *fkconstraint, Relation rel,
536 : Relation pkrel, Oid indexOid,
537 : Oid parentConstr,
538 : int numfks, int16 *pkattnum, int16 *fkattnum,
539 : Oid *pfeqoperators, Oid *ppeqoperators,
540 : Oid *ffeqoperators, int numfkdelsetcols,
541 : int16 *fkdelsetcols, bool is_internal,
542 : bool with_period);
543 : static void addFkRecurseReferenced(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,
549 : Oid parentDelTrigger, Oid parentUpdTrigger,
550 : bool with_period);
551 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
552 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
553 : int numfks, int16 *pkattnum, int16 *fkattnum,
554 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
555 : int numfkdelsetcols, int16 *fkdelsetcols,
556 : bool old_check_ok, LOCKMODE lockmode,
557 : Oid parentInsTrigger, Oid parentUpdTrigger,
558 : bool with_period);
559 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
560 : Relation partitionRel);
561 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
562 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
563 : Relation partRel);
564 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
565 : Constraint *fkconstraint, Oid constraintOid,
566 : Oid indexOid,
567 : Oid parentInsTrigger, Oid parentUpdTrigger,
568 : Oid *insertTrigOid, Oid *updateTrigOid);
569 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
570 : Constraint *fkconstraint, Oid constraintOid,
571 : Oid indexOid,
572 : Oid parentDelTrigger, Oid parentUpdTrigger,
573 : Oid *deleteTrigOid, Oid *updateTrigOid);
574 : static bool tryAttachPartitionForeignKey(List **wqueue,
575 : ForeignKeyCacheInfo *fk,
576 : Relation partition,
577 : Oid parentConstrOid, int numfks,
578 : AttrNumber *mapped_conkey, AttrNumber *confkey,
579 : Oid *conpfeqop,
580 : Oid parentInsTrigger,
581 : Oid parentUpdTrigger,
582 : Relation trigrel);
583 : static void GetForeignKeyActionTriggers(Relation trigrel,
584 : Oid conoid, Oid confrelid, Oid conrelid,
585 : Oid *deleteTriggerOid,
586 : Oid *updateTriggerOid);
587 : static void GetForeignKeyCheckTriggers(Relation trigrel,
588 : Oid conoid, Oid confrelid, Oid conrelid,
589 : Oid *insertTriggerOid,
590 : Oid *updateTriggerOid);
591 : static void ATExecDropConstraint(Relation rel, const char *constrName,
592 : DropBehavior behavior, bool recurse,
593 : bool missing_ok, LOCKMODE lockmode);
594 : static ObjectAddress dropconstraint_internal(Relation rel,
595 : HeapTuple constraintTup, DropBehavior behavior,
596 : bool recurse, bool recursing,
597 : bool missing_ok, LOCKMODE lockmode);
598 : static void ATPrepAlterColumnType(List **wqueue,
599 : AlteredTableInfo *tab, Relation rel,
600 : bool recurse, bool recursing,
601 : AlterTableCmd *cmd, LOCKMODE lockmode,
602 : AlterTableUtilityContext *context);
603 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
604 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
605 : AlterTableCmd *cmd, LOCKMODE lockmode);
606 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
607 : Relation rel, AttrNumber attnum, const char *colName);
608 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
609 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
610 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
611 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
612 : LOCKMODE lockmode);
613 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
614 : char *cmd, List **wqueue, LOCKMODE lockmode,
615 : bool rewrite);
616 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
617 : Oid objid, Relation rel, List *domname,
618 : const char *conname);
619 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
620 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
621 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
622 : List *options, LOCKMODE lockmode);
623 : static void change_owner_fix_column_acls(Oid relationOid,
624 : Oid oldOwnerId, Oid newOwnerId);
625 : static void change_owner_recurse_to_sequences(Oid relationOid,
626 : Oid newOwnerId, LOCKMODE lockmode);
627 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
628 : LOCKMODE lockmode);
629 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
630 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
631 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
632 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
633 : bool toLogged);
634 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
635 : const char *tablespacename, LOCKMODE lockmode);
636 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
637 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
638 : static void ATExecSetRelOptions(Relation rel, List *defList,
639 : AlterTableType operation,
640 : LOCKMODE lockmode);
641 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
642 : char fires_when, bool skip_system, bool recurse,
643 : LOCKMODE lockmode);
644 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
645 : char fires_when, LOCKMODE lockmode);
646 : static void ATPrepAddInherit(Relation child_rel);
647 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
648 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
649 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
650 : DependencyType deptype);
651 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
652 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
653 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
654 : static void ATExecGenericOptions(Relation rel, List *options);
655 : static void ATExecSetRowSecurity(Relation rel, bool rls);
656 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
657 : static ObjectAddress ATExecSetCompression(Relation rel,
658 : const char *column, Node *newValue, LOCKMODE lockmode);
659 :
660 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
661 : static const char *storage_name(char c);
662 :
663 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
664 : Oid oldRelOid, void *arg);
665 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
666 : Oid oldrelid, void *arg);
667 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
668 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
669 : List **partexprs, Oid *partopclass, Oid *partcollation,
670 : PartitionStrategy strategy);
671 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
672 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
673 : bool expect_detached);
674 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
675 : PartitionCmd *cmd,
676 : AlterTableUtilityContext *context);
677 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
678 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
679 : List *partConstraint,
680 : bool validate_default);
681 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
682 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
683 : static void DropClonedTriggersFromPartition(Oid partitionId);
684 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
685 : Relation rel, RangeVar *name,
686 : bool concurrent);
687 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
688 : bool concurrent, Oid defaultPartOid);
689 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
690 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
691 : RangeVar *name);
692 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
693 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
694 : Relation partitionTbl);
695 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
696 : static List *GetParentedForeignKeyRefs(Relation partition);
697 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
698 : static char GetAttributeCompression(Oid atttypid, const char *compression);
699 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
700 :
701 :
702 : /* ----------------------------------------------------------------
703 : * DefineRelation
704 : * Creates a new relation.
705 : *
706 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
707 : * The other arguments are used to extend the behavior for other cases:
708 : * relkind: relkind to assign to the new relation
709 : * ownerId: if not InvalidOid, use this as the new relation's owner.
710 : * typaddress: if not null, it's set to the pg_type entry's address.
711 : * queryString: for error reporting
712 : *
713 : * Note that permissions checks are done against current user regardless of
714 : * ownerId. A nonzero ownerId is used when someone is creating a relation
715 : * "on behalf of" someone else, so we still want to see that the current user
716 : * has permissions to do it.
717 : *
718 : * If successful, returns the address of the new relation.
719 : * ----------------------------------------------------------------
720 : */
721 : ObjectAddress
722 59528 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
723 : ObjectAddress *typaddress, const char *queryString)
724 : {
725 : char relname[NAMEDATALEN];
726 : Oid namespaceId;
727 : Oid relationId;
728 : Oid tablespaceId;
729 : Relation rel;
730 : TupleDesc descriptor;
731 : List *inheritOids;
732 : List *old_constraints;
733 : List *old_notnulls;
734 : List *rawDefaults;
735 : List *cookedDefaults;
736 : List *nncols;
737 : Datum reloptions;
738 : ListCell *listptr;
739 : AttrNumber attnum;
740 : bool partitioned;
741 59528 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
742 : Oid ofTypeId;
743 : ObjectAddress address;
744 : LOCKMODE parentLockmode;
745 59528 : Oid accessMethodId = InvalidOid;
746 :
747 : /*
748 : * Truncate relname to appropriate length (probably a waste of time, as
749 : * parser should have done this already).
750 : */
751 59528 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
752 :
753 : /*
754 : * Check consistency of arguments
755 : */
756 59528 : if (stmt->oncommit != ONCOMMIT_NOOP
757 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
758 12 : ereport(ERROR,
759 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
760 : errmsg("ON COMMIT can only be used on temporary tables")));
761 :
762 59516 : if (stmt->partspec != NULL)
763 : {
764 4910 : if (relkind != RELKIND_RELATION)
765 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
766 :
767 4910 : relkind = RELKIND_PARTITIONED_TABLE;
768 4910 : partitioned = true;
769 : }
770 : else
771 54606 : partitioned = false;
772 :
773 59516 : if (relkind == RELKIND_PARTITIONED_TABLE &&
774 4910 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
775 6 : ereport(ERROR,
776 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
777 : errmsg("partitioned tables cannot be unlogged")));
778 :
779 : /*
780 : * Look up the namespace in which we are supposed to create the relation,
781 : * check we have permission to create there, lock it against concurrent
782 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
783 : * namespace is selected.
784 : */
785 : namespaceId =
786 59510 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
787 :
788 : /*
789 : * Security check: disallow creating temp tables from security-restricted
790 : * code. This is needed because calling code might not expect untrusted
791 : * tables to appear in pg_temp at the front of its search path.
792 : */
793 59510 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
794 3054 : && InSecurityRestrictedOperation())
795 0 : ereport(ERROR,
796 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
797 : errmsg("cannot create temporary table within security-restricted operation")));
798 :
799 : /*
800 : * Determine the lockmode to use when scanning parents. A self-exclusive
801 : * lock is needed here.
802 : *
803 : * For regular inheritance, if two backends attempt to add children to the
804 : * same parent simultaneously, and that parent has no pre-existing
805 : * children, then both will attempt to update the parent's relhassubclass
806 : * field, leading to a "tuple concurrently updated" error. Also, this
807 : * interlocks against a concurrent ANALYZE on the parent table, which
808 : * might otherwise be attempting to clear the parent's relhassubclass
809 : * field, if its previous children were recently dropped.
810 : *
811 : * If the child table is a partition, then we instead grab an exclusive
812 : * lock on the parent because its partition descriptor will be changed by
813 : * addition of the new partition.
814 : */
815 59510 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
816 : ShareUpdateExclusiveLock);
817 :
818 : /* Determine the list of OIDs of the parents. */
819 59510 : inheritOids = NIL;
820 69748 : foreach(listptr, stmt->inhRelations)
821 : {
822 10238 : RangeVar *rv = (RangeVar *) lfirst(listptr);
823 : Oid parentOid;
824 :
825 10238 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
826 :
827 : /*
828 : * Reject duplications in the list of parents.
829 : */
830 10238 : if (list_member_oid(inheritOids, parentOid))
831 0 : ereport(ERROR,
832 : (errcode(ERRCODE_DUPLICATE_TABLE),
833 : errmsg("relation \"%s\" would be inherited from more than once",
834 : get_rel_name(parentOid))));
835 :
836 10238 : inheritOids = lappend_oid(inheritOids, parentOid);
837 : }
838 :
839 : /*
840 : * Select tablespace to use: an explicitly indicated one, or (in the case
841 : * of a partitioned table) the parent's, if it has one.
842 : */
843 59510 : if (stmt->tablespacename)
844 : {
845 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
846 :
847 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
848 6 : ereport(ERROR,
849 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
850 : errmsg("cannot specify default tablespace for partitioned relations")));
851 : }
852 59392 : else if (stmt->partbound)
853 : {
854 : Assert(list_length(inheritOids) == 1);
855 7902 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
856 : }
857 : else
858 51490 : tablespaceId = InvalidOid;
859 :
860 : /* still nothing? use the default */
861 59498 : if (!OidIsValid(tablespaceId))
862 59370 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
863 : partitioned);
864 :
865 : /* Check permissions except when using database's default */
866 59492 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
867 : {
868 : AclResult aclresult;
869 :
870 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
871 : ACL_CREATE);
872 146 : if (aclresult != ACLCHECK_OK)
873 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
874 6 : get_tablespace_name(tablespaceId));
875 : }
876 :
877 : /* In all cases disallow placing user relations in pg_global */
878 59486 : if (tablespaceId == GLOBALTABLESPACE_OID)
879 18 : ereport(ERROR,
880 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
881 : errmsg("only shared relations can be placed in pg_global tablespace")));
882 :
883 : /* Identify user ID that will own the table */
884 59468 : if (!OidIsValid(ownerId))
885 59228 : ownerId = GetUserId();
886 :
887 : /*
888 : * Parse and validate reloptions, if any.
889 : */
890 59468 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
891 : true, false);
892 :
893 59450 : switch (relkind)
894 : {
895 14920 : case RELKIND_VIEW:
896 14920 : (void) view_reloptions(reloptions, true);
897 14902 : break;
898 4886 : case RELKIND_PARTITIONED_TABLE:
899 4886 : (void) partitioned_table_reloptions(reloptions, true);
900 4880 : break;
901 39644 : default:
902 39644 : (void) heap_reloptions(relkind, reloptions, true);
903 : }
904 :
905 59330 : if (stmt->ofTypename)
906 : {
907 : AclResult aclresult;
908 :
909 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
910 :
911 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
912 86 : if (aclresult != ACLCHECK_OK)
913 6 : aclcheck_error_type(aclresult, ofTypeId);
914 : }
915 : else
916 59244 : ofTypeId = InvalidOid;
917 :
918 : /*
919 : * Look up inheritance ancestors and generate relation schema, including
920 : * inherited attributes. (Note that stmt->tableElts is destructively
921 : * modified by MergeAttributes.)
922 : */
923 59084 : stmt->tableElts =
924 59324 : MergeAttributes(stmt->tableElts, inheritOids,
925 59324 : stmt->relation->relpersistence,
926 59324 : stmt->partbound != NULL,
927 : &old_constraints, &old_notnulls);
928 :
929 : /*
930 : * Create a tuple descriptor from the relation schema. Note that this
931 : * deals with column names, types, and in-descriptor NOT NULL flags, but
932 : * not default values, NOT NULL or CHECK constraints; we handle those
933 : * below.
934 : */
935 59084 : descriptor = BuildDescForRelation(stmt->tableElts);
936 :
937 : /*
938 : * Find columns with default values and prepare for insertion of the
939 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
940 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
941 : * while raw defaults go into a list of RawColumnDefault structs that will
942 : * be processed by AddRelationNewConstraints. (We can't deal with raw
943 : * expressions until we can do transformExpr.)
944 : *
945 : * We can set the atthasdef flags now in the tuple descriptor; this just
946 : * saves StoreAttrDefault from having to do an immediate update of the
947 : * pg_attribute rows.
948 : */
949 59036 : rawDefaults = NIL;
950 59036 : cookedDefaults = NIL;
951 59036 : attnum = 0;
952 :
953 291496 : foreach(listptr, stmt->tableElts)
954 : {
955 232460 : ColumnDef *colDef = lfirst(listptr);
956 : Form_pg_attribute attr;
957 :
958 232460 : attnum++;
959 232460 : attr = TupleDescAttr(descriptor, attnum - 1);
960 :
961 232460 : if (colDef->raw_default != NULL)
962 : {
963 : RawColumnDefault *rawEnt;
964 :
965 : Assert(colDef->cooked_default == NULL);
966 :
967 3054 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
968 3054 : rawEnt->attnum = attnum;
969 3054 : rawEnt->raw_default = colDef->raw_default;
970 3054 : rawEnt->missingMode = false;
971 3054 : rawEnt->generated = colDef->generated;
972 3054 : rawDefaults = lappend(rawDefaults, rawEnt);
973 3054 : attr->atthasdef = true;
974 : }
975 229406 : else if (colDef->cooked_default != NULL)
976 : {
977 : CookedConstraint *cooked;
978 :
979 372 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
980 372 : cooked->contype = CONSTR_DEFAULT;
981 372 : cooked->conoid = InvalidOid; /* until created */
982 372 : cooked->name = NULL;
983 372 : cooked->attnum = attnum;
984 372 : cooked->expr = colDef->cooked_default;
985 372 : cooked->is_enforced = true;
986 372 : cooked->skip_validation = false;
987 372 : cooked->is_local = true; /* not used for defaults */
988 372 : cooked->inhcount = 0; /* ditto */
989 372 : cooked->is_no_inherit = false;
990 372 : cookedDefaults = lappend(cookedDefaults, cooked);
991 372 : attr->atthasdef = true;
992 : }
993 :
994 232460 : populate_compact_attribute(descriptor, attnum - 1);
995 : }
996 :
997 : /*
998 : * For relations with table AM and partitioned tables, select access
999 : * method to use: an explicitly indicated one, or (in the case of a
1000 : * partitioned table) the parent's, if it has one.
1001 : */
1002 59036 : if (stmt->accessMethod != NULL)
1003 : {
1004 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1005 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1006 : }
1007 58914 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1008 : {
1009 37298 : if (stmt->partbound)
1010 : {
1011 : Assert(list_length(inheritOids) == 1);
1012 7720 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1013 : }
1014 :
1015 37298 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1016 32404 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1017 : }
1018 :
1019 : /*
1020 : * Create the relation. Inherited defaults and CHECK constraints are
1021 : * passed in for immediate handling --- since they don't need parsing,
1022 : * they can be stored immediately.
1023 : */
1024 59018 : relationId = heap_create_with_catalog(relname,
1025 : namespaceId,
1026 : tablespaceId,
1027 : InvalidOid,
1028 : InvalidOid,
1029 : ofTypeId,
1030 : ownerId,
1031 : accessMethodId,
1032 : descriptor,
1033 : list_concat(cookedDefaults,
1034 : old_constraints),
1035 : relkind,
1036 59018 : stmt->relation->relpersistence,
1037 : false,
1038 : false,
1039 : stmt->oncommit,
1040 : reloptions,
1041 : true,
1042 : allowSystemTableMods,
1043 : false,
1044 : InvalidOid,
1045 : typaddress);
1046 :
1047 : /*
1048 : * We must bump the command counter to make the newly-created relation
1049 : * tuple visible for opening.
1050 : */
1051 58982 : CommandCounterIncrement();
1052 :
1053 : /*
1054 : * Open the new relation and acquire exclusive lock on it. This isn't
1055 : * really necessary for locking out other backends (since they can't see
1056 : * the new rel anyway until we commit), but it keeps the lock manager from
1057 : * complaining about deadlock risks.
1058 : */
1059 58982 : rel = relation_open(relationId, AccessExclusiveLock);
1060 :
1061 : /*
1062 : * Now add any newly specified column default and generation expressions
1063 : * to the new relation. These are passed to us in the form of raw
1064 : * parsetrees; we need to transform them to executable expression trees
1065 : * before they can be added. The most convenient way to do that is to
1066 : * apply the parser's transformExpr routine, but transformExpr doesn't
1067 : * work unless we have a pre-existing relation. So, the transformation has
1068 : * to be postponed to this final step of CREATE TABLE.
1069 : *
1070 : * This needs to be before processing the partitioning clauses because
1071 : * those could refer to generated columns.
1072 : */
1073 58982 : if (rawDefaults)
1074 2622 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1075 : true, true, false, queryString);
1076 :
1077 : /*
1078 : * Make column generation expressions visible for use by partitioning.
1079 : */
1080 58820 : CommandCounterIncrement();
1081 :
1082 : /* Process and store partition bound, if any. */
1083 58820 : if (stmt->partbound)
1084 : {
1085 : PartitionBoundSpec *bound;
1086 : ParseState *pstate;
1087 7824 : Oid parentId = linitial_oid(inheritOids),
1088 : defaultPartOid;
1089 : Relation parent,
1090 7824 : defaultRel = NULL;
1091 : ParseNamespaceItem *nsitem;
1092 :
1093 : /* Already have strong enough lock on the parent */
1094 7824 : parent = table_open(parentId, NoLock);
1095 :
1096 : /*
1097 : * We are going to try to validate the partition bound specification
1098 : * against the partition key of parentRel, so it better have one.
1099 : */
1100 7824 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1101 18 : ereport(ERROR,
1102 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1103 : errmsg("\"%s\" is not partitioned",
1104 : RelationGetRelationName(parent))));
1105 :
1106 : /*
1107 : * The partition constraint of the default partition depends on the
1108 : * partition bounds of every other partition. It is possible that
1109 : * another backend might be about to execute a query on the default
1110 : * partition table, and that the query relies on previously cached
1111 : * default partition constraints. We must therefore take a table lock
1112 : * strong enough to prevent all queries on the default partition from
1113 : * proceeding until we commit and send out a shared-cache-inval notice
1114 : * that will make them update their index lists.
1115 : *
1116 : * Order of locking: The relation being added won't be visible to
1117 : * other backends until it is committed, hence here in
1118 : * DefineRelation() the order of locking the default partition and the
1119 : * relation being added does not matter. But at all other places we
1120 : * need to lock the default relation before we lock the relation being
1121 : * added or removed i.e. we should take the lock in same order at all
1122 : * the places such that lock parent, lock default partition and then
1123 : * lock the partition so as to avoid a deadlock.
1124 : */
1125 : defaultPartOid =
1126 7806 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1127 : true));
1128 7806 : if (OidIsValid(defaultPartOid))
1129 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1130 :
1131 : /* Transform the bound values */
1132 7806 : pstate = make_parsestate(NULL);
1133 7806 : pstate->p_sourcetext = queryString;
1134 :
1135 : /*
1136 : * Add an nsitem containing this relation, so that transformExpr
1137 : * called on partition bound expressions is able to report errors
1138 : * using a proper context.
1139 : */
1140 7806 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1141 : NULL, false, false);
1142 7806 : addNSItemToQuery(pstate, nsitem, false, true, true);
1143 :
1144 7806 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1145 :
1146 : /*
1147 : * Check first that the new partition's bound is valid and does not
1148 : * overlap with any of existing partitions of the parent.
1149 : */
1150 7602 : check_new_partition_bound(relname, parent, bound, pstate);
1151 :
1152 : /*
1153 : * If the default partition exists, its partition constraints will
1154 : * change after the addition of this new partition such that it won't
1155 : * allow any row that qualifies for this new partition. So, check that
1156 : * the existing data in the default partition satisfies the constraint
1157 : * as it will exist after adding this partition.
1158 : */
1159 7488 : if (OidIsValid(defaultPartOid))
1160 : {
1161 348 : check_default_partition_contents(parent, defaultRel, bound);
1162 : /* Keep the lock until commit. */
1163 330 : table_close(defaultRel, NoLock);
1164 : }
1165 :
1166 : /* Update the pg_class entry. */
1167 7470 : StorePartitionBound(rel, parent, bound);
1168 :
1169 7470 : table_close(parent, NoLock);
1170 : }
1171 :
1172 : /* Store inheritance information for new rel. */
1173 58466 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1174 :
1175 : /*
1176 : * Process the partitioning specification (if any) and store the partition
1177 : * key information into the catalog.
1178 : */
1179 58466 : if (partitioned)
1180 : {
1181 : ParseState *pstate;
1182 : int partnatts;
1183 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1184 : Oid partopclass[PARTITION_MAX_KEYS];
1185 : Oid partcollation[PARTITION_MAX_KEYS];
1186 4880 : List *partexprs = NIL;
1187 :
1188 4880 : pstate = make_parsestate(NULL);
1189 4880 : pstate->p_sourcetext = queryString;
1190 :
1191 4880 : partnatts = list_length(stmt->partspec->partParams);
1192 :
1193 : /* Protect fixed-size arrays here and in executor */
1194 4880 : if (partnatts > PARTITION_MAX_KEYS)
1195 0 : ereport(ERROR,
1196 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1197 : errmsg("cannot partition using more than %d columns",
1198 : PARTITION_MAX_KEYS)));
1199 :
1200 : /*
1201 : * We need to transform the raw parsetrees corresponding to partition
1202 : * expressions into executable expression trees. Like column defaults
1203 : * and CHECK constraints, we could not have done the transformation
1204 : * earlier.
1205 : */
1206 4880 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1207 :
1208 4850 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1209 : partattrs, &partexprs, partopclass,
1210 4850 : partcollation, stmt->partspec->strategy);
1211 :
1212 4754 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1213 : partexprs,
1214 : partopclass, partcollation);
1215 :
1216 : /* make it all visible */
1217 4754 : CommandCounterIncrement();
1218 : }
1219 :
1220 : /*
1221 : * If we're creating a partition, create now all the indexes, triggers,
1222 : * FKs defined in the parent.
1223 : *
1224 : * We can't do it earlier, because DefineIndex wants to know the partition
1225 : * key which we just stored.
1226 : */
1227 58340 : if (stmt->partbound)
1228 : {
1229 7464 : Oid parentId = linitial_oid(inheritOids);
1230 : Relation parent;
1231 : List *idxlist;
1232 : ListCell *cell;
1233 :
1234 : /* Already have strong enough lock on the parent */
1235 7464 : parent = table_open(parentId, NoLock);
1236 7464 : idxlist = RelationGetIndexList(parent);
1237 :
1238 : /*
1239 : * For each index in the parent table, create one in the partition
1240 : */
1241 8826 : foreach(cell, idxlist)
1242 : {
1243 1380 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1244 : AttrMap *attmap;
1245 : IndexStmt *idxstmt;
1246 : Oid constraintOid;
1247 :
1248 1380 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1249 : {
1250 36 : if (idxRel->rd_index->indisunique)
1251 12 : ereport(ERROR,
1252 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1253 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1254 : RelationGetRelationName(parent)),
1255 : errdetail("Table \"%s\" contains indexes that are unique.",
1256 : RelationGetRelationName(parent))));
1257 : else
1258 : {
1259 24 : index_close(idxRel, AccessShareLock);
1260 24 : continue;
1261 : }
1262 : }
1263 :
1264 1344 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1265 : RelationGetDescr(parent),
1266 : false);
1267 : idxstmt =
1268 1344 : generateClonedIndexStmt(NULL, idxRel,
1269 : attmap, &constraintOid);
1270 1344 : DefineIndex(RelationGetRelid(rel),
1271 : idxstmt,
1272 : InvalidOid,
1273 : RelationGetRelid(idxRel),
1274 : constraintOid,
1275 : -1,
1276 : false, false, false, false, false);
1277 :
1278 1338 : index_close(idxRel, AccessShareLock);
1279 : }
1280 :
1281 7446 : list_free(idxlist);
1282 :
1283 : /*
1284 : * If there are any row-level triggers, clone them to the new
1285 : * partition.
1286 : */
1287 7446 : if (parent->trigdesc != NULL)
1288 420 : CloneRowTriggersToPartition(parent, rel);
1289 :
1290 : /*
1291 : * And foreign keys too. Note that because we're freshly creating the
1292 : * table, there is no need to verify these new constraints.
1293 : */
1294 7446 : CloneForeignKeyConstraints(NULL, parent, rel);
1295 :
1296 7446 : table_close(parent, NoLock);
1297 : }
1298 :
1299 : /*
1300 : * Now add any newly specified CHECK constraints to the new relation. Same
1301 : * as for defaults above, but these need to come after partitioning is set
1302 : * up.
1303 : */
1304 58322 : if (stmt->constraints)
1305 712 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1306 : true, true, false, queryString);
1307 :
1308 : /*
1309 : * Finally, merge the not-null constraints that are declared directly with
1310 : * those that come from parent relations (making sure to count inheritance
1311 : * appropriately for each), create them, and set the attnotnull flag on
1312 : * columns that don't yet have it.
1313 : */
1314 58292 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1315 : old_notnulls);
1316 131028 : foreach_int(attrnum, nncols)
1317 14636 : set_attnotnull(NULL, rel, attrnum, NoLock);
1318 :
1319 58196 : ObjectAddressSet(address, RelationRelationId, relationId);
1320 :
1321 : /*
1322 : * Clean up. We keep lock on new relation (although it shouldn't be
1323 : * visible to anyone else anyway, until commit).
1324 : */
1325 58196 : relation_close(rel, NoLock);
1326 :
1327 58196 : return address;
1328 : }
1329 :
1330 : /*
1331 : * BuildDescForRelation
1332 : *
1333 : * Given a list of ColumnDef nodes, build a TupleDesc.
1334 : *
1335 : * Note: This is only for the limited purpose of table and view creation. Not
1336 : * everything is filled in. A real tuple descriptor should be obtained from
1337 : * the relcache.
1338 : */
1339 : TupleDesc
1340 61874 : BuildDescForRelation(const List *columns)
1341 : {
1342 : int natts;
1343 : AttrNumber attnum;
1344 : ListCell *l;
1345 : TupleDesc desc;
1346 : char *attname;
1347 : Oid atttypid;
1348 : int32 atttypmod;
1349 : Oid attcollation;
1350 : int attdim;
1351 :
1352 : /*
1353 : * allocate a new tuple descriptor
1354 : */
1355 61874 : natts = list_length(columns);
1356 61874 : desc = CreateTemplateTupleDesc(natts);
1357 :
1358 61874 : attnum = 0;
1359 :
1360 297388 : foreach(l, columns)
1361 : {
1362 235574 : ColumnDef *entry = lfirst(l);
1363 : AclResult aclresult;
1364 : Form_pg_attribute att;
1365 :
1366 : /*
1367 : * for each entry in the list, get the name and type information from
1368 : * the list and have TupleDescInitEntry fill in the attribute
1369 : * information we need.
1370 : */
1371 235574 : attnum++;
1372 :
1373 235574 : attname = entry->colname;
1374 235574 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1375 :
1376 235574 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1377 235574 : if (aclresult != ACLCHECK_OK)
1378 42 : aclcheck_error_type(aclresult, atttypid);
1379 :
1380 235532 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1381 235532 : attdim = list_length(entry->typeName->arrayBounds);
1382 235532 : if (attdim > PG_INT16_MAX)
1383 0 : ereport(ERROR,
1384 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1385 : errmsg("too many array dimensions"));
1386 :
1387 235532 : if (entry->typeName->setof)
1388 0 : ereport(ERROR,
1389 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1390 : errmsg("column \"%s\" cannot be declared SETOF",
1391 : attname)));
1392 :
1393 235532 : TupleDescInitEntry(desc, attnum, attname,
1394 : atttypid, atttypmod, attdim);
1395 235532 : att = TupleDescAttr(desc, attnum - 1);
1396 :
1397 : /* Override TupleDescInitEntry's settings as requested */
1398 235532 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1399 :
1400 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1401 235532 : att->attnotnull = entry->is_not_null;
1402 235532 : att->attislocal = entry->is_local;
1403 235532 : att->attinhcount = entry->inhcount;
1404 235532 : att->attidentity = entry->identity;
1405 235532 : att->attgenerated = entry->generated;
1406 235532 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1407 235520 : if (entry->storage)
1408 19632 : att->attstorage = entry->storage;
1409 215888 : else if (entry->storage_name)
1410 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1411 :
1412 235514 : populate_compact_attribute(desc, attnum - 1);
1413 : }
1414 :
1415 61814 : return desc;
1416 : }
1417 :
1418 : /*
1419 : * Emit the right error or warning message for a "DROP" command issued on a
1420 : * non-existent relation
1421 : */
1422 : static void
1423 1060 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1424 : {
1425 : const struct dropmsgstrings *rentry;
1426 :
1427 1180 : if (rel->schemaname != NULL &&
1428 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1429 : {
1430 42 : if (!missing_ok)
1431 : {
1432 0 : ereport(ERROR,
1433 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1434 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1435 : }
1436 : else
1437 : {
1438 42 : ereport(NOTICE,
1439 : (errmsg("schema \"%s\" does not exist, skipping",
1440 : rel->schemaname)));
1441 : }
1442 42 : return;
1443 : }
1444 :
1445 1338 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1446 : {
1447 1338 : if (rentry->kind == rightkind)
1448 : {
1449 1018 : if (!missing_ok)
1450 : {
1451 138 : ereport(ERROR,
1452 : (errcode(rentry->nonexistent_code),
1453 : errmsg(rentry->nonexistent_msg, rel->relname)));
1454 : }
1455 : else
1456 : {
1457 880 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1458 880 : break;
1459 : }
1460 : }
1461 : }
1462 :
1463 : Assert(rentry->kind != '\0'); /* Should be impossible */
1464 : }
1465 :
1466 : /*
1467 : * Emit the right error message for a "DROP" command issued on a
1468 : * relation of the wrong type
1469 : */
1470 : static void
1471 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1472 : {
1473 : const struct dropmsgstrings *rentry;
1474 : const struct dropmsgstrings *wentry;
1475 :
1476 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1477 0 : if (rentry->kind == rightkind)
1478 0 : break;
1479 : Assert(rentry->kind != '\0');
1480 :
1481 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1482 0 : if (wentry->kind == wrongkind)
1483 0 : break;
1484 : /* wrongkind could be something we don't have in our table... */
1485 :
1486 0 : ereport(ERROR,
1487 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1488 : errmsg(rentry->nota_msg, relname),
1489 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1490 : }
1491 :
1492 : /*
1493 : * RemoveRelations
1494 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1495 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1496 : */
1497 : void
1498 16814 : RemoveRelations(DropStmt *drop)
1499 : {
1500 : ObjectAddresses *objects;
1501 : char relkind;
1502 : ListCell *cell;
1503 16814 : int flags = 0;
1504 16814 : LOCKMODE lockmode = AccessExclusiveLock;
1505 :
1506 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1507 16814 : if (drop->concurrent)
1508 : {
1509 : /*
1510 : * Note that for temporary relations this lock may get upgraded later
1511 : * on, but as no other session can access a temporary relation, this
1512 : * is actually fine.
1513 : */
1514 144 : lockmode = ShareUpdateExclusiveLock;
1515 : Assert(drop->removeType == OBJECT_INDEX);
1516 144 : if (list_length(drop->objects) != 1)
1517 6 : ereport(ERROR,
1518 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1519 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1520 138 : if (drop->behavior == DROP_CASCADE)
1521 0 : ereport(ERROR,
1522 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1523 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1524 : }
1525 :
1526 : /*
1527 : * First we identify all the relations, then we delete them in a single
1528 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1529 : * RESTRICT errors if one of the relations depends on another.
1530 : */
1531 :
1532 : /* Determine required relkind */
1533 16808 : switch (drop->removeType)
1534 : {
1535 14588 : case OBJECT_TABLE:
1536 14588 : relkind = RELKIND_RELATION;
1537 14588 : break;
1538 :
1539 828 : case OBJECT_INDEX:
1540 828 : relkind = RELKIND_INDEX;
1541 828 : break;
1542 :
1543 176 : case OBJECT_SEQUENCE:
1544 176 : relkind = RELKIND_SEQUENCE;
1545 176 : break;
1546 :
1547 934 : case OBJECT_VIEW:
1548 934 : relkind = RELKIND_VIEW;
1549 934 : break;
1550 :
1551 120 : case OBJECT_MATVIEW:
1552 120 : relkind = RELKIND_MATVIEW;
1553 120 : break;
1554 :
1555 162 : case OBJECT_FOREIGN_TABLE:
1556 162 : relkind = RELKIND_FOREIGN_TABLE;
1557 162 : break;
1558 :
1559 0 : default:
1560 0 : elog(ERROR, "unrecognized drop object type: %d",
1561 : (int) drop->removeType);
1562 : relkind = 0; /* keep compiler quiet */
1563 : break;
1564 : }
1565 :
1566 : /* Lock and validate each relation; build a list of object addresses */
1567 16808 : objects = new_object_addresses();
1568 :
1569 37408 : foreach(cell, drop->objects)
1570 : {
1571 20764 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1572 : Oid relOid;
1573 : ObjectAddress obj;
1574 : struct DropRelationCallbackState state;
1575 :
1576 : /*
1577 : * These next few steps are a great deal like relation_openrv, but we
1578 : * don't bother building a relcache entry since we don't need it.
1579 : *
1580 : * Check for shared-cache-inval messages before trying to access the
1581 : * relation. This is needed to cover the case where the name
1582 : * identifies a rel that has been dropped and recreated since the
1583 : * start of our transaction: if we don't flush the old syscache entry,
1584 : * then we'll latch onto that entry and suffer an error later.
1585 : */
1586 20764 : AcceptInvalidationMessages();
1587 :
1588 : /* Look up the appropriate relation using namespace search. */
1589 20764 : state.expected_relkind = relkind;
1590 41528 : state.heap_lockmode = drop->concurrent ?
1591 20764 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1592 : /* We must initialize these fields to show that no locks are held: */
1593 20764 : state.heapOid = InvalidOid;
1594 20764 : state.partParentOid = InvalidOid;
1595 :
1596 20764 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1597 : RangeVarCallbackForDropRelation,
1598 : &state);
1599 :
1600 : /* Not there? */
1601 20744 : if (!OidIsValid(relOid))
1602 : {
1603 1060 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1604 922 : continue;
1605 : }
1606 :
1607 : /*
1608 : * Decide if concurrent mode needs to be used here or not. The
1609 : * callback retrieved the rel's persistence for us.
1610 : */
1611 19684 : if (drop->concurrent &&
1612 132 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1613 : {
1614 : Assert(list_length(drop->objects) == 1 &&
1615 : drop->removeType == OBJECT_INDEX);
1616 114 : flags |= PERFORM_DELETION_CONCURRENTLY;
1617 : }
1618 :
1619 : /*
1620 : * Concurrent index drop cannot be used with partitioned indexes,
1621 : * either.
1622 : */
1623 19684 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1624 114 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1625 6 : ereport(ERROR,
1626 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1627 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1628 : rel->relname)));
1629 :
1630 : /*
1631 : * If we're told to drop a partitioned index, we must acquire lock on
1632 : * all the children of its parent partitioned table before proceeding.
1633 : * Otherwise we'd try to lock the child index partitions before their
1634 : * tables, leading to potential deadlock against other sessions that
1635 : * will lock those objects in the other order.
1636 : */
1637 19678 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1638 70 : (void) find_all_inheritors(state.heapOid,
1639 : state.heap_lockmode,
1640 : NULL);
1641 :
1642 : /* OK, we're ready to delete this one */
1643 19678 : obj.classId = RelationRelationId;
1644 19678 : obj.objectId = relOid;
1645 19678 : obj.objectSubId = 0;
1646 :
1647 19678 : add_exact_object_address(&obj, objects);
1648 : }
1649 :
1650 16644 : performMultipleDeletions(objects, drop->behavior, flags);
1651 :
1652 16508 : free_object_addresses(objects);
1653 16508 : }
1654 :
1655 : /*
1656 : * Before acquiring a table lock, check whether we have sufficient rights.
1657 : * In the case of DROP INDEX, also try to lock the table before the index.
1658 : * Also, if the table to be dropped is a partition, we try to lock the parent
1659 : * first.
1660 : */
1661 : static void
1662 21080 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1663 : void *arg)
1664 : {
1665 : HeapTuple tuple;
1666 : struct DropRelationCallbackState *state;
1667 : char expected_relkind;
1668 : bool is_partition;
1669 : Form_pg_class classform;
1670 : LOCKMODE heap_lockmode;
1671 21080 : bool invalid_system_index = false;
1672 :
1673 21080 : state = (struct DropRelationCallbackState *) arg;
1674 21080 : heap_lockmode = state->heap_lockmode;
1675 :
1676 : /*
1677 : * If we previously locked some other index's heap, and the name we're
1678 : * looking up no longer refers to that relation, release the now-useless
1679 : * lock.
1680 : */
1681 21080 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1682 : {
1683 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1684 0 : state->heapOid = InvalidOid;
1685 : }
1686 :
1687 : /*
1688 : * Similarly, if we previously locked some other partition's heap, and the
1689 : * name we're looking up no longer refers to that relation, release the
1690 : * now-useless lock.
1691 : */
1692 21080 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1693 : {
1694 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1695 0 : state->partParentOid = InvalidOid;
1696 : }
1697 :
1698 : /* Didn't find a relation, so no need for locking or permission checks. */
1699 21080 : if (!OidIsValid(relOid))
1700 1060 : return;
1701 :
1702 20020 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1703 20020 : if (!HeapTupleIsValid(tuple))
1704 0 : return; /* concurrently dropped, so nothing to do */
1705 20020 : classform = (Form_pg_class) GETSTRUCT(tuple);
1706 20020 : is_partition = classform->relispartition;
1707 :
1708 : /* Pass back some data to save lookups in RemoveRelations */
1709 20020 : state->actual_relkind = classform->relkind;
1710 20020 : state->actual_relpersistence = classform->relpersistence;
1711 :
1712 : /*
1713 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1714 : * but RemoveRelations() can only pass one relkind for a given relation.
1715 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1716 : * That means we must be careful before giving the wrong type error when
1717 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1718 : * exists with indexes.
1719 : */
1720 20020 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1721 2916 : expected_relkind = RELKIND_RELATION;
1722 17104 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1723 78 : expected_relkind = RELKIND_INDEX;
1724 : else
1725 17026 : expected_relkind = classform->relkind;
1726 :
1727 20020 : if (state->expected_relkind != expected_relkind)
1728 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1729 0 : state->expected_relkind);
1730 :
1731 : /* Allow DROP to either table owner or schema owner */
1732 20020 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1733 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1734 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1735 18 : get_relkind_objtype(classform->relkind),
1736 18 : rel->relname);
1737 :
1738 : /*
1739 : * Check the case of a system index that might have been invalidated by a
1740 : * failed concurrent process and allow its drop. For the time being, this
1741 : * only concerns indexes of toast relations that became invalid during a
1742 : * REINDEX CONCURRENTLY process.
1743 : */
1744 20002 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1745 : {
1746 : HeapTuple locTuple;
1747 : Form_pg_index indexform;
1748 : bool indisvalid;
1749 :
1750 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1751 0 : if (!HeapTupleIsValid(locTuple))
1752 : {
1753 0 : ReleaseSysCache(tuple);
1754 0 : return;
1755 : }
1756 :
1757 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1758 0 : indisvalid = indexform->indisvalid;
1759 0 : ReleaseSysCache(locTuple);
1760 :
1761 : /* Mark object as being an invalid index of system catalogs */
1762 0 : if (!indisvalid)
1763 0 : invalid_system_index = true;
1764 : }
1765 :
1766 : /* In the case of an invalid index, it is fine to bypass this check */
1767 20002 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1768 2 : ereport(ERROR,
1769 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1770 : errmsg("permission denied: \"%s\" is a system catalog",
1771 : rel->relname)));
1772 :
1773 20000 : ReleaseSysCache(tuple);
1774 :
1775 : /*
1776 : * In DROP INDEX, attempt to acquire lock on the parent table before
1777 : * locking the index. index_drop() will need this anyway, and since
1778 : * regular queries lock tables before their indexes, we risk deadlock if
1779 : * we do it the other way around. No error if we don't find a pg_index
1780 : * entry, though --- the relation may have been dropped. Note that this
1781 : * code will execute for either plain or partitioned indexes.
1782 : */
1783 20000 : if (expected_relkind == RELKIND_INDEX &&
1784 : relOid != oldRelOid)
1785 : {
1786 816 : state->heapOid = IndexGetRelation(relOid, true);
1787 816 : if (OidIsValid(state->heapOid))
1788 816 : LockRelationOid(state->heapOid, heap_lockmode);
1789 : }
1790 :
1791 : /*
1792 : * Similarly, if the relation is a partition, we must acquire lock on its
1793 : * parent before locking the partition. That's because queries lock the
1794 : * parent before its partitions, so we risk deadlock if we do it the other
1795 : * way around.
1796 : */
1797 20000 : if (is_partition && relOid != oldRelOid)
1798 : {
1799 612 : state->partParentOid = get_partition_parent(relOid, true);
1800 612 : if (OidIsValid(state->partParentOid))
1801 612 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1802 : }
1803 : }
1804 :
1805 : /*
1806 : * ExecuteTruncate
1807 : * Executes a TRUNCATE command.
1808 : *
1809 : * This is a multi-relation truncate. We first open and grab exclusive
1810 : * lock on all relations involved, checking permissions and otherwise
1811 : * verifying that the relation is OK for truncation. Note that if relations
1812 : * are foreign tables, at this stage, we have not yet checked that their
1813 : * foreign data in external data sources are OK for truncation. These are
1814 : * checked when foreign data are actually truncated later. In CASCADE mode,
1815 : * relations having FK references to the targeted relations are automatically
1816 : * added to the group; in RESTRICT mode, we check that all FK references are
1817 : * internal to the group that's being truncated. Finally all the relations
1818 : * are truncated and reindexed.
1819 : */
1820 : void
1821 1648 : ExecuteTruncate(TruncateStmt *stmt)
1822 : {
1823 1648 : List *rels = NIL;
1824 1648 : List *relids = NIL;
1825 1648 : List *relids_logged = NIL;
1826 : ListCell *cell;
1827 :
1828 : /*
1829 : * Open, exclusive-lock, and check all the explicitly-specified relations
1830 : */
1831 3544 : foreach(cell, stmt->relations)
1832 : {
1833 1946 : RangeVar *rv = lfirst(cell);
1834 : Relation rel;
1835 1946 : bool recurse = rv->inh;
1836 : Oid myrelid;
1837 1946 : LOCKMODE lockmode = AccessExclusiveLock;
1838 :
1839 1946 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1840 : 0, RangeVarCallbackForTruncate,
1841 : NULL);
1842 :
1843 : /* don't throw error for "TRUNCATE foo, foo" */
1844 1908 : if (list_member_oid(relids, myrelid))
1845 2 : continue;
1846 :
1847 : /* open the relation, we already hold a lock on it */
1848 1906 : rel = table_open(myrelid, NoLock);
1849 :
1850 : /*
1851 : * RangeVarGetRelidExtended() has done most checks with its callback,
1852 : * but other checks with the now-opened Relation remain.
1853 : */
1854 1906 : truncate_check_activity(rel);
1855 :
1856 1906 : rels = lappend(rels, rel);
1857 1906 : relids = lappend_oid(relids, myrelid);
1858 :
1859 : /* Log this relation only if needed for logical decoding */
1860 1906 : if (RelationIsLogicallyLogged(rel))
1861 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1862 :
1863 1906 : if (recurse)
1864 : {
1865 : ListCell *child;
1866 : List *children;
1867 :
1868 1844 : children = find_all_inheritors(myrelid, lockmode, NULL);
1869 :
1870 5546 : foreach(child, children)
1871 : {
1872 3702 : Oid childrelid = lfirst_oid(child);
1873 :
1874 3702 : if (list_member_oid(relids, childrelid))
1875 1844 : continue;
1876 :
1877 : /* find_all_inheritors already got lock */
1878 1858 : rel = table_open(childrelid, NoLock);
1879 :
1880 : /*
1881 : * It is possible that the parent table has children that are
1882 : * temp tables of other backends. We cannot safely access
1883 : * such tables (because of buffering issues), and the best
1884 : * thing to do is to silently ignore them. Note that this
1885 : * check is the same as one of the checks done in
1886 : * truncate_check_activity() called below, still it is kept
1887 : * here for simplicity.
1888 : */
1889 1858 : if (RELATION_IS_OTHER_TEMP(rel))
1890 : {
1891 8 : table_close(rel, lockmode);
1892 8 : continue;
1893 : }
1894 :
1895 : /*
1896 : * Inherited TRUNCATE commands perform access permission
1897 : * checks on the parent table only. So we skip checking the
1898 : * children's permissions and don't call
1899 : * truncate_check_perms() here.
1900 : */
1901 1850 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1902 1850 : truncate_check_activity(rel);
1903 :
1904 1850 : rels = lappend(rels, rel);
1905 1850 : relids = lappend_oid(relids, childrelid);
1906 :
1907 : /* Log this relation only if needed for logical decoding */
1908 1850 : if (RelationIsLogicallyLogged(rel))
1909 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1910 : }
1911 : }
1912 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1913 12 : ereport(ERROR,
1914 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1915 : errmsg("cannot truncate only a partitioned table"),
1916 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1917 : }
1918 :
1919 1598 : ExecuteTruncateGuts(rels, relids, relids_logged,
1920 1598 : stmt->behavior, stmt->restart_seqs, false);
1921 :
1922 : /* And close the rels */
1923 5106 : foreach(cell, rels)
1924 : {
1925 3590 : Relation rel = (Relation) lfirst(cell);
1926 :
1927 3590 : table_close(rel, NoLock);
1928 : }
1929 1516 : }
1930 :
1931 : /*
1932 : * ExecuteTruncateGuts
1933 : *
1934 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1935 : * command (see above) as well as replication subscribers that execute a
1936 : * replicated TRUNCATE action.
1937 : *
1938 : * explicit_rels is the list of Relations to truncate that the command
1939 : * specified. relids is the list of Oids corresponding to explicit_rels.
1940 : * relids_logged is the list of Oids (a subset of relids) that require
1941 : * WAL-logging. This is all a bit redundant, but the existing callers have
1942 : * this information handy in this form.
1943 : */
1944 : void
1945 1636 : ExecuteTruncateGuts(List *explicit_rels,
1946 : List *relids,
1947 : List *relids_logged,
1948 : DropBehavior behavior, bool restart_seqs,
1949 : bool run_as_table_owner)
1950 : {
1951 : List *rels;
1952 1636 : List *seq_relids = NIL;
1953 1636 : HTAB *ft_htab = NULL;
1954 : EState *estate;
1955 : ResultRelInfo *resultRelInfos;
1956 : ResultRelInfo *resultRelInfo;
1957 : SubTransactionId mySubid;
1958 : ListCell *cell;
1959 : Oid *logrelids;
1960 :
1961 : /*
1962 : * Check the explicitly-specified relations.
1963 : *
1964 : * In CASCADE mode, suck in all referencing relations as well. This
1965 : * requires multiple iterations to find indirectly-dependent relations. At
1966 : * each phase, we need to exclusive-lock new rels before looking for their
1967 : * dependencies, else we might miss something. Also, we check each rel as
1968 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1969 : * time on a rel we have no permissions for.
1970 : */
1971 1636 : rels = list_copy(explicit_rels);
1972 1636 : if (behavior == DROP_CASCADE)
1973 : {
1974 : for (;;)
1975 40 : {
1976 : List *newrelids;
1977 :
1978 80 : newrelids = heap_truncate_find_FKs(relids);
1979 80 : if (newrelids == NIL)
1980 40 : break; /* nothing else to add */
1981 :
1982 134 : foreach(cell, newrelids)
1983 : {
1984 94 : Oid relid = lfirst_oid(cell);
1985 : Relation rel;
1986 :
1987 94 : rel = table_open(relid, AccessExclusiveLock);
1988 94 : ereport(NOTICE,
1989 : (errmsg("truncate cascades to table \"%s\"",
1990 : RelationGetRelationName(rel))));
1991 94 : truncate_check_rel(relid, rel->rd_rel);
1992 94 : truncate_check_perms(relid, rel->rd_rel);
1993 94 : truncate_check_activity(rel);
1994 94 : rels = lappend(rels, rel);
1995 94 : relids = lappend_oid(relids, relid);
1996 :
1997 : /* Log this relation only if needed for logical decoding */
1998 94 : if (RelationIsLogicallyLogged(rel))
1999 0 : relids_logged = lappend_oid(relids_logged, relid);
2000 : }
2001 : }
2002 : }
2003 :
2004 : /*
2005 : * Check foreign key references. In CASCADE mode, this should be
2006 : * unnecessary since we just pulled in all the references; but as a
2007 : * cross-check, do it anyway if in an Assert-enabled build.
2008 : */
2009 : #ifdef USE_ASSERT_CHECKING
2010 : heap_truncate_check_FKs(rels, false);
2011 : #else
2012 1636 : if (behavior == DROP_RESTRICT)
2013 1596 : heap_truncate_check_FKs(rels, false);
2014 : #endif
2015 :
2016 : /*
2017 : * If we are asked to restart sequences, find all the sequences, lock them
2018 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2019 : * We want to do this early since it's pointless to do all the truncation
2020 : * work only to fail on sequence permissions.
2021 : */
2022 1562 : if (restart_seqs)
2023 : {
2024 52 : foreach(cell, rels)
2025 : {
2026 26 : Relation rel = (Relation) lfirst(cell);
2027 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2028 : ListCell *seqcell;
2029 :
2030 62 : foreach(seqcell, seqlist)
2031 : {
2032 36 : Oid seq_relid = lfirst_oid(seqcell);
2033 : Relation seq_rel;
2034 :
2035 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2036 :
2037 : /* This check must match AlterSequence! */
2038 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2039 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2040 0 : RelationGetRelationName(seq_rel));
2041 :
2042 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2043 :
2044 36 : relation_close(seq_rel, NoLock);
2045 : }
2046 : }
2047 : }
2048 :
2049 : /* Prepare to catch AFTER triggers. */
2050 1562 : AfterTriggerBeginQuery();
2051 :
2052 : /*
2053 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2054 : * each relation. We don't need to call ExecOpenIndices, though.
2055 : *
2056 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2057 : * though we don't have a range table and don't populate the
2058 : * es_result_relations array. That's a bit bogus, but it's enough to make
2059 : * ExecGetTriggerResultRel() find them.
2060 : */
2061 1562 : estate = CreateExecutorState();
2062 : resultRelInfos = (ResultRelInfo *)
2063 1562 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2064 1562 : resultRelInfo = resultRelInfos;
2065 5324 : foreach(cell, rels)
2066 : {
2067 3762 : Relation rel = (Relation) lfirst(cell);
2068 :
2069 3762 : InitResultRelInfo(resultRelInfo,
2070 : rel,
2071 : 0, /* dummy rangetable index */
2072 : NULL,
2073 : 0);
2074 3762 : estate->es_opened_result_relations =
2075 3762 : lappend(estate->es_opened_result_relations, resultRelInfo);
2076 3762 : resultRelInfo++;
2077 : }
2078 :
2079 : /*
2080 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2081 : * truncating (this is because one of them might throw an error). Also, if
2082 : * we were to allow them to prevent statement execution, that would need
2083 : * to be handled here.
2084 : */
2085 1562 : resultRelInfo = resultRelInfos;
2086 5324 : foreach(cell, rels)
2087 : {
2088 : UserContext ucxt;
2089 :
2090 3762 : if (run_as_table_owner)
2091 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2092 : &ucxt);
2093 3762 : ExecBSTruncateTriggers(estate, resultRelInfo);
2094 3762 : if (run_as_table_owner)
2095 70 : RestoreUserContext(&ucxt);
2096 3762 : resultRelInfo++;
2097 : }
2098 :
2099 : /*
2100 : * OK, truncate each table.
2101 : */
2102 1562 : mySubid = GetCurrentSubTransactionId();
2103 :
2104 5324 : foreach(cell, rels)
2105 : {
2106 3762 : Relation rel = (Relation) lfirst(cell);
2107 :
2108 : /* Skip partitioned tables as there is nothing to do */
2109 3762 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2110 740 : continue;
2111 :
2112 : /*
2113 : * Build the lists of foreign tables belonging to each foreign server
2114 : * and pass each list to the foreign data wrapper's callback function,
2115 : * so that each server can truncate its all foreign tables in bulk.
2116 : * Each list is saved as a single entry in a hash table that uses the
2117 : * server OID as lookup key.
2118 : */
2119 3022 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2120 : {
2121 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2122 : bool found;
2123 : ForeignTruncateInfo *ft_info;
2124 :
2125 : /* First time through, initialize hashtable for foreign tables */
2126 34 : if (!ft_htab)
2127 : {
2128 : HASHCTL hctl;
2129 :
2130 30 : memset(&hctl, 0, sizeof(HASHCTL));
2131 30 : hctl.keysize = sizeof(Oid);
2132 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2133 30 : hctl.hcxt = CurrentMemoryContext;
2134 :
2135 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2136 : 32, /* start small and extend */
2137 : &hctl,
2138 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2139 : }
2140 :
2141 : /* Find or create cached entry for the foreign table */
2142 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2143 34 : if (!found)
2144 30 : ft_info->rels = NIL;
2145 :
2146 : /*
2147 : * Save the foreign table in the entry of the server that the
2148 : * foreign table belongs to.
2149 : */
2150 34 : ft_info->rels = lappend(ft_info->rels, rel);
2151 34 : continue;
2152 : }
2153 :
2154 : /*
2155 : * Normally, we need a transaction-safe truncation here. However, if
2156 : * the table was either created in the current (sub)transaction or has
2157 : * a new relfilenumber in the current (sub)transaction, then we can
2158 : * just truncate it in-place, because a rollback would cause the whole
2159 : * table or the current physical file to be thrown away anyway.
2160 : */
2161 2988 : if (rel->rd_createSubid == mySubid ||
2162 2962 : rel->rd_newRelfilelocatorSubid == mySubid)
2163 : {
2164 : /* Immediate, non-rollbackable truncation is OK */
2165 90 : heap_truncate_one_rel(rel);
2166 : }
2167 : else
2168 : {
2169 : Oid heap_relid;
2170 : Oid toast_relid;
2171 2898 : ReindexParams reindex_params = {0};
2172 :
2173 : /*
2174 : * This effectively deletes all rows in the table, and may be done
2175 : * in a serializable transaction. In that case we must record a
2176 : * rw-conflict in to this transaction from each transaction
2177 : * holding a predicate lock on the table.
2178 : */
2179 2898 : CheckTableForSerializableConflictIn(rel);
2180 :
2181 : /*
2182 : * Need the full transaction-safe pushups.
2183 : *
2184 : * Create a new empty storage file for the relation, and assign it
2185 : * as the relfilenumber value. The old storage file is scheduled
2186 : * for deletion at commit.
2187 : */
2188 2898 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2189 :
2190 2898 : heap_relid = RelationGetRelid(rel);
2191 :
2192 : /*
2193 : * The same for the toast table, if any.
2194 : */
2195 2898 : toast_relid = rel->rd_rel->reltoastrelid;
2196 2898 : if (OidIsValid(toast_relid))
2197 : {
2198 1842 : Relation toastrel = relation_open(toast_relid,
2199 : AccessExclusiveLock);
2200 :
2201 1842 : RelationSetNewRelfilenumber(toastrel,
2202 1842 : toastrel->rd_rel->relpersistence);
2203 1842 : table_close(toastrel, NoLock);
2204 : }
2205 :
2206 : /*
2207 : * Reconstruct the indexes to match, and we're done.
2208 : */
2209 2898 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2210 : &reindex_params);
2211 : }
2212 :
2213 2988 : pgstat_count_truncate(rel);
2214 : }
2215 :
2216 : /* Now go through the hash table, and truncate foreign tables */
2217 1562 : if (ft_htab)
2218 : {
2219 : ForeignTruncateInfo *ft_info;
2220 : HASH_SEQ_STATUS seq;
2221 :
2222 30 : hash_seq_init(&seq, ft_htab);
2223 :
2224 30 : PG_TRY();
2225 : {
2226 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2227 : {
2228 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2229 :
2230 : /* truncate_check_rel() has checked that already */
2231 : Assert(routine->ExecForeignTruncate != NULL);
2232 :
2233 30 : routine->ExecForeignTruncate(ft_info->rels,
2234 : behavior,
2235 : restart_seqs);
2236 : }
2237 : }
2238 8 : PG_FINALLY();
2239 : {
2240 30 : hash_destroy(ft_htab);
2241 : }
2242 30 : PG_END_TRY();
2243 : }
2244 :
2245 : /*
2246 : * Restart owned sequences if we were asked to.
2247 : */
2248 1590 : foreach(cell, seq_relids)
2249 : {
2250 36 : Oid seq_relid = lfirst_oid(cell);
2251 :
2252 36 : ResetSequence(seq_relid);
2253 : }
2254 :
2255 : /*
2256 : * Write a WAL record to allow this set of actions to be logically
2257 : * decoded.
2258 : *
2259 : * Assemble an array of relids so we can write a single WAL record for the
2260 : * whole action.
2261 : */
2262 1554 : if (relids_logged != NIL)
2263 : {
2264 : xl_heap_truncate xlrec;
2265 54 : int i = 0;
2266 :
2267 : /* should only get here if wal_level >= logical */
2268 : Assert(XLogLogicalInfoActive());
2269 :
2270 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2271 144 : foreach(cell, relids_logged)
2272 90 : logrelids[i++] = lfirst_oid(cell);
2273 :
2274 54 : xlrec.dbId = MyDatabaseId;
2275 54 : xlrec.nrelids = list_length(relids_logged);
2276 54 : xlrec.flags = 0;
2277 54 : if (behavior == DROP_CASCADE)
2278 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2279 54 : if (restart_seqs)
2280 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2281 :
2282 54 : XLogBeginInsert();
2283 54 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2284 54 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2285 :
2286 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2287 :
2288 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2289 : }
2290 :
2291 : /*
2292 : * Process all AFTER STATEMENT TRUNCATE triggers.
2293 : */
2294 1554 : resultRelInfo = resultRelInfos;
2295 5308 : foreach(cell, rels)
2296 : {
2297 : UserContext ucxt;
2298 :
2299 3754 : if (run_as_table_owner)
2300 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2301 : &ucxt);
2302 3754 : ExecASTruncateTriggers(estate, resultRelInfo);
2303 3754 : if (run_as_table_owner)
2304 70 : RestoreUserContext(&ucxt);
2305 3754 : resultRelInfo++;
2306 : }
2307 :
2308 : /* Handle queued AFTER triggers */
2309 1554 : AfterTriggerEndQuery(estate);
2310 :
2311 : /* We can clean up the EState now */
2312 1554 : FreeExecutorState(estate);
2313 :
2314 : /*
2315 : * Close any rels opened by CASCADE (can't do this while EState still
2316 : * holds refs)
2317 : */
2318 1554 : rels = list_difference_ptr(rels, explicit_rels);
2319 1648 : foreach(cell, rels)
2320 : {
2321 94 : Relation rel = (Relation) lfirst(cell);
2322 :
2323 94 : table_close(rel, NoLock);
2324 : }
2325 1554 : }
2326 :
2327 : /*
2328 : * Check that a given relation is safe to truncate. Subroutine for
2329 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2330 : */
2331 : static void
2332 3984 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2333 : {
2334 3984 : char *relname = NameStr(reltuple->relname);
2335 :
2336 : /*
2337 : * Only allow truncate on regular tables, foreign tables using foreign
2338 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2339 : * latter are only being included here for the following checks; no
2340 : * physical truncation will occur in their case.).
2341 : */
2342 3984 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2343 : {
2344 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2345 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2346 :
2347 36 : if (!fdwroutine->ExecForeignTruncate)
2348 2 : ereport(ERROR,
2349 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2350 : errmsg("cannot truncate foreign table \"%s\"",
2351 : relname)));
2352 : }
2353 3946 : else if (reltuple->relkind != RELKIND_RELATION &&
2354 754 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2355 0 : ereport(ERROR,
2356 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2357 : errmsg("\"%s\" is not a table", relname)));
2358 :
2359 : /*
2360 : * Most system catalogs can't be truncated at all, or at least not unless
2361 : * allow_system_table_mods=on. As an exception, however, we allow
2362 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2363 : * to change its relfilenode to match the old cluster, and allowing a
2364 : * TRUNCATE command to be executed is the easiest way of doing that.
2365 : */
2366 3980 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2367 22 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2368 2 : ereport(ERROR,
2369 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2370 : errmsg("permission denied: \"%s\" is a system catalog",
2371 : relname)));
2372 :
2373 3978 : InvokeObjectTruncateHook(relid);
2374 3978 : }
2375 :
2376 : /*
2377 : * Check that current user has the permission to truncate given relation.
2378 : */
2379 : static void
2380 2128 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2381 : {
2382 2128 : char *relname = NameStr(reltuple->relname);
2383 : AclResult aclresult;
2384 :
2385 : /* Permissions checks */
2386 2128 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2387 2128 : if (aclresult != ACLCHECK_OK)
2388 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2389 : relname);
2390 2096 : }
2391 :
2392 : /*
2393 : * Set of extra sanity checks to check if a given relation is safe to
2394 : * truncate. This is split with truncate_check_rel() as
2395 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2396 : */
2397 : static void
2398 3850 : truncate_check_activity(Relation rel)
2399 : {
2400 : /*
2401 : * Don't allow truncate on temp tables of other backends ... their local
2402 : * buffer manager is not going to cope.
2403 : */
2404 3850 : if (RELATION_IS_OTHER_TEMP(rel))
2405 0 : ereport(ERROR,
2406 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2407 : errmsg("cannot truncate temporary tables of other sessions")));
2408 :
2409 : /*
2410 : * Also check for active uses of the relation in the current transaction,
2411 : * including open scans and pending AFTER trigger events.
2412 : */
2413 3850 : CheckTableNotInUse(rel, "TRUNCATE");
2414 3850 : }
2415 :
2416 : /*
2417 : * storage_name
2418 : * returns the name corresponding to a typstorage/attstorage enum value
2419 : */
2420 : static const char *
2421 24 : storage_name(char c)
2422 : {
2423 24 : switch (c)
2424 : {
2425 0 : case TYPSTORAGE_PLAIN:
2426 0 : return "PLAIN";
2427 0 : case TYPSTORAGE_EXTERNAL:
2428 0 : return "EXTERNAL";
2429 12 : case TYPSTORAGE_EXTENDED:
2430 12 : return "EXTENDED";
2431 12 : case TYPSTORAGE_MAIN:
2432 12 : return "MAIN";
2433 0 : default:
2434 0 : return "???";
2435 : }
2436 : }
2437 :
2438 : /*----------
2439 : * MergeAttributes
2440 : * Returns new schema given initial schema and superclasses.
2441 : *
2442 : * Input arguments:
2443 : * 'columns' is the column/attribute definition for the table. (It's a list
2444 : * of ColumnDef's.) It is destructively changed.
2445 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2446 : * 'relpersistence' is the persistence type of the table.
2447 : * 'is_partition' tells if the table is a partition.
2448 : *
2449 : * Output arguments:
2450 : * 'supconstr' receives a list of CookedConstraint representing
2451 : * CHECK constraints belonging to parent relations, updated as
2452 : * necessary to be valid for the child.
2453 : * 'supnotnulls' receives a list of CookedConstraint representing
2454 : * not-null constraints based on those from parent relations.
2455 : *
2456 : * Return value:
2457 : * Completed schema list.
2458 : *
2459 : * Notes:
2460 : * The order in which the attributes are inherited is very important.
2461 : * Intuitively, the inherited attributes should come first. If a table
2462 : * inherits from multiple parents, the order of those attributes are
2463 : * according to the order of the parents specified in CREATE TABLE.
2464 : *
2465 : * Here's an example:
2466 : *
2467 : * create table person (name text, age int4, location point);
2468 : * create table emp (salary int4, manager text) inherits(person);
2469 : * create table student (gpa float8) inherits (person);
2470 : * create table stud_emp (percent int4) inherits (emp, student);
2471 : *
2472 : * The order of the attributes of stud_emp is:
2473 : *
2474 : * person {1:name, 2:age, 3:location}
2475 : * / \
2476 : * {6:gpa} student emp {4:salary, 5:manager}
2477 : * \ /
2478 : * stud_emp {7:percent}
2479 : *
2480 : * If the same attribute name appears multiple times, then it appears
2481 : * in the result table in the proper location for its first appearance.
2482 : *
2483 : * Constraints (including not-null constraints) for the child table
2484 : * are the union of all relevant constraints, from both the child schema
2485 : * and parent tables. In addition, in legacy inheritance, each column that
2486 : * appears in a primary key in any of the parents also gets a NOT NULL
2487 : * constraint (partitioning doesn't need this, because the PK itself gets
2488 : * inherited.)
2489 : *
2490 : * The default value for a child column is defined as:
2491 : * (1) If the child schema specifies a default, that value is used.
2492 : * (2) If neither the child nor any parent specifies a default, then
2493 : * the column will not have a default.
2494 : * (3) If conflicting defaults are inherited from different parents
2495 : * (and not overridden by the child), an error is raised.
2496 : * (4) Otherwise the inherited default is used.
2497 : *
2498 : * Note that the default-value infrastructure is used for generated
2499 : * columns' expressions too, so most of the preceding paragraph applies
2500 : * to generation expressions too. We insist that a child column be
2501 : * generated if and only if its parent(s) are, but it need not have
2502 : * the same generation expression.
2503 : *----------
2504 : */
2505 : static List *
2506 59324 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2507 : bool is_partition, List **supconstr, List **supnotnulls)
2508 : {
2509 59324 : List *inh_columns = NIL;
2510 59324 : List *constraints = NIL;
2511 59324 : List *nnconstraints = NIL;
2512 59324 : bool have_bogus_defaults = false;
2513 : int child_attno;
2514 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2515 59324 : List *saved_columns = NIL;
2516 : ListCell *lc;
2517 :
2518 : /*
2519 : * Check for and reject tables with too many columns. We perform this
2520 : * check relatively early for two reasons: (a) we don't run the risk of
2521 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2522 : * okay if we're processing <= 1600 columns, but could take minutes to
2523 : * execute if the user attempts to create a table with hundreds of
2524 : * thousands of columns.
2525 : *
2526 : * Note that we also need to check that we do not exceed this figure after
2527 : * including columns from inherited relations.
2528 : */
2529 59324 : if (list_length(columns) > MaxHeapAttributeNumber)
2530 0 : ereport(ERROR,
2531 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2532 : errmsg("tables can have at most %d columns",
2533 : MaxHeapAttributeNumber)));
2534 :
2535 : /*
2536 : * Check for duplicate names in the explicit list of attributes.
2537 : *
2538 : * Although we might consider merging such entries in the same way that we
2539 : * handle name conflicts for inherited attributes, it seems to make more
2540 : * sense to assume such conflicts are errors.
2541 : *
2542 : * We don't use foreach() here because we have two nested loops over the
2543 : * columns list, with possible element deletions in the inner one. If we
2544 : * used foreach_delete_current() it could only fix up the state of one of
2545 : * the loops, so it seems cleaner to use looping over list indexes for
2546 : * both loops. Note that any deletion will happen beyond where the outer
2547 : * loop is, so its index never needs adjustment.
2548 : */
2549 273028 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2550 : {
2551 213728 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2552 :
2553 213728 : if (!is_partition && coldef->typeName == NULL)
2554 : {
2555 : /*
2556 : * Typed table column option that does not belong to a column from
2557 : * the type. This works because the columns from the type come
2558 : * first in the list. (We omit this check for partition column
2559 : * lists; those are processed separately below.)
2560 : */
2561 6 : ereport(ERROR,
2562 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2563 : errmsg("column \"%s\" does not exist",
2564 : coldef->colname)));
2565 : }
2566 :
2567 : /* restpos scans all entries beyond coldef; incr is in loop body */
2568 6370154 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2569 : {
2570 6156450 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2571 :
2572 6156450 : if (strcmp(coldef->colname, restdef->colname) == 0)
2573 : {
2574 50 : if (coldef->is_from_type)
2575 : {
2576 : /*
2577 : * merge the column options into the column from the type
2578 : */
2579 32 : coldef->is_not_null = restdef->is_not_null;
2580 32 : coldef->raw_default = restdef->raw_default;
2581 32 : coldef->cooked_default = restdef->cooked_default;
2582 32 : coldef->constraints = restdef->constraints;
2583 32 : coldef->is_from_type = false;
2584 32 : columns = list_delete_nth_cell(columns, restpos);
2585 : }
2586 : else
2587 18 : ereport(ERROR,
2588 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2589 : errmsg("column \"%s\" specified more than once",
2590 : coldef->colname)));
2591 : }
2592 : else
2593 6156400 : restpos++;
2594 : }
2595 : }
2596 :
2597 : /*
2598 : * In case of a partition, there are no new column definitions, only dummy
2599 : * ColumnDefs created for column constraints. Set them aside for now and
2600 : * process them at the end.
2601 : */
2602 59300 : if (is_partition)
2603 : {
2604 7890 : saved_columns = columns;
2605 7890 : columns = NIL;
2606 : }
2607 :
2608 : /*
2609 : * Scan the parents left-to-right, and merge their attributes to form a
2610 : * list of inherited columns (inh_columns).
2611 : */
2612 59300 : child_attno = 0;
2613 69430 : foreach(lc, supers)
2614 : {
2615 10214 : Oid parent = lfirst_oid(lc);
2616 : Relation relation;
2617 : TupleDesc tupleDesc;
2618 : TupleConstr *constr;
2619 : AttrMap *newattmap;
2620 : List *inherited_defaults;
2621 : List *cols_with_defaults;
2622 : List *nnconstrs;
2623 : ListCell *lc1;
2624 : ListCell *lc2;
2625 10214 : Bitmapset *nncols = NULL;
2626 :
2627 : /* caller already got lock */
2628 10214 : relation = table_open(parent, NoLock);
2629 :
2630 : /*
2631 : * Check for active uses of the parent partitioned table in the
2632 : * current transaction, such as being used in some manner by an
2633 : * enclosing command.
2634 : */
2635 10214 : if (is_partition)
2636 7890 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2637 :
2638 : /*
2639 : * We do not allow partitioned tables and partitions to participate in
2640 : * regular inheritance.
2641 : */
2642 10208 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2643 6 : ereport(ERROR,
2644 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2645 : errmsg("cannot inherit from partitioned table \"%s\"",
2646 : RelationGetRelationName(relation))));
2647 10202 : if (relation->rd_rel->relispartition && !is_partition)
2648 6 : ereport(ERROR,
2649 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2650 : errmsg("cannot inherit from partition \"%s\"",
2651 : RelationGetRelationName(relation))));
2652 :
2653 10196 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2654 7886 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2655 7866 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2656 0 : ereport(ERROR,
2657 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2658 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2659 : RelationGetRelationName(relation))));
2660 :
2661 : /*
2662 : * If the parent is permanent, so must be all of its partitions. Note
2663 : * that inheritance allows that case.
2664 : */
2665 10196 : if (is_partition &&
2666 7884 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2667 : relpersistence == RELPERSISTENCE_TEMP)
2668 6 : ereport(ERROR,
2669 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2670 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2671 : RelationGetRelationName(relation))));
2672 :
2673 : /* Permanent rels cannot inherit from temporary ones */
2674 10190 : if (relpersistence != RELPERSISTENCE_TEMP &&
2675 9842 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2676 24 : ereport(ERROR,
2677 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2678 : errmsg(!is_partition
2679 : ? "cannot inherit from temporary relation \"%s\""
2680 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2681 : RelationGetRelationName(relation))));
2682 :
2683 : /* If existing rel is temp, it must belong to this session */
2684 10166 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2685 294 : !relation->rd_islocaltemp)
2686 0 : ereport(ERROR,
2687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 : errmsg(!is_partition
2689 : ? "cannot inherit from temporary relation of another session"
2690 : : "cannot create as partition of temporary relation of another session")));
2691 :
2692 : /*
2693 : * We should have an UNDER permission flag for this, but for now,
2694 : * demand that creator of a child table own the parent.
2695 : */
2696 10166 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2697 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2698 0 : RelationGetRelationName(relation));
2699 :
2700 10166 : tupleDesc = RelationGetDescr(relation);
2701 10166 : constr = tupleDesc->constr;
2702 :
2703 : /*
2704 : * newattmap->attnums[] will contain the child-table attribute numbers
2705 : * for the attributes of this parent table. (They are not the same
2706 : * for parents after the first one, nor if we have dropped columns.)
2707 : */
2708 10166 : newattmap = make_attrmap(tupleDesc->natts);
2709 :
2710 : /* We can't process inherited defaults until newattmap is complete. */
2711 10166 : inherited_defaults = cols_with_defaults = NIL;
2712 :
2713 : /*
2714 : * Request attnotnull on columns that have a not-null constraint
2715 : * that's not marked NO INHERIT.
2716 : */
2717 10166 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2718 : true, false);
2719 22616 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2720 2284 : nncols = bms_add_member(nncols, cc->attnum);
2721 :
2722 30550 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2723 20384 : parent_attno++)
2724 : {
2725 20420 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2726 : parent_attno - 1);
2727 20420 : char *attributeName = NameStr(attribute->attname);
2728 : int exist_attno;
2729 : ColumnDef *newdef;
2730 : ColumnDef *mergeddef;
2731 :
2732 : /*
2733 : * Ignore dropped columns in the parent.
2734 : */
2735 20420 : if (attribute->attisdropped)
2736 192 : continue; /* leave newattmap->attnums entry as zero */
2737 :
2738 : /*
2739 : * Create new column definition
2740 : */
2741 20228 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2742 : attribute->atttypmod, attribute->attcollation);
2743 20228 : newdef->storage = attribute->attstorage;
2744 20228 : newdef->generated = attribute->attgenerated;
2745 20228 : if (CompressionMethodIsValid(attribute->attcompression))
2746 30 : newdef->compression =
2747 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2748 :
2749 : /*
2750 : * Regular inheritance children are independent enough not to
2751 : * inherit identity columns. But partitions are integral part of
2752 : * a partitioned table and inherit identity column.
2753 : */
2754 20228 : if (is_partition)
2755 15938 : newdef->identity = attribute->attidentity;
2756 :
2757 : /*
2758 : * Does it match some previously considered column from another
2759 : * parent?
2760 : */
2761 20228 : exist_attno = findAttrByName(attributeName, inh_columns);
2762 20228 : if (exist_attno > 0)
2763 : {
2764 : /*
2765 : * Yes, try to merge the two column definitions.
2766 : */
2767 338 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2768 :
2769 302 : newattmap->attnums[parent_attno - 1] = exist_attno;
2770 :
2771 : /*
2772 : * Partitions have only one parent, so conflict should never
2773 : * occur.
2774 : */
2775 : Assert(!is_partition);
2776 : }
2777 : else
2778 : {
2779 : /*
2780 : * No, create a new inherited column
2781 : */
2782 19890 : newdef->inhcount = 1;
2783 19890 : newdef->is_local = false;
2784 19890 : inh_columns = lappend(inh_columns, newdef);
2785 :
2786 19890 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2787 19890 : mergeddef = newdef;
2788 : }
2789 :
2790 : /*
2791 : * mark attnotnull if parent has it
2792 : */
2793 20192 : if (bms_is_member(parent_attno, nncols))
2794 2284 : mergeddef->is_not_null = true;
2795 :
2796 : /*
2797 : * Locate default/generation expression if any
2798 : */
2799 20192 : if (attribute->atthasdef)
2800 : {
2801 : Node *this_default;
2802 :
2803 674 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2804 674 : if (this_default == NULL)
2805 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2806 : parent_attno, RelationGetRelationName(relation));
2807 :
2808 : /*
2809 : * If it's a GENERATED default, it might contain Vars that
2810 : * need to be mapped to the inherited column(s)' new numbers.
2811 : * We can't do that till newattmap is ready, so just remember
2812 : * all the inherited default expressions for the moment.
2813 : */
2814 674 : inherited_defaults = lappend(inherited_defaults, this_default);
2815 674 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2816 : }
2817 : }
2818 :
2819 : /*
2820 : * Now process any inherited default expressions, adjusting attnos
2821 : * using the completed newattmap map.
2822 : */
2823 10804 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2824 : {
2825 674 : Node *this_default = (Node *) lfirst(lc1);
2826 674 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2827 : bool found_whole_row;
2828 :
2829 : /* Adjust Vars to match new table's column numbering */
2830 674 : this_default = map_variable_attnos(this_default,
2831 : 1, 0,
2832 : newattmap,
2833 : InvalidOid, &found_whole_row);
2834 :
2835 : /*
2836 : * For the moment we have to reject whole-row variables. We could
2837 : * convert them, if we knew the new table's rowtype OID, but that
2838 : * hasn't been assigned yet. (A variable could only appear in a
2839 : * generation expression, so the error message is correct.)
2840 : */
2841 674 : if (found_whole_row)
2842 0 : ereport(ERROR,
2843 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2844 : errmsg("cannot convert whole-row table reference"),
2845 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2846 : def->colname,
2847 : RelationGetRelationName(relation))));
2848 :
2849 : /*
2850 : * If we already had a default from some prior parent, check to
2851 : * see if they are the same. If so, no problem; if not, mark the
2852 : * column as having a bogus default. Below, we will complain if
2853 : * the bogus default isn't overridden by the child columns.
2854 : */
2855 : Assert(def->raw_default == NULL);
2856 674 : if (def->cooked_default == NULL)
2857 632 : def->cooked_default = this_default;
2858 42 : else if (!equal(def->cooked_default, this_default))
2859 : {
2860 36 : def->cooked_default = &bogus_marker;
2861 36 : have_bogus_defaults = true;
2862 : }
2863 : }
2864 :
2865 : /*
2866 : * Now copy the CHECK constraints of this parent, adjusting attnos
2867 : * using the completed newattmap map. Identically named constraints
2868 : * are merged if possible, else we throw error.
2869 : */
2870 10130 : if (constr && constr->num_check > 0)
2871 : {
2872 328 : ConstrCheck *check = constr->check;
2873 :
2874 956 : for (int i = 0; i < constr->num_check; i++)
2875 : {
2876 628 : char *name = check[i].ccname;
2877 : Node *expr;
2878 : bool found_whole_row;
2879 :
2880 : /* ignore if the constraint is non-inheritable */
2881 628 : if (check[i].ccnoinherit)
2882 48 : continue;
2883 :
2884 : /* Adjust Vars to match new table's column numbering */
2885 580 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2886 : 1, 0,
2887 : newattmap,
2888 : InvalidOid, &found_whole_row);
2889 :
2890 : /*
2891 : * For the moment we have to reject whole-row variables. We
2892 : * could convert them, if we knew the new table's rowtype OID,
2893 : * but that hasn't been assigned yet.
2894 : */
2895 580 : if (found_whole_row)
2896 0 : ereport(ERROR,
2897 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2898 : errmsg("cannot convert whole-row table reference"),
2899 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2900 : name,
2901 : RelationGetRelationName(relation))));
2902 :
2903 580 : constraints = MergeCheckConstraint(constraints, name, expr,
2904 580 : check[i].ccenforced);
2905 : }
2906 : }
2907 :
2908 : /*
2909 : * Also copy the not-null constraints from this parent. The
2910 : * attnotnull markings were already installed above.
2911 : */
2912 22544 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2913 : {
2914 : Assert(nn->contype == CONSTR_NOTNULL);
2915 :
2916 2284 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2917 :
2918 2284 : nnconstraints = lappend(nnconstraints, nn);
2919 : }
2920 :
2921 10130 : free_attrmap(newattmap);
2922 :
2923 : /*
2924 : * Close the parent rel, but keep our lock on it until xact commit.
2925 : * That will prevent someone else from deleting or ALTERing the parent
2926 : * before the child is committed.
2927 : */
2928 10130 : table_close(relation, NoLock);
2929 : }
2930 :
2931 : /*
2932 : * If we had no inherited attributes, the result columns are just the
2933 : * explicitly declared columns. Otherwise, we need to merge the declared
2934 : * columns into the inherited column list. Although, we never have any
2935 : * explicitly declared columns if the table is a partition.
2936 : */
2937 59216 : if (inh_columns != NIL)
2938 : {
2939 9732 : int newcol_attno = 0;
2940 :
2941 10642 : foreach(lc, columns)
2942 : {
2943 988 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2944 988 : char *attributeName = newdef->colname;
2945 : int exist_attno;
2946 :
2947 : /*
2948 : * Partitions have only one parent and have no column definitions
2949 : * of their own, so conflict should never occur.
2950 : */
2951 : Assert(!is_partition);
2952 :
2953 988 : newcol_attno++;
2954 :
2955 : /*
2956 : * Does it match some inherited column?
2957 : */
2958 988 : exist_attno = findAttrByName(attributeName, inh_columns);
2959 988 : if (exist_attno > 0)
2960 : {
2961 : /*
2962 : * Yes, try to merge the two column definitions.
2963 : */
2964 352 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2965 : }
2966 : else
2967 : {
2968 : /*
2969 : * No, attach new column unchanged to result columns.
2970 : */
2971 636 : inh_columns = lappend(inh_columns, newdef);
2972 : }
2973 : }
2974 :
2975 9654 : columns = inh_columns;
2976 :
2977 : /*
2978 : * Check that we haven't exceeded the legal # of columns after merging
2979 : * in inherited columns.
2980 : */
2981 9654 : if (list_length(columns) > MaxHeapAttributeNumber)
2982 0 : ereport(ERROR,
2983 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2984 : errmsg("tables can have at most %d columns",
2985 : MaxHeapAttributeNumber)));
2986 : }
2987 :
2988 : /*
2989 : * Now that we have the column definition list for a partition, we can
2990 : * check whether the columns referenced in the column constraint specs
2991 : * actually exist. Also, merge column defaults.
2992 : */
2993 59138 : if (is_partition)
2994 : {
2995 8066 : foreach(lc, saved_columns)
2996 : {
2997 242 : ColumnDef *restdef = lfirst(lc);
2998 242 : bool found = false;
2999 : ListCell *l;
3000 :
3001 900 : foreach(l, columns)
3002 : {
3003 694 : ColumnDef *coldef = lfirst(l);
3004 :
3005 694 : if (strcmp(coldef->colname, restdef->colname) == 0)
3006 : {
3007 242 : found = true;
3008 :
3009 : /*
3010 : * Check for conflicts related to generated columns.
3011 : *
3012 : * Same rules as above: generated-ness has to match the
3013 : * parent, but the contents of the generation expression
3014 : * can be different.
3015 : */
3016 242 : if (coldef->generated)
3017 : {
3018 134 : if (restdef->raw_default && !restdef->generated)
3019 12 : ereport(ERROR,
3020 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3021 : errmsg("column \"%s\" inherits from generated column but specifies default",
3022 : restdef->colname)));
3023 122 : if (restdef->identity)
3024 0 : ereport(ERROR,
3025 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3026 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3027 : restdef->colname)));
3028 : }
3029 : else
3030 : {
3031 108 : if (restdef->generated)
3032 12 : ereport(ERROR,
3033 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3034 : errmsg("child column \"%s\" specifies generation expression",
3035 : restdef->colname),
3036 : errhint("A child table column cannot be generated unless its parent column is.")));
3037 : }
3038 :
3039 218 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3040 12 : ereport(ERROR,
3041 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3042 : errmsg("column \"%s\" inherits from generated column of different kind",
3043 : restdef->colname),
3044 : errdetail("Parent column is %s, child column is %s.",
3045 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3046 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3047 :
3048 : /*
3049 : * Override the parent's default value for this column
3050 : * (coldef->cooked_default) with the partition's local
3051 : * definition (restdef->raw_default), if there's one. It
3052 : * should be physically impossible to get a cooked default
3053 : * in the local definition or a raw default in the
3054 : * inherited definition, but make sure they're nulls, for
3055 : * future-proofing.
3056 : */
3057 : Assert(restdef->cooked_default == NULL);
3058 : Assert(coldef->raw_default == NULL);
3059 206 : if (restdef->raw_default)
3060 : {
3061 134 : coldef->raw_default = restdef->raw_default;
3062 134 : coldef->cooked_default = NULL;
3063 : }
3064 : }
3065 : }
3066 :
3067 : /* complain for constraints on columns not in parent */
3068 206 : if (!found)
3069 0 : ereport(ERROR,
3070 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3071 : errmsg("column \"%s\" does not exist",
3072 : restdef->colname)));
3073 : }
3074 : }
3075 :
3076 : /*
3077 : * If we found any conflicting parent default values, check to make sure
3078 : * they were overridden by the child.
3079 : */
3080 59102 : if (have_bogus_defaults)
3081 : {
3082 90 : foreach(lc, columns)
3083 : {
3084 72 : ColumnDef *def = lfirst(lc);
3085 :
3086 72 : if (def->cooked_default == &bogus_marker)
3087 : {
3088 18 : if (def->generated)
3089 12 : ereport(ERROR,
3090 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3091 : errmsg("column \"%s\" inherits conflicting generation expressions",
3092 : def->colname),
3093 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3094 : else
3095 6 : ereport(ERROR,
3096 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3097 : errmsg("column \"%s\" inherits conflicting default values",
3098 : def->colname),
3099 : errhint("To resolve the conflict, specify a default explicitly.")));
3100 : }
3101 : }
3102 : }
3103 :
3104 59084 : *supconstr = constraints;
3105 59084 : *supnotnulls = nnconstraints;
3106 :
3107 59084 : return columns;
3108 : }
3109 :
3110 :
3111 : /*
3112 : * MergeCheckConstraint
3113 : * Try to merge an inherited CHECK constraint with previous ones
3114 : *
3115 : * If we inherit identically-named constraints from multiple parents, we must
3116 : * merge them, or throw an error if they don't have identical definitions.
3117 : *
3118 : * constraints is a list of CookedConstraint structs for previous constraints.
3119 : *
3120 : * If the new constraint matches an existing one, then the existing
3121 : * constraint's inheritance count is updated. If there is a conflict (same
3122 : * name but different expression), throw an error. If the constraint neither
3123 : * matches nor conflicts with an existing one, a new constraint is appended to
3124 : * the list.
3125 : */
3126 : static List *
3127 580 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3128 : {
3129 : ListCell *lc;
3130 : CookedConstraint *newcon;
3131 :
3132 1480 : foreach(lc, constraints)
3133 : {
3134 1026 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3135 :
3136 : Assert(ccon->contype == CONSTR_CHECK);
3137 :
3138 : /* Non-matching names never conflict */
3139 1026 : if (strcmp(ccon->name, name) != 0)
3140 900 : continue;
3141 :
3142 126 : if (equal(expr, ccon->expr))
3143 : {
3144 : /* OK to merge constraint with existing */
3145 126 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3146 : &ccon->inhcount))
3147 0 : ereport(ERROR,
3148 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3149 : errmsg("too many inheritance parents"));
3150 :
3151 : /*
3152 : * When enforceability differs, the merged constraint should be
3153 : * marked as ENFORCED because one of the parents is ENFORCED.
3154 : */
3155 126 : if (!ccon->is_enforced && is_enforced)
3156 : {
3157 24 : ccon->is_enforced = true;
3158 24 : ccon->skip_validation = false;
3159 : }
3160 :
3161 126 : return constraints;
3162 : }
3163 :
3164 0 : ereport(ERROR,
3165 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3166 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3167 : name)));
3168 : }
3169 :
3170 : /*
3171 : * Constraint couldn't be merged with an existing one and also didn't
3172 : * conflict with an existing one, so add it as a new one to the list.
3173 : */
3174 454 : newcon = palloc0_object(CookedConstraint);
3175 454 : newcon->contype = CONSTR_CHECK;
3176 454 : newcon->name = pstrdup(name);
3177 454 : newcon->expr = expr;
3178 454 : newcon->inhcount = 1;
3179 454 : newcon->is_enforced = is_enforced;
3180 454 : newcon->skip_validation = !is_enforced;
3181 454 : return lappend(constraints, newcon);
3182 : }
3183 :
3184 : /*
3185 : * MergeChildAttribute
3186 : * Merge given child attribute definition into given inherited attribute.
3187 : *
3188 : * Input arguments:
3189 : * 'inh_columns' is the list of inherited ColumnDefs.
3190 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3191 : * 'newcol_attno' is the attribute number in child table's schema definition
3192 : * 'newdef' is the column/attribute definition from the child table.
3193 : *
3194 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3195 : * ColumnDef remains unchanged.
3196 : *
3197 : * Notes:
3198 : * - The attribute is merged according to the rules laid out in the prologue
3199 : * of MergeAttributes().
3200 : * - If matching inherited attribute exists but the child attribute can not be
3201 : * merged into it, the function throws respective errors.
3202 : * - A partition can not have its own column definitions. Hence this function
3203 : * is applicable only to a regular inheritance child.
3204 : */
3205 : static void
3206 352 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3207 : {
3208 352 : char *attributeName = newdef->colname;
3209 : ColumnDef *inhdef;
3210 : Oid inhtypeid,
3211 : newtypeid;
3212 : int32 inhtypmod,
3213 : newtypmod;
3214 : Oid inhcollid,
3215 : newcollid;
3216 :
3217 352 : if (exist_attno == newcol_attno)
3218 318 : ereport(NOTICE,
3219 : (errmsg("merging column \"%s\" with inherited definition",
3220 : attributeName)));
3221 : else
3222 34 : ereport(NOTICE,
3223 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3224 : errdetail("User-specified column moved to the position of the inherited column.")));
3225 :
3226 352 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3227 :
3228 : /*
3229 : * Must have the same type and typmod
3230 : */
3231 352 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3232 352 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3233 352 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3234 12 : ereport(ERROR,
3235 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3236 : errmsg("column \"%s\" has a type conflict",
3237 : attributeName),
3238 : errdetail("%s versus %s",
3239 : format_type_with_typemod(inhtypeid, inhtypmod),
3240 : format_type_with_typemod(newtypeid, newtypmod))));
3241 :
3242 : /*
3243 : * Must have the same collation
3244 : */
3245 340 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3246 340 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3247 340 : if (inhcollid != newcollid)
3248 6 : ereport(ERROR,
3249 : (errcode(ERRCODE_COLLATION_MISMATCH),
3250 : errmsg("column \"%s\" has a collation conflict",
3251 : attributeName),
3252 : errdetail("\"%s\" versus \"%s\"",
3253 : get_collation_name(inhcollid),
3254 : get_collation_name(newcollid))));
3255 :
3256 : /*
3257 : * Identity is never inherited by a regular inheritance child. Pick
3258 : * child's identity definition if there's one.
3259 : */
3260 334 : inhdef->identity = newdef->identity;
3261 :
3262 : /*
3263 : * Copy storage parameter
3264 : */
3265 334 : if (inhdef->storage == 0)
3266 0 : inhdef->storage = newdef->storage;
3267 334 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3268 6 : ereport(ERROR,
3269 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3270 : errmsg("column \"%s\" has a storage parameter conflict",
3271 : attributeName),
3272 : errdetail("%s versus %s",
3273 : storage_name(inhdef->storage),
3274 : storage_name(newdef->storage))));
3275 :
3276 : /*
3277 : * Copy compression parameter
3278 : */
3279 328 : if (inhdef->compression == NULL)
3280 322 : inhdef->compression = newdef->compression;
3281 6 : else if (newdef->compression != NULL)
3282 : {
3283 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3284 6 : ereport(ERROR,
3285 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3286 : errmsg("column \"%s\" has a compression method conflict",
3287 : attributeName),
3288 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3289 : }
3290 :
3291 : /*
3292 : * Merge of not-null constraints = OR 'em together
3293 : */
3294 322 : inhdef->is_not_null |= newdef->is_not_null;
3295 :
3296 : /*
3297 : * Check for conflicts related to generated columns.
3298 : *
3299 : * If the parent column is generated, the child column will be made a
3300 : * generated column if it isn't already. If it is a generated column,
3301 : * we'll take its generation expression in preference to the parent's. We
3302 : * must check that the child column doesn't specify a default value or
3303 : * identity, which matches the rules for a single column in
3304 : * parse_utilcmd.c.
3305 : *
3306 : * Conversely, if the parent column is not generated, the child column
3307 : * can't be either. (We used to allow that, but it results in being able
3308 : * to override the generation expression via UPDATEs through the parent.)
3309 : */
3310 322 : if (inhdef->generated)
3311 : {
3312 62 : if (newdef->raw_default && !newdef->generated)
3313 12 : ereport(ERROR,
3314 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3315 : errmsg("column \"%s\" inherits from generated column but specifies default",
3316 : inhdef->colname)));
3317 50 : if (newdef->identity)
3318 12 : ereport(ERROR,
3319 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3320 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3321 : inhdef->colname)));
3322 : }
3323 : else
3324 : {
3325 260 : if (newdef->generated)
3326 12 : ereport(ERROR,
3327 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3328 : errmsg("child column \"%s\" specifies generation expression",
3329 : inhdef->colname),
3330 : errhint("A child table column cannot be generated unless its parent column is.")));
3331 : }
3332 :
3333 286 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3334 12 : ereport(ERROR,
3335 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3336 : errmsg("column \"%s\" inherits from generated column of different kind",
3337 : inhdef->colname),
3338 : errdetail("Parent column is %s, child column is %s.",
3339 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3340 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3341 :
3342 : /*
3343 : * If new def has a default, override previous default
3344 : */
3345 274 : if (newdef->raw_default != NULL)
3346 : {
3347 30 : inhdef->raw_default = newdef->raw_default;
3348 30 : inhdef->cooked_default = newdef->cooked_default;
3349 : }
3350 :
3351 : /* Mark the column as locally defined */
3352 274 : inhdef->is_local = true;
3353 274 : }
3354 :
3355 : /*
3356 : * MergeInheritedAttribute
3357 : * Merge given parent attribute definition into specified attribute
3358 : * inherited from the previous parents.
3359 : *
3360 : * Input arguments:
3361 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3362 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3363 : * 'newdef' is the new parent column/attribute definition to be merged.
3364 : *
3365 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3366 : *
3367 : * Notes:
3368 : * - The attribute is merged according to the rules laid out in the prologue
3369 : * of MergeAttributes().
3370 : * - If matching inherited attribute exists but the new attribute can not be
3371 : * merged into it, the function throws respective errors.
3372 : * - A partition inherits from only a single parent. Hence this function is
3373 : * applicable only to a regular inheritance.
3374 : */
3375 : static ColumnDef *
3376 338 : MergeInheritedAttribute(List *inh_columns,
3377 : int exist_attno,
3378 : const ColumnDef *newdef)
3379 : {
3380 338 : char *attributeName = newdef->colname;
3381 : ColumnDef *prevdef;
3382 : Oid prevtypeid,
3383 : newtypeid;
3384 : int32 prevtypmod,
3385 : newtypmod;
3386 : Oid prevcollid,
3387 : newcollid;
3388 :
3389 338 : ereport(NOTICE,
3390 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3391 : attributeName)));
3392 338 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3393 :
3394 : /*
3395 : * Must have the same type and typmod
3396 : */
3397 338 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3398 338 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3399 338 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3400 0 : ereport(ERROR,
3401 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3402 : errmsg("inherited column \"%s\" has a type conflict",
3403 : attributeName),
3404 : errdetail("%s versus %s",
3405 : format_type_with_typemod(prevtypeid, prevtypmod),
3406 : format_type_with_typemod(newtypeid, newtypmod))));
3407 :
3408 : /*
3409 : * Must have the same collation
3410 : */
3411 338 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3412 338 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3413 338 : if (prevcollid != newcollid)
3414 0 : ereport(ERROR,
3415 : (errcode(ERRCODE_COLLATION_MISMATCH),
3416 : errmsg("inherited column \"%s\" has a collation conflict",
3417 : attributeName),
3418 : errdetail("\"%s\" versus \"%s\"",
3419 : get_collation_name(prevcollid),
3420 : get_collation_name(newcollid))));
3421 :
3422 : /*
3423 : * Copy/check storage parameter
3424 : */
3425 338 : if (prevdef->storage == 0)
3426 0 : prevdef->storage = newdef->storage;
3427 338 : else if (prevdef->storage != newdef->storage)
3428 6 : ereport(ERROR,
3429 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3430 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3431 : attributeName),
3432 : errdetail("%s versus %s",
3433 : storage_name(prevdef->storage),
3434 : storage_name(newdef->storage))));
3435 :
3436 : /*
3437 : * Copy/check compression parameter
3438 : */
3439 332 : if (prevdef->compression == NULL)
3440 320 : prevdef->compression = newdef->compression;
3441 12 : else if (newdef->compression != NULL)
3442 : {
3443 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3444 6 : ereport(ERROR,
3445 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3446 : errmsg("column \"%s\" has a compression method conflict",
3447 : attributeName),
3448 : errdetail("%s versus %s",
3449 : prevdef->compression, newdef->compression)));
3450 : }
3451 :
3452 : /*
3453 : * Check for GENERATED conflicts
3454 : */
3455 326 : if (prevdef->generated != newdef->generated)
3456 24 : ereport(ERROR,
3457 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3458 : errmsg("inherited column \"%s\" has a generation conflict",
3459 : attributeName)));
3460 :
3461 : /*
3462 : * Default and other constraints are handled by the caller.
3463 : */
3464 :
3465 302 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3466 : &prevdef->inhcount))
3467 0 : ereport(ERROR,
3468 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3469 : errmsg("too many inheritance parents"));
3470 :
3471 302 : return prevdef;
3472 : }
3473 :
3474 : /*
3475 : * StoreCatalogInheritance
3476 : * Updates the system catalogs with proper inheritance information.
3477 : *
3478 : * supers is a list of the OIDs of the new relation's direct ancestors.
3479 : */
3480 : static void
3481 58466 : StoreCatalogInheritance(Oid relationId, List *supers,
3482 : bool child_is_partition)
3483 : {
3484 : Relation relation;
3485 : int32 seqNumber;
3486 : ListCell *entry;
3487 :
3488 : /*
3489 : * sanity checks
3490 : */
3491 : Assert(OidIsValid(relationId));
3492 :
3493 58466 : if (supers == NIL)
3494 49178 : return;
3495 :
3496 : /*
3497 : * Store INHERITS information in pg_inherits using direct ancestors only.
3498 : * Also enter dependencies on the direct ancestors, and make sure they are
3499 : * marked with relhassubclass = true.
3500 : *
3501 : * (Once upon a time, both direct and indirect ancestors were found here
3502 : * and then entered into pg_ipl. Since that catalog doesn't exist
3503 : * anymore, there's no need to look for indirect ancestors.)
3504 : */
3505 9288 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3506 :
3507 9288 : seqNumber = 1;
3508 18878 : foreach(entry, supers)
3509 : {
3510 9590 : Oid parentOid = lfirst_oid(entry);
3511 :
3512 9590 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3513 : child_is_partition);
3514 9590 : seqNumber++;
3515 : }
3516 :
3517 9288 : table_close(relation, RowExclusiveLock);
3518 : }
3519 :
3520 : /*
3521 : * Make catalog entries showing relationId as being an inheritance child
3522 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3523 : */
3524 : static void
3525 11894 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3526 : int32 seqNumber, Relation inhRelation,
3527 : bool child_is_partition)
3528 : {
3529 : ObjectAddress childobject,
3530 : parentobject;
3531 :
3532 : /* store the pg_inherits row */
3533 11894 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3534 :
3535 : /*
3536 : * Store a dependency too
3537 : */
3538 11894 : parentobject.classId = RelationRelationId;
3539 11894 : parentobject.objectId = parentOid;
3540 11894 : parentobject.objectSubId = 0;
3541 11894 : childobject.classId = RelationRelationId;
3542 11894 : childobject.objectId = relationId;
3543 11894 : childobject.objectSubId = 0;
3544 :
3545 11894 : recordDependencyOn(&childobject, &parentobject,
3546 : child_dependency_type(child_is_partition));
3547 :
3548 : /*
3549 : * Post creation hook of this inheritance. Since object_access_hook
3550 : * doesn't take multiple object identifiers, we relay oid of parent
3551 : * relation using auxiliary_id argument.
3552 : */
3553 11894 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3554 : relationId, 0,
3555 : parentOid, false);
3556 :
3557 : /*
3558 : * Mark the parent as having subclasses.
3559 : */
3560 11894 : SetRelationHasSubclass(parentOid, true);
3561 11894 : }
3562 :
3563 : /*
3564 : * Look for an existing column entry with the given name.
3565 : *
3566 : * Returns the index (starting with 1) if attribute already exists in columns,
3567 : * 0 if it doesn't.
3568 : */
3569 : static int
3570 21216 : findAttrByName(const char *attributeName, const List *columns)
3571 : {
3572 : ListCell *lc;
3573 21216 : int i = 1;
3574 :
3575 37832 : foreach(lc, columns)
3576 : {
3577 17306 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3578 690 : return i;
3579 :
3580 16616 : i++;
3581 : }
3582 20526 : return 0;
3583 : }
3584 :
3585 :
3586 : /*
3587 : * SetRelationHasSubclass
3588 : * Set the value of the relation's relhassubclass field in pg_class.
3589 : *
3590 : * It's always safe to set this field to true, because all SQL commands are
3591 : * ready to see true and then find no children. On the other hand, commands
3592 : * generally assume zero children if this is false.
3593 : *
3594 : * Caller must hold any self-exclusive lock until end of transaction. If the
3595 : * new value is false, caller must have acquired that lock before reading the
3596 : * evidence that justified the false value. That way, it properly waits if
3597 : * another backend is simultaneously concluding no need to change the tuple
3598 : * (new and old values are true).
3599 : *
3600 : * NOTE: an important side-effect of this operation is that an SI invalidation
3601 : * message is sent out to all backends --- including me --- causing plans
3602 : * referencing the relation to be rebuilt with the new list of children.
3603 : * This must happen even if we find that no change is needed in the pg_class
3604 : * row.
3605 : */
3606 : void
3607 14840 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3608 : {
3609 : Relation relationRelation;
3610 : HeapTuple tuple;
3611 : Form_pg_class classtuple;
3612 :
3613 : Assert(CheckRelationOidLockedByMe(relationId,
3614 : ShareUpdateExclusiveLock, false) ||
3615 : CheckRelationOidLockedByMe(relationId,
3616 : ShareRowExclusiveLock, true));
3617 :
3618 : /*
3619 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3620 : */
3621 14840 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3622 14840 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3623 14840 : if (!HeapTupleIsValid(tuple))
3624 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3625 14840 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3626 :
3627 14840 : if (classtuple->relhassubclass != relhassubclass)
3628 : {
3629 7496 : classtuple->relhassubclass = relhassubclass;
3630 7496 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3631 : }
3632 : else
3633 : {
3634 : /* no need to change tuple, but force relcache rebuild anyway */
3635 7344 : CacheInvalidateRelcacheByTuple(tuple);
3636 : }
3637 :
3638 14840 : heap_freetuple(tuple);
3639 14840 : table_close(relationRelation, RowExclusiveLock);
3640 14840 : }
3641 :
3642 : /*
3643 : * CheckRelationTableSpaceMove
3644 : * Check if relation can be moved to new tablespace.
3645 : *
3646 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3647 : *
3648 : * Returns true if the relation can be moved to the new tablespace; raises
3649 : * an error if it is not possible to do the move; returns false if the move
3650 : * would have no effect.
3651 : */
3652 : bool
3653 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3654 : {
3655 : Oid oldTableSpaceId;
3656 :
3657 : /*
3658 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3659 : * stored as 0.
3660 : */
3661 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3662 226 : if (newTableSpaceId == oldTableSpaceId ||
3663 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3664 10 : return false;
3665 :
3666 : /*
3667 : * We cannot support moving mapped relations into different tablespaces.
3668 : * (In particular this eliminates all shared catalogs.)
3669 : */
3670 216 : if (RelationIsMapped(rel))
3671 0 : ereport(ERROR,
3672 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3673 : errmsg("cannot move system relation \"%s\"",
3674 : RelationGetRelationName(rel))));
3675 :
3676 : /* Cannot move a non-shared relation into pg_global */
3677 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3678 12 : ereport(ERROR,
3679 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3680 : errmsg("only shared relations can be placed in pg_global tablespace")));
3681 :
3682 : /*
3683 : * Do not allow moving temp tables of other backends ... their local
3684 : * buffer manager is not going to cope.
3685 : */
3686 204 : if (RELATION_IS_OTHER_TEMP(rel))
3687 0 : ereport(ERROR,
3688 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3689 : errmsg("cannot move temporary tables of other sessions")));
3690 :
3691 204 : return true;
3692 : }
3693 :
3694 : /*
3695 : * SetRelationTableSpace
3696 : * Set new reltablespace and relfilenumber in pg_class entry.
3697 : *
3698 : * newTableSpaceId is the new tablespace for the relation, and
3699 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3700 : * InvalidRelFileNumber, this field is not updated.
3701 : *
3702 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3703 : *
3704 : * The caller of this routine had better check if a relation can be
3705 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3706 : * first, and is responsible for making the change visible with
3707 : * CommandCounterIncrement().
3708 : */
3709 : void
3710 204 : SetRelationTableSpace(Relation rel,
3711 : Oid newTableSpaceId,
3712 : RelFileNumber newRelFilenumber)
3713 : {
3714 : Relation pg_class;
3715 : HeapTuple tuple;
3716 : ItemPointerData otid;
3717 : Form_pg_class rd_rel;
3718 204 : Oid reloid = RelationGetRelid(rel);
3719 :
3720 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3721 :
3722 : /* Get a modifiable copy of the relation's pg_class row. */
3723 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3724 :
3725 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3726 204 : if (!HeapTupleIsValid(tuple))
3727 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3728 204 : otid = tuple->t_self;
3729 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3730 :
3731 : /* Update the pg_class row. */
3732 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3733 204 : InvalidOid : newTableSpaceId;
3734 204 : if (RelFileNumberIsValid(newRelFilenumber))
3735 160 : rd_rel->relfilenode = newRelFilenumber;
3736 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3737 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3738 :
3739 : /*
3740 : * Record dependency on tablespace. This is only required for relations
3741 : * that have no physical storage.
3742 : */
3743 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3744 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3745 : rd_rel->reltablespace);
3746 :
3747 204 : heap_freetuple(tuple);
3748 204 : table_close(pg_class, RowExclusiveLock);
3749 204 : }
3750 :
3751 : /*
3752 : * renameatt_check - basic sanity checks before attribute rename
3753 : */
3754 : static void
3755 1018 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3756 : {
3757 1018 : char relkind = classform->relkind;
3758 :
3759 1018 : if (classform->reloftype && !recursing)
3760 6 : ereport(ERROR,
3761 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3762 : errmsg("cannot rename column of typed table")));
3763 :
3764 : /*
3765 : * Renaming the columns of sequences or toast tables doesn't actually
3766 : * break anything from the system's point of view, since internal
3767 : * references are by attnum. But it doesn't seem right to allow users to
3768 : * change names that are hardcoded into the system, hence the following
3769 : * restriction.
3770 : */
3771 1012 : if (relkind != RELKIND_RELATION &&
3772 84 : relkind != RELKIND_VIEW &&
3773 84 : relkind != RELKIND_MATVIEW &&
3774 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3775 36 : relkind != RELKIND_INDEX &&
3776 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3777 0 : relkind != RELKIND_FOREIGN_TABLE &&
3778 : relkind != RELKIND_PARTITIONED_TABLE)
3779 0 : ereport(ERROR,
3780 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3781 : errmsg("cannot rename columns of relation \"%s\"",
3782 : NameStr(classform->relname)),
3783 : errdetail_relkind_not_supported(relkind)));
3784 :
3785 : /*
3786 : * permissions checking. only the owner of a class can change its schema.
3787 : */
3788 1012 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3789 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3790 0 : NameStr(classform->relname));
3791 1012 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3792 2 : ereport(ERROR,
3793 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3794 : errmsg("permission denied: \"%s\" is a system catalog",
3795 : NameStr(classform->relname))));
3796 1010 : }
3797 :
3798 : /*
3799 : * renameatt_internal - workhorse for renameatt
3800 : *
3801 : * Return value is the attribute number in the 'myrelid' relation.
3802 : */
3803 : static AttrNumber
3804 552 : renameatt_internal(Oid myrelid,
3805 : const char *oldattname,
3806 : const char *newattname,
3807 : bool recurse,
3808 : bool recursing,
3809 : int expected_parents,
3810 : DropBehavior behavior)
3811 : {
3812 : Relation targetrelation;
3813 : Relation attrelation;
3814 : HeapTuple atttup;
3815 : Form_pg_attribute attform;
3816 : AttrNumber attnum;
3817 :
3818 : /*
3819 : * Grab an exclusive lock on the target table, which we will NOT release
3820 : * until end of transaction.
3821 : */
3822 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3823 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3824 :
3825 : /*
3826 : * if the 'recurse' flag is set then we are supposed to rename this
3827 : * attribute in all classes that inherit from 'relname' (as well as in
3828 : * 'relname').
3829 : *
3830 : * any permissions or problems with duplicate attributes will cause the
3831 : * whole transaction to abort, which is what we want -- all or nothing.
3832 : */
3833 552 : if (recurse)
3834 : {
3835 : List *child_oids,
3836 : *child_numparents;
3837 : ListCell *lo,
3838 : *li;
3839 :
3840 : /*
3841 : * we need the number of parents for each child so that the recursive
3842 : * calls to renameatt() can determine whether there are any parents
3843 : * outside the inheritance hierarchy being processed.
3844 : */
3845 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3846 : &child_numparents);
3847 :
3848 : /*
3849 : * find_all_inheritors does the recursive search of the inheritance
3850 : * hierarchy, so all we have to do is process all of the relids in the
3851 : * list that it returns.
3852 : */
3853 734 : forboth(lo, child_oids, li, child_numparents)
3854 : {
3855 516 : Oid childrelid = lfirst_oid(lo);
3856 516 : int numparents = lfirst_int(li);
3857 :
3858 516 : if (childrelid == myrelid)
3859 248 : continue;
3860 : /* note we need not recurse again */
3861 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3862 : }
3863 : }
3864 : else
3865 : {
3866 : /*
3867 : * If we are told not to recurse, there had better not be any child
3868 : * tables; else the rename would put them out of step.
3869 : *
3870 : * expected_parents will only be 0 if we are not already recursing.
3871 : */
3872 340 : if (expected_parents == 0 &&
3873 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3874 12 : ereport(ERROR,
3875 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3876 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3877 : oldattname)));
3878 : }
3879 :
3880 : /* rename attributes in typed tables of composite type */
3881 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3882 : {
3883 : List *child_oids;
3884 : ListCell *lo;
3885 :
3886 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3887 24 : RelationGetRelationName(targetrelation),
3888 : behavior);
3889 :
3890 24 : foreach(lo, child_oids)
3891 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3892 : }
3893 :
3894 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3895 :
3896 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3897 504 : if (!HeapTupleIsValid(atttup))
3898 24 : ereport(ERROR,
3899 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3900 : errmsg("column \"%s\" does not exist",
3901 : oldattname)));
3902 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3903 :
3904 480 : attnum = attform->attnum;
3905 480 : if (attnum <= 0)
3906 0 : ereport(ERROR,
3907 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3908 : errmsg("cannot rename system column \"%s\"",
3909 : oldattname)));
3910 :
3911 : /*
3912 : * if the attribute is inherited, forbid the renaming. if this is a
3913 : * top-level call to renameatt(), then expected_parents will be 0, so the
3914 : * effect of this code will be to prohibit the renaming if the attribute
3915 : * is inherited at all. if this is a recursive call to renameatt(),
3916 : * expected_parents will be the number of parents the current relation has
3917 : * within the inheritance hierarchy being processed, so we'll prohibit the
3918 : * renaming only if there are additional parents from elsewhere.
3919 : */
3920 480 : if (attform->attinhcount > expected_parents)
3921 30 : ereport(ERROR,
3922 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3923 : errmsg("cannot rename inherited column \"%s\"",
3924 : oldattname)));
3925 :
3926 : /* new name should not already exist */
3927 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3928 :
3929 : /* apply the update */
3930 438 : namestrcpy(&(attform->attname), newattname);
3931 :
3932 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3933 :
3934 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3935 :
3936 438 : heap_freetuple(atttup);
3937 :
3938 438 : table_close(attrelation, RowExclusiveLock);
3939 :
3940 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3941 :
3942 438 : return attnum;
3943 : }
3944 :
3945 : /*
3946 : * Perform permissions and integrity checks before acquiring a relation lock.
3947 : */
3948 : static void
3949 422 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3950 : void *arg)
3951 : {
3952 : HeapTuple tuple;
3953 : Form_pg_class form;
3954 :
3955 422 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3956 422 : if (!HeapTupleIsValid(tuple))
3957 40 : return; /* concurrently dropped */
3958 382 : form = (Form_pg_class) GETSTRUCT(tuple);
3959 382 : renameatt_check(relid, form, false);
3960 374 : ReleaseSysCache(tuple);
3961 : }
3962 :
3963 : /*
3964 : * renameatt - changes the name of an attribute in a relation
3965 : *
3966 : * The returned ObjectAddress is that of the renamed column.
3967 : */
3968 : ObjectAddress
3969 316 : renameatt(RenameStmt *stmt)
3970 : {
3971 : Oid relid;
3972 : AttrNumber attnum;
3973 : ObjectAddress address;
3974 :
3975 : /* lock level taken here should match renameatt_internal */
3976 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3977 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3978 : RangeVarCallbackForRenameAttribute,
3979 : NULL);
3980 :
3981 302 : if (!OidIsValid(relid))
3982 : {
3983 24 : ereport(NOTICE,
3984 : (errmsg("relation \"%s\" does not exist, skipping",
3985 : stmt->relation->relname)));
3986 24 : return InvalidObjectAddress;
3987 : }
3988 :
3989 : attnum =
3990 278 : renameatt_internal(relid,
3991 278 : stmt->subname, /* old att name */
3992 278 : stmt->newname, /* new att name */
3993 278 : stmt->relation->inh, /* recursive? */
3994 : false, /* recursing? */
3995 : 0, /* expected inhcount */
3996 : stmt->behavior);
3997 :
3998 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3999 :
4000 194 : return address;
4001 : }
4002 :
4003 : /*
4004 : * same logic as renameatt_internal
4005 : */
4006 : static ObjectAddress
4007 90 : rename_constraint_internal(Oid myrelid,
4008 : Oid mytypid,
4009 : const char *oldconname,
4010 : const char *newconname,
4011 : bool recurse,
4012 : bool recursing,
4013 : int expected_parents)
4014 : {
4015 90 : Relation targetrelation = NULL;
4016 : Oid constraintOid;
4017 : HeapTuple tuple;
4018 : Form_pg_constraint con;
4019 : ObjectAddress address;
4020 :
4021 : Assert(!myrelid || !mytypid);
4022 :
4023 90 : if (mytypid)
4024 : {
4025 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4026 : }
4027 : else
4028 : {
4029 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4030 :
4031 : /*
4032 : * don't tell it whether we're recursing; we allow changing typed
4033 : * tables here
4034 : */
4035 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4036 :
4037 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4038 : }
4039 :
4040 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4041 90 : if (!HeapTupleIsValid(tuple))
4042 0 : elog(ERROR, "cache lookup failed for constraint %u",
4043 : constraintOid);
4044 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4045 :
4046 90 : if (myrelid &&
4047 84 : (con->contype == CONSTRAINT_CHECK ||
4048 24 : con->contype == CONSTRAINT_NOTNULL) &&
4049 66 : !con->connoinherit)
4050 : {
4051 54 : if (recurse)
4052 : {
4053 : List *child_oids,
4054 : *child_numparents;
4055 : ListCell *lo,
4056 : *li;
4057 :
4058 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4059 : &child_numparents);
4060 :
4061 84 : forboth(lo, child_oids, li, child_numparents)
4062 : {
4063 48 : Oid childrelid = lfirst_oid(lo);
4064 48 : int numparents = lfirst_int(li);
4065 :
4066 48 : if (childrelid == myrelid)
4067 36 : continue;
4068 :
4069 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4070 : }
4071 : }
4072 : else
4073 : {
4074 24 : if (expected_parents == 0 &&
4075 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4076 6 : ereport(ERROR,
4077 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4078 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4079 : oldconname)));
4080 : }
4081 :
4082 48 : if (con->coninhcount > expected_parents)
4083 6 : ereport(ERROR,
4084 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4085 : errmsg("cannot rename inherited constraint \"%s\"",
4086 : oldconname)));
4087 : }
4088 :
4089 78 : if (con->conindid
4090 18 : && (con->contype == CONSTRAINT_PRIMARY
4091 6 : || con->contype == CONSTRAINT_UNIQUE
4092 0 : || con->contype == CONSTRAINT_EXCLUSION))
4093 : /* rename the index; this renames the constraint as well */
4094 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4095 : else
4096 60 : RenameConstraintById(constraintOid, newconname);
4097 :
4098 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4099 :
4100 78 : ReleaseSysCache(tuple);
4101 :
4102 78 : if (targetrelation)
4103 : {
4104 : /*
4105 : * Invalidate relcache so as others can see the new constraint name.
4106 : */
4107 72 : CacheInvalidateRelcache(targetrelation);
4108 :
4109 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4110 : }
4111 :
4112 78 : return address;
4113 : }
4114 :
4115 : ObjectAddress
4116 84 : RenameConstraint(RenameStmt *stmt)
4117 : {
4118 84 : Oid relid = InvalidOid;
4119 84 : Oid typid = InvalidOid;
4120 :
4121 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4122 : {
4123 : Relation rel;
4124 : HeapTuple tup;
4125 :
4126 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4127 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4128 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4129 6 : if (!HeapTupleIsValid(tup))
4130 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4131 6 : checkDomainOwner(tup);
4132 6 : ReleaseSysCache(tup);
4133 6 : table_close(rel, NoLock);
4134 : }
4135 : else
4136 : {
4137 : /* lock level taken here should match rename_constraint_internal */
4138 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4139 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4140 : RangeVarCallbackForRenameAttribute,
4141 : NULL);
4142 78 : if (!OidIsValid(relid))
4143 : {
4144 6 : ereport(NOTICE,
4145 : (errmsg("relation \"%s\" does not exist, skipping",
4146 : stmt->relation->relname)));
4147 6 : return InvalidObjectAddress;
4148 : }
4149 : }
4150 :
4151 : return
4152 78 : rename_constraint_internal(relid, typid,
4153 78 : stmt->subname,
4154 78 : stmt->newname,
4155 150 : (stmt->relation &&
4156 72 : stmt->relation->inh), /* recursive? */
4157 : false, /* recursing? */
4158 : 0 /* expected inhcount */ );
4159 : }
4160 :
4161 : /*
4162 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4163 : * RENAME
4164 : */
4165 : ObjectAddress
4166 510 : RenameRelation(RenameStmt *stmt)
4167 : {
4168 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4169 : Oid relid;
4170 : ObjectAddress address;
4171 :
4172 : /*
4173 : * Grab an exclusive lock on the target table, index, sequence, view,
4174 : * materialized view, or foreign table, which we will NOT release until
4175 : * end of transaction.
4176 : *
4177 : * Lock level used here should match RenameRelationInternal, to avoid lock
4178 : * escalation. However, because ALTER INDEX can be used with any relation
4179 : * type, we mustn't believe without verification.
4180 : */
4181 : for (;;)
4182 12 : {
4183 : LOCKMODE lockmode;
4184 : char relkind;
4185 : bool obj_is_index;
4186 :
4187 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4188 :
4189 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4190 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4191 : RangeVarCallbackForAlterRelation,
4192 : stmt);
4193 :
4194 472 : if (!OidIsValid(relid))
4195 : {
4196 18 : ereport(NOTICE,
4197 : (errmsg("relation \"%s\" does not exist, skipping",
4198 : stmt->relation->relname)));
4199 18 : return InvalidObjectAddress;
4200 : }
4201 :
4202 : /*
4203 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4204 : * to rename a table), but we might've used the wrong lock level. If
4205 : * that happens, retry with the correct lock level. We don't bother
4206 : * if we already acquired AccessExclusiveLock with an index, however.
4207 : */
4208 454 : relkind = get_rel_relkind(relid);
4209 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4210 : relkind == RELKIND_PARTITIONED_INDEX);
4211 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4212 : break;
4213 :
4214 12 : UnlockRelationOid(relid, lockmode);
4215 12 : is_index_stmt = obj_is_index;
4216 : }
4217 :
4218 : /* Do the work */
4219 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4220 :
4221 430 : ObjectAddressSet(address, RelationRelationId, relid);
4222 :
4223 430 : return address;
4224 : }
4225 :
4226 : /*
4227 : * RenameRelationInternal - change the name of a relation
4228 : */
4229 : void
4230 1638 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4231 : {
4232 : Relation targetrelation;
4233 : Relation relrelation; /* for RELATION relation */
4234 : ItemPointerData otid;
4235 : HeapTuple reltup;
4236 : Form_pg_class relform;
4237 : Oid namespaceId;
4238 :
4239 : /*
4240 : * Grab a lock on the target relation, which we will NOT release until end
4241 : * of transaction. We need at least a self-exclusive lock so that
4242 : * concurrent DDL doesn't overwrite the rename if they start updating
4243 : * while still seeing the old version. The lock also guards against
4244 : * triggering relcache reloads in concurrent sessions, which might not
4245 : * handle this information changing under them. For indexes, we can use a
4246 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4247 : * specially.
4248 : */
4249 1638 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4250 1638 : namespaceId = RelationGetNamespace(targetrelation);
4251 :
4252 : /*
4253 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4254 : */
4255 1638 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4256 :
4257 1638 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4258 1638 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4259 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4260 1638 : otid = reltup->t_self;
4261 1638 : relform = (Form_pg_class) GETSTRUCT(reltup);
4262 :
4263 1638 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4264 12 : ereport(ERROR,
4265 : (errcode(ERRCODE_DUPLICATE_TABLE),
4266 : errmsg("relation \"%s\" already exists",
4267 : newrelname)));
4268 :
4269 : /*
4270 : * RenameRelation is careful not to believe the caller's idea of the
4271 : * relation kind being handled. We don't have to worry about this, but
4272 : * let's not be totally oblivious to it. We can process an index as
4273 : * not-an-index, but not the other way around.
4274 : */
4275 : Assert(!is_index ||
4276 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4277 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4278 :
4279 : /*
4280 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4281 : * because it's a copy...)
4282 : */
4283 1626 : namestrcpy(&(relform->relname), newrelname);
4284 :
4285 1626 : CatalogTupleUpdate(relrelation, &otid, reltup);
4286 1626 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4287 :
4288 1626 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4289 : InvalidOid, is_internal);
4290 :
4291 1626 : heap_freetuple(reltup);
4292 1626 : table_close(relrelation, RowExclusiveLock);
4293 :
4294 : /*
4295 : * Also rename the associated type, if any.
4296 : */
4297 1626 : if (OidIsValid(targetrelation->rd_rel->reltype))
4298 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4299 : newrelname, namespaceId);
4300 :
4301 : /*
4302 : * Also rename the associated constraint, if any.
4303 : */
4304 1626 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4305 854 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4306 : {
4307 790 : Oid constraintId = get_index_constraint(myrelid);
4308 :
4309 790 : if (OidIsValid(constraintId))
4310 36 : RenameConstraintById(constraintId, newrelname);
4311 : }
4312 :
4313 : /*
4314 : * Close rel, but keep lock!
4315 : */
4316 1626 : relation_close(targetrelation, NoLock);
4317 1626 : }
4318 :
4319 : /*
4320 : * ResetRelRewrite - reset relrewrite
4321 : */
4322 : void
4323 578 : ResetRelRewrite(Oid myrelid)
4324 : {
4325 : Relation relrelation; /* for RELATION relation */
4326 : HeapTuple reltup;
4327 : Form_pg_class relform;
4328 :
4329 : /*
4330 : * Find relation's pg_class tuple.
4331 : */
4332 578 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4333 :
4334 578 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4335 578 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4336 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4337 578 : relform = (Form_pg_class) GETSTRUCT(reltup);
4338 :
4339 : /*
4340 : * Update pg_class tuple.
4341 : */
4342 578 : relform->relrewrite = InvalidOid;
4343 :
4344 578 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4345 :
4346 578 : heap_freetuple(reltup);
4347 578 : table_close(relrelation, RowExclusiveLock);
4348 578 : }
4349 :
4350 : /*
4351 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4352 : * any open reference to the target table besides the one just acquired by
4353 : * the calling command; this implies there's an open cursor or active plan.
4354 : * We need this check because our lock doesn't protect us against stomping
4355 : * on our own foot, only other people's feet!
4356 : *
4357 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4358 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4359 : * possibly be relaxed to only error out for certain types of alterations.
4360 : * But the use-case for allowing any of these things is not obvious, so we
4361 : * won't work hard at it for now.
4362 : *
4363 : * We also reject these commands if there are any pending AFTER trigger events
4364 : * for the rel. This is certainly necessary for the rewriting variants of
4365 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4366 : * events would try to fetch the wrong tuples. It might be overly cautious
4367 : * in other cases, but again it seems better to err on the side of paranoia.
4368 : *
4369 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4370 : * we are worried about active indexscans on the index. The trigger-event
4371 : * check can be skipped, since we are doing no damage to the parent table.
4372 : *
4373 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4374 : */
4375 : void
4376 162536 : CheckTableNotInUse(Relation rel, const char *stmt)
4377 : {
4378 : int expected_refcnt;
4379 :
4380 162536 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4381 162536 : if (rel->rd_refcnt != expected_refcnt)
4382 30 : ereport(ERROR,
4383 : (errcode(ERRCODE_OBJECT_IN_USE),
4384 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4385 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4386 : stmt, RelationGetRelationName(rel))));
4387 :
4388 162506 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4389 263538 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4390 130740 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4391 18 : ereport(ERROR,
4392 : (errcode(ERRCODE_OBJECT_IN_USE),
4393 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4394 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4395 : stmt, RelationGetRelationName(rel))));
4396 162488 : }
4397 :
4398 : /*
4399 : * CheckAlterTableIsSafe
4400 : * Verify that it's safe to allow ALTER TABLE on this relation.
4401 : *
4402 : * This consists of CheckTableNotInUse() plus a check that the relation
4403 : * isn't another session's temp table. We must split out the temp-table
4404 : * check because there are callers of CheckTableNotInUse() that don't want
4405 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4406 : * an orphaned temp schema.) Compare truncate_check_activity().
4407 : */
4408 : static void
4409 56928 : CheckAlterTableIsSafe(Relation rel)
4410 : {
4411 : /*
4412 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4413 : * manager is not going to cope if we need to change the table's contents.
4414 : * Even if we don't, there may be optimizations that assume temp tables
4415 : * aren't subject to such interference.
4416 : */
4417 56928 : if (RELATION_IS_OTHER_TEMP(rel))
4418 0 : ereport(ERROR,
4419 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4420 : errmsg("cannot alter temporary tables of other sessions")));
4421 :
4422 : /*
4423 : * Also check for active uses of the relation in the current transaction,
4424 : * including open scans and pending AFTER trigger events.
4425 : */
4426 56928 : CheckTableNotInUse(rel, "ALTER TABLE");
4427 56892 : }
4428 :
4429 : /*
4430 : * AlterTableLookupRelation
4431 : * Look up, and lock, the OID for the relation named by an alter table
4432 : * statement.
4433 : */
4434 : Oid
4435 30024 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4436 : {
4437 59962 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4438 30024 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4439 : RangeVarCallbackForAlterRelation,
4440 : stmt);
4441 : }
4442 :
4443 : /*
4444 : * AlterTable
4445 : * Execute ALTER TABLE, which can be a list of subcommands
4446 : *
4447 : * ALTER TABLE is performed in three phases:
4448 : * 1. Examine subcommands and perform pre-transformation checking.
4449 : * 2. Validate and transform subcommands, and update system catalogs.
4450 : * 3. Scan table(s) to check new constraints, and optionally recopy
4451 : * the data into new table(s).
4452 : * Phase 3 is not performed unless one or more of the subcommands requires
4453 : * it. The intention of this design is to allow multiple independent
4454 : * updates of the table schema to be performed with only one pass over the
4455 : * data.
4456 : *
4457 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4458 : * each table to be affected (there may be multiple affected tables if the
4459 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4460 : * validation of the subcommands. Because earlier subcommands may change
4461 : * the catalog state seen by later commands, there are limits to what can
4462 : * be done in this phase. Generally, this phase acquires table locks,
4463 : * checks permissions and relkind, and recurses to find child tables.
4464 : *
4465 : * ATRewriteCatalogs performs phase 2 for each affected table.
4466 : * Certain subcommands need to be performed before others to avoid
4467 : * unnecessary conflicts; for example, DROP COLUMN should come before
4468 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4469 : * lists, one for each logical "pass" of phase 2.
4470 : *
4471 : * ATRewriteTables performs phase 3 for those tables that need it.
4472 : *
4473 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4474 : * since phase 1 already does it. However, for certain subcommand types
4475 : * it is only possible to determine how to recurse at phase 2 time; for
4476 : * those cases, phase 1 sets the cmd->recurse flag.
4477 : *
4478 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4479 : * the whole operation; we don't have to do anything special to clean up.
4480 : *
4481 : * The caller must lock the relation, with an appropriate lock level
4482 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4483 : * or higher. We pass the lock level down
4484 : * so that we can apply it recursively to inherited tables. Note that the
4485 : * lock level we want as we recurse might well be higher than required for
4486 : * that specific subcommand. So we pass down the overall lock requirement,
4487 : * rather than reassess it at lower levels.
4488 : *
4489 : * The caller also provides a "context" which is to be passed back to
4490 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4491 : * Some of the fields therein, such as the relid, are used here as well.
4492 : */
4493 : void
4494 29800 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4495 : AlterTableUtilityContext *context)
4496 : {
4497 : Relation rel;
4498 :
4499 : /* Caller is required to provide an adequate lock. */
4500 29800 : rel = relation_open(context->relid, NoLock);
4501 :
4502 29800 : CheckAlterTableIsSafe(rel);
4503 :
4504 29782 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4505 26340 : }
4506 :
4507 : /*
4508 : * AlterTableInternal
4509 : *
4510 : * ALTER TABLE with target specified by OID
4511 : *
4512 : * We do not reject if the relation is already open, because it's quite
4513 : * likely that one or more layers of caller have it open. That means it
4514 : * is unsafe to use this entry point for alterations that could break
4515 : * existing query plans. On the assumption it's not used for such, we
4516 : * don't have to reject pending AFTER triggers, either.
4517 : *
4518 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4519 : * used for any subcommand types that require parse transformation or
4520 : * could generate subcommands that have to be passed to ProcessUtility.
4521 : */
4522 : void
4523 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4524 : {
4525 : Relation rel;
4526 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4527 :
4528 278 : rel = relation_open(relid, lockmode);
4529 :
4530 278 : EventTriggerAlterTableRelid(relid);
4531 :
4532 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4533 278 : }
4534 :
4535 : /*
4536 : * AlterTableGetLockLevel
4537 : *
4538 : * Sets the overall lock level required for the supplied list of subcommands.
4539 : * Policy for doing this set according to needs of AlterTable(), see
4540 : * comments there for overall explanation.
4541 : *
4542 : * Function is called before and after parsing, so it must give same
4543 : * answer each time it is called. Some subcommands are transformed
4544 : * into other subcommand types, so the transform must never be made to a
4545 : * lower lock level than previously assigned. All transforms are noted below.
4546 : *
4547 : * Since this is called before we lock the table we cannot use table metadata
4548 : * to influence the type of lock we acquire.
4549 : *
4550 : * There should be no lockmodes hardcoded into the subcommand functions. All
4551 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4552 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4553 : * and does not travel through this section of code and cannot be combined with
4554 : * any of the subcommands given here.
4555 : *
4556 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4557 : * so any changes that might affect SELECTs running on standbys need to use
4558 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4559 : * have a solution for that also.
4560 : *
4561 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4562 : * that takes a lock less than AccessExclusiveLock can change object definitions
4563 : * while pg_dump is running. Be careful to check that the appropriate data is
4564 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4565 : * otherwise we might end up with an inconsistent dump that can't restore.
4566 : */
4567 : LOCKMODE
4568 30302 : AlterTableGetLockLevel(List *cmds)
4569 : {
4570 : /*
4571 : * This only works if we read catalog tables using MVCC snapshots.
4572 : */
4573 : ListCell *lcmd;
4574 30302 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4575 :
4576 61746 : foreach(lcmd, cmds)
4577 : {
4578 31444 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4579 31444 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4580 :
4581 31444 : switch (cmd->subtype)
4582 : {
4583 : /*
4584 : * These subcommands rewrite the heap, so require full locks.
4585 : */
4586 3452 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4587 : * to SELECT */
4588 : case AT_SetAccessMethod: /* must rewrite heap */
4589 : case AT_SetTableSpace: /* must rewrite heap */
4590 : case AT_AlterColumnType: /* must rewrite heap */
4591 3452 : cmd_lockmode = AccessExclusiveLock;
4592 3452 : break;
4593 :
4594 : /*
4595 : * These subcommands may require addition of toast tables. If
4596 : * we add a toast table to a table currently being scanned, we
4597 : * might miss data added to the new toast table by concurrent
4598 : * insert transactions.
4599 : */
4600 218 : case AT_SetStorage: /* may add toast tables, see
4601 : * ATRewriteCatalogs() */
4602 218 : cmd_lockmode = AccessExclusiveLock;
4603 218 : break;
4604 :
4605 : /*
4606 : * Removing constraints can affect SELECTs that have been
4607 : * optimized assuming the constraint holds true. See also
4608 : * CloneFkReferenced.
4609 : */
4610 1128 : case AT_DropConstraint: /* as DROP INDEX */
4611 : case AT_DropNotNull: /* may change some SQL plans */
4612 1128 : cmd_lockmode = AccessExclusiveLock;
4613 1128 : break;
4614 :
4615 : /*
4616 : * Subcommands that may be visible to concurrent SELECTs
4617 : */
4618 1744 : case AT_DropColumn: /* change visible to SELECT */
4619 : case AT_AddColumnToView: /* CREATE VIEW */
4620 : case AT_DropOids: /* used to equiv to DropColumn */
4621 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4622 : case AT_EnableReplicaRule: /* may change SELECT rules */
4623 : case AT_EnableRule: /* may change SELECT rules */
4624 : case AT_DisableRule: /* may change SELECT rules */
4625 1744 : cmd_lockmode = AccessExclusiveLock;
4626 1744 : break;
4627 :
4628 : /*
4629 : * Changing owner may remove implicit SELECT privileges
4630 : */
4631 1922 : case AT_ChangeOwner: /* change visible to SELECT */
4632 1922 : cmd_lockmode = AccessExclusiveLock;
4633 1922 : break;
4634 :
4635 : /*
4636 : * Changing foreign table options may affect optimization.
4637 : */
4638 254 : case AT_GenericOptions:
4639 : case AT_AlterColumnGenericOptions:
4640 254 : cmd_lockmode = AccessExclusiveLock;
4641 254 : break;
4642 :
4643 : /*
4644 : * These subcommands affect write operations only.
4645 : */
4646 340 : case AT_EnableTrig:
4647 : case AT_EnableAlwaysTrig:
4648 : case AT_EnableReplicaTrig:
4649 : case AT_EnableTrigAll:
4650 : case AT_EnableTrigUser:
4651 : case AT_DisableTrig:
4652 : case AT_DisableTrigAll:
4653 : case AT_DisableTrigUser:
4654 340 : cmd_lockmode = ShareRowExclusiveLock;
4655 340 : break;
4656 :
4657 : /*
4658 : * These subcommands affect write operations only. XXX
4659 : * Theoretically, these could be ShareRowExclusiveLock.
4660 : */
4661 2634 : case AT_ColumnDefault:
4662 : case AT_CookedColumnDefault:
4663 : case AT_AlterConstraint:
4664 : case AT_AddIndex: /* from ADD CONSTRAINT */
4665 : case AT_AddIndexConstraint:
4666 : case AT_ReplicaIdentity:
4667 : case AT_SetNotNull:
4668 : case AT_EnableRowSecurity:
4669 : case AT_DisableRowSecurity:
4670 : case AT_ForceRowSecurity:
4671 : case AT_NoForceRowSecurity:
4672 : case AT_AddIdentity:
4673 : case AT_DropIdentity:
4674 : case AT_SetIdentity:
4675 : case AT_SetExpression:
4676 : case AT_DropExpression:
4677 : case AT_SetCompression:
4678 2634 : cmd_lockmode = AccessExclusiveLock;
4679 2634 : break;
4680 :
4681 14172 : case AT_AddConstraint:
4682 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4683 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4684 14172 : if (IsA(cmd->def, Constraint))
4685 : {
4686 14172 : Constraint *con = (Constraint *) cmd->def;
4687 :
4688 14172 : switch (con->contype)
4689 : {
4690 10776 : case CONSTR_EXCLUSION:
4691 : case CONSTR_PRIMARY:
4692 : case CONSTR_UNIQUE:
4693 :
4694 : /*
4695 : * Cases essentially the same as CREATE INDEX. We
4696 : * could reduce the lock strength to ShareLock if
4697 : * we can work out how to allow concurrent catalog
4698 : * updates. XXX Might be set down to
4699 : * ShareRowExclusiveLock but requires further
4700 : * analysis.
4701 : */
4702 10776 : cmd_lockmode = AccessExclusiveLock;
4703 10776 : break;
4704 2518 : case CONSTR_FOREIGN:
4705 :
4706 : /*
4707 : * We add triggers to both tables when we add a
4708 : * Foreign Key, so the lock level must be at least
4709 : * as strong as CREATE TRIGGER.
4710 : */
4711 2518 : cmd_lockmode = ShareRowExclusiveLock;
4712 2518 : break;
4713 :
4714 878 : default:
4715 878 : cmd_lockmode = AccessExclusiveLock;
4716 : }
4717 0 : }
4718 14172 : break;
4719 :
4720 : /*
4721 : * These subcommands affect inheritance behaviour. Queries
4722 : * started before us will continue to see the old inheritance
4723 : * behaviour, while queries started after we commit will see
4724 : * new behaviour. No need to prevent reads or writes to the
4725 : * subtable while we hook it up though. Changing the TupDesc
4726 : * may be a problem, so keep highest lock.
4727 : */
4728 472 : case AT_AddInherit:
4729 : case AT_DropInherit:
4730 472 : cmd_lockmode = AccessExclusiveLock;
4731 472 : break;
4732 :
4733 : /*
4734 : * These subcommands affect implicit row type conversion. They
4735 : * have affects similar to CREATE/DROP CAST on queries. don't
4736 : * provide for invalidating parse trees as a result of such
4737 : * changes, so we keep these at AccessExclusiveLock.
4738 : */
4739 72 : case AT_AddOf:
4740 : case AT_DropOf:
4741 72 : cmd_lockmode = AccessExclusiveLock;
4742 72 : break;
4743 :
4744 : /*
4745 : * Only used by CREATE OR REPLACE VIEW which must conflict
4746 : * with an SELECTs currently using the view.
4747 : */
4748 194 : case AT_ReplaceRelOptions:
4749 194 : cmd_lockmode = AccessExclusiveLock;
4750 194 : break;
4751 :
4752 : /*
4753 : * These subcommands affect general strategies for performance
4754 : * and maintenance, though don't change the semantic results
4755 : * from normal data reads and writes. Delaying an ALTER TABLE
4756 : * behind currently active writes only delays the point where
4757 : * the new strategy begins to take effect, so there is no
4758 : * benefit in waiting. In this case the minimum restriction
4759 : * applies: we don't currently allow concurrent catalog
4760 : * updates.
4761 : */
4762 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4763 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4764 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4765 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4766 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4767 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4768 234 : break;
4769 :
4770 112 : case AT_SetLogged:
4771 : case AT_SetUnLogged:
4772 112 : cmd_lockmode = AccessExclusiveLock;
4773 112 : break;
4774 :
4775 412 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4776 412 : cmd_lockmode = ShareUpdateExclusiveLock;
4777 412 : break;
4778 :
4779 : /*
4780 : * Rel options are more complex than first appears. Options
4781 : * are set here for tables, views and indexes; for historical
4782 : * reasons these can all be used with ALTER TABLE, so we can't
4783 : * decide between them using the basic grammar.
4784 : */
4785 752 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4786 : * getTables() */
4787 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4788 : * getTables() */
4789 752 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4790 752 : break;
4791 :
4792 2736 : case AT_AttachPartition:
4793 2736 : cmd_lockmode = ShareUpdateExclusiveLock;
4794 2736 : break;
4795 :
4796 576 : case AT_DetachPartition:
4797 576 : if (((PartitionCmd *) cmd->def)->concurrent)
4798 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4799 : else
4800 412 : cmd_lockmode = AccessExclusiveLock;
4801 576 : break;
4802 :
4803 20 : case AT_DetachPartitionFinalize:
4804 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4805 20 : break;
4806 :
4807 0 : default: /* oops */
4808 0 : elog(ERROR, "unrecognized alter table type: %d",
4809 : (int) cmd->subtype);
4810 : break;
4811 : }
4812 :
4813 : /*
4814 : * Take the greatest lockmode from any subcommand
4815 : */
4816 31444 : if (cmd_lockmode > lockmode)
4817 26170 : lockmode = cmd_lockmode;
4818 : }
4819 :
4820 30302 : return lockmode;
4821 : }
4822 :
4823 : /*
4824 : * ATController provides top level control over the phases.
4825 : *
4826 : * parsetree is passed in to allow it to be passed to event triggers
4827 : * when requested.
4828 : */
4829 : static void
4830 30060 : ATController(AlterTableStmt *parsetree,
4831 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4832 : AlterTableUtilityContext *context)
4833 : {
4834 30060 : List *wqueue = NIL;
4835 : ListCell *lcmd;
4836 :
4837 : /* Phase 1: preliminary examination of commands, create work queue */
4838 60882 : foreach(lcmd, cmds)
4839 : {
4840 31196 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4841 :
4842 31196 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4843 : }
4844 :
4845 : /* Close the relation, but keep lock until commit */
4846 29686 : relation_close(rel, NoLock);
4847 :
4848 : /* Phase 2: update system catalogs */
4849 29686 : ATRewriteCatalogs(&wqueue, lockmode, context);
4850 :
4851 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4852 26972 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4853 26618 : }
4854 :
4855 : /*
4856 : * ATPrepCmd
4857 : *
4858 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4859 : * recursion and permission checks.
4860 : *
4861 : * Caller must have acquired appropriate lock type on relation already.
4862 : * This lock should be held until commit.
4863 : */
4864 : static void
4865 32612 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4866 : bool recurse, bool recursing, LOCKMODE lockmode,
4867 : AlterTableUtilityContext *context)
4868 : {
4869 : AlteredTableInfo *tab;
4870 32612 : AlterTablePass pass = AT_PASS_UNSET;
4871 :
4872 : /* Find or create work queue entry for this table */
4873 32612 : tab = ATGetQueueEntry(wqueue, rel);
4874 :
4875 : /*
4876 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4877 : * partitions that are pending detach.
4878 : */
4879 32612 : if (rel->rd_rel->relispartition &&
4880 2772 : cmd->subtype != AT_DetachPartitionFinalize &&
4881 1386 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4882 2 : ereport(ERROR,
4883 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4884 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4885 : RelationGetRelationName(rel)),
4886 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4887 :
4888 : /*
4889 : * Copy the original subcommand for each table, so we can scribble on it.
4890 : * This avoids conflicts when different child tables need to make
4891 : * different parse transformations (for example, the same column may have
4892 : * different column numbers in different children).
4893 : */
4894 32610 : cmd = copyObject(cmd);
4895 :
4896 : /*
4897 : * Do permissions and relkind checking, recursion to child tables if
4898 : * needed, and any additional phase-1 processing needed. (But beware of
4899 : * adding any processing that looks at table details that another
4900 : * subcommand could change. In some cases we reject multiple subcommands
4901 : * that could try to change the same state in contrary ways.)
4902 : */
4903 32610 : switch (cmd->subtype)
4904 : {
4905 2068 : case AT_AddColumn: /* ADD COLUMN */
4906 2068 : ATSimplePermissions(cmd->subtype, rel,
4907 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4908 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4909 2068 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4910 : lockmode, context);
4911 : /* Recursion occurs during execution phase */
4912 2056 : pass = AT_PASS_ADD_COL;
4913 2056 : break;
4914 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4915 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4916 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4917 : lockmode, context);
4918 : /* Recursion occurs during execution phase */
4919 24 : pass = AT_PASS_ADD_COL;
4920 24 : break;
4921 620 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4922 :
4923 : /*
4924 : * We allow defaults on views so that INSERT into a view can have
4925 : * default-ish behavior. This works because the rewriter
4926 : * substitutes default values into INSERTs before it expands
4927 : * rules.
4928 : */
4929 620 : ATSimplePermissions(cmd->subtype, rel,
4930 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4931 : ATT_FOREIGN_TABLE);
4932 620 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4933 : /* No command-specific prep needed */
4934 620 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4935 620 : break;
4936 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4937 : /* This is currently used only in CREATE TABLE */
4938 : /* (so the permission check really isn't necessary) */
4939 80 : ATSimplePermissions(cmd->subtype, rel,
4940 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4941 : /* This command never recurses */
4942 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4943 80 : break;
4944 166 : case AT_AddIdentity:
4945 166 : ATSimplePermissions(cmd->subtype, rel,
4946 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4947 : ATT_FOREIGN_TABLE);
4948 : /* Set up recursion for phase 2; no other prep needed */
4949 166 : if (recurse)
4950 160 : cmd->recurse = true;
4951 166 : pass = AT_PASS_ADD_OTHERCONSTR;
4952 166 : break;
4953 62 : case AT_SetIdentity:
4954 62 : ATSimplePermissions(cmd->subtype, rel,
4955 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4956 : ATT_FOREIGN_TABLE);
4957 : /* Set up recursion for phase 2; no other prep needed */
4958 62 : if (recurse)
4959 56 : cmd->recurse = true;
4960 : /* This should run after AddIdentity, so do it in MISC pass */
4961 62 : pass = AT_PASS_MISC;
4962 62 : break;
4963 56 : case AT_DropIdentity:
4964 56 : ATSimplePermissions(cmd->subtype, rel,
4965 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4966 : ATT_FOREIGN_TABLE);
4967 : /* Set up recursion for phase 2; no other prep needed */
4968 56 : if (recurse)
4969 50 : cmd->recurse = true;
4970 56 : pass = AT_PASS_DROP;
4971 56 : break;
4972 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4973 268 : ATSimplePermissions(cmd->subtype, rel,
4974 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4975 : /* Set up recursion for phase 2; no other prep needed */
4976 262 : if (recurse)
4977 244 : cmd->recurse = true;
4978 262 : pass = AT_PASS_DROP;
4979 262 : break;
4980 390 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4981 390 : ATSimplePermissions(cmd->subtype, rel,
4982 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4983 : /* Set up recursion for phase 2; no other prep needed */
4984 384 : if (recurse)
4985 360 : cmd->recurse = true;
4986 384 : pass = AT_PASS_COL_ATTRS;
4987 384 : break;
4988 156 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4989 156 : ATSimplePermissions(cmd->subtype, rel,
4990 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4991 156 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4992 156 : pass = AT_PASS_SET_EXPRESSION;
4993 156 : break;
4994 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4995 86 : ATSimplePermissions(cmd->subtype, rel,
4996 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4997 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4998 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4999 62 : pass = AT_PASS_DROP;
5000 62 : break;
5001 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5002 164 : ATSimplePermissions(cmd->subtype, rel,
5003 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5004 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5005 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5006 : /* No command-specific prep needed */
5007 164 : pass = AT_PASS_MISC;
5008 164 : break;
5009 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5010 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5011 44 : ATSimplePermissions(cmd->subtype, rel,
5012 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5013 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5014 : /* This command never recurses */
5015 32 : pass = AT_PASS_MISC;
5016 32 : break;
5017 240 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5018 240 : ATSimplePermissions(cmd->subtype, rel,
5019 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5020 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5021 240 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5022 : /* No command-specific prep needed */
5023 240 : pass = AT_PASS_MISC;
5024 240 : break;
5025 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5026 68 : ATSimplePermissions(cmd->subtype, rel,
5027 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5028 : /* This command never recurses */
5029 : /* No command-specific prep needed */
5030 68 : pass = AT_PASS_MISC;
5031 68 : break;
5032 1650 : case AT_DropColumn: /* DROP COLUMN */
5033 1650 : ATSimplePermissions(cmd->subtype, rel,
5034 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5035 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5036 1644 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5037 : lockmode, context);
5038 : /* Recursion occurs during execution phase */
5039 1632 : pass = AT_PASS_DROP;
5040 1632 : break;
5041 0 : case AT_AddIndex: /* ADD INDEX */
5042 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5043 : /* This command never recurses */
5044 : /* No command-specific prep needed */
5045 0 : pass = AT_PASS_ADD_INDEX;
5046 0 : break;
5047 15124 : case AT_AddConstraint: /* ADD CONSTRAINT */
5048 15124 : ATSimplePermissions(cmd->subtype, rel,
5049 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5050 15124 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5051 15118 : if (recurse)
5052 : {
5053 : /* recurses at exec time; lock descendants and set flag */
5054 14736 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5055 14736 : cmd->recurse = true;
5056 : }
5057 15118 : pass = AT_PASS_ADD_CONSTR;
5058 15118 : break;
5059 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5060 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5061 : /* This command never recurses */
5062 : /* No command-specific prep needed */
5063 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5064 0 : break;
5065 822 : case AT_DropConstraint: /* DROP CONSTRAINT */
5066 822 : ATSimplePermissions(cmd->subtype, rel,
5067 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5068 822 : ATCheckPartitionsNotInUse(rel, lockmode);
5069 : /* Other recursion occurs during execution phase */
5070 : /* No command-specific prep needed except saving recurse flag */
5071 816 : if (recurse)
5072 780 : cmd->recurse = true;
5073 816 : pass = AT_PASS_DROP;
5074 816 : break;
5075 1270 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5076 1270 : ATSimplePermissions(cmd->subtype, rel,
5077 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5078 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5079 : /* See comments for ATPrepAlterColumnType */
5080 1270 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5081 : AT_PASS_UNSET, context);
5082 : Assert(cmd != NULL);
5083 : /* Performs own recursion */
5084 1264 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5085 : lockmode, context);
5086 1072 : pass = AT_PASS_ALTER_TYPE;
5087 1072 : break;
5088 172 : case AT_AlterColumnGenericOptions:
5089 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5090 : /* This command never recurses */
5091 : /* No command-specific prep needed */
5092 172 : pass = AT_PASS_MISC;
5093 172 : break;
5094 1898 : case AT_ChangeOwner: /* ALTER OWNER */
5095 : /* This command never recurses */
5096 : /* No command-specific prep needed */
5097 1898 : pass = AT_PASS_MISC;
5098 1898 : break;
5099 64 : case AT_ClusterOn: /* CLUSTER ON */
5100 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5101 64 : ATSimplePermissions(cmd->subtype, rel,
5102 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5103 : /* These commands never recurse */
5104 : /* No command-specific prep needed */
5105 64 : pass = AT_PASS_MISC;
5106 64 : break;
5107 112 : case AT_SetLogged: /* SET LOGGED */
5108 : case AT_SetUnLogged: /* SET UNLOGGED */
5109 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5110 100 : if (tab->chgPersistence)
5111 0 : ereport(ERROR,
5112 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5113 : errmsg("cannot change persistence setting twice")));
5114 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5115 88 : pass = AT_PASS_MISC;
5116 88 : break;
5117 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5118 6 : ATSimplePermissions(cmd->subtype, rel,
5119 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5120 6 : pass = AT_PASS_DROP;
5121 6 : break;
5122 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5123 128 : ATSimplePermissions(cmd->subtype, rel,
5124 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5125 :
5126 : /* check if another access method change was already requested */
5127 128 : if (tab->chgAccessMethod)
5128 18 : ereport(ERROR,
5129 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5130 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5131 :
5132 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5133 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5134 110 : break;
5135 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5136 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5137 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5138 : /* This command never recurses */
5139 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5140 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5141 158 : break;
5142 946 : case AT_SetRelOptions: /* SET (...) */
5143 : case AT_ResetRelOptions: /* RESET (...) */
5144 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5145 946 : ATSimplePermissions(cmd->subtype, rel,
5146 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5147 : ATT_MATVIEW | ATT_INDEX);
5148 : /* This command never recurses */
5149 : /* No command-specific prep needed */
5150 946 : pass = AT_PASS_MISC;
5151 946 : break;
5152 386 : case AT_AddInherit: /* INHERIT */
5153 386 : ATSimplePermissions(cmd->subtype, rel,
5154 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5155 : /* This command never recurses */
5156 386 : ATPrepAddInherit(rel);
5157 368 : pass = AT_PASS_MISC;
5158 368 : break;
5159 86 : case AT_DropInherit: /* NO INHERIT */
5160 86 : ATSimplePermissions(cmd->subtype, rel,
5161 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5162 : /* This command never recurses */
5163 : /* No command-specific prep needed */
5164 86 : pass = AT_PASS_MISC;
5165 86 : break;
5166 132 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5167 132 : ATSimplePermissions(cmd->subtype, rel,
5168 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5169 : /* Recursion occurs during execution phase */
5170 126 : if (recurse)
5171 126 : cmd->recurse = true;
5172 126 : pass = AT_PASS_MISC;
5173 126 : break;
5174 412 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5175 412 : ATSimplePermissions(cmd->subtype, rel,
5176 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5177 : /* Recursion occurs during execution phase */
5178 : /* No command-specific prep needed except saving recurse flag */
5179 412 : if (recurse)
5180 412 : cmd->recurse = true;
5181 412 : pass = AT_PASS_MISC;
5182 412 : break;
5183 490 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5184 490 : ATSimplePermissions(cmd->subtype, rel,
5185 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5186 490 : pass = AT_PASS_MISC;
5187 : /* This command never recurses */
5188 : /* No command-specific prep needed */
5189 490 : break;
5190 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5191 : case AT_EnableAlwaysTrig:
5192 : case AT_EnableReplicaTrig:
5193 : case AT_EnableTrigAll:
5194 : case AT_EnableTrigUser:
5195 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5196 : case AT_DisableTrigAll:
5197 : case AT_DisableTrigUser:
5198 340 : ATSimplePermissions(cmd->subtype, rel,
5199 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5200 : /* Set up recursion for phase 2; no other prep needed */
5201 340 : if (recurse)
5202 312 : cmd->recurse = true;
5203 340 : pass = AT_PASS_MISC;
5204 340 : break;
5205 544 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5206 : case AT_EnableAlwaysRule:
5207 : case AT_EnableReplicaRule:
5208 : case AT_DisableRule:
5209 : case AT_AddOf: /* OF */
5210 : case AT_DropOf: /* NOT OF */
5211 : case AT_EnableRowSecurity:
5212 : case AT_DisableRowSecurity:
5213 : case AT_ForceRowSecurity:
5214 : case AT_NoForceRowSecurity:
5215 544 : ATSimplePermissions(cmd->subtype, rel,
5216 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5217 : /* These commands never recurse */
5218 : /* No command-specific prep needed */
5219 544 : pass = AT_PASS_MISC;
5220 544 : break;
5221 58 : case AT_GenericOptions:
5222 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5223 : /* No command-specific prep needed */
5224 58 : pass = AT_PASS_MISC;
5225 58 : break;
5226 2724 : case AT_AttachPartition:
5227 2724 : ATSimplePermissions(cmd->subtype, rel,
5228 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5229 : /* No command-specific prep needed */
5230 2718 : pass = AT_PASS_MISC;
5231 2718 : break;
5232 576 : case AT_DetachPartition:
5233 576 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5234 : /* No command-specific prep needed */
5235 558 : pass = AT_PASS_MISC;
5236 558 : break;
5237 20 : case AT_DetachPartitionFinalize:
5238 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5239 : /* No command-specific prep needed */
5240 14 : pass = AT_PASS_MISC;
5241 14 : break;
5242 0 : default: /* oops */
5243 0 : elog(ERROR, "unrecognized alter table type: %d",
5244 : (int) cmd->subtype);
5245 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5246 : break;
5247 : }
5248 : Assert(pass > AT_PASS_UNSET);
5249 :
5250 : /* Add the subcommand to the appropriate list for phase 2 */
5251 32226 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5252 32226 : }
5253 :
5254 : /*
5255 : * ATRewriteCatalogs
5256 : *
5257 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5258 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5259 : * conflicts).
5260 : */
5261 : static void
5262 29686 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5263 : AlterTableUtilityContext *context)
5264 : {
5265 : ListCell *ltab;
5266 :
5267 : /*
5268 : * We process all the tables "in parallel", one pass at a time. This is
5269 : * needed because we may have to propagate work from one table to another
5270 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5271 : * re-adding of the foreign key constraint to the other table). Work can
5272 : * only be propagated into later passes, however.
5273 : */
5274 373526 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5275 : {
5276 : /* Go through each table that needs to be processed */
5277 704788 : foreach(ltab, *wqueue)
5278 : {
5279 360948 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5280 360948 : List *subcmds = tab->subcmds[pass];
5281 : ListCell *lcmd;
5282 :
5283 360948 : if (subcmds == NIL)
5284 309394 : continue;
5285 :
5286 : /*
5287 : * Open the relation and store it in tab. This allows subroutines
5288 : * close and reopen, if necessary. Appropriate lock was obtained
5289 : * by phase 1, needn't get it again.
5290 : */
5291 51554 : tab->rel = relation_open(tab->relid, NoLock);
5292 :
5293 104406 : foreach(lcmd, subcmds)
5294 55566 : ATExecCmd(wqueue, tab,
5295 55566 : lfirst_node(AlterTableCmd, lcmd),
5296 : lockmode, pass, context);
5297 :
5298 : /*
5299 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5300 : * (this is not done in ATExecAlterColumnType since it should be
5301 : * done only once if multiple columns of a table are altered).
5302 : */
5303 48840 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5304 1084 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5305 :
5306 48840 : if (tab->rel)
5307 : {
5308 48840 : relation_close(tab->rel, NoLock);
5309 48840 : tab->rel = NULL;
5310 : }
5311 : }
5312 : }
5313 :
5314 : /* Check to see if a toast table must be added. */
5315 57806 : foreach(ltab, *wqueue)
5316 : {
5317 30834 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5318 :
5319 : /*
5320 : * If the table is source table of ATTACH PARTITION command, we did
5321 : * not modify anything about it that will change its toasting
5322 : * requirement, so no need to check.
5323 : */
5324 30834 : if (((tab->relkind == RELKIND_RELATION ||
5325 5906 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5326 28936 : tab->partition_constraint == NULL) ||
5327 3884 : tab->relkind == RELKIND_MATVIEW)
5328 27000 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5329 : }
5330 26972 : }
5331 :
5332 : /*
5333 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5334 : */
5335 : static void
5336 55566 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5337 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5338 : AlterTableUtilityContext *context)
5339 : {
5340 55566 : ObjectAddress address = InvalidObjectAddress;
5341 55566 : Relation rel = tab->rel;
5342 :
5343 55566 : switch (cmd->subtype)
5344 : {
5345 2074 : case AT_AddColumn: /* ADD COLUMN */
5346 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5347 2074 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5348 2074 : cmd->recurse, false,
5349 : lockmode, cur_pass, context);
5350 1942 : break;
5351 584 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5352 584 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5353 518 : break;
5354 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5355 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5356 80 : break;
5357 166 : case AT_AddIdentity:
5358 166 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5359 : cur_pass, context);
5360 : Assert(cmd != NULL);
5361 154 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5362 106 : break;
5363 62 : case AT_SetIdentity:
5364 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5365 : cur_pass, context);
5366 : Assert(cmd != NULL);
5367 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5368 38 : break;
5369 56 : case AT_DropIdentity:
5370 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5371 38 : break;
5372 262 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5373 262 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5374 160 : break;
5375 384 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5376 384 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5377 384 : cmd->recurse, false, lockmode);
5378 348 : break;
5379 156 : case AT_SetExpression:
5380 156 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5381 126 : break;
5382 56 : case AT_DropExpression:
5383 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5384 32 : break;
5385 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5386 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5387 116 : break;
5388 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5389 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5390 26 : break;
5391 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5392 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5393 6 : break;
5394 240 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5395 240 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5396 228 : break;
5397 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5398 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5399 : lockmode);
5400 62 : break;
5401 1632 : case AT_DropColumn: /* DROP COLUMN */
5402 1632 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5403 1632 : cmd->behavior, cmd->recurse, false,
5404 1632 : cmd->missing_ok, lockmode,
5405 : NULL);
5406 1452 : break;
5407 1178 : case AT_AddIndex: /* ADD INDEX */
5408 1178 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5409 : lockmode);
5410 1008 : break;
5411 444 : case AT_ReAddIndex: /* ADD INDEX */
5412 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5413 : lockmode);
5414 444 : break;
5415 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5416 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5417 : true, lockmode);
5418 14 : break;
5419 26930 : case AT_AddConstraint: /* ADD CONSTRAINT */
5420 : /* Transform the command only during initial examination */
5421 26930 : if (cur_pass == AT_PASS_ADD_CONSTR)
5422 15088 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5423 15118 : cmd->recurse, lockmode,
5424 : cur_pass, context);
5425 : /* Depending on constraint type, might be no more work to do now */
5426 26900 : if (cmd != NULL)
5427 : address =
5428 11812 : ATExecAddConstraint(wqueue, tab, rel,
5429 11812 : (Constraint *) cmd->def,
5430 11812 : cmd->recurse, false, lockmode);
5431 26250 : break;
5432 326 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5433 : address =
5434 326 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5435 : true, true, lockmode);
5436 314 : break;
5437 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5438 : * constraint */
5439 : address =
5440 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5441 14 : ((AlterDomainStmt *) cmd->def)->def,
5442 : NULL);
5443 8 : break;
5444 78 : case AT_ReAddComment: /* Re-add existing comment */
5445 78 : address = CommentObject((CommentStmt *) cmd->def);
5446 78 : break;
5447 9540 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5448 9540 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5449 : lockmode);
5450 9528 : break;
5451 126 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5452 126 : address = ATExecAlterConstraint(rel, castNode(ATAlterConstraint,
5453 : cmd->def),
5454 126 : cmd->recurse, lockmode);
5455 114 : break;
5456 412 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5457 412 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5458 : false, lockmode);
5459 406 : break;
5460 816 : case AT_DropConstraint: /* DROP CONSTRAINT */
5461 816 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5462 816 : cmd->recurse,
5463 816 : cmd->missing_ok, lockmode);
5464 606 : break;
5465 1036 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5466 : /* parse transformation was done earlier */
5467 1036 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5468 994 : break;
5469 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5470 : address =
5471 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5472 172 : (List *) cmd->def, lockmode);
5473 166 : break;
5474 1898 : case AT_ChangeOwner: /* ALTER OWNER */
5475 1892 : ATExecChangeOwner(RelationGetRelid(rel),
5476 1898 : get_rolespec_oid(cmd->newowner, false),
5477 : false, lockmode);
5478 1880 : break;
5479 64 : case AT_ClusterOn: /* CLUSTER ON */
5480 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5481 58 : break;
5482 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5483 18 : ATExecDropCluster(rel, lockmode);
5484 12 : break;
5485 88 : case AT_SetLogged: /* SET LOGGED */
5486 : case AT_SetUnLogged: /* SET UNLOGGED */
5487 88 : break;
5488 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5489 : /* nothing to do here, oid columns don't exist anymore */
5490 6 : break;
5491 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5492 :
5493 : /*
5494 : * Only do this for partitioned tables, for which this is just a
5495 : * catalog change. Tables with storage are handled by Phase 3.
5496 : */
5497 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5498 50 : tab->chgAccessMethod)
5499 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5500 92 : break;
5501 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5502 :
5503 : /*
5504 : * Only do this for partitioned tables and indexes, for which this
5505 : * is just a catalog change. Other relation types which have
5506 : * storage are handled by Phase 3.
5507 : */
5508 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5509 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5510 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5511 :
5512 152 : break;
5513 946 : case AT_SetRelOptions: /* SET (...) */
5514 : case AT_ResetRelOptions: /* RESET (...) */
5515 : case AT_ReplaceRelOptions: /* replace entire option list */
5516 946 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5517 894 : break;
5518 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5519 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5520 : TRIGGER_FIRES_ON_ORIGIN, false,
5521 122 : cmd->recurse,
5522 : lockmode);
5523 122 : break;
5524 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5525 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5526 : TRIGGER_FIRES_ALWAYS, false,
5527 40 : cmd->recurse,
5528 : lockmode);
5529 40 : break;
5530 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5531 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5532 : TRIGGER_FIRES_ON_REPLICA, false,
5533 16 : cmd->recurse,
5534 : lockmode);
5535 16 : break;
5536 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5537 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5538 : TRIGGER_DISABLED, false,
5539 138 : cmd->recurse,
5540 : lockmode);
5541 138 : break;
5542 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5543 0 : ATExecEnableDisableTrigger(rel, NULL,
5544 : TRIGGER_FIRES_ON_ORIGIN, false,
5545 0 : cmd->recurse,
5546 : lockmode);
5547 0 : break;
5548 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5549 12 : ATExecEnableDisableTrigger(rel, NULL,
5550 : TRIGGER_DISABLED, false,
5551 12 : cmd->recurse,
5552 : lockmode);
5553 12 : break;
5554 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5555 0 : ATExecEnableDisableTrigger(rel, NULL,
5556 : TRIGGER_FIRES_ON_ORIGIN, true,
5557 0 : cmd->recurse,
5558 : lockmode);
5559 0 : break;
5560 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5561 12 : ATExecEnableDisableTrigger(rel, NULL,
5562 : TRIGGER_DISABLED, true,
5563 12 : cmd->recurse,
5564 : lockmode);
5565 12 : break;
5566 :
5567 8 : case AT_EnableRule: /* ENABLE RULE name */
5568 8 : ATExecEnableDisableRule(rel, cmd->name,
5569 : RULE_FIRES_ON_ORIGIN, lockmode);
5570 8 : break;
5571 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5572 0 : ATExecEnableDisableRule(rel, cmd->name,
5573 : RULE_FIRES_ALWAYS, lockmode);
5574 0 : break;
5575 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5576 6 : ATExecEnableDisableRule(rel, cmd->name,
5577 : RULE_FIRES_ON_REPLICA, lockmode);
5578 6 : break;
5579 32 : case AT_DisableRule: /* DISABLE RULE name */
5580 32 : ATExecEnableDisableRule(rel, cmd->name,
5581 : RULE_DISABLED, lockmode);
5582 32 : break;
5583 :
5584 368 : case AT_AddInherit:
5585 368 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5586 260 : break;
5587 86 : case AT_DropInherit:
5588 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5589 80 : break;
5590 66 : case AT_AddOf:
5591 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5592 30 : break;
5593 6 : case AT_DropOf:
5594 6 : ATExecDropOf(rel, lockmode);
5595 6 : break;
5596 508 : case AT_ReplicaIdentity:
5597 508 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5598 460 : break;
5599 290 : case AT_EnableRowSecurity:
5600 290 : ATExecSetRowSecurity(rel, true);
5601 290 : break;
5602 10 : case AT_DisableRowSecurity:
5603 10 : ATExecSetRowSecurity(rel, false);
5604 10 : break;
5605 94 : case AT_ForceRowSecurity:
5606 94 : ATExecForceNoForceRowSecurity(rel, true);
5607 94 : break;
5608 32 : case AT_NoForceRowSecurity:
5609 32 : ATExecForceNoForceRowSecurity(rel, false);
5610 32 : break;
5611 58 : case AT_GenericOptions:
5612 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5613 56 : break;
5614 2718 : case AT_AttachPartition:
5615 2718 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5616 : cur_pass, context);
5617 : Assert(cmd != NULL);
5618 2694 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5619 2302 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5620 : context);
5621 : else
5622 392 : address = ATExecAttachPartitionIdx(wqueue, rel,
5623 392 : ((PartitionCmd *) cmd->def)->name);
5624 2316 : break;
5625 558 : case AT_DetachPartition:
5626 558 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5627 : cur_pass, context);
5628 : Assert(cmd != NULL);
5629 : /* ATPrepCmd ensures it must be a table */
5630 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5631 558 : address = ATExecDetachPartition(wqueue, tab, rel,
5632 558 : ((PartitionCmd *) cmd->def)->name,
5633 558 : ((PartitionCmd *) cmd->def)->concurrent);
5634 428 : break;
5635 14 : case AT_DetachPartitionFinalize:
5636 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5637 14 : break;
5638 0 : default: /* oops */
5639 0 : elog(ERROR, "unrecognized alter table type: %d",
5640 : (int) cmd->subtype);
5641 : break;
5642 : }
5643 :
5644 : /*
5645 : * Report the subcommand to interested event triggers.
5646 : */
5647 52852 : if (cmd)
5648 37764 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5649 :
5650 : /*
5651 : * Bump the command counter to ensure the next subcommand in the sequence
5652 : * can see the changes so far
5653 : */
5654 52852 : CommandCounterIncrement();
5655 52852 : }
5656 :
5657 : /*
5658 : * ATParseTransformCmd: perform parse transformation for one subcommand
5659 : *
5660 : * Returns the transformed subcommand tree, if there is one, else NULL.
5661 : *
5662 : * The parser may hand back additional AlterTableCmd(s) and/or other
5663 : * utility statements, either before or after the original subcommand.
5664 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5665 : * AlteredTableInfo (they had better be for later passes than the current one).
5666 : * Utility statements that are supposed to happen before the AlterTableCmd
5667 : * are executed immediately. Those that are supposed to happen afterwards
5668 : * are added to the tab->afterStmts list to be done at the very end.
5669 : */
5670 : static AlterTableCmd *
5671 21846 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5672 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5673 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5674 : {
5675 21846 : AlterTableCmd *newcmd = NULL;
5676 21846 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5677 : List *beforeStmts;
5678 : List *afterStmts;
5679 : ListCell *lc;
5680 :
5681 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5682 21846 : atstmt->relation =
5683 21846 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5684 21846 : pstrdup(RelationGetRelationName(rel)),
5685 : -1);
5686 21846 : atstmt->relation->inh = recurse;
5687 21846 : atstmt->cmds = list_make1(cmd);
5688 21846 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5689 21846 : atstmt->missing_ok = false;
5690 :
5691 : /* Transform the AlterTableStmt */
5692 21846 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5693 : atstmt,
5694 : context->queryString,
5695 : &beforeStmts,
5696 : &afterStmts);
5697 :
5698 : /* Execute any statements that should happen before these subcommand(s) */
5699 22254 : foreach(lc, beforeStmts)
5700 : {
5701 486 : Node *stmt = (Node *) lfirst(lc);
5702 :
5703 486 : ProcessUtilityForAlterTable(stmt, context);
5704 474 : CommandCounterIncrement();
5705 : }
5706 :
5707 : /* Examine the transformed subcommands and schedule them appropriately */
5708 51014 : foreach(lc, atstmt->cmds)
5709 : {
5710 29246 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5711 : AlterTablePass pass;
5712 :
5713 : /*
5714 : * This switch need only cover the subcommand types that can be added
5715 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5716 : * executing the subcommand immediately, as a substitute for the
5717 : * original subcommand. (Note, however, that this does cause
5718 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5719 : * which is important for index and foreign key constraints.)
5720 : *
5721 : * We assume we needn't do any phase-1 checks for added subcommands.
5722 : */
5723 29246 : switch (cmd2->subtype)
5724 : {
5725 1202 : case AT_AddIndex:
5726 1202 : pass = AT_PASS_ADD_INDEX;
5727 1202 : break;
5728 9540 : case AT_AddIndexConstraint:
5729 9540 : pass = AT_PASS_ADD_INDEXCONSTR;
5730 9540 : break;
5731 11824 : case AT_AddConstraint:
5732 : /* Recursion occurs during execution phase */
5733 11824 : if (recurse)
5734 11774 : cmd2->recurse = true;
5735 11824 : switch (castNode(Constraint, cmd2->def)->contype)
5736 : {
5737 8458 : case CONSTR_NOTNULL:
5738 8458 : pass = AT_PASS_COL_ATTRS;
5739 8458 : break;
5740 0 : case CONSTR_PRIMARY:
5741 : case CONSTR_UNIQUE:
5742 : case CONSTR_EXCLUSION:
5743 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5744 0 : break;
5745 3366 : default:
5746 3366 : pass = AT_PASS_ADD_OTHERCONSTR;
5747 3366 : break;
5748 : }
5749 11824 : break;
5750 0 : case AT_AlterColumnGenericOptions:
5751 : /* This command never recurses */
5752 : /* No command-specific prep needed */
5753 0 : pass = AT_PASS_MISC;
5754 0 : break;
5755 6680 : default:
5756 6680 : pass = cur_pass;
5757 6680 : break;
5758 : }
5759 :
5760 29246 : if (pass < cur_pass)
5761 : {
5762 : /* Cannot schedule into a pass we already finished */
5763 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5764 : pass);
5765 : }
5766 29246 : else if (pass > cur_pass)
5767 : {
5768 : /* OK, queue it up for later */
5769 22566 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5770 : }
5771 : else
5772 : {
5773 : /*
5774 : * We should see at most one subcommand for the current pass,
5775 : * which is the transformed version of the original subcommand.
5776 : */
5777 6680 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5778 : {
5779 : /* Found the transformed version of our subcommand */
5780 6680 : newcmd = cmd2;
5781 : }
5782 : else
5783 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5784 : pass);
5785 : }
5786 : }
5787 :
5788 : /* Queue up any after-statements to happen at the end */
5789 21768 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5790 :
5791 21768 : return newcmd;
5792 : }
5793 :
5794 : /*
5795 : * ATRewriteTables: ALTER TABLE phase 3
5796 : */
5797 : static void
5798 26972 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5799 : AlterTableUtilityContext *context)
5800 : {
5801 : ListCell *ltab;
5802 :
5803 : /* Go through each table that needs to be checked or rewritten */
5804 57526 : foreach(ltab, *wqueue)
5805 : {
5806 30822 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5807 :
5808 : /* Relations without storage may be ignored here */
5809 30822 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5810 5608 : continue;
5811 :
5812 : /*
5813 : * If we change column data types, the operation has to be propagated
5814 : * to tables that use this table's rowtype as a column type.
5815 : * tab->newvals will also be non-NULL in the case where we're adding a
5816 : * column with a default. We choose to forbid that case as well,
5817 : * since composite types might eventually support defaults.
5818 : *
5819 : * (Eventually we'll probably need to check for composite type
5820 : * dependencies even when we're just scanning the table without a
5821 : * rewrite, but at the moment a composite type does not enforce any
5822 : * constraints, so it's not necessary/appropriate to enforce them just
5823 : * during ALTER.)
5824 : */
5825 25214 : if (tab->newvals != NIL || tab->rewrite > 0)
5826 : {
5827 : Relation rel;
5828 :
5829 1560 : rel = table_open(tab->relid, NoLock);
5830 1560 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5831 1542 : table_close(rel, NoLock);
5832 : }
5833 :
5834 : /*
5835 : * We only need to rewrite the table if at least one column needs to
5836 : * be recomputed, or we are changing its persistence or access method.
5837 : *
5838 : * There are two reasons for requiring a rewrite when changing
5839 : * persistence: on one hand, we need to ensure that the buffers
5840 : * belonging to each of the two relations are marked with or without
5841 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5842 : * and assigns a new relfilenumber, we automatically create or drop an
5843 : * init fork for the relation as appropriate.
5844 : */
5845 25196 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5846 880 : {
5847 : /* Build a temporary relation and copy data */
5848 : Relation OldHeap;
5849 : Oid OIDNewHeap;
5850 : Oid NewAccessMethod;
5851 : Oid NewTableSpace;
5852 : char persistence;
5853 :
5854 918 : OldHeap = table_open(tab->relid, NoLock);
5855 :
5856 : /*
5857 : * We don't support rewriting of system catalogs; there are too
5858 : * many corner cases and too little benefit. In particular this
5859 : * is certainly not going to work for mapped catalogs.
5860 : */
5861 918 : if (IsSystemRelation(OldHeap))
5862 0 : ereport(ERROR,
5863 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5864 : errmsg("cannot rewrite system relation \"%s\"",
5865 : RelationGetRelationName(OldHeap))));
5866 :
5867 918 : if (RelationIsUsedAsCatalogTable(OldHeap))
5868 2 : ereport(ERROR,
5869 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5870 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5871 : RelationGetRelationName(OldHeap))));
5872 :
5873 : /*
5874 : * Don't allow rewrite on temp tables of other backends ... their
5875 : * local buffer manager is not going to cope. (This is redundant
5876 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5877 : * check here too.)
5878 : */
5879 916 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5880 0 : ereport(ERROR,
5881 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5882 : errmsg("cannot rewrite temporary tables of other sessions")));
5883 :
5884 : /*
5885 : * Select destination tablespace (same as original unless user
5886 : * requested a change)
5887 : */
5888 916 : if (tab->newTableSpace)
5889 0 : NewTableSpace = tab->newTableSpace;
5890 : else
5891 916 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5892 :
5893 : /*
5894 : * Select destination access method (same as original unless user
5895 : * requested a change)
5896 : */
5897 916 : if (tab->chgAccessMethod)
5898 36 : NewAccessMethod = tab->newAccessMethod;
5899 : else
5900 880 : NewAccessMethod = OldHeap->rd_rel->relam;
5901 :
5902 : /*
5903 : * Select persistence of transient table (same as original unless
5904 : * user requested a change)
5905 : */
5906 916 : persistence = tab->chgPersistence ?
5907 864 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5908 :
5909 916 : table_close(OldHeap, NoLock);
5910 :
5911 : /*
5912 : * Fire off an Event Trigger now, before actually rewriting the
5913 : * table.
5914 : *
5915 : * We don't support Event Trigger for nested commands anywhere,
5916 : * here included, and parsetree is given NULL when coming from
5917 : * AlterTableInternal.
5918 : *
5919 : * And fire it only once.
5920 : */
5921 916 : if (parsetree)
5922 916 : EventTriggerTableRewrite((Node *) parsetree,
5923 : tab->relid,
5924 : tab->rewrite);
5925 :
5926 : /*
5927 : * Create transient table that will receive the modified data.
5928 : *
5929 : * Ensure it is marked correctly as logged or unlogged. We have
5930 : * to do this here so that buffers for the new relfilenumber will
5931 : * have the right persistence set, and at the same time ensure
5932 : * that the original filenumbers's buffers will get read in with
5933 : * the correct setting (i.e. the original one). Otherwise a
5934 : * rollback after the rewrite would possibly result with buffers
5935 : * for the original filenumbers having the wrong persistence
5936 : * setting.
5937 : *
5938 : * NB: This relies on swap_relation_files() also swapping the
5939 : * persistence. That wouldn't work for pg_class, but that can't be
5940 : * unlogged anyway.
5941 : */
5942 910 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5943 : persistence, lockmode);
5944 :
5945 : /*
5946 : * Copy the heap data into the new table with the desired
5947 : * modifications, and test the current data within the table
5948 : * against new constraints generated by ALTER TABLE commands.
5949 : */
5950 910 : ATRewriteTable(tab, OIDNewHeap);
5951 :
5952 : /*
5953 : * Swap the physical files of the old and new heaps, then rebuild
5954 : * indexes and discard the old heap. We can use RecentXmin for
5955 : * the table's new relfrozenxid because we rewrote all the tuples
5956 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5957 : * we never try to swap toast tables by content, since we have no
5958 : * interest in letting this code work on system catalogs.
5959 : */
5960 886 : finish_heap_swap(tab->relid, OIDNewHeap,
5961 : false, false, true,
5962 886 : !OidIsValid(tab->newTableSpace),
5963 : RecentXmin,
5964 : ReadNextMultiXactId(),
5965 : persistence);
5966 :
5967 880 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5968 : }
5969 24278 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5970 : {
5971 24 : if (tab->chgPersistence)
5972 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5973 : }
5974 : else
5975 : {
5976 : /*
5977 : * If required, test the current data within the table against new
5978 : * constraints generated by ALTER TABLE commands, but don't
5979 : * rebuild data.
5980 : */
5981 24254 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5982 21630 : tab->partition_constraint != NULL)
5983 4450 : ATRewriteTable(tab, InvalidOid);
5984 :
5985 : /*
5986 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5987 : * just do a block-by-block copy.
5988 : */
5989 24042 : if (tab->newTableSpace)
5990 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5991 : }
5992 :
5993 : /*
5994 : * Also change persistence of owned sequences, so that it matches the
5995 : * table persistence.
5996 : */
5997 24946 : if (tab->chgPersistence)
5998 : {
5999 76 : List *seqlist = getOwnedSequences(tab->relid);
6000 : ListCell *lc;
6001 :
6002 124 : foreach(lc, seqlist)
6003 : {
6004 48 : Oid seq_relid = lfirst_oid(lc);
6005 :
6006 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6007 : }
6008 : }
6009 : }
6010 :
6011 : /*
6012 : * Foreign key constraints are checked in a final pass, since (a) it's
6013 : * generally best to examine each one separately, and (b) it's at least
6014 : * theoretically possible that we have changed both relations of the
6015 : * foreign key, and we'd better have finished both rewrites before we try
6016 : * to read the tables.
6017 : */
6018 57042 : foreach(ltab, *wqueue)
6019 : {
6020 30424 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6021 30424 : Relation rel = NULL;
6022 : ListCell *lcon;
6023 :
6024 : /* Relations without storage may be ignored here too */
6025 30424 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6026 5528 : continue;
6027 :
6028 26660 : foreach(lcon, tab->constraints)
6029 : {
6030 1850 : NewConstraint *con = lfirst(lcon);
6031 :
6032 1850 : if (con->contype == CONSTR_FOREIGN)
6033 : {
6034 1150 : Constraint *fkconstraint = (Constraint *) con->qual;
6035 : Relation refrel;
6036 :
6037 1150 : if (rel == NULL)
6038 : {
6039 : /* Long since locked, no need for another */
6040 1132 : rel = table_open(tab->relid, NoLock);
6041 : }
6042 :
6043 1150 : refrel = table_open(con->refrelid, RowShareLock);
6044 :
6045 1150 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6046 : con->refindid,
6047 : con->conid,
6048 1150 : con->conwithperiod);
6049 :
6050 : /*
6051 : * No need to mark the constraint row as validated, we did
6052 : * that when we inserted the row earlier.
6053 : */
6054 :
6055 1064 : table_close(refrel, NoLock);
6056 : }
6057 : }
6058 :
6059 24810 : if (rel)
6060 1046 : table_close(rel, NoLock);
6061 : }
6062 :
6063 : /* Finally, run any afterStmts that were queued up */
6064 56912 : foreach(ltab, *wqueue)
6065 : {
6066 30294 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6067 : ListCell *lc;
6068 :
6069 30380 : foreach(lc, tab->afterStmts)
6070 : {
6071 86 : Node *stmt = (Node *) lfirst(lc);
6072 :
6073 86 : ProcessUtilityForAlterTable(stmt, context);
6074 86 : CommandCounterIncrement();
6075 : }
6076 : }
6077 26618 : }
6078 :
6079 : /*
6080 : * ATRewriteTable: scan or rewrite one table
6081 : *
6082 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6083 : * must already hold AccessExclusiveLock on it.
6084 : */
6085 : static void
6086 5360 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6087 : {
6088 : Relation oldrel;
6089 : Relation newrel;
6090 : TupleDesc oldTupDesc;
6091 : TupleDesc newTupDesc;
6092 5360 : bool needscan = false;
6093 : List *notnull_attrs;
6094 : int i;
6095 : ListCell *l;
6096 : EState *estate;
6097 : CommandId mycid;
6098 : BulkInsertState bistate;
6099 : int ti_options;
6100 5360 : ExprState *partqualstate = NULL;
6101 :
6102 : /*
6103 : * Open the relation(s). We have surely already locked the existing
6104 : * table.
6105 : */
6106 5360 : oldrel = table_open(tab->relid, NoLock);
6107 5360 : oldTupDesc = tab->oldDesc;
6108 5360 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6109 :
6110 5360 : if (OidIsValid(OIDNewHeap))
6111 : {
6112 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6113 : false));
6114 910 : newrel = table_open(OIDNewHeap, NoLock);
6115 : }
6116 : else
6117 4450 : newrel = NULL;
6118 :
6119 : /*
6120 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6121 : * is empty, so don't bother using it.
6122 : */
6123 5360 : if (newrel)
6124 : {
6125 910 : mycid = GetCurrentCommandId(true);
6126 910 : bistate = GetBulkInsertState();
6127 910 : ti_options = TABLE_INSERT_SKIP_FSM;
6128 : }
6129 : else
6130 : {
6131 : /* keep compiler quiet about using these uninitialized */
6132 4450 : mycid = 0;
6133 4450 : bistate = NULL;
6134 4450 : ti_options = 0;
6135 : }
6136 :
6137 : /*
6138 : * Generate the constraint and default execution states
6139 : */
6140 :
6141 5360 : estate = CreateExecutorState();
6142 :
6143 : /* Build the needed expression execution states */
6144 7318 : foreach(l, tab->constraints)
6145 : {
6146 1958 : NewConstraint *con = lfirst(l);
6147 :
6148 1958 : switch (con->contype)
6149 : {
6150 802 : case CONSTR_CHECK:
6151 802 : needscan = true;
6152 802 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, newrel ? newrel : oldrel, 1), estate);
6153 802 : break;
6154 1156 : case CONSTR_FOREIGN:
6155 : /* Nothing to do here */
6156 1156 : break;
6157 0 : default:
6158 0 : elog(ERROR, "unrecognized constraint type: %d",
6159 : (int) con->contype);
6160 : }
6161 : }
6162 :
6163 : /* Build expression execution states for partition check quals */
6164 5360 : if (tab->partition_constraint)
6165 : {
6166 1980 : needscan = true;
6167 1980 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6168 : }
6169 :
6170 6290 : foreach(l, tab->newvals)
6171 : {
6172 930 : NewColumnValue *ex = lfirst(l);
6173 :
6174 : /* expr already planned */
6175 930 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6176 : }
6177 :
6178 5360 : notnull_attrs = NIL;
6179 5360 : if (newrel || tab->verify_new_notnull)
6180 : {
6181 : /*
6182 : * If we are rebuilding the tuples OR if we added any new but not
6183 : * verified not-null constraints, check all not-null constraints. This
6184 : * is a bit of overkill but it minimizes risk of bugs.
6185 : */
6186 6444 : for (i = 0; i < newTupDesc->natts; i++)
6187 : {
6188 4716 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6189 :
6190 4716 : if (attr->attnotnull && !attr->attisdropped)
6191 1814 : notnull_attrs = lappend_int(notnull_attrs, i);
6192 : }
6193 1728 : if (notnull_attrs)
6194 1294 : needscan = true;
6195 : }
6196 :
6197 5360 : if (newrel || needscan)
6198 : {
6199 : ExprContext *econtext;
6200 : TupleTableSlot *oldslot;
6201 : TupleTableSlot *newslot;
6202 : TableScanDesc scan;
6203 : MemoryContext oldCxt;
6204 4418 : List *dropped_attrs = NIL;
6205 : ListCell *lc;
6206 : Snapshot snapshot;
6207 :
6208 4418 : if (newrel)
6209 910 : ereport(DEBUG1,
6210 : (errmsg_internal("rewriting table \"%s\"",
6211 : RelationGetRelationName(oldrel))));
6212 : else
6213 3508 : ereport(DEBUG1,
6214 : (errmsg_internal("verifying table \"%s\"",
6215 : RelationGetRelationName(oldrel))));
6216 :
6217 4418 : if (newrel)
6218 : {
6219 : /*
6220 : * All predicate locks on the tuples or pages are about to be made
6221 : * invalid, because we move tuples around. Promote them to
6222 : * relation locks.
6223 : */
6224 910 : TransferPredicateLocksToHeapRelation(oldrel);
6225 : }
6226 :
6227 4418 : econtext = GetPerTupleExprContext(estate);
6228 :
6229 : /*
6230 : * Create necessary tuple slots. When rewriting, two slots are needed,
6231 : * otherwise one suffices. In the case where one slot suffices, we
6232 : * need to use the new tuple descriptor, otherwise some constraints
6233 : * can't be evaluated. Note that even when the tuple layout is the
6234 : * same and no rewrite is required, the tupDescs might not be
6235 : * (consider ADD COLUMN without a default).
6236 : */
6237 4418 : if (tab->rewrite)
6238 : {
6239 : Assert(newrel != NULL);
6240 910 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6241 : table_slot_callbacks(oldrel));
6242 910 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6243 : table_slot_callbacks(newrel));
6244 :
6245 : /*
6246 : * Set all columns in the new slot to NULL initially, to ensure
6247 : * columns added as part of the rewrite are initialized to NULL.
6248 : * That is necessary as tab->newvals will not contain an
6249 : * expression for columns with a NULL default, e.g. when adding a
6250 : * column without a default together with a column with a default
6251 : * requiring an actual rewrite.
6252 : */
6253 910 : ExecStoreAllNullTuple(newslot);
6254 : }
6255 : else
6256 : {
6257 3508 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6258 : table_slot_callbacks(oldrel));
6259 3508 : newslot = NULL;
6260 : }
6261 :
6262 : /*
6263 : * Any attributes that are dropped according to the new tuple
6264 : * descriptor can be set to NULL. We precompute the list of dropped
6265 : * attributes to avoid needing to do so in the per-tuple loop.
6266 : */
6267 15604 : for (i = 0; i < newTupDesc->natts; i++)
6268 : {
6269 11186 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6270 784 : dropped_attrs = lappend_int(dropped_attrs, i);
6271 : }
6272 :
6273 : /*
6274 : * Scan through the rows, generating a new row if needed and then
6275 : * checking all the constraints.
6276 : */
6277 4418 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6278 4418 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6279 :
6280 : /*
6281 : * Switch to per-tuple memory context and reset it for each tuple
6282 : * produced, so we don't leak memory.
6283 : */
6284 4418 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6285 :
6286 769116 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6287 : {
6288 : TupleTableSlot *insertslot;
6289 :
6290 764934 : if (tab->rewrite > 0)
6291 : {
6292 : /* Extract data from old tuple */
6293 99714 : slot_getallattrs(oldslot);
6294 99714 : ExecClearTuple(newslot);
6295 :
6296 : /* copy attributes */
6297 99714 : memcpy(newslot->tts_values, oldslot->tts_values,
6298 99714 : sizeof(Datum) * oldslot->tts_nvalid);
6299 99714 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6300 99714 : sizeof(bool) * oldslot->tts_nvalid);
6301 :
6302 : /* Set dropped attributes to null in new tuple */
6303 99812 : foreach(lc, dropped_attrs)
6304 98 : newslot->tts_isnull[lfirst_int(lc)] = true;
6305 :
6306 : /*
6307 : * Constraints and GENERATED expressions might reference the
6308 : * tableoid column, so fill tts_tableOid with the desired
6309 : * value. (We must do this each time, because it gets
6310 : * overwritten with newrel's OID during storing.)
6311 : */
6312 99714 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6313 :
6314 : /*
6315 : * Process supplied expressions to replace selected columns.
6316 : *
6317 : * First, evaluate expressions whose inputs come from the old
6318 : * tuple.
6319 : */
6320 99714 : econtext->ecxt_scantuple = oldslot;
6321 :
6322 205344 : foreach(l, tab->newvals)
6323 : {
6324 105642 : NewColumnValue *ex = lfirst(l);
6325 :
6326 105642 : if (ex->is_generated)
6327 294 : continue;
6328 :
6329 105348 : newslot->tts_values[ex->attnum - 1]
6330 105336 : = ExecEvalExpr(ex->exprstate,
6331 : econtext,
6332 105348 : &newslot->tts_isnull[ex->attnum - 1]);
6333 : }
6334 :
6335 99702 : ExecStoreVirtualTuple(newslot);
6336 :
6337 : /*
6338 : * Now, evaluate any expressions whose inputs come from the
6339 : * new tuple. We assume these columns won't reference each
6340 : * other, so that there's no ordering dependency.
6341 : */
6342 99702 : econtext->ecxt_scantuple = newslot;
6343 :
6344 205332 : foreach(l, tab->newvals)
6345 : {
6346 105630 : NewColumnValue *ex = lfirst(l);
6347 :
6348 105630 : if (!ex->is_generated)
6349 105336 : continue;
6350 :
6351 294 : newslot->tts_values[ex->attnum - 1]
6352 294 : = ExecEvalExpr(ex->exprstate,
6353 : econtext,
6354 294 : &newslot->tts_isnull[ex->attnum - 1]);
6355 : }
6356 :
6357 99702 : insertslot = newslot;
6358 : }
6359 : else
6360 : {
6361 : /*
6362 : * If there's no rewrite, old and new table are guaranteed to
6363 : * have the same AM, so we can just use the old slot to verify
6364 : * new constraints etc.
6365 : */
6366 665220 : insertslot = oldslot;
6367 : }
6368 :
6369 : /* Now check any constraints on the possibly-changed tuple */
6370 764922 : econtext->ecxt_scantuple = insertslot;
6371 :
6372 3341216 : foreach(l, notnull_attrs)
6373 : {
6374 2576360 : int attn = lfirst_int(l);
6375 :
6376 2576360 : if (slot_attisnull(insertslot, attn + 1))
6377 : {
6378 66 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6379 :
6380 66 : ereport(ERROR,
6381 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6382 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6383 : NameStr(attr->attname),
6384 : RelationGetRelationName(oldrel)),
6385 : errtablecol(oldrel, attn + 1)));
6386 : }
6387 : }
6388 :
6389 772974 : foreach(l, tab->constraints)
6390 : {
6391 8202 : NewConstraint *con = lfirst(l);
6392 :
6393 8202 : switch (con->contype)
6394 : {
6395 8096 : case CONSTR_CHECK:
6396 8096 : if (!ExecCheck(con->qualstate, econtext))
6397 84 : ereport(ERROR,
6398 : (errcode(ERRCODE_CHECK_VIOLATION),
6399 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6400 : con->name,
6401 : RelationGetRelationName(oldrel)),
6402 : errtableconstraint(oldrel, con->name)));
6403 8012 : break;
6404 106 : case CONSTR_NOTNULL:
6405 : case CONSTR_FOREIGN:
6406 : /* Nothing to do here */
6407 106 : break;
6408 0 : default:
6409 0 : elog(ERROR, "unrecognized constraint type: %d",
6410 : (int) con->contype);
6411 : }
6412 : }
6413 :
6414 764772 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6415 : {
6416 74 : if (tab->validate_default)
6417 26 : ereport(ERROR,
6418 : (errcode(ERRCODE_CHECK_VIOLATION),
6419 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6420 : RelationGetRelationName(oldrel)),
6421 : errtable(oldrel)));
6422 : else
6423 48 : ereport(ERROR,
6424 : (errcode(ERRCODE_CHECK_VIOLATION),
6425 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6426 : RelationGetRelationName(oldrel)),
6427 : errtable(oldrel)));
6428 : }
6429 :
6430 : /* Write the tuple out to the new relation */
6431 764698 : if (newrel)
6432 99690 : table_tuple_insert(newrel, insertslot, mycid,
6433 : ti_options, bistate);
6434 :
6435 764698 : ResetExprContext(econtext);
6436 :
6437 764698 : CHECK_FOR_INTERRUPTS();
6438 : }
6439 :
6440 4182 : MemoryContextSwitchTo(oldCxt);
6441 4182 : table_endscan(scan);
6442 4182 : UnregisterSnapshot(snapshot);
6443 :
6444 4182 : ExecDropSingleTupleTableSlot(oldslot);
6445 4182 : if (newslot)
6446 886 : ExecDropSingleTupleTableSlot(newslot);
6447 : }
6448 :
6449 5124 : FreeExecutorState(estate);
6450 :
6451 5124 : table_close(oldrel, NoLock);
6452 5124 : if (newrel)
6453 : {
6454 886 : FreeBulkInsertState(bistate);
6455 :
6456 886 : table_finish_bulk_insert(newrel, ti_options);
6457 :
6458 886 : table_close(newrel, NoLock);
6459 : }
6460 5124 : }
6461 :
6462 : /*
6463 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6464 : */
6465 : static AlteredTableInfo *
6466 39236 : ATGetQueueEntry(List **wqueue, Relation rel)
6467 : {
6468 39236 : Oid relid = RelationGetRelid(rel);
6469 : AlteredTableInfo *tab;
6470 : ListCell *ltab;
6471 :
6472 48322 : foreach(ltab, *wqueue)
6473 : {
6474 14210 : tab = (AlteredTableInfo *) lfirst(ltab);
6475 14210 : if (tab->relid == relid)
6476 5124 : return tab;
6477 : }
6478 :
6479 : /*
6480 : * Not there, so add it. Note that we make a copy of the relation's
6481 : * existing descriptor before anything interesting can happen to it.
6482 : */
6483 34112 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6484 34112 : tab->relid = relid;
6485 34112 : tab->rel = NULL; /* set later */
6486 34112 : tab->relkind = rel->rd_rel->relkind;
6487 34112 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6488 34112 : tab->newAccessMethod = InvalidOid;
6489 34112 : tab->chgAccessMethod = false;
6490 34112 : tab->newTableSpace = InvalidOid;
6491 34112 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6492 34112 : tab->chgPersistence = false;
6493 :
6494 34112 : *wqueue = lappend(*wqueue, tab);
6495 :
6496 34112 : return tab;
6497 : }
6498 :
6499 : static const char *
6500 78 : alter_table_type_to_string(AlterTableType cmdtype)
6501 : {
6502 78 : switch (cmdtype)
6503 : {
6504 0 : case AT_AddColumn:
6505 : case AT_AddColumnToView:
6506 0 : return "ADD COLUMN";
6507 0 : case AT_ColumnDefault:
6508 : case AT_CookedColumnDefault:
6509 0 : return "ALTER COLUMN ... SET DEFAULT";
6510 6 : case AT_DropNotNull:
6511 6 : return "ALTER COLUMN ... DROP NOT NULL";
6512 6 : case AT_SetNotNull:
6513 6 : return "ALTER COLUMN ... SET NOT NULL";
6514 0 : case AT_SetExpression:
6515 0 : return "ALTER COLUMN ... SET EXPRESSION";
6516 0 : case AT_DropExpression:
6517 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6518 0 : case AT_SetStatistics:
6519 0 : return "ALTER COLUMN ... SET STATISTICS";
6520 12 : case AT_SetOptions:
6521 12 : return "ALTER COLUMN ... SET";
6522 0 : case AT_ResetOptions:
6523 0 : return "ALTER COLUMN ... RESET";
6524 0 : case AT_SetStorage:
6525 0 : return "ALTER COLUMN ... SET STORAGE";
6526 0 : case AT_SetCompression:
6527 0 : return "ALTER COLUMN ... SET COMPRESSION";
6528 6 : case AT_DropColumn:
6529 6 : return "DROP COLUMN";
6530 0 : case AT_AddIndex:
6531 : case AT_ReAddIndex:
6532 0 : return NULL; /* not real grammar */
6533 0 : case AT_AddConstraint:
6534 : case AT_ReAddConstraint:
6535 : case AT_ReAddDomainConstraint:
6536 : case AT_AddIndexConstraint:
6537 0 : return "ADD CONSTRAINT";
6538 6 : case AT_AlterConstraint:
6539 6 : return "ALTER CONSTRAINT";
6540 0 : case AT_ValidateConstraint:
6541 0 : return "VALIDATE CONSTRAINT";
6542 0 : case AT_DropConstraint:
6543 0 : return "DROP CONSTRAINT";
6544 0 : case AT_ReAddComment:
6545 0 : return NULL; /* not real grammar */
6546 0 : case AT_AlterColumnType:
6547 0 : return "ALTER COLUMN ... SET DATA TYPE";
6548 0 : case AT_AlterColumnGenericOptions:
6549 0 : return "ALTER COLUMN ... OPTIONS";
6550 0 : case AT_ChangeOwner:
6551 0 : return "OWNER TO";
6552 0 : case AT_ClusterOn:
6553 0 : return "CLUSTER ON";
6554 0 : case AT_DropCluster:
6555 0 : return "SET WITHOUT CLUSTER";
6556 0 : case AT_SetAccessMethod:
6557 0 : return "SET ACCESS METHOD";
6558 6 : case AT_SetLogged:
6559 6 : return "SET LOGGED";
6560 6 : case AT_SetUnLogged:
6561 6 : return "SET UNLOGGED";
6562 0 : case AT_DropOids:
6563 0 : return "SET WITHOUT OIDS";
6564 0 : case AT_SetTableSpace:
6565 0 : return "SET TABLESPACE";
6566 0 : case AT_SetRelOptions:
6567 0 : return "SET";
6568 0 : case AT_ResetRelOptions:
6569 0 : return "RESET";
6570 0 : case AT_ReplaceRelOptions:
6571 0 : return NULL; /* not real grammar */
6572 0 : case AT_EnableTrig:
6573 0 : return "ENABLE TRIGGER";
6574 0 : case AT_EnableAlwaysTrig:
6575 0 : return "ENABLE ALWAYS TRIGGER";
6576 0 : case AT_EnableReplicaTrig:
6577 0 : return "ENABLE REPLICA TRIGGER";
6578 0 : case AT_DisableTrig:
6579 0 : return "DISABLE TRIGGER";
6580 0 : case AT_EnableTrigAll:
6581 0 : return "ENABLE TRIGGER ALL";
6582 0 : case AT_DisableTrigAll:
6583 0 : return "DISABLE TRIGGER ALL";
6584 0 : case AT_EnableTrigUser:
6585 0 : return "ENABLE TRIGGER USER";
6586 0 : case AT_DisableTrigUser:
6587 0 : return "DISABLE TRIGGER USER";
6588 0 : case AT_EnableRule:
6589 0 : return "ENABLE RULE";
6590 0 : case AT_EnableAlwaysRule:
6591 0 : return "ENABLE ALWAYS RULE";
6592 0 : case AT_EnableReplicaRule:
6593 0 : return "ENABLE REPLICA RULE";
6594 0 : case AT_DisableRule:
6595 0 : return "DISABLE RULE";
6596 0 : case AT_AddInherit:
6597 0 : return "INHERIT";
6598 0 : case AT_DropInherit:
6599 0 : return "NO INHERIT";
6600 0 : case AT_AddOf:
6601 0 : return "OF";
6602 0 : case AT_DropOf:
6603 0 : return "NOT OF";
6604 0 : case AT_ReplicaIdentity:
6605 0 : return "REPLICA IDENTITY";
6606 0 : case AT_EnableRowSecurity:
6607 0 : return "ENABLE ROW SECURITY";
6608 0 : case AT_DisableRowSecurity:
6609 0 : return "DISABLE ROW SECURITY";
6610 0 : case AT_ForceRowSecurity:
6611 0 : return "FORCE ROW SECURITY";
6612 0 : case AT_NoForceRowSecurity:
6613 0 : return "NO FORCE ROW SECURITY";
6614 0 : case AT_GenericOptions:
6615 0 : return "OPTIONS";
6616 6 : case AT_AttachPartition:
6617 6 : return "ATTACH PARTITION";
6618 18 : case AT_DetachPartition:
6619 18 : return "DETACH PARTITION";
6620 6 : case AT_DetachPartitionFinalize:
6621 6 : return "DETACH PARTITION ... FINALIZE";
6622 0 : case AT_AddIdentity:
6623 0 : return "ALTER COLUMN ... ADD IDENTITY";
6624 0 : case AT_SetIdentity:
6625 0 : return "ALTER COLUMN ... SET";
6626 0 : case AT_DropIdentity:
6627 0 : return "ALTER COLUMN ... DROP IDENTITY";
6628 0 : case AT_ReAddStatistics:
6629 0 : return NULL; /* not real grammar */
6630 : }
6631 :
6632 0 : return NULL;
6633 : }
6634 :
6635 : /*
6636 : * ATSimplePermissions
6637 : *
6638 : * - Ensure that it is a relation (or possibly a view)
6639 : * - Ensure this user is the owner
6640 : * - Ensure that it is not a system table
6641 : */
6642 : static void
6643 35670 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6644 : {
6645 : int actual_target;
6646 :
6647 35670 : switch (rel->rd_rel->relkind)
6648 : {
6649 27940 : case RELKIND_RELATION:
6650 27940 : actual_target = ATT_TABLE;
6651 27940 : break;
6652 5458 : case RELKIND_PARTITIONED_TABLE:
6653 5458 : actual_target = ATT_PARTITIONED_TABLE;
6654 5458 : break;
6655 402 : case RELKIND_VIEW:
6656 402 : actual_target = ATT_VIEW;
6657 402 : break;
6658 46 : case RELKIND_MATVIEW:
6659 46 : actual_target = ATT_MATVIEW;
6660 46 : break;
6661 226 : case RELKIND_INDEX:
6662 226 : actual_target = ATT_INDEX;
6663 226 : break;
6664 434 : case RELKIND_PARTITIONED_INDEX:
6665 434 : actual_target = ATT_PARTITIONED_INDEX;
6666 434 : break;
6667 214 : case RELKIND_COMPOSITE_TYPE:
6668 214 : actual_target = ATT_COMPOSITE_TYPE;
6669 214 : break;
6670 926 : case RELKIND_FOREIGN_TABLE:
6671 926 : actual_target = ATT_FOREIGN_TABLE;
6672 926 : break;
6673 24 : case RELKIND_SEQUENCE:
6674 24 : actual_target = ATT_SEQUENCE;
6675 24 : break;
6676 0 : default:
6677 0 : actual_target = 0;
6678 0 : break;
6679 : }
6680 :
6681 : /* Wrong target type? */
6682 35670 : if ((actual_target & allowed_targets) == 0)
6683 : {
6684 78 : const char *action_str = alter_table_type_to_string(cmdtype);
6685 :
6686 78 : if (action_str)
6687 78 : ereport(ERROR,
6688 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6689 : /* translator: %s is a group of some SQL keywords */
6690 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6691 : action_str, RelationGetRelationName(rel)),
6692 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6693 : else
6694 : /* internal error? */
6695 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6696 : RelationGetRelationName(rel));
6697 : }
6698 :
6699 : /* Permissions checks */
6700 35592 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6701 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6702 12 : RelationGetRelationName(rel));
6703 :
6704 35580 : if (!allowSystemTableMods && IsSystemRelation(rel))
6705 0 : ereport(ERROR,
6706 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6707 : errmsg("permission denied: \"%s\" is a system catalog",
6708 : RelationGetRelationName(rel))));
6709 35580 : }
6710 :
6711 : /*
6712 : * ATSimpleRecursion
6713 : *
6714 : * Simple table recursion sufficient for most ALTER TABLE operations.
6715 : * All direct and indirect children are processed in an unspecified order.
6716 : * Note that if a child inherits from the original table via multiple
6717 : * inheritance paths, it will be visited just once.
6718 : */
6719 : static void
6720 1266 : ATSimpleRecursion(List **wqueue, Relation rel,
6721 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6722 : AlterTableUtilityContext *context)
6723 : {
6724 : /*
6725 : * Propagate to children, if desired and if there are (or might be) any
6726 : * children.
6727 : */
6728 1266 : if (recurse && rel->rd_rel->relhassubclass)
6729 : {
6730 78 : Oid relid = RelationGetRelid(rel);
6731 : ListCell *child;
6732 : List *children;
6733 :
6734 78 : children = find_all_inheritors(relid, lockmode, NULL);
6735 :
6736 : /*
6737 : * find_all_inheritors does the recursive search of the inheritance
6738 : * hierarchy, so all we have to do is process all of the relids in the
6739 : * list that it returns.
6740 : */
6741 342 : foreach(child, children)
6742 : {
6743 264 : Oid childrelid = lfirst_oid(child);
6744 : Relation childrel;
6745 :
6746 264 : if (childrelid == relid)
6747 78 : continue;
6748 : /* find_all_inheritors already got lock */
6749 186 : childrel = relation_open(childrelid, NoLock);
6750 186 : CheckAlterTableIsSafe(childrel);
6751 186 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6752 186 : relation_close(childrel, NoLock);
6753 : }
6754 : }
6755 1266 : }
6756 :
6757 : /*
6758 : * Obtain list of partitions of the given table, locking them all at the given
6759 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6760 : *
6761 : * This function is a no-op if the given relation is not a partitioned table;
6762 : * in particular, nothing is done if it's a legacy inheritance parent.
6763 : */
6764 : static void
6765 822 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6766 : {
6767 822 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6768 : {
6769 : List *inh;
6770 : ListCell *cell;
6771 :
6772 188 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6773 : /* first element is the parent rel; must ignore it */
6774 610 : for_each_from(cell, inh, 1)
6775 : {
6776 : Relation childrel;
6777 :
6778 : /* find_all_inheritors already got lock */
6779 428 : childrel = table_open(lfirst_oid(cell), NoLock);
6780 428 : CheckAlterTableIsSafe(childrel);
6781 422 : table_close(childrel, NoLock);
6782 : }
6783 182 : list_free(inh);
6784 : }
6785 816 : }
6786 :
6787 : /*
6788 : * ATTypedTableRecursion
6789 : *
6790 : * Propagate ALTER TYPE operations to the typed tables of that type.
6791 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6792 : * recursion to inheritance children of the typed tables.
6793 : */
6794 : static void
6795 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6796 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6797 : {
6798 : ListCell *child;
6799 : List *children;
6800 :
6801 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6802 :
6803 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6804 190 : RelationGetRelationName(rel),
6805 : cmd->behavior);
6806 :
6807 202 : foreach(child, children)
6808 : {
6809 30 : Oid childrelid = lfirst_oid(child);
6810 : Relation childrel;
6811 :
6812 30 : childrel = relation_open(childrelid, lockmode);
6813 30 : CheckAlterTableIsSafe(childrel);
6814 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6815 30 : relation_close(childrel, NoLock);
6816 : }
6817 172 : }
6818 :
6819 :
6820 : /*
6821 : * find_composite_type_dependencies
6822 : *
6823 : * Check to see if the type "typeOid" is being used as a column in some table
6824 : * (possibly nested several levels deep in composite types, arrays, etc!).
6825 : * Eventually, we'd like to propagate the check or rewrite operation
6826 : * into such tables, but for now, just error out if we find any.
6827 : *
6828 : * Caller should provide either the associated relation of a rowtype,
6829 : * or a type name (not both) for use in the error message, if any.
6830 : *
6831 : * Note that "typeOid" is not necessarily a composite type; it could also be
6832 : * another container type such as an array or range, or a domain over one of
6833 : * these things. The name of this function is therefore somewhat historical,
6834 : * but it's not worth changing.
6835 : *
6836 : * We assume that functions and views depending on the type are not reasons
6837 : * to reject the ALTER. (How safe is this really?)
6838 : */
6839 : void
6840 4176 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6841 : const char *origTypeName)
6842 : {
6843 : Relation depRel;
6844 : ScanKeyData key[2];
6845 : SysScanDesc depScan;
6846 : HeapTuple depTup;
6847 :
6848 : /* since this function recurses, it could be driven to stack overflow */
6849 4176 : check_stack_depth();
6850 :
6851 : /*
6852 : * We scan pg_depend to find those things that depend on the given type.
6853 : * (We assume we can ignore refobjsubid for a type.)
6854 : */
6855 4176 : depRel = table_open(DependRelationId, AccessShareLock);
6856 :
6857 4176 : ScanKeyInit(&key[0],
6858 : Anum_pg_depend_refclassid,
6859 : BTEqualStrategyNumber, F_OIDEQ,
6860 : ObjectIdGetDatum(TypeRelationId));
6861 4176 : ScanKeyInit(&key[1],
6862 : Anum_pg_depend_refobjid,
6863 : BTEqualStrategyNumber, F_OIDEQ,
6864 : ObjectIdGetDatum(typeOid));
6865 :
6866 4176 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6867 : NULL, 2, key);
6868 :
6869 6432 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6870 : {
6871 2376 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6872 : Relation rel;
6873 : TupleDesc tupleDesc;
6874 : Form_pg_attribute att;
6875 :
6876 : /* Check for directly dependent types */
6877 2376 : if (pg_depend->classid == TypeRelationId)
6878 : {
6879 : /*
6880 : * This must be an array, domain, or range containing the given
6881 : * type, so recursively check for uses of this type. Note that
6882 : * any error message will mention the original type not the
6883 : * container; this is intentional.
6884 : */
6885 2006 : find_composite_type_dependencies(pg_depend->objid,
6886 : origRelation, origTypeName);
6887 1982 : continue;
6888 : }
6889 :
6890 : /* Else, ignore dependees that aren't relations */
6891 370 : if (pg_depend->classid != RelationRelationId)
6892 122 : continue;
6893 :
6894 248 : rel = relation_open(pg_depend->objid, AccessShareLock);
6895 248 : tupleDesc = RelationGetDescr(rel);
6896 :
6897 : /*
6898 : * If objsubid identifies a specific column, refer to that in error
6899 : * messages. Otherwise, search to see if there's a user column of the
6900 : * type. (We assume system columns are never of interesting types.)
6901 : * The search is needed because an index containing an expression
6902 : * column of the target type will just be recorded as a whole-relation
6903 : * dependency. If we do not find a column of the type, the dependency
6904 : * must indicate that the type is transiently referenced in an index
6905 : * expression but not stored on disk, which we assume is OK, just as
6906 : * we do for references in views. (It could also be that the target
6907 : * type is embedded in some container type that is stored in an index
6908 : * column, but the previous recursion should catch such cases.)
6909 : */
6910 248 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6911 90 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6912 : else
6913 : {
6914 158 : att = NULL;
6915 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6916 : {
6917 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6918 254 : if (att->atttypid == typeOid && !att->attisdropped)
6919 6 : break;
6920 248 : att = NULL;
6921 : }
6922 158 : if (att == NULL)
6923 : {
6924 : /* No such column, so assume OK */
6925 152 : relation_close(rel, AccessShareLock);
6926 152 : continue;
6927 : }
6928 : }
6929 :
6930 : /*
6931 : * We definitely should reject if the relation has storage. If it's
6932 : * partitioned, then perhaps we don't have to reject: if there are
6933 : * partitions then we'll fail when we find one, else there is no
6934 : * stored data to worry about. However, it's possible that the type
6935 : * change would affect conclusions about whether the type is sortable
6936 : * or hashable and thus (if it's a partitioning column) break the
6937 : * partitioning rule. For now, reject for partitioned rels too.
6938 : */
6939 96 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6940 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6941 : {
6942 96 : if (origTypeName)
6943 30 : ereport(ERROR,
6944 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6945 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6946 : origTypeName,
6947 : RelationGetRelationName(rel),
6948 : NameStr(att->attname))));
6949 66 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6950 18 : ereport(ERROR,
6951 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6952 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6953 : RelationGetRelationName(origRelation),
6954 : RelationGetRelationName(rel),
6955 : NameStr(att->attname))));
6956 48 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6957 6 : ereport(ERROR,
6958 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6959 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6960 : RelationGetRelationName(origRelation),
6961 : RelationGetRelationName(rel),
6962 : NameStr(att->attname))));
6963 : else
6964 42 : ereport(ERROR,
6965 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6966 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6967 : RelationGetRelationName(origRelation),
6968 : RelationGetRelationName(rel),
6969 : NameStr(att->attname))));
6970 : }
6971 0 : else if (OidIsValid(rel->rd_rel->reltype))
6972 : {
6973 : /*
6974 : * A view or composite type itself isn't a problem, but we must
6975 : * recursively check for indirect dependencies via its rowtype.
6976 : */
6977 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6978 : origRelation, origTypeName);
6979 : }
6980 :
6981 0 : relation_close(rel, AccessShareLock);
6982 : }
6983 :
6984 4056 : systable_endscan(depScan);
6985 :
6986 4056 : relation_close(depRel, AccessShareLock);
6987 4056 : }
6988 :
6989 :
6990 : /*
6991 : * find_typed_table_dependencies
6992 : *
6993 : * Check to see if a composite type is being used as the type of a
6994 : * typed table. Abort if any are found and behavior is RESTRICT.
6995 : * Else return the list of tables.
6996 : */
6997 : static List *
6998 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6999 : {
7000 : Relation classRel;
7001 : ScanKeyData key[1];
7002 : TableScanDesc scan;
7003 : HeapTuple tuple;
7004 214 : List *result = NIL;
7005 :
7006 214 : classRel = table_open(RelationRelationId, AccessShareLock);
7007 :
7008 214 : ScanKeyInit(&key[0],
7009 : Anum_pg_class_reloftype,
7010 : BTEqualStrategyNumber, F_OIDEQ,
7011 : ObjectIdGetDatum(typeOid));
7012 :
7013 214 : scan = table_beginscan_catalog(classRel, 1, key);
7014 :
7015 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7016 : {
7017 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7018 :
7019 60 : if (behavior == DROP_RESTRICT)
7020 24 : ereport(ERROR,
7021 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7022 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7023 : typeName),
7024 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7025 : else
7026 36 : result = lappend_oid(result, classform->oid);
7027 : }
7028 :
7029 190 : table_endscan(scan);
7030 190 : table_close(classRel, AccessShareLock);
7031 :
7032 190 : return result;
7033 : }
7034 :
7035 :
7036 : /*
7037 : * check_of_type
7038 : *
7039 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7040 : * isn't suitable, throw an error. Currently, we require that the type
7041 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7042 : * would require handling a number of extra corner cases in the DDL commands.
7043 : * (Also, allowing domain-over-composite would open up a can of worms about
7044 : * whether and how the domain's constraints should apply to derived tables.)
7045 : */
7046 : void
7047 182 : check_of_type(HeapTuple typetuple)
7048 : {
7049 182 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7050 182 : bool typeOk = false;
7051 :
7052 182 : if (typ->typtype == TYPTYPE_COMPOSITE)
7053 : {
7054 : Relation typeRelation;
7055 :
7056 : Assert(OidIsValid(typ->typrelid));
7057 176 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7058 176 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7059 :
7060 : /*
7061 : * Close the parent rel, but keep our AccessShareLock on it until xact
7062 : * commit. That will prevent someone else from deleting or ALTERing
7063 : * the type before the typed table creation/conversion commits.
7064 : */
7065 176 : relation_close(typeRelation, NoLock);
7066 :
7067 176 : if (!typeOk)
7068 6 : ereport(ERROR,
7069 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7070 : errmsg("type %s is the row type of another table",
7071 : format_type_be(typ->oid)),
7072 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7073 : }
7074 : else
7075 6 : ereport(ERROR,
7076 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7077 : errmsg("type %s is not a composite type",
7078 : format_type_be(typ->oid))));
7079 170 : }
7080 :
7081 :
7082 : /*
7083 : * ALTER TABLE ADD COLUMN
7084 : *
7085 : * Adds an additional attribute to a relation making the assumption that
7086 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7087 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7088 : * AlterTableCmd's.
7089 : *
7090 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7091 : * have to decide at runtime whether to recurse or not depending on whether we
7092 : * actually add a column or merely merge with an existing column. (We can't
7093 : * check this in a static pre-pass because it won't handle multiple inheritance
7094 : * situations correctly.)
7095 : */
7096 : static void
7097 2092 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7098 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7099 : AlterTableUtilityContext *context)
7100 : {
7101 2092 : if (rel->rd_rel->reloftype && !recursing)
7102 6 : ereport(ERROR,
7103 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7104 : errmsg("cannot add column to typed table")));
7105 :
7106 2086 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7107 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7108 :
7109 2080 : if (recurse && !is_view)
7110 1980 : cmd->recurse = true;
7111 2080 : }
7112 :
7113 : /*
7114 : * Add a column to a table. The return value is the address of the
7115 : * new column in the parent relation.
7116 : *
7117 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7118 : * copy (but that happens only after we check for IF NOT EXISTS).
7119 : */
7120 : static ObjectAddress
7121 2722 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7122 : AlterTableCmd **cmd, bool recurse, bool recursing,
7123 : LOCKMODE lockmode, AlterTablePass cur_pass,
7124 : AlterTableUtilityContext *context)
7125 : {
7126 2722 : Oid myrelid = RelationGetRelid(rel);
7127 2722 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7128 2722 : bool if_not_exists = (*cmd)->missing_ok;
7129 : Relation pgclass,
7130 : attrdesc;
7131 : HeapTuple reltup;
7132 : Form_pg_class relform;
7133 : Form_pg_attribute attribute;
7134 : int newattnum;
7135 : char relkind;
7136 : Expr *defval;
7137 : List *children;
7138 : ListCell *child;
7139 : AlterTableCmd *childcmd;
7140 : ObjectAddress address;
7141 : TupleDesc tupdesc;
7142 :
7143 : /* since this function recurses, it could be driven to stack overflow */
7144 2722 : check_stack_depth();
7145 :
7146 : /* At top level, permission check was done in ATPrepCmd, else do it */
7147 2722 : if (recursing)
7148 648 : ATSimplePermissions((*cmd)->subtype, rel,
7149 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7150 :
7151 2722 : if (rel->rd_rel->relispartition && !recursing)
7152 12 : ereport(ERROR,
7153 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7154 : errmsg("cannot add column to a partition")));
7155 :
7156 2710 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7157 :
7158 : /*
7159 : * Are we adding the column to a recursion child? If so, check whether to
7160 : * merge with an existing definition for the column. If we do merge, we
7161 : * must not recurse. Children will already have the column, and recursing
7162 : * into them would mess up attinhcount.
7163 : */
7164 2710 : if (colDef->inhcount > 0)
7165 : {
7166 : HeapTuple tuple;
7167 :
7168 : /* Does child already have a column by this name? */
7169 648 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7170 648 : if (HeapTupleIsValid(tuple))
7171 : {
7172 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7173 : Oid ctypeId;
7174 : int32 ctypmod;
7175 : Oid ccollid;
7176 :
7177 : /* Child column must match on type, typmod, and collation */
7178 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7179 48 : if (ctypeId != childatt->atttypid ||
7180 48 : ctypmod != childatt->atttypmod)
7181 0 : ereport(ERROR,
7182 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7183 : errmsg("child table \"%s\" has different type for column \"%s\"",
7184 : RelationGetRelationName(rel), colDef->colname)));
7185 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7186 48 : if (ccollid != childatt->attcollation)
7187 0 : ereport(ERROR,
7188 : (errcode(ERRCODE_COLLATION_MISMATCH),
7189 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7190 : RelationGetRelationName(rel), colDef->colname),
7191 : errdetail("\"%s\" versus \"%s\"",
7192 : get_collation_name(ccollid),
7193 : get_collation_name(childatt->attcollation))));
7194 :
7195 : /* Bump the existing child att's inhcount */
7196 48 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7197 : &childatt->attinhcount))
7198 0 : ereport(ERROR,
7199 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7200 : errmsg("too many inheritance parents"));
7201 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7202 :
7203 48 : heap_freetuple(tuple);
7204 :
7205 : /* Inform the user about the merge */
7206 48 : ereport(NOTICE,
7207 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7208 : colDef->colname, RelationGetRelationName(rel))));
7209 :
7210 48 : table_close(attrdesc, RowExclusiveLock);
7211 :
7212 : /* Make the child column change visible */
7213 48 : CommandCounterIncrement();
7214 :
7215 48 : return InvalidObjectAddress;
7216 : }
7217 : }
7218 :
7219 : /* skip if the name already exists and if_not_exists is true */
7220 2662 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7221 : {
7222 54 : table_close(attrdesc, RowExclusiveLock);
7223 54 : return InvalidObjectAddress;
7224 : }
7225 :
7226 : /*
7227 : * Okay, we need to add the column, so go ahead and do parse
7228 : * transformation. This can result in queueing up, or even immediately
7229 : * executing, subsidiary operations (such as creation of unique indexes);
7230 : * so we mustn't do it until we have made the if_not_exists check.
7231 : *
7232 : * When recursing, the command was already transformed and we needn't do
7233 : * so again. Also, if context isn't given we can't transform. (That
7234 : * currently happens only for AT_AddColumnToView; we expect that view.c
7235 : * passed us a ColumnDef that doesn't need work.)
7236 : */
7237 2578 : if (context != NULL && !recursing)
7238 : {
7239 1954 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7240 : cur_pass, context);
7241 : Assert(*cmd != NULL);
7242 1948 : colDef = castNode(ColumnDef, (*cmd)->def);
7243 : }
7244 :
7245 : /*
7246 : * Regular inheritance children are independent enough not to inherit the
7247 : * identity column from parent hence cannot recursively add identity
7248 : * column if the table has inheritance children.
7249 : *
7250 : * Partitions, on the other hand, are integral part of a partitioned table
7251 : * and inherit identity column. Hence propagate identity column down the
7252 : * partition hierarchy.
7253 : */
7254 2572 : if (colDef->identity &&
7255 54 : recurse &&
7256 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7257 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7258 6 : ereport(ERROR,
7259 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7260 : errmsg("cannot recursively add identity column to table that has child tables")));
7261 :
7262 2566 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7263 :
7264 2566 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7265 2566 : if (!HeapTupleIsValid(reltup))
7266 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7267 2566 : relform = (Form_pg_class) GETSTRUCT(reltup);
7268 2566 : relkind = relform->relkind;
7269 :
7270 : /* Determine the new attribute's number */
7271 2566 : newattnum = relform->relnatts + 1;
7272 2566 : if (newattnum > MaxHeapAttributeNumber)
7273 0 : ereport(ERROR,
7274 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7275 : errmsg("tables can have at most %d columns",
7276 : MaxHeapAttributeNumber)));
7277 :
7278 : /*
7279 : * Construct new attribute's pg_attribute entry.
7280 : */
7281 2566 : tupdesc = BuildDescForRelation(list_make1(colDef));
7282 :
7283 2554 : attribute = TupleDescAttr(tupdesc, 0);
7284 :
7285 : /* Fix up attribute number */
7286 2554 : attribute->attnum = newattnum;
7287 :
7288 : /* make sure datatype is legal for a column */
7289 2554 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7290 2554 : list_make1_oid(rel->rd_rel->reltype),
7291 : 0);
7292 :
7293 2524 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7294 :
7295 2524 : table_close(attrdesc, RowExclusiveLock);
7296 :
7297 : /*
7298 : * Update pg_class tuple as appropriate
7299 : */
7300 2524 : relform->relnatts = newattnum;
7301 :
7302 2524 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7303 :
7304 2524 : heap_freetuple(reltup);
7305 :
7306 : /* Post creation hook for new attribute */
7307 2524 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7308 :
7309 2524 : table_close(pgclass, RowExclusiveLock);
7310 :
7311 : /* Make the attribute's catalog entry visible */
7312 2524 : CommandCounterIncrement();
7313 :
7314 : /*
7315 : * Store the DEFAULT, if any, in the catalogs
7316 : */
7317 2524 : if (colDef->raw_default)
7318 : {
7319 : RawColumnDefault *rawEnt;
7320 :
7321 786 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7322 786 : rawEnt->attnum = attribute->attnum;
7323 786 : rawEnt->raw_default = copyObject(colDef->raw_default);
7324 :
7325 : /*
7326 : * Attempt to skip a complete table rewrite by storing the specified
7327 : * DEFAULT value outside of the heap. This may be disabled inside
7328 : * AddRelationNewConstraints if the optimization cannot be applied.
7329 : */
7330 786 : rawEnt->missingMode = (colDef->generated != ATTRIBUTE_GENERATED_STORED);
7331 :
7332 786 : rawEnt->generated = colDef->generated;
7333 :
7334 : /*
7335 : * This function is intended for CREATE TABLE, so it processes a
7336 : * _list_ of defaults, but we just do one.
7337 : */
7338 786 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7339 : false, true, false, NULL);
7340 :
7341 : /* Make the additional catalog changes visible */
7342 762 : CommandCounterIncrement();
7343 :
7344 : /*
7345 : * Did the request for a missing value work? If not we'll have to do a
7346 : * rewrite
7347 : */
7348 762 : if (!rawEnt->missingMode)
7349 114 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7350 : }
7351 :
7352 : /*
7353 : * Tell Phase 3 to fill in the default expression, if there is one.
7354 : *
7355 : * If there is no default, Phase 3 doesn't have to do anything, because
7356 : * that effectively means that the default is NULL. The heap tuple access
7357 : * routines always check for attnum > # of attributes in tuple, and return
7358 : * NULL if so, so without any modification of the tuple data we will get
7359 : * the effect of NULL values in the new column.
7360 : *
7361 : * An exception occurs when the new column is of a domain type: the domain
7362 : * might have a not-null constraint, or a check constraint that indirectly
7363 : * rejects nulls. If there are any domain constraints then we construct
7364 : * an explicit NULL default value that will be passed through
7365 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7366 : * rewriting the table which we really don't have to do, but the present
7367 : * design of domain processing doesn't offer any simple way of checking
7368 : * the constraints more directly.)
7369 : *
7370 : * Note: we use build_column_default, and not just the cooked default
7371 : * returned by AddRelationNewConstraints, so that the right thing happens
7372 : * when a datatype's default applies.
7373 : *
7374 : * Note: it might seem that this should happen at the end of Phase 2, so
7375 : * that the effects of subsequent subcommands can be taken into account.
7376 : * It's intentional that we do it now, though. The new column should be
7377 : * filled according to what is said in the ADD COLUMN subcommand, so that
7378 : * the effects are the same as if this subcommand had been run by itself
7379 : * and the later subcommands had been issued in new ALTER TABLE commands.
7380 : *
7381 : * We can skip this entirely for relations without storage, since Phase 3
7382 : * is certainly not going to touch them. System attributes don't have
7383 : * interesting defaults, either.
7384 : */
7385 2500 : if (RELKIND_HAS_STORAGE(relkind))
7386 : {
7387 : /*
7388 : * For an identity column, we can't use build_column_default(),
7389 : * because the sequence ownership isn't set yet. So do it manually.
7390 : */
7391 2148 : if (colDef->identity)
7392 : {
7393 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7394 :
7395 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7396 42 : nve->typeId = attribute->atttypid;
7397 :
7398 42 : defval = (Expr *) nve;
7399 :
7400 : /* must do a rewrite for identity columns */
7401 42 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7402 : }
7403 : else
7404 2106 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7405 :
7406 2148 : if (!defval && DomainHasConstraints(attribute->atttypid))
7407 : {
7408 : Oid baseTypeId;
7409 : int32 baseTypeMod;
7410 : Oid baseTypeColl;
7411 :
7412 6 : baseTypeMod = attribute->atttypmod;
7413 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7414 6 : baseTypeColl = get_typcollation(baseTypeId);
7415 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7416 6 : defval = (Expr *) coerce_to_target_type(NULL,
7417 : (Node *) defval,
7418 : baseTypeId,
7419 : attribute->atttypid,
7420 : attribute->atttypmod,
7421 : COERCION_ASSIGNMENT,
7422 : COERCE_IMPLICIT_CAST,
7423 : -1);
7424 6 : if (defval == NULL) /* should not happen */
7425 0 : elog(ERROR, "failed to coerce base type to domain");
7426 : }
7427 :
7428 2148 : if (defval)
7429 : {
7430 : NewColumnValue *newval;
7431 :
7432 676 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7433 676 : newval->attnum = attribute->attnum;
7434 676 : newval->expr = expression_planner(defval);
7435 676 : newval->is_generated = (colDef->generated != '\0');
7436 :
7437 676 : tab->newvals = lappend(tab->newvals, newval);
7438 : }
7439 :
7440 2148 : if (DomainHasConstraints(attribute->atttypid))
7441 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7442 :
7443 2148 : if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7444 : {
7445 : /*
7446 : * If the new column is NOT NULL, and there is no missing value,
7447 : * tell Phase 3 it needs to check for NULLs.
7448 : */
7449 1682 : tab->verify_new_notnull |= colDef->is_not_null;
7450 : }
7451 : }
7452 :
7453 : /*
7454 : * Add needed dependency entries for the new column.
7455 : */
7456 2500 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7457 2500 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7458 :
7459 : /*
7460 : * Propagate to children as appropriate. Unlike most other ALTER
7461 : * routines, we have to do this one level of recursion at a time; we can't
7462 : * use find_all_inheritors to do it in one pass.
7463 : */
7464 : children =
7465 2500 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7466 :
7467 : /*
7468 : * If we are told not to recurse, there had better not be any child
7469 : * tables; else the addition would put them out of step.
7470 : */
7471 2500 : if (children && !recurse)
7472 12 : ereport(ERROR,
7473 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7474 : errmsg("column must be added to child tables too")));
7475 :
7476 : /* Children should see column as singly inherited */
7477 2488 : if (!recursing)
7478 : {
7479 1888 : childcmd = copyObject(*cmd);
7480 1888 : colDef = castNode(ColumnDef, childcmd->def);
7481 1888 : colDef->inhcount = 1;
7482 1888 : colDef->is_local = false;
7483 : }
7484 : else
7485 600 : childcmd = *cmd; /* no need to copy again */
7486 :
7487 3136 : foreach(child, children)
7488 : {
7489 648 : Oid childrelid = lfirst_oid(child);
7490 : Relation childrel;
7491 : AlteredTableInfo *childtab;
7492 :
7493 : /* find_inheritance_children already got lock */
7494 648 : childrel = table_open(childrelid, NoLock);
7495 648 : CheckAlterTableIsSafe(childrel);
7496 :
7497 : /* Find or create work queue entry for this table */
7498 648 : childtab = ATGetQueueEntry(wqueue, childrel);
7499 :
7500 : /* Recurse to child; return value is ignored */
7501 648 : ATExecAddColumn(wqueue, childtab, childrel,
7502 : &childcmd, recurse, true,
7503 : lockmode, cur_pass, context);
7504 :
7505 648 : table_close(childrel, NoLock);
7506 : }
7507 :
7508 2488 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7509 2488 : return address;
7510 : }
7511 :
7512 : /*
7513 : * If a new or renamed column will collide with the name of an existing
7514 : * column and if_not_exists is false then error out, else do nothing.
7515 : */
7516 : static bool
7517 3112 : check_for_column_name_collision(Relation rel, const char *colname,
7518 : bool if_not_exists)
7519 : {
7520 : HeapTuple attTuple;
7521 : int attnum;
7522 :
7523 : /*
7524 : * this test is deliberately not attisdropped-aware, since if one tries to
7525 : * add a column matching a dropped column name, it's gonna fail anyway.
7526 : */
7527 3112 : attTuple = SearchSysCache2(ATTNAME,
7528 : ObjectIdGetDatum(RelationGetRelid(rel)),
7529 : PointerGetDatum(colname));
7530 3112 : if (!HeapTupleIsValid(attTuple))
7531 3016 : return true;
7532 :
7533 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7534 96 : ReleaseSysCache(attTuple);
7535 :
7536 : /*
7537 : * We throw a different error message for conflicts with system column
7538 : * names, since they are normally not shown and the user might otherwise
7539 : * be confused about the reason for the conflict.
7540 : */
7541 96 : if (attnum <= 0)
7542 12 : ereport(ERROR,
7543 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7544 : errmsg("column name \"%s\" conflicts with a system column name",
7545 : colname)));
7546 : else
7547 : {
7548 84 : if (if_not_exists)
7549 : {
7550 54 : ereport(NOTICE,
7551 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7552 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7553 : colname, RelationGetRelationName(rel))));
7554 54 : return false;
7555 : }
7556 :
7557 30 : ereport(ERROR,
7558 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7559 : errmsg("column \"%s\" of relation \"%s\" already exists",
7560 : colname, RelationGetRelationName(rel))));
7561 : }
7562 :
7563 : return true;
7564 : }
7565 :
7566 : /*
7567 : * Install a column's dependency on its datatype.
7568 : */
7569 : static void
7570 3494 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7571 : {
7572 : ObjectAddress myself,
7573 : referenced;
7574 :
7575 3494 : myself.classId = RelationRelationId;
7576 3494 : myself.objectId = relid;
7577 3494 : myself.objectSubId = attnum;
7578 3494 : referenced.classId = TypeRelationId;
7579 3494 : referenced.objectId = typid;
7580 3494 : referenced.objectSubId = 0;
7581 3494 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7582 3494 : }
7583 :
7584 : /*
7585 : * Install a column's dependency on its collation.
7586 : */
7587 : static void
7588 3494 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7589 : {
7590 : ObjectAddress myself,
7591 : referenced;
7592 :
7593 : /* We know the default collation is pinned, so don't bother recording it */
7594 3494 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7595 : {
7596 18 : myself.classId = RelationRelationId;
7597 18 : myself.objectId = relid;
7598 18 : myself.objectSubId = attnum;
7599 18 : referenced.classId = CollationRelationId;
7600 18 : referenced.objectId = collid;
7601 18 : referenced.objectSubId = 0;
7602 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7603 : }
7604 3494 : }
7605 :
7606 : /*
7607 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7608 : *
7609 : * Return the address of the modified column. If the column was already
7610 : * nullable, InvalidObjectAddress is returned.
7611 : */
7612 : static ObjectAddress
7613 262 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7614 : LOCKMODE lockmode)
7615 : {
7616 : HeapTuple tuple;
7617 : HeapTuple conTup;
7618 : Form_pg_attribute attTup;
7619 : AttrNumber attnum;
7620 : Relation attr_rel;
7621 : ObjectAddress address;
7622 :
7623 : /*
7624 : * lookup the attribute
7625 : */
7626 262 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7627 :
7628 262 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7629 262 : if (!HeapTupleIsValid(tuple))
7630 18 : ereport(ERROR,
7631 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7632 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7633 : colName, RelationGetRelationName(rel))));
7634 244 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7635 244 : attnum = attTup->attnum;
7636 244 : ObjectAddressSubSet(address, RelationRelationId,
7637 : RelationGetRelid(rel), attnum);
7638 :
7639 : /* If the column is already nullable there's nothing to do. */
7640 244 : if (!attTup->attnotnull)
7641 : {
7642 6 : table_close(attr_rel, RowExclusiveLock);
7643 6 : return InvalidObjectAddress;
7644 : }
7645 :
7646 : /* Prevent them from altering a system attribute */
7647 238 : if (attnum <= 0)
7648 0 : ereport(ERROR,
7649 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7650 : errmsg("cannot alter system column \"%s\"",
7651 : colName)));
7652 :
7653 238 : if (attTup->attidentity)
7654 18 : ereport(ERROR,
7655 : (errcode(ERRCODE_SYNTAX_ERROR),
7656 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7657 : colName, RelationGetRelationName(rel))));
7658 :
7659 : /*
7660 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7661 : */
7662 220 : if (rel->rd_rel->relispartition)
7663 : {
7664 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7665 12 : Relation parent = table_open(parentId, AccessShareLock);
7666 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7667 : AttrNumber parent_attnum;
7668 :
7669 12 : parent_attnum = get_attnum(parentId, colName);
7670 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7671 12 : ereport(ERROR,
7672 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7673 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7674 : colName)));
7675 0 : table_close(parent, AccessShareLock);
7676 : }
7677 :
7678 : /*
7679 : * Find the constraint that makes this column NOT NULL, and drop it.
7680 : * dropconstraint_internal() resets attnotnull.
7681 : */
7682 208 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7683 208 : if (conTup == NULL)
7684 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7685 : colName, RelationGetRelationName(rel));
7686 :
7687 : /* The normal case: we have a pg_constraint row, remove it */
7688 208 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7689 : false, lockmode);
7690 154 : heap_freetuple(conTup);
7691 :
7692 154 : InvokeObjectPostAlterHook(RelationRelationId,
7693 : RelationGetRelid(rel), attnum);
7694 :
7695 154 : table_close(attr_rel, RowExclusiveLock);
7696 :
7697 154 : return address;
7698 : }
7699 :
7700 : /*
7701 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7702 : * to verify it.
7703 : *
7704 : * When called to alter an existing table, 'wqueue' must be given so that we
7705 : * can queue a check that existing tuples pass the constraint. When called
7706 : * from table creation, 'wqueue' should be passed as NULL.
7707 : */
7708 : static void
7709 23340 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7710 : LOCKMODE lockmode)
7711 : {
7712 : Form_pg_attribute attr;
7713 :
7714 23340 : CheckAlterTableIsSafe(rel);
7715 :
7716 : /*
7717 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7718 : * attribute.
7719 : */
7720 23340 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7721 23340 : if (attr->attisdropped)
7722 0 : return;
7723 :
7724 23340 : if (!attr->attnotnull)
7725 : {
7726 : Relation attr_rel;
7727 : HeapTuple tuple;
7728 :
7729 1196 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7730 :
7731 1196 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7732 1196 : if (!HeapTupleIsValid(tuple))
7733 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7734 : attnum, RelationGetRelid(rel));
7735 :
7736 1196 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7737 : Assert(!attr->attnotnull);
7738 1196 : attr->attnotnull = true;
7739 1196 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7740 :
7741 : /*
7742 : * If the nullness isn't already proven by validated constraints, have
7743 : * ALTER TABLE phase 3 test for it.
7744 : */
7745 1196 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7746 : {
7747 : AlteredTableInfo *tab;
7748 :
7749 1098 : tab = ATGetQueueEntry(wqueue, rel);
7750 1098 : tab->verify_new_notnull = true;
7751 : }
7752 :
7753 1196 : CommandCounterIncrement();
7754 :
7755 1196 : table_close(attr_rel, RowExclusiveLock);
7756 1196 : heap_freetuple(tuple);
7757 : }
7758 : }
7759 :
7760 : /*
7761 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7762 : *
7763 : * Add a not-null constraint to a single table and its children. Returns
7764 : * the address of the constraint added to the parent relation, if one gets
7765 : * added, or InvalidObjectAddress otherwise.
7766 : *
7767 : * We must recurse to child tables during execution, rather than using
7768 : * ALTER TABLE's normal prep-time recursion.
7769 : */
7770 : static ObjectAddress
7771 586 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7772 : bool recurse, bool recursing, LOCKMODE lockmode)
7773 : {
7774 : HeapTuple tuple;
7775 : AttrNumber attnum;
7776 : ObjectAddress address;
7777 : Constraint *constraint;
7778 : CookedConstraint *ccon;
7779 : List *cooked;
7780 586 : bool is_no_inherit = false;
7781 :
7782 : /* Guard against stack overflow due to overly deep inheritance tree. */
7783 586 : check_stack_depth();
7784 :
7785 : /* At top level, permission check was done in ATPrepCmd, else do it */
7786 586 : if (recursing)
7787 : {
7788 202 : ATSimplePermissions(AT_AddConstraint, rel,
7789 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7790 : Assert(conName != NULL);
7791 : }
7792 :
7793 586 : attnum = get_attnum(RelationGetRelid(rel), colName);
7794 586 : if (attnum == InvalidAttrNumber)
7795 18 : ereport(ERROR,
7796 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7797 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7798 : colName, RelationGetRelationName(rel))));
7799 :
7800 : /* Prevent them from altering a system attribute */
7801 568 : if (attnum <= 0)
7802 0 : ereport(ERROR,
7803 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7804 : errmsg("cannot alter system column \"%s\"",
7805 : colName)));
7806 :
7807 : /* TODO: see transformColumnDefinition() */
7808 568 : if (TupleDescAttr(RelationGetDescr(rel), attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
7809 6 : ereport(ERROR,
7810 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7811 : errmsg("not-null constraints are not supported on virtual generated columns"),
7812 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
7813 : colName, RelationGetRelationName(rel))));
7814 :
7815 : /* See if there's already a constraint */
7816 562 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7817 562 : if (HeapTupleIsValid(tuple))
7818 : {
7819 92 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7820 92 : bool changed = false;
7821 :
7822 : /*
7823 : * Don't let a NO INHERIT constraint be changed into inherit.
7824 : */
7825 92 : if (conForm->connoinherit && recurse)
7826 6 : ereport(ERROR,
7827 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7828 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7829 : NameStr(conForm->conname),
7830 : RelationGetRelationName(rel)));
7831 :
7832 : /*
7833 : * If we find an appropriate constraint, we're almost done, but just
7834 : * need to change some properties on it: if we're recursing, increment
7835 : * coninhcount; if not, set conislocal if not already set.
7836 : */
7837 86 : if (recursing)
7838 : {
7839 66 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7840 : &conForm->coninhcount))
7841 0 : ereport(ERROR,
7842 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7843 : errmsg("too many inheritance parents"));
7844 66 : changed = true;
7845 : }
7846 20 : else if (!conForm->conislocal)
7847 : {
7848 0 : conForm->conislocal = true;
7849 0 : changed = true;
7850 : }
7851 :
7852 86 : if (changed)
7853 : {
7854 : Relation constr_rel;
7855 :
7856 66 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7857 :
7858 66 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7859 66 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7860 66 : table_close(constr_rel, RowExclusiveLock);
7861 : }
7862 :
7863 86 : if (changed)
7864 66 : return address;
7865 : else
7866 20 : return InvalidObjectAddress;
7867 : }
7868 :
7869 : /*
7870 : * If we're asked not to recurse, and children exist, raise an error for
7871 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7872 : * specified.
7873 : */
7874 494 : if (!recurse &&
7875 24 : find_inheritance_children(RelationGetRelid(rel),
7876 : NoLock) != NIL)
7877 : {
7878 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7879 6 : ereport(ERROR,
7880 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7881 : errmsg("constraint must be added to child tables too"),
7882 : errhint("Do not specify the ONLY keyword."));
7883 : else
7884 12 : is_no_inherit = true;
7885 : }
7886 :
7887 : /*
7888 : * No constraint exists; we must add one. First determine a name to use,
7889 : * if we haven't already.
7890 : */
7891 464 : if (!recursing)
7892 : {
7893 : Assert(conName == NULL);
7894 328 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7895 : colName, "not_null",
7896 328 : RelationGetNamespace(rel),
7897 : NIL);
7898 : }
7899 :
7900 464 : constraint = makeNotNullConstraint(makeString(colName));
7901 464 : constraint->is_no_inherit = is_no_inherit;
7902 464 : constraint->conname = conName;
7903 :
7904 : /* and do it */
7905 464 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7906 464 : false, !recursing, false, NULL);
7907 464 : ccon = linitial(cooked);
7908 464 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7909 :
7910 464 : InvokeObjectPostAlterHook(RelationRelationId,
7911 : RelationGetRelid(rel), attnum);
7912 :
7913 : /* Mark pg_attribute.attnotnull for the column */
7914 464 : set_attnotnull(wqueue, rel, attnum, lockmode);
7915 :
7916 : /*
7917 : * Recurse to propagate the constraint to children that don't have one.
7918 : */
7919 464 : if (recurse)
7920 : {
7921 : List *children;
7922 :
7923 446 : children = find_inheritance_children(RelationGetRelid(rel),
7924 : lockmode);
7925 :
7926 1094 : foreach_oid(childoid, children)
7927 : {
7928 202 : Relation childrel = table_open(childoid, NoLock);
7929 :
7930 202 : CommandCounterIncrement();
7931 :
7932 202 : ATExecSetNotNull(wqueue, childrel, conName, colName,
7933 : recurse, true, lockmode);
7934 202 : table_close(childrel, NoLock);
7935 : }
7936 : }
7937 :
7938 464 : return address;
7939 : }
7940 :
7941 : /*
7942 : * NotNullImpliedByRelConstraints
7943 : * Does rel's existing constraints imply NOT NULL for the given attribute?
7944 : */
7945 : static bool
7946 1148 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7947 : {
7948 1148 : NullTest *nnulltest = makeNode(NullTest);
7949 :
7950 2296 : nnulltest->arg = (Expr *) makeVar(1,
7951 1148 : attr->attnum,
7952 : attr->atttypid,
7953 : attr->atttypmod,
7954 : attr->attcollation,
7955 : 0);
7956 1148 : nnulltest->nulltesttype = IS_NOT_NULL;
7957 :
7958 : /*
7959 : * argisrow = false is correct even for a composite column, because
7960 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7961 : * case, just IS DISTINCT FROM NULL.
7962 : */
7963 1148 : nnulltest->argisrow = false;
7964 1148 : nnulltest->location = -1;
7965 :
7966 1148 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7967 : {
7968 50 : ereport(DEBUG1,
7969 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7970 : RelationGetRelationName(rel), NameStr(attr->attname))));
7971 50 : return true;
7972 : }
7973 :
7974 1098 : return false;
7975 : }
7976 :
7977 : /*
7978 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7979 : *
7980 : * Return the address of the affected column.
7981 : */
7982 : static ObjectAddress
7983 584 : ATExecColumnDefault(Relation rel, const char *colName,
7984 : Node *newDefault, LOCKMODE lockmode)
7985 : {
7986 584 : TupleDesc tupdesc = RelationGetDescr(rel);
7987 : AttrNumber attnum;
7988 : ObjectAddress address;
7989 :
7990 : /*
7991 : * get the number of the attribute
7992 : */
7993 584 : attnum = get_attnum(RelationGetRelid(rel), colName);
7994 584 : if (attnum == InvalidAttrNumber)
7995 30 : ereport(ERROR,
7996 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7997 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7998 : colName, RelationGetRelationName(rel))));
7999 :
8000 : /* Prevent them from altering a system attribute */
8001 554 : if (attnum <= 0)
8002 0 : ereport(ERROR,
8003 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8004 : errmsg("cannot alter system column \"%s\"",
8005 : colName)));
8006 :
8007 554 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8008 18 : ereport(ERROR,
8009 : (errcode(ERRCODE_SYNTAX_ERROR),
8010 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8011 : colName, RelationGetRelationName(rel)),
8012 : /* translator: %s is an SQL ALTER command */
8013 : newDefault ? 0 : errhint("Use %s instead.",
8014 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8015 :
8016 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8017 12 : ereport(ERROR,
8018 : (errcode(ERRCODE_SYNTAX_ERROR),
8019 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8020 : colName, RelationGetRelationName(rel)),
8021 : newDefault ?
8022 : /* translator: %s is an SQL ALTER command */
8023 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8024 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8025 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8026 :
8027 : /*
8028 : * Remove any old default for the column. We use RESTRICT here for
8029 : * safety, but at present we do not expect anything to depend on the
8030 : * default.
8031 : *
8032 : * We treat removing the existing default as an internal operation when it
8033 : * is preparatory to adding a new default, but as a user-initiated
8034 : * operation when the user asked for a drop.
8035 : */
8036 524 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8037 : newDefault != NULL);
8038 :
8039 524 : if (newDefault)
8040 : {
8041 : /* SET DEFAULT */
8042 : RawColumnDefault *rawEnt;
8043 :
8044 350 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8045 350 : rawEnt->attnum = attnum;
8046 350 : rawEnt->raw_default = newDefault;
8047 350 : rawEnt->missingMode = false;
8048 350 : rawEnt->generated = '\0';
8049 :
8050 : /*
8051 : * This function is intended for CREATE TABLE, so it processes a
8052 : * _list_ of defaults, but we just do one.
8053 : */
8054 350 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8055 : false, true, false, NULL);
8056 : }
8057 :
8058 518 : ObjectAddressSubSet(address, RelationRelationId,
8059 : RelationGetRelid(rel), attnum);
8060 518 : return address;
8061 : }
8062 :
8063 : /*
8064 : * Add a pre-cooked default expression.
8065 : *
8066 : * Return the address of the affected column.
8067 : */
8068 : static ObjectAddress
8069 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8070 : Node *newDefault)
8071 : {
8072 : ObjectAddress address;
8073 :
8074 : /* We assume no checking is required */
8075 :
8076 : /*
8077 : * Remove any old default for the column. We use RESTRICT here for
8078 : * safety, but at present we do not expect anything to depend on the
8079 : * default. (In ordinary cases, there could not be a default in place
8080 : * anyway, but it's possible when combining LIKE with inheritance.)
8081 : */
8082 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8083 : true);
8084 :
8085 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8086 :
8087 80 : ObjectAddressSubSet(address, RelationRelationId,
8088 : RelationGetRelid(rel), attnum);
8089 80 : return address;
8090 : }
8091 :
8092 : /*
8093 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8094 : *
8095 : * Return the address of the affected column.
8096 : */
8097 : static ObjectAddress
8098 160 : ATExecAddIdentity(Relation rel, const char *colName,
8099 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8100 : {
8101 : Relation attrelation;
8102 : HeapTuple tuple;
8103 : Form_pg_attribute attTup;
8104 : AttrNumber attnum;
8105 : ObjectAddress address;
8106 160 : ColumnDef *cdef = castNode(ColumnDef, def);
8107 : bool ispartitioned;
8108 :
8109 160 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8110 160 : if (ispartitioned && !recurse)
8111 6 : ereport(ERROR,
8112 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8113 : errmsg("cannot add identity to a column of only the partitioned table"),
8114 : errhint("Do not specify the ONLY keyword.")));
8115 :
8116 154 : if (rel->rd_rel->relispartition && !recursing)
8117 12 : ereport(ERROR,
8118 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8119 : errmsg("cannot add identity to a column of a partition"));
8120 :
8121 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8122 :
8123 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8124 142 : if (!HeapTupleIsValid(tuple))
8125 0 : ereport(ERROR,
8126 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8127 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8128 : colName, RelationGetRelationName(rel))));
8129 142 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8130 142 : attnum = attTup->attnum;
8131 :
8132 : /* Can't alter a system attribute */
8133 142 : if (attnum <= 0)
8134 0 : ereport(ERROR,
8135 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8136 : errmsg("cannot alter system column \"%s\"",
8137 : colName)));
8138 :
8139 : /*
8140 : * Creating a column as identity implies NOT NULL, so adding the identity
8141 : * to an existing column that is not NOT NULL would create a state that
8142 : * cannot be reproduced without contortions.
8143 : */
8144 142 : if (!attTup->attnotnull)
8145 6 : ereport(ERROR,
8146 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8147 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8148 : colName, RelationGetRelationName(rel))));
8149 :
8150 136 : if (attTup->attidentity)
8151 18 : ereport(ERROR,
8152 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8153 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8154 : colName, RelationGetRelationName(rel))));
8155 :
8156 118 : if (attTup->atthasdef)
8157 6 : ereport(ERROR,
8158 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8159 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8160 : colName, RelationGetRelationName(rel))));
8161 :
8162 112 : attTup->attidentity = cdef->identity;
8163 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8164 :
8165 112 : InvokeObjectPostAlterHook(RelationRelationId,
8166 : RelationGetRelid(rel),
8167 : attTup->attnum);
8168 112 : ObjectAddressSubSet(address, RelationRelationId,
8169 : RelationGetRelid(rel), attnum);
8170 112 : heap_freetuple(tuple);
8171 :
8172 112 : table_close(attrelation, RowExclusiveLock);
8173 :
8174 : /*
8175 : * Recurse to propagate the identity column to partitions. Identity is
8176 : * not inherited in regular inheritance children.
8177 : */
8178 112 : if (recurse && ispartitioned)
8179 : {
8180 : List *children;
8181 : ListCell *lc;
8182 :
8183 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8184 :
8185 16 : foreach(lc, children)
8186 : {
8187 : Relation childrel;
8188 :
8189 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8190 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8191 6 : table_close(childrel, NoLock);
8192 : }
8193 : }
8194 :
8195 112 : return address;
8196 : }
8197 :
8198 : /*
8199 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8200 : *
8201 : * Return the address of the affected column.
8202 : */
8203 : static ObjectAddress
8204 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8205 : LOCKMODE lockmode, bool recurse, bool recursing)
8206 : {
8207 : ListCell *option;
8208 74 : DefElem *generatedEl = NULL;
8209 : HeapTuple tuple;
8210 : Form_pg_attribute attTup;
8211 : AttrNumber attnum;
8212 : Relation attrelation;
8213 : ObjectAddress address;
8214 : bool ispartitioned;
8215 :
8216 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8217 74 : if (ispartitioned && !recurse)
8218 6 : ereport(ERROR,
8219 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8220 : errmsg("cannot change identity column of only the partitioned table"),
8221 : errhint("Do not specify the ONLY keyword.")));
8222 :
8223 68 : if (rel->rd_rel->relispartition && !recursing)
8224 12 : ereport(ERROR,
8225 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8226 : errmsg("cannot change identity column of a partition"));
8227 :
8228 100 : foreach(option, castNode(List, def))
8229 : {
8230 44 : DefElem *defel = lfirst_node(DefElem, option);
8231 :
8232 44 : if (strcmp(defel->defname, "generated") == 0)
8233 : {
8234 44 : if (generatedEl)
8235 0 : ereport(ERROR,
8236 : (errcode(ERRCODE_SYNTAX_ERROR),
8237 : errmsg("conflicting or redundant options")));
8238 44 : generatedEl = defel;
8239 : }
8240 : else
8241 0 : elog(ERROR, "option \"%s\" not recognized",
8242 : defel->defname);
8243 : }
8244 :
8245 : /*
8246 : * Even if there is nothing to change here, we run all the checks. There
8247 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8248 : * there.
8249 : */
8250 :
8251 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8252 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8253 56 : if (!HeapTupleIsValid(tuple))
8254 0 : ereport(ERROR,
8255 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8256 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8257 : colName, RelationGetRelationName(rel))));
8258 :
8259 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8260 56 : attnum = attTup->attnum;
8261 :
8262 56 : if (attnum <= 0)
8263 0 : ereport(ERROR,
8264 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8265 : errmsg("cannot alter system column \"%s\"",
8266 : colName)));
8267 :
8268 56 : if (!attTup->attidentity)
8269 6 : ereport(ERROR,
8270 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8271 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8272 : colName, RelationGetRelationName(rel))));
8273 :
8274 50 : if (generatedEl)
8275 : {
8276 44 : attTup->attidentity = defGetInt32(generatedEl);
8277 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8278 :
8279 44 : InvokeObjectPostAlterHook(RelationRelationId,
8280 : RelationGetRelid(rel),
8281 : attTup->attnum);
8282 44 : ObjectAddressSubSet(address, RelationRelationId,
8283 : RelationGetRelid(rel), attnum);
8284 : }
8285 : else
8286 6 : address = InvalidObjectAddress;
8287 :
8288 50 : heap_freetuple(tuple);
8289 50 : table_close(attrelation, RowExclusiveLock);
8290 :
8291 : /*
8292 : * Recurse to propagate the identity change to partitions. Identity is not
8293 : * inherited in regular inheritance children.
8294 : */
8295 50 : if (generatedEl && recurse && ispartitioned)
8296 : {
8297 : List *children;
8298 : ListCell *lc;
8299 :
8300 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8301 :
8302 18 : foreach(lc, children)
8303 : {
8304 : Relation childrel;
8305 :
8306 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8307 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8308 12 : table_close(childrel, NoLock);
8309 : }
8310 : }
8311 :
8312 50 : return address;
8313 : }
8314 :
8315 : /*
8316 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8317 : *
8318 : * Return the address of the affected column.
8319 : */
8320 : static ObjectAddress
8321 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8322 : bool recurse, bool recursing)
8323 : {
8324 : HeapTuple tuple;
8325 : Form_pg_attribute attTup;
8326 : AttrNumber attnum;
8327 : Relation attrelation;
8328 : ObjectAddress address;
8329 : Oid seqid;
8330 : ObjectAddress seqaddress;
8331 : bool ispartitioned;
8332 :
8333 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8334 68 : if (ispartitioned && !recurse)
8335 6 : ereport(ERROR,
8336 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8337 : errmsg("cannot drop identity from a column of only the partitioned table"),
8338 : errhint("Do not specify the ONLY keyword.")));
8339 :
8340 62 : if (rel->rd_rel->relispartition && !recursing)
8341 6 : ereport(ERROR,
8342 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8343 : errmsg("cannot drop identity from a column of a partition"));
8344 :
8345 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8346 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8347 56 : if (!HeapTupleIsValid(tuple))
8348 0 : ereport(ERROR,
8349 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8350 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8351 : colName, RelationGetRelationName(rel))));
8352 :
8353 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8354 56 : attnum = attTup->attnum;
8355 :
8356 56 : if (attnum <= 0)
8357 0 : ereport(ERROR,
8358 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8359 : errmsg("cannot alter system column \"%s\"",
8360 : colName)));
8361 :
8362 56 : if (!attTup->attidentity)
8363 : {
8364 12 : if (!missing_ok)
8365 6 : ereport(ERROR,
8366 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8367 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8368 : colName, RelationGetRelationName(rel))));
8369 : else
8370 : {
8371 6 : ereport(NOTICE,
8372 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8373 : colName, RelationGetRelationName(rel))));
8374 6 : heap_freetuple(tuple);
8375 6 : table_close(attrelation, RowExclusiveLock);
8376 6 : return InvalidObjectAddress;
8377 : }
8378 : }
8379 :
8380 44 : attTup->attidentity = '\0';
8381 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8382 :
8383 44 : InvokeObjectPostAlterHook(RelationRelationId,
8384 : RelationGetRelid(rel),
8385 : attTup->attnum);
8386 44 : ObjectAddressSubSet(address, RelationRelationId,
8387 : RelationGetRelid(rel), attnum);
8388 44 : heap_freetuple(tuple);
8389 :
8390 44 : table_close(attrelation, RowExclusiveLock);
8391 :
8392 : /*
8393 : * Recurse to drop the identity from column in partitions. Identity is
8394 : * not inherited in regular inheritance children so ignore them.
8395 : */
8396 44 : if (recurse && ispartitioned)
8397 : {
8398 : List *children;
8399 : ListCell *lc;
8400 :
8401 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8402 :
8403 12 : foreach(lc, children)
8404 : {
8405 : Relation childrel;
8406 :
8407 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8408 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8409 6 : table_close(childrel, NoLock);
8410 : }
8411 : }
8412 :
8413 44 : if (!recursing)
8414 : {
8415 : /* drop the internal sequence */
8416 32 : seqid = getIdentitySequence(rel, attnum, false);
8417 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8418 : RelationRelationId, DEPENDENCY_INTERNAL);
8419 32 : CommandCounterIncrement();
8420 32 : seqaddress.classId = RelationRelationId;
8421 32 : seqaddress.objectId = seqid;
8422 32 : seqaddress.objectSubId = 0;
8423 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8424 : }
8425 :
8426 44 : return address;
8427 : }
8428 :
8429 : /*
8430 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8431 : *
8432 : * Return the address of the affected column.
8433 : */
8434 : static ObjectAddress
8435 156 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8436 : Node *newExpr, LOCKMODE lockmode)
8437 : {
8438 : HeapTuple tuple;
8439 : Form_pg_attribute attTup;
8440 : AttrNumber attnum;
8441 : char attgenerated;
8442 : bool rewrite;
8443 : Oid attrdefoid;
8444 : ObjectAddress address;
8445 : Expr *defval;
8446 : NewColumnValue *newval;
8447 : RawColumnDefault *rawEnt;
8448 :
8449 156 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8450 156 : if (!HeapTupleIsValid(tuple))
8451 0 : ereport(ERROR,
8452 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8453 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8454 : colName, RelationGetRelationName(rel))));
8455 :
8456 156 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8457 :
8458 156 : attnum = attTup->attnum;
8459 156 : if (attnum <= 0)
8460 0 : ereport(ERROR,
8461 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8462 : errmsg("cannot alter system column \"%s\"",
8463 : colName)));
8464 :
8465 156 : attgenerated = attTup->attgenerated;
8466 156 : if (!attgenerated)
8467 12 : ereport(ERROR,
8468 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8469 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8470 : colName, RelationGetRelationName(rel))));
8471 :
8472 : /*
8473 : * TODO: This could be done, just need to recheck any constraints
8474 : * afterwards.
8475 : */
8476 144 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8477 66 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8478 12 : ereport(ERROR,
8479 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8480 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8481 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8482 : colName, RelationGetRelationName(rel))));
8483 :
8484 : /*
8485 : * We need to prevent this because a change of expression could affect a
8486 : * row filter and inject expressions that are not permitted in a row
8487 : * filter. XXX We could try to have a more precise check to catch only
8488 : * publications with row filters, or even re-verify the row filter
8489 : * expressions.
8490 : */
8491 186 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8492 54 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8493 6 : ereport(ERROR,
8494 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8495 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8496 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8497 : colName, RelationGetRelationName(rel))));
8498 :
8499 126 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8500 :
8501 126 : ReleaseSysCache(tuple);
8502 :
8503 126 : if (rewrite)
8504 : {
8505 : /*
8506 : * Clear all the missing values if we're rewriting the table, since
8507 : * this renders them pointless.
8508 : */
8509 78 : RelationClearMissing(rel);
8510 :
8511 : /* make sure we don't conflict with later attribute modifications */
8512 78 : CommandCounterIncrement();
8513 :
8514 : /*
8515 : * Find everything that depends on the column (constraints, indexes,
8516 : * etc), and record enough information to let us recreate the objects
8517 : * after rewrite.
8518 : */
8519 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8520 : }
8521 :
8522 : /*
8523 : * Drop the dependency records of the GENERATED expression, in particular
8524 : * its INTERNAL dependency on the column, which would otherwise cause
8525 : * dependency.c to refuse to perform the deletion.
8526 : */
8527 126 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8528 126 : if (!OidIsValid(attrdefoid))
8529 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8530 : RelationGetRelid(rel), attnum);
8531 126 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8532 :
8533 : /* Make above changes visible */
8534 126 : CommandCounterIncrement();
8535 :
8536 : /*
8537 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8538 : * safety, but at present we do not expect anything to depend on the
8539 : * expression.
8540 : */
8541 126 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8542 : false, false);
8543 :
8544 : /* Prepare to store the new expression, in the catalogs */
8545 126 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8546 126 : rawEnt->attnum = attnum;
8547 126 : rawEnt->raw_default = newExpr;
8548 126 : rawEnt->missingMode = false;
8549 126 : rawEnt->generated = attgenerated;
8550 :
8551 : /* Store the generated expression */
8552 126 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8553 : false, true, false, NULL);
8554 :
8555 : /* Make above new expression visible */
8556 126 : CommandCounterIncrement();
8557 :
8558 126 : if (rewrite)
8559 : {
8560 : /* Prepare for table rewrite */
8561 78 : defval = (Expr *) build_column_default(rel, attnum);
8562 :
8563 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8564 78 : newval->attnum = attnum;
8565 78 : newval->expr = expression_planner(defval);
8566 78 : newval->is_generated = true;
8567 :
8568 78 : tab->newvals = lappend(tab->newvals, newval);
8569 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8570 : }
8571 :
8572 : /* Drop any pg_statistic entry for the column */
8573 126 : RemoveStatistics(RelationGetRelid(rel), attnum);
8574 :
8575 126 : InvokeObjectPostAlterHook(RelationRelationId,
8576 : RelationGetRelid(rel), attnum);
8577 :
8578 126 : ObjectAddressSubSet(address, RelationRelationId,
8579 : RelationGetRelid(rel), attnum);
8580 126 : return address;
8581 : }
8582 :
8583 : /*
8584 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8585 : */
8586 : static void
8587 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8588 : {
8589 : /*
8590 : * Reject ONLY if there are child tables. We could implement this, but it
8591 : * is a bit complicated. GENERATED clauses must be attached to the column
8592 : * definition and cannot be added later like DEFAULT, so if a child table
8593 : * has a generation expression that the parent does not have, the child
8594 : * column will necessarily be an attislocal column. So to implement ONLY
8595 : * here, we'd need extra code to update attislocal of the direct child
8596 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8597 : * resulting state can be properly dumped and restored.
8598 : */
8599 110 : if (!recurse &&
8600 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8601 12 : ereport(ERROR,
8602 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8603 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8604 :
8605 : /*
8606 : * Cannot drop generation expression from inherited columns.
8607 : */
8608 74 : if (!recursing)
8609 : {
8610 : HeapTuple tuple;
8611 : Form_pg_attribute attTup;
8612 :
8613 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8614 62 : if (!HeapTupleIsValid(tuple))
8615 0 : ereport(ERROR,
8616 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8617 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8618 : cmd->name, RelationGetRelationName(rel))));
8619 :
8620 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8621 :
8622 62 : if (attTup->attinhcount > 0)
8623 12 : ereport(ERROR,
8624 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8625 : errmsg("cannot drop generation expression from inherited column")));
8626 : }
8627 62 : }
8628 :
8629 : /*
8630 : * Return the address of the affected column.
8631 : */
8632 : static ObjectAddress
8633 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8634 : {
8635 : HeapTuple tuple;
8636 : Form_pg_attribute attTup;
8637 : AttrNumber attnum;
8638 : Relation attrelation;
8639 : Oid attrdefoid;
8640 : ObjectAddress address;
8641 :
8642 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8643 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8644 56 : if (!HeapTupleIsValid(tuple))
8645 0 : ereport(ERROR,
8646 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8647 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8648 : colName, RelationGetRelationName(rel))));
8649 :
8650 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8651 56 : attnum = attTup->attnum;
8652 :
8653 56 : if (attnum <= 0)
8654 0 : ereport(ERROR,
8655 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8656 : errmsg("cannot alter system column \"%s\"",
8657 : colName)));
8658 :
8659 : /*
8660 : * TODO: This could be done, but it would need a table rewrite to
8661 : * materialize the generated values. Note that for the time being, we
8662 : * still error with missing_ok, so that we don't silently leave the column
8663 : * as generated.
8664 : */
8665 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8666 12 : ereport(ERROR,
8667 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8668 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8669 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8670 : colName, RelationGetRelationName(rel))));
8671 :
8672 44 : if (!attTup->attgenerated)
8673 : {
8674 24 : if (!missing_ok)
8675 12 : ereport(ERROR,
8676 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8677 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8678 : colName, RelationGetRelationName(rel))));
8679 : else
8680 : {
8681 12 : ereport(NOTICE,
8682 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8683 : colName, RelationGetRelationName(rel))));
8684 12 : heap_freetuple(tuple);
8685 12 : table_close(attrelation, RowExclusiveLock);
8686 12 : return InvalidObjectAddress;
8687 : }
8688 : }
8689 :
8690 : /*
8691 : * Mark the column as no longer generated. (The atthasdef flag needs to
8692 : * get cleared too, but RemoveAttrDefault will handle that.)
8693 : */
8694 20 : attTup->attgenerated = '\0';
8695 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8696 :
8697 20 : InvokeObjectPostAlterHook(RelationRelationId,
8698 : RelationGetRelid(rel),
8699 : attnum);
8700 20 : heap_freetuple(tuple);
8701 :
8702 20 : table_close(attrelation, RowExclusiveLock);
8703 :
8704 : /*
8705 : * Drop the dependency records of the GENERATED expression, in particular
8706 : * its INTERNAL dependency on the column, which would otherwise cause
8707 : * dependency.c to refuse to perform the deletion.
8708 : */
8709 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8710 20 : if (!OidIsValid(attrdefoid))
8711 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8712 : RelationGetRelid(rel), attnum);
8713 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8714 :
8715 : /* Make above changes visible */
8716 20 : CommandCounterIncrement();
8717 :
8718 : /*
8719 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8720 : * safety, but at present we do not expect anything to depend on the
8721 : * default.
8722 : */
8723 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8724 : false, false);
8725 :
8726 20 : ObjectAddressSubSet(address, RelationRelationId,
8727 : RelationGetRelid(rel), attnum);
8728 20 : return address;
8729 : }
8730 :
8731 : /*
8732 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8733 : *
8734 : * Return value is the address of the modified column
8735 : */
8736 : static ObjectAddress
8737 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8738 : {
8739 164 : int newtarget = 0;
8740 : bool newtarget_default;
8741 : Relation attrelation;
8742 : HeapTuple tuple,
8743 : newtuple;
8744 : Form_pg_attribute attrtuple;
8745 : AttrNumber attnum;
8746 : ObjectAddress address;
8747 : Datum repl_val[Natts_pg_attribute];
8748 : bool repl_null[Natts_pg_attribute];
8749 : bool repl_repl[Natts_pg_attribute];
8750 :
8751 : /*
8752 : * We allow referencing columns by numbers only for indexes, since table
8753 : * column numbers could contain gaps if columns are later dropped.
8754 : */
8755 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8756 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8757 : !colName)
8758 0 : ereport(ERROR,
8759 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8760 : errmsg("cannot refer to non-index column by number")));
8761 :
8762 : /* -1 was used in previous versions for the default setting */
8763 164 : if (newValue && intVal(newValue) != -1)
8764 : {
8765 120 : newtarget = intVal(newValue);
8766 120 : newtarget_default = false;
8767 : }
8768 : else
8769 44 : newtarget_default = true;
8770 :
8771 164 : if (!newtarget_default)
8772 : {
8773 : /*
8774 : * Limit target to a sane range
8775 : */
8776 120 : if (newtarget < 0)
8777 : {
8778 0 : ereport(ERROR,
8779 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8780 : errmsg("statistics target %d is too low",
8781 : newtarget)));
8782 : }
8783 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8784 : {
8785 0 : newtarget = MAX_STATISTICS_TARGET;
8786 0 : ereport(WARNING,
8787 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8788 : errmsg("lowering statistics target to %d",
8789 : newtarget)));
8790 : }
8791 : }
8792 :
8793 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8794 :
8795 164 : if (colName)
8796 : {
8797 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8798 :
8799 100 : if (!HeapTupleIsValid(tuple))
8800 12 : ereport(ERROR,
8801 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8802 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8803 : colName, RelationGetRelationName(rel))));
8804 : }
8805 : else
8806 : {
8807 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8808 :
8809 64 : if (!HeapTupleIsValid(tuple))
8810 12 : ereport(ERROR,
8811 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8812 : errmsg("column number %d of relation \"%s\" does not exist",
8813 : colNum, RelationGetRelationName(rel))));
8814 : }
8815 :
8816 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8817 :
8818 140 : attnum = attrtuple->attnum;
8819 140 : if (attnum <= 0)
8820 0 : ereport(ERROR,
8821 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8822 : errmsg("cannot alter system column \"%s\"",
8823 : colName)));
8824 :
8825 : /*
8826 : * Prevent this as long as the ANALYZE code skips virtual generated
8827 : * columns.
8828 : */
8829 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8830 0 : ereport(ERROR,
8831 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8832 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8833 : colName)));
8834 :
8835 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8836 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8837 : {
8838 52 : if (attnum > rel->rd_index->indnkeyatts)
8839 6 : ereport(ERROR,
8840 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8841 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8842 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8843 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8844 18 : ereport(ERROR,
8845 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8846 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8847 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8848 : errhint("Alter statistics on table column instead.")));
8849 : }
8850 :
8851 : /* Build new tuple. */
8852 116 : memset(repl_null, false, sizeof(repl_null));
8853 116 : memset(repl_repl, false, sizeof(repl_repl));
8854 116 : if (!newtarget_default)
8855 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8856 : else
8857 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8858 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8859 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8860 : repl_val, repl_null, repl_repl);
8861 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8862 :
8863 116 : InvokeObjectPostAlterHook(RelationRelationId,
8864 : RelationGetRelid(rel),
8865 : attrtuple->attnum);
8866 116 : ObjectAddressSubSet(address, RelationRelationId,
8867 : RelationGetRelid(rel), attnum);
8868 :
8869 116 : heap_freetuple(newtuple);
8870 :
8871 116 : ReleaseSysCache(tuple);
8872 :
8873 116 : table_close(attrelation, RowExclusiveLock);
8874 :
8875 116 : return address;
8876 : }
8877 :
8878 : /*
8879 : * Return value is the address of the modified column
8880 : */
8881 : static ObjectAddress
8882 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8883 : bool isReset, LOCKMODE lockmode)
8884 : {
8885 : Relation attrelation;
8886 : HeapTuple tuple,
8887 : newtuple;
8888 : Form_pg_attribute attrtuple;
8889 : AttrNumber attnum;
8890 : Datum datum,
8891 : newOptions;
8892 : bool isnull;
8893 : ObjectAddress address;
8894 : Datum repl_val[Natts_pg_attribute];
8895 : bool repl_null[Natts_pg_attribute];
8896 : bool repl_repl[Natts_pg_attribute];
8897 :
8898 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8899 :
8900 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8901 :
8902 32 : if (!HeapTupleIsValid(tuple))
8903 0 : ereport(ERROR,
8904 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8905 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8906 : colName, RelationGetRelationName(rel))));
8907 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8908 :
8909 32 : attnum = attrtuple->attnum;
8910 32 : if (attnum <= 0)
8911 0 : ereport(ERROR,
8912 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8913 : errmsg("cannot alter system column \"%s\"",
8914 : colName)));
8915 :
8916 : /* Generate new proposed attoptions (text array) */
8917 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8918 : &isnull);
8919 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8920 : castNode(List, options), NULL, NULL,
8921 : false, isReset);
8922 : /* Validate new options */
8923 32 : (void) attribute_reloptions(newOptions, true);
8924 :
8925 : /* Build new tuple. */
8926 32 : memset(repl_null, false, sizeof(repl_null));
8927 32 : memset(repl_repl, false, sizeof(repl_repl));
8928 32 : if (newOptions != (Datum) 0)
8929 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8930 : else
8931 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8932 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8933 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8934 : repl_val, repl_null, repl_repl);
8935 :
8936 : /* Update system catalog. */
8937 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8938 :
8939 32 : InvokeObjectPostAlterHook(RelationRelationId,
8940 : RelationGetRelid(rel),
8941 : attrtuple->attnum);
8942 32 : ObjectAddressSubSet(address, RelationRelationId,
8943 : RelationGetRelid(rel), attnum);
8944 :
8945 32 : heap_freetuple(newtuple);
8946 :
8947 32 : ReleaseSysCache(tuple);
8948 :
8949 32 : table_close(attrelation, RowExclusiveLock);
8950 :
8951 32 : return address;
8952 : }
8953 :
8954 : /*
8955 : * Helper function for ATExecSetStorage and ATExecSetCompression
8956 : *
8957 : * Set the attstorage and/or attcompression fields for index columns
8958 : * associated with the specified table column.
8959 : */
8960 : static void
8961 290 : SetIndexStorageProperties(Relation rel, Relation attrelation,
8962 : AttrNumber attnum,
8963 : bool setstorage, char newstorage,
8964 : bool setcompression, char newcompression,
8965 : LOCKMODE lockmode)
8966 : {
8967 : ListCell *lc;
8968 :
8969 374 : foreach(lc, RelationGetIndexList(rel))
8970 : {
8971 84 : Oid indexoid = lfirst_oid(lc);
8972 : Relation indrel;
8973 84 : AttrNumber indattnum = 0;
8974 : HeapTuple tuple;
8975 :
8976 84 : indrel = index_open(indexoid, lockmode);
8977 :
8978 144 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
8979 : {
8980 90 : if (indrel->rd_index->indkey.values[i] == attnum)
8981 : {
8982 30 : indattnum = i + 1;
8983 30 : break;
8984 : }
8985 : }
8986 :
8987 84 : if (indattnum == 0)
8988 : {
8989 54 : index_close(indrel, lockmode);
8990 54 : continue;
8991 : }
8992 :
8993 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8994 :
8995 30 : if (HeapTupleIsValid(tuple))
8996 : {
8997 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8998 :
8999 30 : if (setstorage)
9000 24 : attrtuple->attstorage = newstorage;
9001 :
9002 30 : if (setcompression)
9003 6 : attrtuple->attcompression = newcompression;
9004 :
9005 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9006 :
9007 30 : InvokeObjectPostAlterHook(RelationRelationId,
9008 : RelationGetRelid(rel),
9009 : attrtuple->attnum);
9010 :
9011 30 : heap_freetuple(tuple);
9012 : }
9013 :
9014 30 : index_close(indrel, lockmode);
9015 : }
9016 290 : }
9017 :
9018 : /*
9019 : * ALTER TABLE ALTER COLUMN SET STORAGE
9020 : *
9021 : * Return value is the address of the modified column
9022 : */
9023 : static ObjectAddress
9024 240 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9025 : {
9026 : Relation attrelation;
9027 : HeapTuple tuple;
9028 : Form_pg_attribute attrtuple;
9029 : AttrNumber attnum;
9030 : ObjectAddress address;
9031 :
9032 240 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9033 :
9034 240 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9035 :
9036 240 : if (!HeapTupleIsValid(tuple))
9037 12 : ereport(ERROR,
9038 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9039 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9040 : colName, RelationGetRelationName(rel))));
9041 228 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9042 :
9043 228 : attnum = attrtuple->attnum;
9044 228 : if (attnum <= 0)
9045 0 : ereport(ERROR,
9046 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9047 : errmsg("cannot alter system column \"%s\"",
9048 : colName)));
9049 :
9050 228 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9051 :
9052 228 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9053 :
9054 228 : InvokeObjectPostAlterHook(RelationRelationId,
9055 : RelationGetRelid(rel),
9056 : attrtuple->attnum);
9057 :
9058 : /*
9059 : * Apply the change to indexes as well (only for simple index columns,
9060 : * matching behavior of index.c ConstructTupleDescriptor()).
9061 : */
9062 228 : SetIndexStorageProperties(rel, attrelation, attnum,
9063 228 : true, attrtuple->attstorage,
9064 : false, 0,
9065 : lockmode);
9066 :
9067 228 : heap_freetuple(tuple);
9068 :
9069 228 : table_close(attrelation, RowExclusiveLock);
9070 :
9071 228 : ObjectAddressSubSet(address, RelationRelationId,
9072 : RelationGetRelid(rel), attnum);
9073 228 : return address;
9074 : }
9075 :
9076 :
9077 : /*
9078 : * ALTER TABLE DROP COLUMN
9079 : *
9080 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9081 : * because we have to decide at runtime whether to recurse or not depending
9082 : * on whether attinhcount goes to zero or not. (We can't check this in a
9083 : * static pre-pass because it won't handle multiple inheritance situations
9084 : * correctly.)
9085 : */
9086 : static void
9087 1644 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9088 : AlterTableCmd *cmd, LOCKMODE lockmode,
9089 : AlterTableUtilityContext *context)
9090 : {
9091 1644 : if (rel->rd_rel->reloftype && !recursing)
9092 6 : ereport(ERROR,
9093 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9094 : errmsg("cannot drop column from typed table")));
9095 :
9096 1638 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9097 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9098 :
9099 1632 : if (recurse)
9100 1354 : cmd->recurse = true;
9101 1632 : }
9102 :
9103 : /*
9104 : * Drops column 'colName' from relation 'rel' and returns the address of the
9105 : * dropped column. The column is also dropped (or marked as no longer
9106 : * inherited from relation) from the relation's inheritance children, if any.
9107 : *
9108 : * In the recursive invocations for inheritance child relations, instead of
9109 : * dropping the column directly (if to be dropped at all), its object address
9110 : * is added to 'addrs', which must be non-NULL in such invocations. All
9111 : * columns are dropped at the same time after all the children have been
9112 : * checked recursively.
9113 : */
9114 : static ObjectAddress
9115 2188 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9116 : DropBehavior behavior,
9117 : bool recurse, bool recursing,
9118 : bool missing_ok, LOCKMODE lockmode,
9119 : ObjectAddresses *addrs)
9120 : {
9121 : HeapTuple tuple;
9122 : Form_pg_attribute targetatt;
9123 : AttrNumber attnum;
9124 : List *children;
9125 : ObjectAddress object;
9126 : bool is_expr;
9127 :
9128 : /* At top level, permission check was done in ATPrepCmd, else do it */
9129 2188 : if (recursing)
9130 556 : ATSimplePermissions(AT_DropColumn, rel,
9131 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9132 :
9133 : /* Initialize addrs on the first invocation */
9134 : Assert(!recursing || addrs != NULL);
9135 :
9136 : /* since this function recurses, it could be driven to stack overflow */
9137 2188 : check_stack_depth();
9138 :
9139 2188 : if (!recursing)
9140 1632 : addrs = new_object_addresses();
9141 :
9142 : /*
9143 : * get the number of the attribute
9144 : */
9145 2188 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9146 2188 : if (!HeapTupleIsValid(tuple))
9147 : {
9148 54 : if (!missing_ok)
9149 : {
9150 36 : ereport(ERROR,
9151 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9152 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9153 : colName, RelationGetRelationName(rel))));
9154 : }
9155 : else
9156 : {
9157 18 : ereport(NOTICE,
9158 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9159 : colName, RelationGetRelationName(rel))));
9160 18 : return InvalidObjectAddress;
9161 : }
9162 : }
9163 2134 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9164 :
9165 2134 : attnum = targetatt->attnum;
9166 :
9167 : /* Can't drop a system attribute */
9168 2134 : if (attnum <= 0)
9169 6 : ereport(ERROR,
9170 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9171 : errmsg("cannot drop system column \"%s\"",
9172 : colName)));
9173 :
9174 : /*
9175 : * Don't drop inherited columns, unless recursing (presumably from a drop
9176 : * of the parent column)
9177 : */
9178 2128 : if (targetatt->attinhcount > 0 && !recursing)
9179 48 : ereport(ERROR,
9180 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9181 : errmsg("cannot drop inherited column \"%s\"",
9182 : colName)));
9183 :
9184 : /*
9185 : * Don't drop columns used in the partition key, either. (If we let this
9186 : * go through, the key column's dependencies would cause a cascaded drop
9187 : * of the whole table, which is surely not what the user expected.)
9188 : */
9189 2080 : if (has_partition_attrs(rel,
9190 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9191 : &is_expr))
9192 30 : ereport(ERROR,
9193 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9194 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9195 : colName, RelationGetRelationName(rel))));
9196 :
9197 2050 : ReleaseSysCache(tuple);
9198 :
9199 : /*
9200 : * Propagate to children as appropriate. Unlike most other ALTER
9201 : * routines, we have to do this one level of recursion at a time; we can't
9202 : * use find_all_inheritors to do it in one pass.
9203 : */
9204 : children =
9205 2050 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9206 :
9207 2050 : if (children)
9208 : {
9209 : Relation attr_rel;
9210 : ListCell *child;
9211 :
9212 : /*
9213 : * In case of a partitioned table, the column must be dropped from the
9214 : * partitions as well.
9215 : */
9216 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9217 6 : ereport(ERROR,
9218 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9219 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9220 : errhint("Do not specify the ONLY keyword.")));
9221 :
9222 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9223 882 : foreach(child, children)
9224 : {
9225 592 : Oid childrelid = lfirst_oid(child);
9226 : Relation childrel;
9227 : Form_pg_attribute childatt;
9228 :
9229 : /* find_inheritance_children already got lock */
9230 592 : childrel = table_open(childrelid, NoLock);
9231 592 : CheckAlterTableIsSafe(childrel);
9232 :
9233 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9234 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9235 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9236 : colName, childrelid);
9237 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9238 :
9239 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9240 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9241 : childrelid, colName);
9242 :
9243 592 : if (recurse)
9244 : {
9245 : /*
9246 : * If the child column has other definition sources, just
9247 : * decrement its inheritance count; if not, recurse to delete
9248 : * it.
9249 : */
9250 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9251 : {
9252 : /* Time to delete this child column, too */
9253 556 : ATExecDropColumn(wqueue, childrel, colName,
9254 : behavior, true, true,
9255 : false, lockmode, addrs);
9256 : }
9257 : else
9258 : {
9259 : /* Child column must survive my deletion */
9260 12 : childatt->attinhcount--;
9261 :
9262 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9263 :
9264 : /* Make update visible */
9265 12 : CommandCounterIncrement();
9266 : }
9267 : }
9268 : else
9269 : {
9270 : /*
9271 : * If we were told to drop ONLY in this table (no recursion),
9272 : * we need to mark the inheritors' attributes as locally
9273 : * defined rather than inherited.
9274 : */
9275 24 : childatt->attinhcount--;
9276 24 : childatt->attislocal = true;
9277 :
9278 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9279 :
9280 : /* Make update visible */
9281 24 : CommandCounterIncrement();
9282 : }
9283 :
9284 586 : heap_freetuple(tuple);
9285 :
9286 586 : table_close(childrel, NoLock);
9287 : }
9288 290 : table_close(attr_rel, RowExclusiveLock);
9289 : }
9290 :
9291 : /* Add object to delete */
9292 2038 : object.classId = RelationRelationId;
9293 2038 : object.objectId = RelationGetRelid(rel);
9294 2038 : object.objectSubId = attnum;
9295 2038 : add_exact_object_address(&object, addrs);
9296 :
9297 2038 : if (!recursing)
9298 : {
9299 : /* Recursion has ended, drop everything that was collected */
9300 1488 : performMultipleDeletions(addrs, behavior, 0);
9301 1434 : free_object_addresses(addrs);
9302 : }
9303 :
9304 1984 : return object;
9305 : }
9306 :
9307 : /*
9308 : * Prepare to add a primary key on table, by adding not-null constraints
9309 : * on all columns.
9310 : */
9311 : static void
9312 15124 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9313 : bool recurse, LOCKMODE lockmode,
9314 : AlterTableUtilityContext *context)
9315 : {
9316 : ListCell *lc;
9317 : Constraint *pkconstr;
9318 :
9319 15124 : pkconstr = castNode(Constraint, cmd->def);
9320 15124 : if (pkconstr->contype != CONSTR_PRIMARY)
9321 8932 : return;
9322 :
9323 : /*
9324 : * If not recursing, we must ensure that all children have a NOT NULL
9325 : * constraint on the columns, and error out if not.
9326 : */
9327 6192 : if (!recurse)
9328 : {
9329 : List *children;
9330 :
9331 284 : children = find_inheritance_children(RelationGetRelid(rel),
9332 : lockmode);
9333 698 : foreach_oid(childrelid, children)
9334 : {
9335 280 : foreach(lc, pkconstr->keys)
9336 : {
9337 : HeapTuple tup;
9338 : Form_pg_attribute attrForm;
9339 144 : char *attname = strVal(lfirst(lc));
9340 :
9341 144 : tup = SearchSysCacheAttName(childrelid, attname);
9342 144 : if (!tup)
9343 0 : elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9344 : attname, childrelid);
9345 144 : attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9346 144 : if (!attrForm->attnotnull)
9347 6 : ereport(ERROR,
9348 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9349 : attname, get_rel_name(childrelid)));
9350 138 : ReleaseSysCache(tup);
9351 : }
9352 : }
9353 : }
9354 :
9355 : /* Insert not-null constraints in the queue for the PK columns */
9356 7166 : foreach(lc, pkconstr->keys)
9357 : {
9358 : AlterTableCmd *newcmd;
9359 : Constraint *nnconstr;
9360 :
9361 980 : nnconstr = makeNotNullConstraint(lfirst(lc));
9362 :
9363 980 : newcmd = makeNode(AlterTableCmd);
9364 980 : newcmd->subtype = AT_AddConstraint;
9365 980 : newcmd->recurse = true;
9366 980 : newcmd->def = (Node *) nnconstr;
9367 :
9368 980 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9369 : }
9370 : }
9371 :
9372 : /*
9373 : * ALTER TABLE ADD INDEX
9374 : *
9375 : * There is no such command in the grammar, but parse_utilcmd.c converts
9376 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9377 : * us schedule creation of the index at the appropriate time during ALTER.
9378 : *
9379 : * Return value is the address of the new index.
9380 : */
9381 : static ObjectAddress
9382 1622 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9383 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9384 : {
9385 : bool check_rights;
9386 : bool skip_build;
9387 : bool quiet;
9388 : ObjectAddress address;
9389 :
9390 : Assert(IsA(stmt, IndexStmt));
9391 : Assert(!stmt->concurrent);
9392 :
9393 : /* The IndexStmt has already been through transformIndexStmt */
9394 : Assert(stmt->transformed);
9395 :
9396 : /* suppress schema rights check when rebuilding existing index */
9397 1622 : check_rights = !is_rebuild;
9398 : /* skip index build if phase 3 will do it or we're reusing an old one */
9399 1622 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9400 : /* suppress notices when rebuilding existing index */
9401 1622 : quiet = is_rebuild;
9402 :
9403 1622 : address = DefineIndex(RelationGetRelid(rel),
9404 : stmt,
9405 : InvalidOid, /* no predefined OID */
9406 : InvalidOid, /* no parent index */
9407 : InvalidOid, /* no parent constraint */
9408 : -1, /* total_parts unknown */
9409 : true, /* is_alter_table */
9410 : check_rights,
9411 : false, /* check_not_in_use - we did it already */
9412 : skip_build,
9413 : quiet);
9414 :
9415 : /*
9416 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9417 : * new index instead of building from scratch. Restore associated fields.
9418 : * This may store InvalidSubTransactionId in both fields, in which case
9419 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9420 : * this after the CCI that made catalog rows visible to any rebuild. The
9421 : * DROP of the old edition of this index will have scheduled the storage
9422 : * for deletion at commit, so cancel that pending deletion.
9423 : */
9424 1452 : if (RelFileNumberIsValid(stmt->oldNumber))
9425 : {
9426 74 : Relation irel = index_open(address.objectId, NoLock);
9427 :
9428 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9429 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9430 74 : RelationPreserveStorage(irel->rd_locator, true);
9431 74 : index_close(irel, NoLock);
9432 : }
9433 :
9434 1452 : return address;
9435 : }
9436 :
9437 : /*
9438 : * ALTER TABLE ADD STATISTICS
9439 : *
9440 : * This is no such command in the grammar, but we use this internally to add
9441 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9442 : * column type change.
9443 : */
9444 : static ObjectAddress
9445 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9446 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9447 : {
9448 : ObjectAddress address;
9449 :
9450 : Assert(IsA(stmt, CreateStatsStmt));
9451 :
9452 : /* The CreateStatsStmt has already been through transformStatsStmt */
9453 : Assert(stmt->transformed);
9454 :
9455 14 : address = CreateStatistics(stmt);
9456 :
9457 14 : return address;
9458 : }
9459 :
9460 : /*
9461 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9462 : *
9463 : * Returns the address of the new constraint.
9464 : */
9465 : static ObjectAddress
9466 9540 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9467 : IndexStmt *stmt, LOCKMODE lockmode)
9468 : {
9469 9540 : Oid index_oid = stmt->indexOid;
9470 : Relation indexRel;
9471 : char *indexName;
9472 : IndexInfo *indexInfo;
9473 : char *constraintName;
9474 : char constraintType;
9475 : ObjectAddress address;
9476 : bits16 flags;
9477 :
9478 : Assert(IsA(stmt, IndexStmt));
9479 : Assert(OidIsValid(index_oid));
9480 : Assert(stmt->isconstraint);
9481 :
9482 : /*
9483 : * Doing this on partitioned tables is not a simple feature to implement,
9484 : * so let's punt for now.
9485 : */
9486 9540 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9487 6 : ereport(ERROR,
9488 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9489 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9490 :
9491 9534 : indexRel = index_open(index_oid, AccessShareLock);
9492 :
9493 9534 : indexName = pstrdup(RelationGetRelationName(indexRel));
9494 :
9495 9534 : indexInfo = BuildIndexInfo(indexRel);
9496 :
9497 : /* this should have been checked at parse time */
9498 9534 : if (!indexInfo->ii_Unique)
9499 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9500 :
9501 : /*
9502 : * Determine name to assign to constraint. We require a constraint to
9503 : * have the same name as the underlying index; therefore, use the index's
9504 : * existing name as the default constraint name, and if the user
9505 : * explicitly gives some other name for the constraint, rename the index
9506 : * to match.
9507 : */
9508 9534 : constraintName = stmt->idxname;
9509 9534 : if (constraintName == NULL)
9510 9508 : constraintName = indexName;
9511 26 : else if (strcmp(constraintName, indexName) != 0)
9512 : {
9513 20 : ereport(NOTICE,
9514 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9515 : indexName, constraintName)));
9516 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9517 : }
9518 :
9519 : /* Extra checks needed if making primary key */
9520 9534 : if (stmt->primary)
9521 5386 : index_check_primary_key(rel, indexInfo, true, stmt);
9522 :
9523 : /* Note we currently don't support EXCLUSION constraints here */
9524 9528 : if (stmt->primary)
9525 5380 : constraintType = CONSTRAINT_PRIMARY;
9526 : else
9527 4148 : constraintType = CONSTRAINT_UNIQUE;
9528 :
9529 : /* Create the catalog entries for the constraint */
9530 9528 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9531 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9532 19056 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9533 9528 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9534 9528 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9535 :
9536 9528 : address = index_constraint_create(rel,
9537 : index_oid,
9538 : InvalidOid,
9539 : indexInfo,
9540 : constraintName,
9541 : constraintType,
9542 : flags,
9543 : allowSystemTableMods,
9544 : false); /* is_internal */
9545 :
9546 9528 : index_close(indexRel, NoLock);
9547 :
9548 9528 : return address;
9549 : }
9550 :
9551 : /*
9552 : * ALTER TABLE ADD CONSTRAINT
9553 : *
9554 : * Return value is the address of the new constraint; if no constraint was
9555 : * added, InvalidObjectAddress is returned.
9556 : */
9557 : static ObjectAddress
9558 12138 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9559 : Constraint *newConstraint, bool recurse, bool is_readd,
9560 : LOCKMODE lockmode)
9561 : {
9562 12138 : ObjectAddress address = InvalidObjectAddress;
9563 :
9564 : Assert(IsA(newConstraint, Constraint));
9565 :
9566 : /*
9567 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9568 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9569 : * parse_utilcmd.c).
9570 : */
9571 12138 : switch (newConstraint->contype)
9572 : {
9573 9542 : case CONSTR_CHECK:
9574 : case CONSTR_NOTNULL:
9575 : address =
9576 9542 : ATAddCheckNNConstraint(wqueue, tab, rel,
9577 : newConstraint, recurse, false, is_readd,
9578 : lockmode);
9579 9410 : break;
9580 :
9581 2596 : case CONSTR_FOREIGN:
9582 :
9583 : /*
9584 : * Assign or validate constraint name
9585 : */
9586 2596 : if (newConstraint->conname)
9587 : {
9588 1172 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9589 : RelationGetRelid(rel),
9590 1172 : newConstraint->conname))
9591 0 : ereport(ERROR,
9592 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9593 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9594 : newConstraint->conname,
9595 : RelationGetRelationName(rel))));
9596 : }
9597 : else
9598 1424 : newConstraint->conname =
9599 1424 : ChooseConstraintName(RelationGetRelationName(rel),
9600 1424 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9601 : "fkey",
9602 1424 : RelationGetNamespace(rel),
9603 : NIL);
9604 :
9605 2596 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9606 : newConstraint,
9607 : recurse, false,
9608 : lockmode);
9609 2066 : break;
9610 :
9611 0 : default:
9612 0 : elog(ERROR, "unrecognized constraint type: %d",
9613 : (int) newConstraint->contype);
9614 : }
9615 :
9616 11476 : return address;
9617 : }
9618 :
9619 : /*
9620 : * Generate the column-name portion of the constraint name for a new foreign
9621 : * key given the list of column names that reference the referenced
9622 : * table. This will be passed to ChooseConstraintName along with the parent
9623 : * table name and the "fkey" suffix.
9624 : *
9625 : * We know that less than NAMEDATALEN characters will actually be used, so we
9626 : * can truncate the result once we've generated that many.
9627 : *
9628 : * XXX see also ChooseExtendedStatisticNameAddition and
9629 : * ChooseIndexNameAddition.
9630 : */
9631 : static char *
9632 2432 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9633 : {
9634 : char buf[NAMEDATALEN * 2];
9635 2432 : int buflen = 0;
9636 : ListCell *lc;
9637 :
9638 2432 : buf[0] = '\0';
9639 5460 : foreach(lc, colnames)
9640 : {
9641 3028 : const char *name = strVal(lfirst(lc));
9642 :
9643 3028 : if (buflen > 0)
9644 596 : buf[buflen++] = '_'; /* insert _ between names */
9645 :
9646 : /*
9647 : * At this point we have buflen <= NAMEDATALEN. name should be less
9648 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9649 : */
9650 3028 : strlcpy(buf + buflen, name, NAMEDATALEN);
9651 3028 : buflen += strlen(buf + buflen);
9652 3028 : if (buflen >= NAMEDATALEN)
9653 0 : break;
9654 : }
9655 2432 : return pstrdup(buf);
9656 : }
9657 :
9658 : /*
9659 : * Add a check or not-null constraint to a single table and its children.
9660 : * Returns the address of the constraint added to the parent relation,
9661 : * if one gets added, or InvalidObjectAddress otherwise.
9662 : *
9663 : * Subroutine for ATExecAddConstraint.
9664 : *
9665 : * We must recurse to child tables during execution, rather than using
9666 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9667 : * constraints *must* be given the same name, else they won't be seen as
9668 : * related later. If the user didn't explicitly specify a name, then
9669 : * AddRelationNewConstraints would normally assign different names to the
9670 : * child constraints. To fix that, we must capture the name assigned at
9671 : * the parent table and pass that down.
9672 : */
9673 : static ObjectAddress
9674 10354 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9675 : Constraint *constr, bool recurse, bool recursing,
9676 : bool is_readd, LOCKMODE lockmode)
9677 : {
9678 : List *newcons;
9679 : ListCell *lcon;
9680 : List *children;
9681 : ListCell *child;
9682 10354 : ObjectAddress address = InvalidObjectAddress;
9683 :
9684 : /* Guard against stack overflow due to overly deep inheritance tree. */
9685 10354 : check_stack_depth();
9686 :
9687 : /* At top level, permission check was done in ATPrepCmd, else do it */
9688 10354 : if (recursing)
9689 678 : ATSimplePermissions(AT_AddConstraint, rel,
9690 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9691 :
9692 : /*
9693 : * Call AddRelationNewConstraints to do the work, making sure it works on
9694 : * a copy of the Constraint so transformExpr can't modify the original. It
9695 : * returns a list of cooked constraints.
9696 : *
9697 : * If the constraint ends up getting merged with a pre-existing one, it's
9698 : * omitted from the returned list, which is what we want: we do not need
9699 : * to do any validation work. That can only happen at child tables,
9700 : * though, since we disallow merging at the top level.
9701 : */
9702 10354 : newcons = AddRelationNewConstraints(rel, NIL,
9703 10354 : list_make1(copyObject(constr)),
9704 10354 : recursing || is_readd, /* allow_merge */
9705 10354 : !recursing, /* is_local */
9706 : is_readd, /* is_internal */
9707 10354 : NULL); /* queryString not available
9708 : * here */
9709 :
9710 : /* we don't expect more than one constraint here */
9711 : Assert(list_length(newcons) <= 1);
9712 :
9713 : /* Add each to-be-validated constraint to Phase 3's queue */
9714 19814 : foreach(lcon, newcons)
9715 : {
9716 9586 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9717 :
9718 9586 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9719 : {
9720 : NewConstraint *newcon;
9721 :
9722 856 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9723 856 : newcon->name = ccon->name;
9724 856 : newcon->contype = ccon->contype;
9725 856 : newcon->qual = ccon->expr;
9726 :
9727 856 : tab->constraints = lappend(tab->constraints, newcon);
9728 : }
9729 :
9730 : /* Save the actually assigned name if it was defaulted */
9731 9586 : if (constr->conname == NULL)
9732 8134 : constr->conname = ccon->name;
9733 :
9734 : /*
9735 : * If adding a not-null constraint, set the pg_attribute flag and tell
9736 : * phase 3 to verify existing rows, if needed.
9737 : */
9738 9586 : if (constr->contype == CONSTR_NOTNULL)
9739 8240 : set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9740 :
9741 9586 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9742 : }
9743 :
9744 : /* At this point we must have a locked-down name to use */
9745 : Assert(newcons == NIL || constr->conname != NULL);
9746 :
9747 : /* Advance command counter in case same table is visited multiple times */
9748 10228 : CommandCounterIncrement();
9749 :
9750 : /*
9751 : * If the constraint got merged with an existing constraint, we're done.
9752 : * We mustn't recurse to child tables in this case, because they've
9753 : * already got the constraint, and visiting them again would lead to an
9754 : * incorrect value for coninhcount.
9755 : */
9756 10228 : if (newcons == NIL)
9757 642 : return address;
9758 :
9759 : /*
9760 : * If adding a NO INHERIT constraint, no need to find our children.
9761 : */
9762 9586 : if (constr->is_no_inherit)
9763 72 : return address;
9764 :
9765 : /*
9766 : * Propagate to children as appropriate. Unlike most other ALTER
9767 : * routines, we have to do this one level of recursion at a time; we can't
9768 : * use find_all_inheritors to do it in one pass.
9769 : */
9770 : children =
9771 9514 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9772 :
9773 : /*
9774 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9775 : * constraint creation only if there are no children currently. Error out
9776 : * otherwise.
9777 : */
9778 9514 : if (!recurse && children != NIL)
9779 6 : ereport(ERROR,
9780 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9781 : errmsg("constraint must be added to child tables too")));
9782 :
9783 : /*
9784 : * Recurse to create the constraint on each child.
9785 : */
9786 10156 : foreach(child, children)
9787 : {
9788 678 : Oid childrelid = lfirst_oid(child);
9789 : Relation childrel;
9790 : AlteredTableInfo *childtab;
9791 :
9792 : /* find_inheritance_children already got lock */
9793 678 : childrel = table_open(childrelid, NoLock);
9794 678 : CheckAlterTableIsSafe(childrel);
9795 :
9796 : /* Find or create work queue entry for this table */
9797 678 : childtab = ATGetQueueEntry(wqueue, childrel);
9798 :
9799 : /* Recurse to this child */
9800 678 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9801 : constr, recurse, true, is_readd, lockmode);
9802 :
9803 648 : table_close(childrel, NoLock);
9804 : }
9805 :
9806 9478 : return address;
9807 : }
9808 :
9809 : /*
9810 : * Add a foreign-key constraint to a single table; return the new constraint's
9811 : * address.
9812 : *
9813 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9814 : * lock on the rel, and have done appropriate validity checks for it.
9815 : * We do permissions checks here, however.
9816 : *
9817 : * When the referenced or referencing tables (or both) are partitioned,
9818 : * multiple pg_constraint rows are required -- one for each partitioned table
9819 : * and each partition on each side (fortunately, not one for every combination
9820 : * thereof). We also need action triggers on each leaf partition on the
9821 : * referenced side, and check triggers on each leaf partition on the
9822 : * referencing side.
9823 : */
9824 : static ObjectAddress
9825 2596 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9826 : Constraint *fkconstraint,
9827 : bool recurse, bool recursing, LOCKMODE lockmode)
9828 : {
9829 : Relation pkrel;
9830 2596 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9831 2596 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9832 2596 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9833 2596 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9834 2596 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9835 2596 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9836 2596 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9837 2596 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9838 2596 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9839 2596 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9840 2596 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9841 : bool with_period;
9842 : bool pk_has_without_overlaps;
9843 : int i;
9844 : int numfks,
9845 : numpks,
9846 : numfkdelsetcols;
9847 : Oid indexOid;
9848 : bool old_check_ok;
9849 : ObjectAddress address;
9850 2596 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9851 :
9852 : /*
9853 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9854 : * delete rows out from under us.
9855 : */
9856 2596 : if (OidIsValid(fkconstraint->old_pktable_oid))
9857 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9858 : else
9859 2524 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9860 :
9861 : /*
9862 : * Validity checks (permission checks wait till we have the column
9863 : * numbers)
9864 : */
9865 2590 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9866 6 : ereport(ERROR,
9867 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
9868 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9869 : RelationGetRelationName(rel),
9870 : RelationGetRelationName(pkrel)));
9871 :
9872 2584 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9873 344 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9874 0 : ereport(ERROR,
9875 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9876 : errmsg("referenced relation \"%s\" is not a table",
9877 : RelationGetRelationName(pkrel))));
9878 :
9879 2584 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9880 2 : ereport(ERROR,
9881 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9882 : errmsg("permission denied: \"%s\" is a system catalog",
9883 : RelationGetRelationName(pkrel))));
9884 :
9885 : /*
9886 : * References from permanent or unlogged tables to temp tables, and from
9887 : * permanent tables to unlogged tables, are disallowed because the
9888 : * referenced data can vanish out from under us. References from temp
9889 : * tables to any other table type are also disallowed, because other
9890 : * backends might need to run the RI triggers on the perm table, but they
9891 : * can't reliably see tuples in the local buffers of other backends.
9892 : */
9893 2582 : switch (rel->rd_rel->relpersistence)
9894 : {
9895 2292 : case RELPERSISTENCE_PERMANENT:
9896 2292 : if (!RelationIsPermanent(pkrel))
9897 0 : ereport(ERROR,
9898 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9899 : errmsg("constraints on permanent tables may reference only permanent tables")));
9900 2292 : break;
9901 12 : case RELPERSISTENCE_UNLOGGED:
9902 12 : if (!RelationIsPermanent(pkrel)
9903 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9904 0 : ereport(ERROR,
9905 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9906 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9907 12 : break;
9908 278 : case RELPERSISTENCE_TEMP:
9909 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9910 0 : ereport(ERROR,
9911 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9912 : errmsg("constraints on temporary tables may reference only temporary tables")));
9913 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9914 0 : ereport(ERROR,
9915 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9916 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
9917 278 : break;
9918 : }
9919 :
9920 : /*
9921 : * Look up the referencing attributes to make sure they exist, and record
9922 : * their attnums and type and collation OIDs.
9923 : */
9924 2582 : numfks = transformColumnNameList(RelationGetRelid(rel),
9925 : fkconstraint->fk_attrs,
9926 : fkattnum, fktypoid, fkcolloid);
9927 2552 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9928 2552 : if (with_period && !fkconstraint->fk_with_period)
9929 24 : ereport(ERROR,
9930 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9931 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9932 :
9933 2528 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9934 : fkconstraint->fk_del_set_cols,
9935 : fkdelsetcols, NULL, NULL);
9936 2522 : validateFkOnDeleteSetColumns(numfks, fkattnum,
9937 : numfkdelsetcols, fkdelsetcols,
9938 : fkconstraint->fk_del_set_cols);
9939 :
9940 : /*
9941 : * If the attribute list for the referenced table was omitted, lookup the
9942 : * definition of the primary key and use it. Otherwise, validate the
9943 : * supplied attribute list. In either case, discover the index OID and
9944 : * index opclasses, and the attnums and type and collation OIDs of the
9945 : * attributes.
9946 : */
9947 2516 : if (fkconstraint->pk_attrs == NIL)
9948 : {
9949 1172 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9950 : &fkconstraint->pk_attrs,
9951 : pkattnum, pktypoid, pkcolloid,
9952 : opclasses, &pk_has_without_overlaps);
9953 :
9954 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9955 1172 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9956 24 : ereport(ERROR,
9957 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9958 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9959 : }
9960 : else
9961 : {
9962 1344 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
9963 : fkconstraint->pk_attrs,
9964 : pkattnum, pktypoid, pkcolloid);
9965 :
9966 : /* Since we got pk_attrs, one should be a period. */
9967 1314 : if (with_period && !fkconstraint->pk_with_period)
9968 24 : ereport(ERROR,
9969 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9970 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9971 :
9972 : /* Look for an index matching the column list */
9973 1290 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9974 : with_period, opclasses, &pk_has_without_overlaps);
9975 : }
9976 :
9977 : /*
9978 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9979 : * must use PERIOD.
9980 : */
9981 2402 : if (pk_has_without_overlaps && !with_period)
9982 12 : ereport(ERROR,
9983 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9984 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9985 :
9986 : /*
9987 : * Now we can check permissions.
9988 : */
9989 2390 : checkFkeyPermissions(pkrel, pkattnum, numpks);
9990 :
9991 : /*
9992 : * Check some things for generated columns.
9993 : */
9994 5598 : for (i = 0; i < numfks; i++)
9995 : {
9996 3238 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9997 :
9998 3238 : if (attgenerated)
9999 : {
10000 : /*
10001 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10002 : */
10003 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10004 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10005 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10006 12 : ereport(ERROR,
10007 : (errcode(ERRCODE_SYNTAX_ERROR),
10008 : errmsg("invalid %s action for foreign key constraint containing generated column",
10009 : "ON UPDATE")));
10010 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10011 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10012 12 : ereport(ERROR,
10013 : (errcode(ERRCODE_SYNTAX_ERROR),
10014 : errmsg("invalid %s action for foreign key constraint containing generated column",
10015 : "ON DELETE")));
10016 : }
10017 :
10018 : /*
10019 : * FKs on virtual columns are not supported. This would require
10020 : * various additional support in ri_triggers.c, including special
10021 : * handling in ri_NullCheck(), ri_KeysEqual(),
10022 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10023 : * as NULL there). Also not really practical as long as you can't
10024 : * index virtual columns.
10025 : */
10026 3214 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10027 6 : ereport(ERROR,
10028 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10029 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10030 : }
10031 :
10032 : /*
10033 : * Some actions are currently unsupported for foreign keys using PERIOD.
10034 : */
10035 2360 : if (fkconstraint->fk_with_period)
10036 : {
10037 262 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10038 244 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10039 226 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10040 54 : ereport(ERROR,
10041 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10042 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10043 : "ON UPDATE"));
10044 :
10045 208 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10046 208 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10047 208 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10048 0 : ereport(ERROR,
10049 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10050 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10051 : "ON DELETE"));
10052 : }
10053 :
10054 : /*
10055 : * Look up the equality operators to use in the constraint.
10056 : *
10057 : * Note that we have to be careful about the difference between the actual
10058 : * PK column type and the opclass' declared input type, which might be
10059 : * only binary-compatible with it. The declared opcintype is the right
10060 : * thing to probe pg_amop with.
10061 : */
10062 2306 : if (numfks != numpks)
10063 0 : ereport(ERROR,
10064 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10065 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10066 :
10067 : /*
10068 : * On the strength of a previous constraint, we might avoid scanning
10069 : * tables to validate this one. See below.
10070 : */
10071 2306 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10072 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10073 :
10074 5022 : for (i = 0; i < numpks; i++)
10075 : {
10076 2956 : Oid pktype = pktypoid[i];
10077 2956 : Oid fktype = fktypoid[i];
10078 : Oid fktyped;
10079 2956 : Oid pkcoll = pkcolloid[i];
10080 2956 : Oid fkcoll = fkcolloid[i];
10081 : HeapTuple cla_ht;
10082 : Form_pg_opclass cla_tup;
10083 : Oid amid;
10084 : Oid opfamily;
10085 : Oid opcintype;
10086 : bool for_overlaps;
10087 : CompareType cmptype;
10088 : Oid pfeqop;
10089 : Oid ppeqop;
10090 : Oid ffeqop;
10091 : int16 eqstrategy;
10092 : Oid pfeqop_right;
10093 :
10094 : /* We need several fields out of the pg_opclass entry */
10095 2956 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10096 2956 : if (!HeapTupleIsValid(cla_ht))
10097 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10098 2956 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10099 2956 : amid = cla_tup->opcmethod;
10100 2956 : opfamily = cla_tup->opcfamily;
10101 2956 : opcintype = cla_tup->opcintype;
10102 2956 : ReleaseSysCache(cla_ht);
10103 :
10104 : /*
10105 : * Get strategy number from index AM.
10106 : *
10107 : * For a normal foreign-key constraint, this should not fail, since we
10108 : * already checked that the index is unique and should therefore have
10109 : * appropriate equal operators. For a period foreign key, this could
10110 : * fail if we selected a non-matching exclusion constraint earlier.
10111 : * (XXX Maybe we should do these lookups earlier so we don't end up
10112 : * doing that.)
10113 : */
10114 2956 : for_overlaps = with_period && i == numpks - 1;
10115 2956 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10116 2956 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10117 2956 : if (eqstrategy == InvalidStrategy)
10118 0 : ereport(ERROR,
10119 : errcode(ERRCODE_UNDEFINED_OBJECT),
10120 : for_overlaps
10121 : ? errmsg("could not identify an overlaps operator for foreign key")
10122 : : errmsg("could not identify an equality operator for foreign key"),
10123 : errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10124 : cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10125 :
10126 : /*
10127 : * There had better be a primary equality operator for the index.
10128 : * We'll use it for PK = PK comparisons.
10129 : */
10130 2956 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10131 : eqstrategy);
10132 :
10133 2956 : if (!OidIsValid(ppeqop))
10134 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10135 : eqstrategy, opcintype, opcintype, opfamily);
10136 :
10137 : /*
10138 : * Are there equality operators that take exactly the FK type? Assume
10139 : * we should look through any domain here.
10140 : */
10141 2956 : fktyped = getBaseType(fktype);
10142 :
10143 2956 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10144 : eqstrategy);
10145 2956 : if (OidIsValid(pfeqop))
10146 : {
10147 2256 : pfeqop_right = fktyped;
10148 2256 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10149 : eqstrategy);
10150 : }
10151 : else
10152 : {
10153 : /* keep compiler quiet */
10154 700 : pfeqop_right = InvalidOid;
10155 700 : ffeqop = InvalidOid;
10156 : }
10157 :
10158 2956 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10159 : {
10160 : /*
10161 : * Otherwise, look for an implicit cast from the FK type to the
10162 : * opcintype, and if found, use the primary equality operator.
10163 : * This is a bit tricky because opcintype might be a polymorphic
10164 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10165 : * whether the two actual column types can be concurrently cast to
10166 : * that type. (Otherwise, we'd fail to reject combinations such
10167 : * as int[] and point[].)
10168 : */
10169 : Oid input_typeids[2];
10170 : Oid target_typeids[2];
10171 :
10172 700 : input_typeids[0] = pktype;
10173 700 : input_typeids[1] = fktype;
10174 700 : target_typeids[0] = opcintype;
10175 700 : target_typeids[1] = opcintype;
10176 700 : if (can_coerce_type(2, input_typeids, target_typeids,
10177 : COERCION_IMPLICIT))
10178 : {
10179 472 : pfeqop = ffeqop = ppeqop;
10180 472 : pfeqop_right = opcintype;
10181 : }
10182 : }
10183 :
10184 2956 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10185 228 : ereport(ERROR,
10186 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10187 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10188 : fkconstraint->conname),
10189 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10190 : "are of incompatible types: %s and %s.",
10191 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10192 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10193 : format_type_be(fktype),
10194 : format_type_be(pktype))));
10195 :
10196 : /*
10197 : * This shouldn't be possible, but better check to make sure we have a
10198 : * consistent state for the check below.
10199 : */
10200 2728 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10201 0 : elog(ERROR, "key columns are not both collatable");
10202 :
10203 2728 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10204 : {
10205 : bool pkcolldet;
10206 : bool fkcolldet;
10207 :
10208 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10209 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10210 :
10211 : /*
10212 : * SQL requires that both collations are the same. This is
10213 : * because we need a consistent notion of equality on both
10214 : * columns. We relax this by allowing different collations if
10215 : * they are both deterministic. (This is also for backward
10216 : * compatibility, because PostgreSQL has always allowed this.)
10217 : */
10218 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10219 12 : ereport(ERROR,
10220 : (errcode(ERRCODE_COLLATION_MISMATCH),
10221 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10222 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10223 : "have incompatible collations: \"%s\" and \"%s\". "
10224 : "If either collation is nondeterministic, then both collations have to be the same.",
10225 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10226 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10227 : get_collation_name(fkcoll),
10228 : get_collation_name(pkcoll))));
10229 : }
10230 :
10231 2716 : if (old_check_ok)
10232 : {
10233 : /*
10234 : * When a pfeqop changes, revalidate the constraint. We could
10235 : * permit intra-opfamily changes, but that adds subtle complexity
10236 : * without any concrete benefit for core types. We need not
10237 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10238 : */
10239 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10240 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10241 : old_pfeqop_item);
10242 : }
10243 2716 : if (old_check_ok)
10244 : {
10245 : Oid old_fktype;
10246 : Oid new_fktype;
10247 : CoercionPathType old_pathtype;
10248 : CoercionPathType new_pathtype;
10249 : Oid old_castfunc;
10250 : Oid new_castfunc;
10251 : Oid old_fkcoll;
10252 : Oid new_fkcoll;
10253 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10254 6 : fkattnum[i] - 1);
10255 :
10256 : /*
10257 : * Identify coercion pathways from each of the old and new FK-side
10258 : * column types to the right (foreign) operand type of the pfeqop.
10259 : * We may assume that pg_constraint.conkey is not changing.
10260 : */
10261 6 : old_fktype = attr->atttypid;
10262 6 : new_fktype = fktype;
10263 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10264 : &old_castfunc);
10265 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10266 : &new_castfunc);
10267 :
10268 6 : old_fkcoll = attr->attcollation;
10269 6 : new_fkcoll = fkcoll;
10270 :
10271 : /*
10272 : * Upon a change to the cast from the FK column to its pfeqop
10273 : * operand, revalidate the constraint. For this evaluation, a
10274 : * binary coercion cast is equivalent to no cast at all. While
10275 : * type implementors should design implicit casts with an eye
10276 : * toward consistency of operations like equality, we cannot
10277 : * assume here that they have done so.
10278 : *
10279 : * A function with a polymorphic argument could change behavior
10280 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10281 : * when the cast destination is polymorphic, we only avoid
10282 : * revalidation if the input type has not changed at all. Given
10283 : * just the core data types and operator classes, this requirement
10284 : * prevents no would-be optimizations.
10285 : *
10286 : * If the cast converts from a base type to a domain thereon, then
10287 : * that domain type must be the opcintype of the unique index.
10288 : * Necessarily, the primary key column must then be of the domain
10289 : * type. Since the constraint was previously valid, all values on
10290 : * the foreign side necessarily exist on the primary side and in
10291 : * turn conform to the domain. Consequently, we need not treat
10292 : * domains specially here.
10293 : *
10294 : * If the collation changes, revalidation is required, unless both
10295 : * collations are deterministic, because those share the same
10296 : * notion of equality (because texteq reduces to bitwise
10297 : * equality).
10298 : *
10299 : * We need not directly consider the PK type. It's necessarily
10300 : * binary coercible to the opcintype of the unique index column,
10301 : * and ri_triggers.c will only deal with PK datums in terms of
10302 : * that opcintype. Changing the opcintype also changes pfeqop.
10303 : */
10304 6 : old_check_ok = (new_pathtype == old_pathtype &&
10305 6 : new_castfunc == old_castfunc &&
10306 6 : (!IsPolymorphicType(pfeqop_right) ||
10307 12 : new_fktype == old_fktype) &&
10308 0 : (new_fkcoll == old_fkcoll ||
10309 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10310 : }
10311 :
10312 2716 : pfeqoperators[i] = pfeqop;
10313 2716 : ppeqoperators[i] = ppeqop;
10314 2716 : ffeqoperators[i] = ffeqop;
10315 : }
10316 :
10317 : /*
10318 : * For FKs with PERIOD we need additional operators to check whether the
10319 : * referencing row's range is contained by the aggregated ranges of the
10320 : * referenced row(s). For rangetypes and multirangetypes this is
10321 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10322 : * support for now. FKs will look these up at "runtime", but we should
10323 : * make sure the lookup works here, even if we don't use the values.
10324 : */
10325 2066 : if (with_period)
10326 : {
10327 : Oid periodoperoid;
10328 : Oid aggedperiodoperoid;
10329 : Oid intersectoperoid;
10330 :
10331 190 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10332 : &intersectoperoid);
10333 : }
10334 :
10335 : /* First, create the constraint catalog entry itself. */
10336 2066 : address = addFkConstraint(addFkBothSides,
10337 : fkconstraint->conname, fkconstraint, rel, pkrel,
10338 : indexOid,
10339 : InvalidOid, /* no parent constraint */
10340 : numfks,
10341 : pkattnum,
10342 : fkattnum,
10343 : pfeqoperators,
10344 : ppeqoperators,
10345 : ffeqoperators,
10346 : numfkdelsetcols,
10347 : fkdelsetcols,
10348 : false,
10349 : with_period);
10350 :
10351 : /* Next process the action triggers at the referenced side and recurse */
10352 2066 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10353 : indexOid,
10354 : address.objectId,
10355 : numfks,
10356 : pkattnum,
10357 : fkattnum,
10358 : pfeqoperators,
10359 : ppeqoperators,
10360 : ffeqoperators,
10361 : numfkdelsetcols,
10362 : fkdelsetcols,
10363 : old_check_ok,
10364 : InvalidOid, InvalidOid,
10365 : with_period);
10366 :
10367 : /* Lastly create the check triggers at the referencing side and recurse */
10368 2066 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10369 : indexOid,
10370 : address.objectId,
10371 : numfks,
10372 : pkattnum,
10373 : fkattnum,
10374 : pfeqoperators,
10375 : ppeqoperators,
10376 : ffeqoperators,
10377 : numfkdelsetcols,
10378 : fkdelsetcols,
10379 : old_check_ok,
10380 : lockmode,
10381 : InvalidOid, InvalidOid,
10382 : with_period);
10383 :
10384 : /*
10385 : * Done. Close pk table, but keep lock until we've committed.
10386 : */
10387 2066 : table_close(pkrel, NoLock);
10388 :
10389 2066 : return address;
10390 : }
10391 :
10392 : /*
10393 : * validateFkOnDeleteSetColumns
10394 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10395 : * column lists are valid.
10396 : */
10397 : void
10398 2522 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10399 : int numfksetcols, const int16 *fksetcolsattnums,
10400 : List *fksetcols)
10401 : {
10402 2546 : for (int i = 0; i < numfksetcols; i++)
10403 : {
10404 30 : int16 setcol_attnum = fksetcolsattnums[i];
10405 30 : bool seen = false;
10406 :
10407 54 : for (int j = 0; j < numfks; j++)
10408 : {
10409 48 : if (fkattnums[j] == setcol_attnum)
10410 : {
10411 24 : seen = true;
10412 24 : break;
10413 : }
10414 : }
10415 :
10416 30 : if (!seen)
10417 : {
10418 6 : char *col = strVal(list_nth(fksetcols, i));
10419 :
10420 6 : ereport(ERROR,
10421 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10422 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10423 : }
10424 : }
10425 2516 : }
10426 :
10427 : /*
10428 : * addFkConstraint
10429 : * Install pg_constraint entries to implement a foreign key constraint.
10430 : * Caller must separately invoke addFkRecurseReferenced and
10431 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10432 : * and (for partitioned tables) recurse to partitions.
10433 : *
10434 : * fkside: the side of the FK (or both) to create. Caller should
10435 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10436 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10437 : * addFkBothSides.
10438 : * constraintname: the base name for the constraint being added,
10439 : * copied to fkconstraint->conname if the latter is not set
10440 : * fkconstraint: the constraint being added
10441 : * rel: the root referencing relation
10442 : * pkrel: the referenced relation; might be a partition, if recursing
10443 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10444 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10445 : * top-level constraint
10446 : * numfks: the number of columns in the foreign key
10447 : * pkattnum: the attnum array of referenced attributes
10448 : * fkattnum: the attnum array of referencing attributes
10449 : * pf/pp/ffeqoperators: OID array of operators between columns
10450 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10451 : * (...) clause
10452 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10453 : * NULL/DEFAULT clause
10454 : * with_period: true if this is a temporal FK
10455 : */
10456 : static ObjectAddress
10457 3868 : addFkConstraint(addFkConstraintSides fkside,
10458 : char *constraintname, Constraint *fkconstraint,
10459 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10460 : int numfks, int16 *pkattnum,
10461 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10462 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10463 : bool is_internal, bool with_period)
10464 : {
10465 : ObjectAddress address;
10466 : Oid constrOid;
10467 : char *conname;
10468 : bool conislocal;
10469 : int16 coninhcount;
10470 : bool connoinherit;
10471 :
10472 : /*
10473 : * Verify relkind for each referenced partition. At the top level, this
10474 : * is redundant with a previous check, but we need it when recursing.
10475 : */
10476 3868 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10477 826 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10478 0 : ereport(ERROR,
10479 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10480 : errmsg("referenced relation \"%s\" is not a table",
10481 : RelationGetRelationName(pkrel))));
10482 :
10483 : /*
10484 : * Caller supplies us with a constraint name; however, it may be used in
10485 : * this partition, so come up with a different one in that case.
10486 : */
10487 3868 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10488 : RelationGetRelid(rel),
10489 : constraintname))
10490 1008 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10491 1008 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10492 : "fkey",
10493 1008 : RelationGetNamespace(rel), NIL);
10494 : else
10495 2860 : conname = constraintname;
10496 :
10497 3868 : if (fkconstraint->conname == NULL)
10498 418 : fkconstraint->conname = pstrdup(conname);
10499 :
10500 3868 : if (OidIsValid(parentConstr))
10501 : {
10502 1802 : conislocal = false;
10503 1802 : coninhcount = 1;
10504 1802 : connoinherit = false;
10505 : }
10506 : else
10507 : {
10508 2066 : conislocal = true;
10509 2066 : coninhcount = 0;
10510 :
10511 : /*
10512 : * always inherit for partitioned tables, never for legacy inheritance
10513 : */
10514 2066 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10515 : }
10516 :
10517 : /*
10518 : * Record the FK constraint in pg_constraint.
10519 : */
10520 3868 : constrOid = CreateConstraintEntry(conname,
10521 3868 : RelationGetNamespace(rel),
10522 : CONSTRAINT_FOREIGN,
10523 3868 : fkconstraint->deferrable,
10524 3868 : fkconstraint->initdeferred,
10525 : true, /* Is Enforced */
10526 3868 : fkconstraint->initially_valid,
10527 : parentConstr,
10528 : RelationGetRelid(rel),
10529 : fkattnum,
10530 : numfks,
10531 : numfks,
10532 : InvalidOid, /* not a domain constraint */
10533 : indexOid,
10534 : RelationGetRelid(pkrel),
10535 : pkattnum,
10536 : pfeqoperators,
10537 : ppeqoperators,
10538 : ffeqoperators,
10539 : numfks,
10540 3868 : fkconstraint->fk_upd_action,
10541 3868 : fkconstraint->fk_del_action,
10542 : fkdelsetcols,
10543 : numfkdelsetcols,
10544 3868 : fkconstraint->fk_matchtype,
10545 : NULL, /* no exclusion constraint */
10546 : NULL, /* no check constraint */
10547 : NULL,
10548 : conislocal, /* islocal */
10549 : coninhcount, /* inhcount */
10550 : connoinherit, /* conNoInherit */
10551 : with_period, /* conPeriod */
10552 : is_internal); /* is_internal */
10553 :
10554 3868 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10555 :
10556 : /*
10557 : * In partitioning cases, create the dependency entries for this
10558 : * constraint. (For non-partitioned cases, relevant entries were created
10559 : * by CreateConstraintEntry.)
10560 : *
10561 : * On the referenced side, we need the constraint to have an internal
10562 : * dependency on its parent constraint; this means that this constraint
10563 : * cannot be dropped on its own -- only through the parent constraint. It
10564 : * also means the containing partition cannot be dropped on its own, but
10565 : * it can be detached, at which point this dependency is removed (after
10566 : * verifying that no rows are referenced via this FK.)
10567 : *
10568 : * When processing the referencing side, we link the constraint via the
10569 : * special partitioning dependencies: the parent constraint is the primary
10570 : * dependent, and the partition on which the foreign key exists is the
10571 : * secondary dependency. That way, this constraint is dropped if either
10572 : * of these objects is.
10573 : *
10574 : * Note that this is only necessary for the subsidiary pg_constraint rows
10575 : * in partitions; the topmost row doesn't need any of this.
10576 : */
10577 3868 : if (OidIsValid(parentConstr))
10578 : {
10579 : ObjectAddress referenced;
10580 :
10581 1802 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10582 :
10583 : Assert(fkside != addFkBothSides);
10584 1802 : if (fkside == addFkReferencedSide)
10585 1002 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10586 : else
10587 : {
10588 800 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10589 800 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10590 800 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10591 : }
10592 : }
10593 :
10594 : /* make new constraint visible, in case we add more */
10595 3868 : CommandCounterIncrement();
10596 :
10597 3868 : return address;
10598 : }
10599 :
10600 : /*
10601 : * addFkRecurseReferenced
10602 : * Recursive helper for the referenced side of foreign key creation,
10603 : * which creates the action triggers and recurses
10604 : *
10605 : * If the referenced relation is a plain relation, create the necessary action
10606 : * triggers that implement the constraint. If the referenced relation is a
10607 : * partitioned table, then we create a pg_constraint row referencing the parent
10608 : * of the referencing side for it and recurse on this routine for each
10609 : * partition.
10610 : *
10611 : * fkconstraint: the constraint being added
10612 : * rel: the root referencing relation
10613 : * pkrel: the referenced relation; might be a partition, if recursing
10614 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10615 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10616 : * top-level constraint
10617 : * numfks: the number of columns in the foreign key
10618 : * pkattnum: the attnum array of referenced attributes
10619 : * fkattnum: the attnum array of referencing attributes
10620 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10621 : * NULL/DEFAULT (...) clause
10622 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10623 : * NULL/DEFAULT clause
10624 : * pf/pp/ffeqoperators: OID array of operators between columns
10625 : * old_check_ok: true if this constraint replaces an existing one that
10626 : * was already validated (thus this one doesn't need validation)
10627 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10628 : * partition, the OIDs of the parent action triggers for DELETE and
10629 : * UPDATE respectively.
10630 : * with_period: true if this is a temporal FK
10631 : */
10632 : static void
10633 3158 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10634 : Relation pkrel, Oid indexOid, Oid parentConstr,
10635 : int numfks,
10636 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10637 : Oid *ppeqoperators, Oid *ffeqoperators,
10638 : int numfkdelsetcols, int16 *fkdelsetcols,
10639 : bool old_check_ok,
10640 : Oid parentDelTrigger, Oid parentUpdTrigger,
10641 : bool with_period)
10642 : {
10643 : Oid deleteTriggerOid,
10644 : updateTriggerOid;
10645 :
10646 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10647 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10648 :
10649 : /*
10650 : * Create the action triggers that enforce the constraint.
10651 : */
10652 3158 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10653 : fkconstraint,
10654 : parentConstr, indexOid,
10655 : parentDelTrigger, parentUpdTrigger,
10656 : &deleteTriggerOid, &updateTriggerOid);
10657 :
10658 : /*
10659 : * If the referenced table is partitioned, recurse on ourselves to handle
10660 : * each partition. We need one pg_constraint row created for each
10661 : * partition in addition to the pg_constraint row for the parent table.
10662 : */
10663 3158 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10664 : {
10665 504 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10666 :
10667 1380 : for (int i = 0; i < pd->nparts; i++)
10668 : {
10669 : Relation partRel;
10670 : AttrMap *map;
10671 : AttrNumber *mapped_pkattnum;
10672 : Oid partIndexId;
10673 : ObjectAddress address;
10674 :
10675 : /* XXX would it be better to acquire these locks beforehand? */
10676 876 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10677 :
10678 : /*
10679 : * Map the attribute numbers in the referenced side of the FK
10680 : * definition to match the partition's column layout.
10681 : */
10682 876 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10683 : RelationGetDescr(pkrel),
10684 : false);
10685 876 : if (map)
10686 : {
10687 130 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10688 272 : for (int j = 0; j < numfks; j++)
10689 142 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10690 : }
10691 : else
10692 746 : mapped_pkattnum = pkattnum;
10693 :
10694 : /* Determine the index to use at this level */
10695 876 : partIndexId = index_get_partition(partRel, indexOid);
10696 876 : if (!OidIsValid(partIndexId))
10697 0 : elog(ERROR, "index for %u not found in partition %s",
10698 : indexOid, RelationGetRelationName(partRel));
10699 :
10700 : /* Create entry at this level ... */
10701 876 : address = addFkConstraint(addFkReferencedSide,
10702 : fkconstraint->conname, fkconstraint, rel,
10703 : partRel, partIndexId, parentConstr,
10704 : numfks, mapped_pkattnum,
10705 : fkattnum, pfeqoperators, ppeqoperators,
10706 : ffeqoperators, numfkdelsetcols,
10707 : fkdelsetcols, true, with_period);
10708 : /* ... and recurse to our children */
10709 876 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10710 : partIndexId, address.objectId, numfks,
10711 : mapped_pkattnum, fkattnum,
10712 : pfeqoperators, ppeqoperators, ffeqoperators,
10713 : numfkdelsetcols, fkdelsetcols,
10714 : old_check_ok,
10715 : deleteTriggerOid, updateTriggerOid,
10716 : with_period);
10717 :
10718 : /* Done -- clean up (but keep the lock) */
10719 876 : table_close(partRel, NoLock);
10720 876 : if (map)
10721 : {
10722 130 : pfree(mapped_pkattnum);
10723 130 : free_attrmap(map);
10724 : }
10725 : }
10726 : }
10727 3158 : }
10728 :
10729 : /*
10730 : * addFkRecurseReferencing
10731 : * Recursive helper for the referencing side of foreign key creation,
10732 : * which creates the check triggers and recurses
10733 : *
10734 : * If the referencing relation is a plain relation, create the necessary check
10735 : * triggers that implement the constraint, and set up for Phase 3 constraint
10736 : * verification. If the referencing relation is a partitioned table, then
10737 : * we create a pg_constraint row for it and recurse on this routine for each
10738 : * partition.
10739 : *
10740 : * We assume that the referenced relation is locked against concurrent
10741 : * deletions. If it's a partitioned relation, every partition must be so
10742 : * locked.
10743 : *
10744 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10745 : * of an ALTER TABLE sequence.
10746 : * fkconstraint: the constraint being added
10747 : * rel: the referencing relation; might be a partition, if recursing
10748 : * pkrel: the root referenced relation
10749 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10750 : * parentConstr: the OID of the parent constraint (there is always one)
10751 : * numfks: the number of columns in the foreign key
10752 : * pkattnum: the attnum array of referenced attributes
10753 : * fkattnum: the attnum array of referencing attributes
10754 : * pf/pp/ffeqoperators: OID array of operators between columns
10755 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10756 : * (...) clause
10757 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10758 : * NULL/DEFAULT clause
10759 : * old_check_ok: true if this constraint replaces an existing one that
10760 : * was already validated (thus this one doesn't need validation)
10761 : * lockmode: the lockmode to acquire on partitions when recursing
10762 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10763 : * a partition, the OIDs of the parent check triggers for INSERT and
10764 : * UPDATE respectively.
10765 : * with_period: true if this is a temporal FK
10766 : */
10767 : static void
10768 2866 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10769 : Relation pkrel, Oid indexOid, Oid parentConstr,
10770 : int numfks, int16 *pkattnum, int16 *fkattnum,
10771 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10772 : int numfkdelsetcols, int16 *fkdelsetcols,
10773 : bool old_check_ok, LOCKMODE lockmode,
10774 : Oid parentInsTrigger, Oid parentUpdTrigger,
10775 : bool with_period)
10776 : {
10777 : Oid insertTriggerOid,
10778 : updateTriggerOid;
10779 :
10780 : Assert(OidIsValid(parentConstr));
10781 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10782 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10783 :
10784 2866 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10785 0 : ereport(ERROR,
10786 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10787 : errmsg("foreign key constraints are not supported on foreign tables")));
10788 :
10789 : /*
10790 : * Add the check triggers to it and, if necessary, schedule it to be
10791 : * checked in Phase 3.
10792 : *
10793 : * If the relation is partitioned, drill down to do it to its partitions.
10794 : */
10795 2866 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10796 : RelationGetRelid(pkrel),
10797 : fkconstraint,
10798 : parentConstr,
10799 : indexOid,
10800 : parentInsTrigger, parentUpdTrigger,
10801 : &insertTriggerOid, &updateTriggerOid);
10802 :
10803 2866 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10804 : {
10805 : /*
10806 : * Tell Phase 3 to check that the constraint is satisfied by existing
10807 : * rows. We can skip this during table creation, when requested
10808 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10809 : * and when we're recreating a constraint following a SET DATA TYPE
10810 : * operation that did not impugn its validity.
10811 : */
10812 2388 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10813 : {
10814 : NewConstraint *newcon;
10815 : AlteredTableInfo *tab;
10816 :
10817 834 : tab = ATGetQueueEntry(wqueue, rel);
10818 :
10819 834 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10820 834 : newcon->name = get_constraint_name(parentConstr);
10821 834 : newcon->contype = CONSTR_FOREIGN;
10822 834 : newcon->refrelid = RelationGetRelid(pkrel);
10823 834 : newcon->refindid = indexOid;
10824 834 : newcon->conid = parentConstr;
10825 834 : newcon->conwithperiod = fkconstraint->fk_with_period;
10826 834 : newcon->qual = (Node *) fkconstraint;
10827 :
10828 834 : tab->constraints = lappend(tab->constraints, newcon);
10829 : }
10830 : }
10831 478 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10832 : {
10833 478 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10834 : Relation trigrel;
10835 :
10836 : /*
10837 : * Triggers of the foreign keys will be manipulated a bunch of times
10838 : * in the loop below. To avoid repeatedly opening/closing the trigger
10839 : * catalog relation, we open it here and pass it to the subroutines
10840 : * called below.
10841 : */
10842 478 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10843 :
10844 : /*
10845 : * Recurse to take appropriate action on each partition; either we
10846 : * find an existing constraint to reparent to ours, or we create a new
10847 : * one.
10848 : */
10849 872 : for (int i = 0; i < pd->nparts; i++)
10850 : {
10851 400 : Relation partition = table_open(pd->oids[i], lockmode);
10852 : List *partFKs;
10853 : AttrMap *attmap;
10854 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10855 : bool attached;
10856 : ObjectAddress address;
10857 :
10858 400 : CheckAlterTableIsSafe(partition);
10859 :
10860 394 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10861 : RelationGetDescr(rel),
10862 : false);
10863 1022 : for (int j = 0; j < numfks; j++)
10864 628 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10865 :
10866 : /* Check whether an existing constraint can be repurposed */
10867 394 : partFKs = copyObject(RelationGetFKeyList(partition));
10868 394 : attached = false;
10869 806 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
10870 : {
10871 30 : if (tryAttachPartitionForeignKey(wqueue,
10872 : fk,
10873 : partition,
10874 : parentConstr,
10875 : numfks,
10876 : mapped_fkattnum,
10877 : pkattnum,
10878 : pfeqoperators,
10879 : insertTriggerOid,
10880 : updateTriggerOid,
10881 : trigrel))
10882 : {
10883 12 : attached = true;
10884 12 : break;
10885 : }
10886 : }
10887 394 : if (attached)
10888 : {
10889 12 : table_close(partition, NoLock);
10890 12 : continue;
10891 : }
10892 :
10893 : /*
10894 : * No luck finding a good constraint to reuse; create our own.
10895 : */
10896 382 : address = addFkConstraint(addFkReferencingSide,
10897 : fkconstraint->conname, fkconstraint,
10898 : partition, pkrel, indexOid, parentConstr,
10899 : numfks, pkattnum,
10900 : mapped_fkattnum, pfeqoperators,
10901 : ppeqoperators, ffeqoperators,
10902 : numfkdelsetcols, fkdelsetcols, true,
10903 : with_period);
10904 :
10905 : /* call ourselves to finalize the creation and we're done */
10906 382 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10907 : indexOid,
10908 : address.objectId,
10909 : numfks,
10910 : pkattnum,
10911 : mapped_fkattnum,
10912 : pfeqoperators,
10913 : ppeqoperators,
10914 : ffeqoperators,
10915 : numfkdelsetcols,
10916 : fkdelsetcols,
10917 : old_check_ok,
10918 : lockmode,
10919 : insertTriggerOid,
10920 : updateTriggerOid,
10921 : with_period);
10922 :
10923 382 : table_close(partition, NoLock);
10924 : }
10925 :
10926 472 : table_close(trigrel, RowExclusiveLock);
10927 : }
10928 2860 : }
10929 :
10930 : /*
10931 : * CloneForeignKeyConstraints
10932 : * Clone foreign keys from a partitioned table to a newly acquired
10933 : * partition.
10934 : *
10935 : * partitionRel is a partition of parentRel, so we can be certain that it has
10936 : * the same columns with the same datatypes. The columns may be in different
10937 : * order, though.
10938 : *
10939 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10940 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10941 : * PARTITION OF).
10942 : */
10943 : static void
10944 9454 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10945 : Relation partitionRel)
10946 : {
10947 : /* This only works for declarative partitioning */
10948 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10949 :
10950 : /*
10951 : * Clone constraints for which the parent is on the referenced side.
10952 : */
10953 9454 : CloneFkReferenced(parentRel, partitionRel);
10954 :
10955 : /*
10956 : * Now clone constraints where the parent is on the referencing side.
10957 : */
10958 9454 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10959 9442 : }
10960 :
10961 : /*
10962 : * CloneFkReferenced
10963 : * Subroutine for CloneForeignKeyConstraints
10964 : *
10965 : * Find all the FKs that have the parent relation on the referenced side;
10966 : * clone those constraints to the given partition. This is to be called
10967 : * when the partition is being created or attached.
10968 : *
10969 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10970 : *
10971 : * This recurses to partitions, if the relation being attached is partitioned.
10972 : * Recursion is done by calling addFkRecurseReferenced.
10973 : */
10974 : static void
10975 9454 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10976 : {
10977 : Relation pg_constraint;
10978 : AttrMap *attmap;
10979 : ListCell *cell;
10980 : SysScanDesc scan;
10981 : ScanKeyData key[2];
10982 : HeapTuple tuple;
10983 9454 : List *clone = NIL;
10984 : Relation trigrel;
10985 :
10986 : /*
10987 : * Search for any constraints where this partition's parent is in the
10988 : * referenced side. However, we must not clone any constraint whose
10989 : * parent constraint is also going to be cloned, to avoid duplicates. So
10990 : * do it in two steps: first construct the list of constraints to clone,
10991 : * then go over that list cloning those whose parents are not in the list.
10992 : * (We must not rely on the parent being seen first, since the catalog
10993 : * scan could return children first.)
10994 : */
10995 9454 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10996 9454 : ScanKeyInit(&key[0],
10997 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10998 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10999 9454 : ScanKeyInit(&key[1],
11000 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11001 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11002 : /* This is a seqscan, as we don't have a usable index ... */
11003 9454 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11004 : NULL, 2, key);
11005 9760 : while ((tuple = systable_getnext(scan)) != NULL)
11006 : {
11007 306 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11008 :
11009 306 : clone = lappend_oid(clone, constrForm->oid);
11010 : }
11011 9454 : systable_endscan(scan);
11012 9454 : table_close(pg_constraint, RowShareLock);
11013 :
11014 : /*
11015 : * Triggers of the foreign keys will be manipulated a bunch of times in
11016 : * the loop below. To avoid repeatedly opening/closing the trigger
11017 : * catalog relation, we open it here and pass it to the subroutines called
11018 : * below.
11019 : */
11020 9454 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11021 :
11022 9454 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11023 : RelationGetDescr(parentRel),
11024 : false);
11025 9760 : foreach(cell, clone)
11026 : {
11027 306 : Oid constrOid = lfirst_oid(cell);
11028 : Form_pg_constraint constrForm;
11029 : Relation fkRel;
11030 : Oid indexOid;
11031 : Oid partIndexId;
11032 : int numfks;
11033 : AttrNumber conkey[INDEX_MAX_KEYS];
11034 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11035 : AttrNumber confkey[INDEX_MAX_KEYS];
11036 : Oid conpfeqop[INDEX_MAX_KEYS];
11037 : Oid conppeqop[INDEX_MAX_KEYS];
11038 : Oid conffeqop[INDEX_MAX_KEYS];
11039 : int numfkdelsetcols;
11040 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11041 : Constraint *fkconstraint;
11042 : ObjectAddress address;
11043 : Oid deleteTriggerOid,
11044 : updateTriggerOid;
11045 :
11046 306 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11047 306 : if (!HeapTupleIsValid(tuple))
11048 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11049 306 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11050 :
11051 : /*
11052 : * As explained above: don't try to clone a constraint for which we're
11053 : * going to clone the parent.
11054 : */
11055 306 : if (list_member_oid(clone, constrForm->conparentid))
11056 : {
11057 126 : ReleaseSysCache(tuple);
11058 180 : continue;
11059 : }
11060 :
11061 : /*
11062 : * Don't clone self-referencing foreign keys, which can be in the
11063 : * partitioned table or in the partition-to-be.
11064 : */
11065 180 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11066 138 : constrForm->conrelid == RelationGetRelid(partitionRel))
11067 : {
11068 54 : ReleaseSysCache(tuple);
11069 54 : continue;
11070 : }
11071 :
11072 : /* We need the same lock level that CreateTrigger will acquire */
11073 126 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11074 :
11075 126 : indexOid = constrForm->conindid;
11076 126 : DeconstructFkConstraintRow(tuple,
11077 : &numfks,
11078 : conkey,
11079 : confkey,
11080 : conpfeqop,
11081 : conppeqop,
11082 : conffeqop,
11083 : &numfkdelsetcols,
11084 : confdelsetcols);
11085 :
11086 258 : for (int i = 0; i < numfks; i++)
11087 132 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11088 :
11089 126 : fkconstraint = makeNode(Constraint);
11090 126 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11091 126 : fkconstraint->conname = NameStr(constrForm->conname);
11092 126 : fkconstraint->deferrable = constrForm->condeferrable;
11093 126 : fkconstraint->initdeferred = constrForm->condeferred;
11094 126 : fkconstraint->location = -1;
11095 126 : fkconstraint->pktable = NULL;
11096 : /* ->fk_attrs determined below */
11097 126 : fkconstraint->pk_attrs = NIL;
11098 126 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11099 126 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11100 126 : fkconstraint->fk_del_action = constrForm->confdeltype;
11101 126 : fkconstraint->fk_del_set_cols = NIL;
11102 126 : fkconstraint->old_conpfeqop = NIL;
11103 126 : fkconstraint->old_pktable_oid = InvalidOid;
11104 126 : fkconstraint->skip_validation = false;
11105 126 : fkconstraint->initially_valid = true;
11106 :
11107 : /* set up colnames that are used to generate the constraint name */
11108 258 : for (int i = 0; i < numfks; i++)
11109 : {
11110 : Form_pg_attribute att;
11111 :
11112 132 : att = TupleDescAttr(RelationGetDescr(fkRel),
11113 132 : conkey[i] - 1);
11114 132 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11115 132 : makeString(NameStr(att->attname)));
11116 : }
11117 :
11118 : /*
11119 : * Add the new foreign key constraint pointing to the new partition.
11120 : * Because this new partition appears in the referenced side of the
11121 : * constraint, we don't need to set up for Phase 3 check.
11122 : */
11123 126 : partIndexId = index_get_partition(partitionRel, indexOid);
11124 126 : if (!OidIsValid(partIndexId))
11125 0 : elog(ERROR, "index for %u not found in partition %s",
11126 : indexOid, RelationGetRelationName(partitionRel));
11127 :
11128 : /*
11129 : * Get the "action" triggers belonging to the constraint to pass as
11130 : * parent OIDs for similar triggers that will be created on the
11131 : * partition in addFkRecurseReferenced().
11132 : */
11133 126 : GetForeignKeyActionTriggers(trigrel, constrOid,
11134 : constrForm->confrelid, constrForm->conrelid,
11135 : &deleteTriggerOid, &updateTriggerOid);
11136 :
11137 : /* Add this constraint ... */
11138 126 : address = addFkConstraint(addFkReferencedSide,
11139 : fkconstraint->conname, fkconstraint, fkRel,
11140 : partitionRel, partIndexId, constrOid,
11141 : numfks, mapped_confkey,
11142 : conkey, conpfeqop, conppeqop, conffeqop,
11143 : numfkdelsetcols, confdelsetcols, false,
11144 126 : constrForm->conperiod);
11145 : /* ... and recurse */
11146 126 : addFkRecurseReferenced(fkconstraint,
11147 : fkRel,
11148 : partitionRel,
11149 : partIndexId,
11150 : address.objectId,
11151 : numfks,
11152 : mapped_confkey,
11153 : conkey,
11154 : conpfeqop,
11155 : conppeqop,
11156 : conffeqop,
11157 : numfkdelsetcols,
11158 : confdelsetcols,
11159 : true,
11160 : deleteTriggerOid,
11161 : updateTriggerOid,
11162 126 : constrForm->conperiod);
11163 :
11164 126 : table_close(fkRel, NoLock);
11165 126 : ReleaseSysCache(tuple);
11166 : }
11167 :
11168 9454 : table_close(trigrel, RowExclusiveLock);
11169 9454 : }
11170 :
11171 : /*
11172 : * CloneFkReferencing
11173 : * Subroutine for CloneForeignKeyConstraints
11174 : *
11175 : * For each FK constraint of the parent relation in the given list, find an
11176 : * equivalent constraint in its partition relation that can be reparented;
11177 : * if one cannot be found, create a new constraint in the partition as its
11178 : * child.
11179 : *
11180 : * If wqueue is given, it is used to set up phase-3 verification for each
11181 : * cloned constraint; omit it if such verification is not needed
11182 : * (example: the partition is being created anew).
11183 : */
11184 : static void
11185 9454 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11186 : {
11187 : AttrMap *attmap;
11188 : List *partFKs;
11189 9454 : List *clone = NIL;
11190 : ListCell *cell;
11191 : Relation trigrel;
11192 :
11193 : /* obtain a list of constraints that we need to clone */
11194 10626 : foreach(cell, RelationGetFKeyList(parentRel))
11195 : {
11196 1178 : ForeignKeyCacheInfo *fk = lfirst(cell);
11197 :
11198 : /*
11199 : * Refuse to attach a table as partition that this partitioned table
11200 : * already has a foreign key to. This isn't useful schema, which is
11201 : * proven by the fact that there have been no user complaints that
11202 : * it's already impossible to achieve this in the opposite direction,
11203 : * i.e., creating a foreign key that references a partition. This
11204 : * restriction allows us to dodge some complexities around
11205 : * pg_constraint and pg_trigger row creations that would be needed
11206 : * during ATTACH/DETACH for this kind of relationship.
11207 : */
11208 1178 : if (fk->confrelid == RelationGetRelid(partRel))
11209 6 : ereport(ERROR,
11210 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11211 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11212 : RelationGetRelationName(partRel),
11213 : get_constraint_name(fk->conoid))));
11214 :
11215 1172 : clone = lappend_oid(clone, fk->conoid);
11216 : }
11217 :
11218 : /*
11219 : * Silently do nothing if there's nothing to do. In particular, this
11220 : * avoids throwing a spurious error for foreign tables.
11221 : */
11222 9448 : if (clone == NIL)
11223 8946 : return;
11224 :
11225 502 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11226 0 : ereport(ERROR,
11227 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11228 : errmsg("foreign key constraints are not supported on foreign tables")));
11229 :
11230 : /*
11231 : * Triggers of the foreign keys will be manipulated a bunch of times in
11232 : * the loop below. To avoid repeatedly opening/closing the trigger
11233 : * catalog relation, we open it here and pass it to the subroutines called
11234 : * below.
11235 : */
11236 502 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11237 :
11238 : /*
11239 : * The constraint key may differ, if the columns in the partition are
11240 : * different. This map is used to convert them.
11241 : */
11242 502 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11243 : RelationGetDescr(parentRel),
11244 : false);
11245 :
11246 502 : partFKs = copyObject(RelationGetFKeyList(partRel));
11247 :
11248 1668 : foreach(cell, clone)
11249 : {
11250 1172 : Oid parentConstrOid = lfirst_oid(cell);
11251 : Form_pg_constraint constrForm;
11252 : Relation pkrel;
11253 : HeapTuple tuple;
11254 : int numfks;
11255 : AttrNumber conkey[INDEX_MAX_KEYS];
11256 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11257 : AttrNumber confkey[INDEX_MAX_KEYS];
11258 : Oid conpfeqop[INDEX_MAX_KEYS];
11259 : Oid conppeqop[INDEX_MAX_KEYS];
11260 : Oid conffeqop[INDEX_MAX_KEYS];
11261 : int numfkdelsetcols;
11262 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11263 : Constraint *fkconstraint;
11264 : bool attached;
11265 : Oid indexOid;
11266 : ObjectAddress address;
11267 : ListCell *lc;
11268 : Oid insertTriggerOid,
11269 : updateTriggerOid;
11270 : bool with_period;
11271 :
11272 1172 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11273 1172 : if (!HeapTupleIsValid(tuple))
11274 0 : elog(ERROR, "cache lookup failed for constraint %u",
11275 : parentConstrOid);
11276 1172 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11277 :
11278 : /* Don't clone constraints whose parents are being cloned */
11279 1172 : if (list_member_oid(clone, constrForm->conparentid))
11280 : {
11281 634 : ReleaseSysCache(tuple);
11282 754 : continue;
11283 : }
11284 :
11285 : /*
11286 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11287 : * relation, that means to lock all partitions.
11288 : */
11289 538 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11290 538 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11291 226 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11292 : ShareRowExclusiveLock, NULL);
11293 :
11294 538 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11295 : conpfeqop, conppeqop, conffeqop,
11296 : &numfkdelsetcols, confdelsetcols);
11297 1280 : for (int i = 0; i < numfks; i++)
11298 742 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11299 :
11300 : /*
11301 : * Get the "check" triggers belonging to the constraint to pass as
11302 : * parent OIDs for similar triggers that will be created on the
11303 : * partition in addFkRecurseReferencing(). They are also passed to
11304 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11305 : * the partition's existing "check" triggers, that is, if the
11306 : * corresponding constraints is deemed attachable to the parent
11307 : * constraint.
11308 : */
11309 538 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11310 : constrForm->confrelid, constrForm->conrelid,
11311 : &insertTriggerOid, &updateTriggerOid);
11312 :
11313 : /*
11314 : * Before creating a new constraint, see whether any existing FKs are
11315 : * fit for the purpose. If one is, attach the parent constraint to
11316 : * it, and don't clone anything. This way we avoid the expensive
11317 : * verification step and don't end up with a duplicate FK, and we
11318 : * don't need to recurse to partitions for this constraint.
11319 : */
11320 538 : attached = false;
11321 748 : foreach(lc, partFKs)
11322 : {
11323 330 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11324 :
11325 330 : if (tryAttachPartitionForeignKey(wqueue,
11326 : fk,
11327 : partRel,
11328 : parentConstrOid,
11329 : numfks,
11330 : mapped_conkey,
11331 : confkey,
11332 : conpfeqop,
11333 : insertTriggerOid,
11334 : updateTriggerOid,
11335 : trigrel))
11336 : {
11337 120 : attached = true;
11338 120 : table_close(pkrel, NoLock);
11339 120 : break;
11340 : }
11341 : }
11342 538 : if (attached)
11343 : {
11344 120 : ReleaseSysCache(tuple);
11345 120 : continue;
11346 : }
11347 :
11348 : /* No dice. Set up to create our own constraint */
11349 418 : fkconstraint = makeNode(Constraint);
11350 418 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11351 : /* ->conname determined below */
11352 418 : fkconstraint->deferrable = constrForm->condeferrable;
11353 418 : fkconstraint->initdeferred = constrForm->condeferred;
11354 418 : fkconstraint->location = -1;
11355 418 : fkconstraint->pktable = NULL;
11356 : /* ->fk_attrs determined below */
11357 418 : fkconstraint->pk_attrs = NIL;
11358 418 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11359 418 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11360 418 : fkconstraint->fk_del_action = constrForm->confdeltype;
11361 418 : fkconstraint->fk_del_set_cols = NIL;
11362 418 : fkconstraint->old_conpfeqop = NIL;
11363 418 : fkconstraint->old_pktable_oid = InvalidOid;
11364 418 : fkconstraint->skip_validation = false;
11365 418 : fkconstraint->initially_valid = constrForm->convalidated;
11366 950 : for (int i = 0; i < numfks; i++)
11367 : {
11368 : Form_pg_attribute att;
11369 :
11370 532 : att = TupleDescAttr(RelationGetDescr(partRel),
11371 532 : mapped_conkey[i] - 1);
11372 532 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11373 532 : makeString(NameStr(att->attname)));
11374 : }
11375 :
11376 418 : indexOid = constrForm->conindid;
11377 418 : with_period = constrForm->conperiod;
11378 :
11379 : /* Create the pg_constraint entry at this level */
11380 418 : address = addFkConstraint(addFkReferencingSide,
11381 418 : NameStr(constrForm->conname), fkconstraint,
11382 : partRel, pkrel, indexOid, parentConstrOid,
11383 : numfks, confkey,
11384 : mapped_conkey, conpfeqop,
11385 : conppeqop, conffeqop,
11386 : numfkdelsetcols, confdelsetcols,
11387 : false, with_period);
11388 :
11389 : /* Done with the cloned constraint's tuple */
11390 418 : ReleaseSysCache(tuple);
11391 :
11392 : /* Create the check triggers, and recurse to partitions, if any */
11393 418 : addFkRecurseReferencing(wqueue,
11394 : fkconstraint,
11395 : partRel,
11396 : pkrel,
11397 : indexOid,
11398 : address.objectId,
11399 : numfks,
11400 : confkey,
11401 : mapped_conkey,
11402 : conpfeqop,
11403 : conppeqop,
11404 : conffeqop,
11405 : numfkdelsetcols,
11406 : confdelsetcols,
11407 : false, /* no old check exists */
11408 : AccessExclusiveLock,
11409 : insertTriggerOid,
11410 : updateTriggerOid,
11411 : with_period);
11412 412 : table_close(pkrel, NoLock);
11413 : }
11414 :
11415 496 : table_close(trigrel, RowExclusiveLock);
11416 : }
11417 :
11418 : /*
11419 : * When the parent of a partition receives [the referencing side of] a foreign
11420 : * key, we must propagate that foreign key to the partition. However, the
11421 : * partition might already have an equivalent foreign key; this routine
11422 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11423 : * by the other parameters. If they are equivalent, create the link between
11424 : * the two constraints and return true.
11425 : *
11426 : * If the given FK does not match the one defined by rest of the params,
11427 : * return false.
11428 : */
11429 : static bool
11430 360 : tryAttachPartitionForeignKey(List **wqueue,
11431 : ForeignKeyCacheInfo *fk,
11432 : Relation partition,
11433 : Oid parentConstrOid,
11434 : int numfks,
11435 : AttrNumber *mapped_conkey,
11436 : AttrNumber *confkey,
11437 : Oid *conpfeqop,
11438 : Oid parentInsTrigger,
11439 : Oid parentUpdTrigger,
11440 : Relation trigrel)
11441 : {
11442 : HeapTuple parentConstrTup;
11443 : Form_pg_constraint parentConstr;
11444 : HeapTuple partcontup;
11445 : Form_pg_constraint partConstr;
11446 : bool queueValidation;
11447 : ScanKeyData key;
11448 : SysScanDesc scan;
11449 : HeapTuple trigtup;
11450 : Oid insertTriggerOid,
11451 : updateTriggerOid;
11452 :
11453 360 : parentConstrTup = SearchSysCache1(CONSTROID,
11454 : ObjectIdGetDatum(parentConstrOid));
11455 360 : if (!HeapTupleIsValid(parentConstrTup))
11456 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11457 360 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11458 :
11459 : /*
11460 : * Do some quick & easy initial checks. If any of these fail, we cannot
11461 : * use this constraint.
11462 : */
11463 360 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11464 : {
11465 120 : ReleaseSysCache(parentConstrTup);
11466 120 : return false;
11467 : }
11468 672 : for (int i = 0; i < numfks; i++)
11469 : {
11470 432 : if (fk->conkey[i] != mapped_conkey[i] ||
11471 432 : fk->confkey[i] != confkey[i] ||
11472 432 : fk->conpfeqop[i] != conpfeqop[i])
11473 : {
11474 0 : ReleaseSysCache(parentConstrTup);
11475 0 : return false;
11476 : }
11477 : }
11478 :
11479 : /* Looks good so far; perform more extensive checks. */
11480 240 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11481 240 : if (!HeapTupleIsValid(partcontup))
11482 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11483 240 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11484 240 : if (OidIsValid(partConstr->conparentid) ||
11485 210 : partConstr->condeferrable != parentConstr->condeferrable ||
11486 182 : partConstr->condeferred != parentConstr->condeferred ||
11487 182 : partConstr->confupdtype != parentConstr->confupdtype ||
11488 146 : partConstr->confdeltype != parentConstr->confdeltype ||
11489 146 : partConstr->confmatchtype != parentConstr->confmatchtype)
11490 : {
11491 108 : ReleaseSysCache(parentConstrTup);
11492 108 : ReleaseSysCache(partcontup);
11493 108 : return false;
11494 : }
11495 :
11496 : /*
11497 : * Will we need to validate this constraint? A valid parent constraint
11498 : * implies that all child constraints have been validated, so if this one
11499 : * isn't, we must trigger phase 3 validation.
11500 : */
11501 132 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11502 :
11503 132 : ReleaseSysCache(partcontup);
11504 132 : ReleaseSysCache(parentConstrTup);
11505 :
11506 : /*
11507 : * Looks good! Attach this constraint. The action triggers in the new
11508 : * partition become redundant -- the parent table already has equivalent
11509 : * ones, and those will be able to reach the partition. Remove the ones
11510 : * in the partition. We identify them because they have our constraint
11511 : * OID, as well as being on the referenced rel.
11512 : */
11513 132 : ScanKeyInit(&key,
11514 : Anum_pg_trigger_tgconstraint,
11515 : BTEqualStrategyNumber, F_OIDEQ,
11516 : ObjectIdGetDatum(fk->conoid));
11517 132 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11518 : NULL, 1, &key);
11519 660 : while ((trigtup = systable_getnext(scan)) != NULL)
11520 : {
11521 528 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11522 : ObjectAddress trigger;
11523 :
11524 528 : if (trgform->tgconstrrelid != fk->conrelid)
11525 264 : continue;
11526 264 : if (trgform->tgrelid != fk->confrelid)
11527 0 : continue;
11528 :
11529 : /*
11530 : * The constraint is originally set up to contain this trigger as an
11531 : * implementation object, so there's a dependency record that links
11532 : * the two; however, since the trigger is no longer needed, we remove
11533 : * the dependency link in order to be able to drop the trigger while
11534 : * keeping the constraint intact.
11535 : */
11536 264 : deleteDependencyRecordsFor(TriggerRelationId,
11537 : trgform->oid,
11538 : false);
11539 : /* make dependency deletion visible to performDeletion */
11540 264 : CommandCounterIncrement();
11541 264 : ObjectAddressSet(trigger, TriggerRelationId,
11542 : trgform->oid);
11543 264 : performDeletion(&trigger, DROP_RESTRICT, 0);
11544 : /* make trigger drop visible, in case the loop iterates */
11545 264 : CommandCounterIncrement();
11546 : }
11547 :
11548 132 : systable_endscan(scan);
11549 :
11550 132 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
11551 : RelationGetRelid(partition));
11552 :
11553 : /*
11554 : * Like the constraint, attach partition's "check" triggers to the
11555 : * corresponding parent triggers.
11556 : */
11557 132 : GetForeignKeyCheckTriggers(trigrel,
11558 : fk->conoid, fk->confrelid, fk->conrelid,
11559 : &insertTriggerOid, &updateTriggerOid);
11560 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11561 132 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11562 : RelationGetRelid(partition));
11563 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11564 132 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11565 : RelationGetRelid(partition));
11566 :
11567 : /*
11568 : * If the referenced table is partitioned, then the partition we're
11569 : * attaching now has extra pg_constraint rows and action triggers that are
11570 : * no longer needed. Remove those.
11571 : */
11572 132 : if (get_rel_relkind(fk->confrelid) == RELKIND_PARTITIONED_TABLE)
11573 : {
11574 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11575 : ObjectAddresses *objs;
11576 : HeapTuple consttup;
11577 :
11578 24 : ScanKeyInit(&key,
11579 : Anum_pg_constraint_conrelid,
11580 : BTEqualStrategyNumber, F_OIDEQ,
11581 : ObjectIdGetDatum(fk->conrelid));
11582 :
11583 24 : scan = systable_beginscan(pg_constraint,
11584 : ConstraintRelidTypidNameIndexId,
11585 : true, NULL, 1, &key);
11586 24 : objs = new_object_addresses();
11587 240 : while ((consttup = systable_getnext(scan)) != NULL)
11588 : {
11589 216 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11590 :
11591 216 : if (conform->conparentid != fk->conoid)
11592 168 : continue;
11593 : else
11594 : {
11595 : ObjectAddress addr;
11596 : SysScanDesc scan2;
11597 : ScanKeyData key2;
11598 : int n PG_USED_FOR_ASSERTS_ONLY;
11599 :
11600 48 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11601 48 : add_exact_object_address(&addr, objs);
11602 :
11603 : /*
11604 : * First we must delete the dependency record that binds the
11605 : * constraint records together.
11606 : */
11607 48 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11608 : conform->oid,
11609 : DEPENDENCY_INTERNAL,
11610 : ConstraintRelationId,
11611 : fk->conoid);
11612 : Assert(n == 1); /* actually only one is expected */
11613 :
11614 : /*
11615 : * Now search for the triggers for this constraint and set
11616 : * them up for deletion too
11617 : */
11618 48 : ScanKeyInit(&key2,
11619 : Anum_pg_trigger_tgconstraint,
11620 : BTEqualStrategyNumber, F_OIDEQ,
11621 : ObjectIdGetDatum(conform->oid));
11622 48 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11623 : true, NULL, 1, &key2);
11624 144 : while ((trigtup = systable_getnext(scan2)) != NULL)
11625 : {
11626 96 : ObjectAddressSet(addr, TriggerRelationId,
11627 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11628 96 : add_exact_object_address(&addr, objs);
11629 : }
11630 48 : systable_endscan(scan2);
11631 : }
11632 : }
11633 : /* make the dependency deletions visible */
11634 24 : CommandCounterIncrement();
11635 24 : performMultipleDeletions(objs, DROP_RESTRICT,
11636 : PERFORM_DELETION_INTERNAL);
11637 24 : systable_endscan(scan);
11638 :
11639 24 : table_close(pg_constraint, RowShareLock);
11640 : }
11641 :
11642 : /*
11643 : * We updated this pg_constraint row above to set its parent; validating
11644 : * it will cause its convalidated flag to change, so we need CCI here. In
11645 : * addition, we need it unconditionally for the rare case where the parent
11646 : * table has *two* identical constraints; when reaching this function for
11647 : * the second one, we must have made our changes visible, otherwise we
11648 : * would try to attach both to this one.
11649 : */
11650 132 : CommandCounterIncrement();
11651 :
11652 : /* If validation is needed, put it in the queue now. */
11653 132 : if (queueValidation)
11654 : {
11655 : Relation conrel;
11656 :
11657 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11658 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11659 18 : if (!HeapTupleIsValid(partcontup))
11660 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11661 :
11662 : /* Use the same lock as for AT_ValidateConstraint */
11663 18 : QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11664 : ShareUpdateExclusiveLock);
11665 18 : ReleaseSysCache(partcontup);
11666 18 : table_close(conrel, RowExclusiveLock);
11667 : }
11668 :
11669 132 : return true;
11670 : }
11671 :
11672 : /*
11673 : * GetForeignKeyActionTriggers
11674 : * Returns delete and update "action" triggers of the given relation
11675 : * belonging to the given constraint
11676 : */
11677 : static void
11678 126 : GetForeignKeyActionTriggers(Relation trigrel,
11679 : Oid conoid, Oid confrelid, Oid conrelid,
11680 : Oid *deleteTriggerOid,
11681 : Oid *updateTriggerOid)
11682 : {
11683 : ScanKeyData key;
11684 : SysScanDesc scan;
11685 : HeapTuple trigtup;
11686 :
11687 126 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11688 126 : ScanKeyInit(&key,
11689 : Anum_pg_trigger_tgconstraint,
11690 : BTEqualStrategyNumber, F_OIDEQ,
11691 : ObjectIdGetDatum(conoid));
11692 :
11693 126 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11694 : NULL, 1, &key);
11695 252 : while ((trigtup = systable_getnext(scan)) != NULL)
11696 : {
11697 252 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11698 :
11699 252 : if (trgform->tgconstrrelid != conrelid)
11700 0 : continue;
11701 252 : if (trgform->tgrelid != confrelid)
11702 0 : continue;
11703 : /* Only ever look at "action" triggers on the PK side. */
11704 252 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11705 0 : continue;
11706 252 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11707 : {
11708 : Assert(*deleteTriggerOid == InvalidOid);
11709 126 : *deleteTriggerOid = trgform->oid;
11710 : }
11711 126 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11712 : {
11713 : Assert(*updateTriggerOid == InvalidOid);
11714 126 : *updateTriggerOid = trgform->oid;
11715 : }
11716 : #ifndef USE_ASSERT_CHECKING
11717 : /* In an assert-enabled build, continue looking to find duplicates */
11718 252 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11719 126 : break;
11720 : #endif
11721 : }
11722 :
11723 126 : if (!OidIsValid(*deleteTriggerOid))
11724 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11725 : conoid);
11726 126 : if (!OidIsValid(*updateTriggerOid))
11727 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11728 : conoid);
11729 :
11730 126 : systable_endscan(scan);
11731 126 : }
11732 :
11733 : /*
11734 : * GetForeignKeyCheckTriggers
11735 : * Returns insert and update "check" triggers of the given relation
11736 : * belonging to the given constraint
11737 : */
11738 : static void
11739 760 : GetForeignKeyCheckTriggers(Relation trigrel,
11740 : Oid conoid, Oid confrelid, Oid conrelid,
11741 : Oid *insertTriggerOid,
11742 : Oid *updateTriggerOid)
11743 : {
11744 : ScanKeyData key;
11745 : SysScanDesc scan;
11746 : HeapTuple trigtup;
11747 :
11748 760 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11749 760 : ScanKeyInit(&key,
11750 : Anum_pg_trigger_tgconstraint,
11751 : BTEqualStrategyNumber, F_OIDEQ,
11752 : ObjectIdGetDatum(conoid));
11753 :
11754 760 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11755 : NULL, 1, &key);
11756 2476 : while ((trigtup = systable_getnext(scan)) != NULL)
11757 : {
11758 2476 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11759 :
11760 2476 : if (trgform->tgconstrrelid != confrelid)
11761 872 : continue;
11762 1604 : if (trgform->tgrelid != conrelid)
11763 0 : continue;
11764 : /* Only ever look at "check" triggers on the FK side. */
11765 1604 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11766 84 : continue;
11767 1520 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11768 : {
11769 : Assert(*insertTriggerOid == InvalidOid);
11770 760 : *insertTriggerOid = trgform->oid;
11771 : }
11772 760 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11773 : {
11774 : Assert(*updateTriggerOid == InvalidOid);
11775 760 : *updateTriggerOid = trgform->oid;
11776 : }
11777 : #ifndef USE_ASSERT_CHECKING
11778 : /* In an assert-enabled build, continue looking to find duplicates. */
11779 1520 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11780 760 : break;
11781 : #endif
11782 : }
11783 :
11784 760 : if (!OidIsValid(*insertTriggerOid))
11785 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11786 : conoid);
11787 760 : if (!OidIsValid(*updateTriggerOid))
11788 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11789 : conoid);
11790 :
11791 760 : systable_endscan(scan);
11792 760 : }
11793 :
11794 : /*
11795 : * ALTER TABLE ALTER CONSTRAINT
11796 : *
11797 : * Update the attributes of a constraint.
11798 : *
11799 : * Currently only works for Foreign Key constraints.
11800 : *
11801 : * If the constraint is modified, returns its address; otherwise, return
11802 : * InvalidObjectAddress.
11803 : */
11804 : static ObjectAddress
11805 126 : ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
11806 : LOCKMODE lockmode)
11807 : {
11808 : Relation conrel;
11809 : Relation tgrel;
11810 : SysScanDesc scan;
11811 : ScanKeyData skey[3];
11812 : HeapTuple contuple;
11813 : Form_pg_constraint currcon;
11814 : ObjectAddress address;
11815 126 : List *otherrelids = NIL;
11816 :
11817 : /*
11818 : * Disallow altering ONLY a partitioned table, as it would make no sense.
11819 : * This is okay for legacy inheritance.
11820 : */
11821 126 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
11822 0 : ereport(ERROR,
11823 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11824 : errmsg("constraint must be altered in child tables too"),
11825 : errhint("Do not specify the ONLY keyword."));
11826 :
11827 :
11828 126 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11829 126 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11830 :
11831 : /*
11832 : * Find and check the target constraint
11833 : */
11834 126 : ScanKeyInit(&skey[0],
11835 : Anum_pg_constraint_conrelid,
11836 : BTEqualStrategyNumber, F_OIDEQ,
11837 : ObjectIdGetDatum(RelationGetRelid(rel)));
11838 126 : ScanKeyInit(&skey[1],
11839 : Anum_pg_constraint_contypid,
11840 : BTEqualStrategyNumber, F_OIDEQ,
11841 : ObjectIdGetDatum(InvalidOid));
11842 126 : ScanKeyInit(&skey[2],
11843 : Anum_pg_constraint_conname,
11844 : BTEqualStrategyNumber, F_NAMEEQ,
11845 126 : CStringGetDatum(cmdcon->conname));
11846 126 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11847 : true, NULL, 3, skey);
11848 :
11849 : /* There can be at most one matching row */
11850 126 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11851 0 : ereport(ERROR,
11852 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11853 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11854 : cmdcon->conname, RelationGetRelationName(rel))));
11855 :
11856 126 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11857 126 : if (currcon->contype != CONSTRAINT_FOREIGN)
11858 0 : ereport(ERROR,
11859 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11860 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11861 : cmdcon->conname, RelationGetRelationName(rel))));
11862 :
11863 : /*
11864 : * If it's not the topmost constraint, raise an error.
11865 : *
11866 : * Altering a non-topmost constraint leaves some triggers untouched, since
11867 : * they are not directly connected to this constraint; also, pg_dump would
11868 : * ignore the deferrability status of the individual constraint, since it
11869 : * only dumps topmost constraints. Avoid these problems by refusing this
11870 : * operation and telling the user to alter the parent constraint instead.
11871 : */
11872 126 : if (OidIsValid(currcon->conparentid))
11873 : {
11874 : HeapTuple tp;
11875 12 : Oid parent = currcon->conparentid;
11876 12 : char *ancestorname = NULL;
11877 12 : char *ancestortable = NULL;
11878 :
11879 : /* Loop to find the topmost constraint */
11880 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11881 : {
11882 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11883 :
11884 : /* If no parent, this is the constraint we want */
11885 24 : if (!OidIsValid(contup->conparentid))
11886 : {
11887 12 : ancestorname = pstrdup(NameStr(contup->conname));
11888 12 : ancestortable = get_rel_name(contup->conrelid);
11889 12 : ReleaseSysCache(tp);
11890 12 : break;
11891 : }
11892 :
11893 12 : parent = contup->conparentid;
11894 12 : ReleaseSysCache(tp);
11895 : }
11896 :
11897 12 : ereport(ERROR,
11898 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11899 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11900 : cmdcon->conname, RelationGetRelationName(rel)),
11901 : ancestorname && ancestortable ?
11902 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11903 : cmdcon->conname, ancestorname, ancestortable) : 0,
11904 : errhint("You may alter the constraint it derives from instead.")));
11905 : }
11906 :
11907 114 : address = InvalidObjectAddress;
11908 :
11909 : /*
11910 : * Do the actual catalog work, and recurse if necessary.
11911 : */
11912 114 : if (ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, rel, contuple,
11913 : recurse, &otherrelids, lockmode))
11914 114 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11915 :
11916 : /*
11917 : * ATExecAlterConstraintInternal already invalidated relcache for the
11918 : * relations having the constraint itself; here we also invalidate for
11919 : * relations that have any triggers that are part of the constraint.
11920 : */
11921 372 : foreach_oid(relid, otherrelids)
11922 144 : CacheInvalidateRelcacheByRelid(relid);
11923 :
11924 114 : systable_endscan(scan);
11925 :
11926 114 : table_close(tgrel, RowExclusiveLock);
11927 114 : table_close(conrel, RowExclusiveLock);
11928 :
11929 114 : return address;
11930 : }
11931 :
11932 : /*
11933 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11934 : * constraint is altered.
11935 : *
11936 : * *otherrelids is appended OIDs of relations containing affected triggers.
11937 : *
11938 : * Note that we must recurse even when the values are correct, in case
11939 : * indirect descendants have had their constraints altered locally.
11940 : * (This could be avoided if we forbade altering constraints in partitions
11941 : * but existing releases don't do that.)
11942 : */
11943 : static bool
11944 180 : ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
11945 : Relation tgrel, Relation rel, HeapTuple contuple,
11946 : bool recurse, List **otherrelids, LOCKMODE lockmode)
11947 : {
11948 : Form_pg_constraint currcon;
11949 180 : Oid refrelid = InvalidOid;
11950 180 : bool changed = false;
11951 :
11952 : /* since this function recurses, it could be driven to stack overflow */
11953 180 : check_stack_depth();
11954 :
11955 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11956 180 : if (currcon->contype == CONSTRAINT_FOREIGN)
11957 180 : refrelid = currcon->confrelid;
11958 :
11959 : /*
11960 : * Update pg_constraint with the flags from cmdcon.
11961 : *
11962 : * If called to modify a constraint that's already in the desired state,
11963 : * silently do nothing.
11964 : */
11965 180 : if (cmdcon->alterDeferrability &&
11966 180 : (currcon->condeferrable != cmdcon->deferrable ||
11967 6 : currcon->condeferred != cmdcon->initdeferred))
11968 : {
11969 : HeapTuple copyTuple;
11970 : Form_pg_constraint copy_con;
11971 :
11972 180 : copyTuple = heap_copytuple(contuple);
11973 180 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11974 180 : copy_con->condeferrable = cmdcon->deferrable;
11975 180 : copy_con->condeferred = cmdcon->initdeferred;
11976 180 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11977 :
11978 180 : InvokeObjectPostAlterHook(ConstraintRelationId, currcon->oid, 0);
11979 :
11980 180 : heap_freetuple(copyTuple);
11981 180 : changed = true;
11982 :
11983 : /* Make new constraint flags visible to others */
11984 180 : CacheInvalidateRelcache(rel);
11985 :
11986 : /*
11987 : * Now we need to update the multiple entries in pg_trigger that
11988 : * implement the constraint.
11989 : */
11990 180 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
11991 180 : cmdcon->deferrable,
11992 180 : cmdcon->initdeferred, otherrelids);
11993 : }
11994 :
11995 : /*
11996 : * If the table at either end of the constraint is partitioned, we need to
11997 : * handle every constraint that is a child of this one.
11998 : *
11999 : * Note that this doesn't handle recursion the normal way, viz. by
12000 : * scanning the list of child relations and recursing; instead it uses the
12001 : * conparentid relationships. This may need to be reconsidered.
12002 : */
12003 180 : if (recurse && changed &&
12004 180 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12005 156 : (OidIsValid(refrelid) &&
12006 156 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)))
12007 : {
12008 : ScanKeyData pkey;
12009 : SysScanDesc pscan;
12010 : HeapTuple childtup;
12011 :
12012 42 : ScanKeyInit(&pkey,
12013 : Anum_pg_constraint_conparentid,
12014 : BTEqualStrategyNumber, F_OIDEQ,
12015 : ObjectIdGetDatum(currcon->oid));
12016 :
12017 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12018 : true, NULL, 1, &pkey);
12019 :
12020 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12021 : {
12022 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12023 : Relation childrel;
12024 :
12025 66 : childrel = table_open(childcon->conrelid, lockmode);
12026 66 : ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, childrel, childtup,
12027 : recurse, otherrelids, lockmode);
12028 66 : table_close(childrel, NoLock);
12029 : }
12030 :
12031 42 : systable_endscan(pscan);
12032 : }
12033 :
12034 180 : return changed;
12035 : }
12036 :
12037 : /*
12038 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12039 : * trigger's deferrability.
12040 : *
12041 : * The arguments to this function have the same meaning as the arguments to
12042 : * ATExecAlterConstrDeferrability.
12043 : */
12044 : static void
12045 180 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12046 : bool deferrable, bool initdeferred,
12047 : List **otherrelids)
12048 : {
12049 : HeapTuple tgtuple;
12050 : ScanKeyData tgkey;
12051 : SysScanDesc tgscan;
12052 :
12053 180 : ScanKeyInit(&tgkey,
12054 : Anum_pg_trigger_tgconstraint,
12055 : BTEqualStrategyNumber, F_OIDEQ,
12056 : ObjectIdGetDatum(conoid));
12057 180 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12058 : NULL, 1, &tgkey);
12059 768 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12060 : {
12061 588 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12062 : Form_pg_trigger copy_tg;
12063 : HeapTuple tgCopyTuple;
12064 :
12065 : /*
12066 : * Remember OIDs of other relation(s) involved in FK constraint.
12067 : * (Note: it's likely that we could skip forcing a relcache inval for
12068 : * other rels that don't have a trigger whose properties change, but
12069 : * let's be conservative.)
12070 : */
12071 588 : if (tgform->tgrelid != RelationGetRelid(rel))
12072 288 : *otherrelids = list_append_unique_oid(*otherrelids,
12073 : tgform->tgrelid);
12074 :
12075 : /*
12076 : * Update enable status and deferrability of RI_FKey_noaction_del,
12077 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12078 : * triggers, but not others; see createForeignKeyActionTriggers and
12079 : * CreateFKCheckTrigger.
12080 : */
12081 588 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12082 474 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12083 342 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12084 192 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12085 42 : continue;
12086 :
12087 546 : tgCopyTuple = heap_copytuple(tgtuple);
12088 546 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12089 :
12090 546 : copy_tg->tgdeferrable = deferrable;
12091 546 : copy_tg->tginitdeferred = initdeferred;
12092 546 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12093 :
12094 546 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12095 :
12096 546 : heap_freetuple(tgCopyTuple);
12097 : }
12098 :
12099 180 : systable_endscan(tgscan);
12100 180 : }
12101 :
12102 : /*
12103 : * ALTER TABLE VALIDATE CONSTRAINT
12104 : *
12105 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12106 : * there's no good way to skip recursing when handling foreign keys: there is
12107 : * no need to lock children in that case, yet we wouldn't be able to avoid
12108 : * doing so at that level.
12109 : *
12110 : * Return value is the address of the validated constraint. If the constraint
12111 : * was already validated, InvalidObjectAddress is returned.
12112 : */
12113 : static ObjectAddress
12114 460 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12115 : bool recurse, bool recursing, LOCKMODE lockmode)
12116 : {
12117 : Relation conrel;
12118 : SysScanDesc scan;
12119 : ScanKeyData skey[3];
12120 : HeapTuple tuple;
12121 : Form_pg_constraint con;
12122 : ObjectAddress address;
12123 :
12124 460 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12125 :
12126 : /*
12127 : * Find and check the target constraint
12128 : */
12129 460 : ScanKeyInit(&skey[0],
12130 : Anum_pg_constraint_conrelid,
12131 : BTEqualStrategyNumber, F_OIDEQ,
12132 : ObjectIdGetDatum(RelationGetRelid(rel)));
12133 460 : ScanKeyInit(&skey[1],
12134 : Anum_pg_constraint_contypid,
12135 : BTEqualStrategyNumber, F_OIDEQ,
12136 : ObjectIdGetDatum(InvalidOid));
12137 460 : ScanKeyInit(&skey[2],
12138 : Anum_pg_constraint_conname,
12139 : BTEqualStrategyNumber, F_NAMEEQ,
12140 : CStringGetDatum(constrName));
12141 460 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12142 : true, NULL, 3, skey);
12143 :
12144 : /* There can be at most one matching row */
12145 460 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12146 0 : ereport(ERROR,
12147 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12148 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12149 : constrName, RelationGetRelationName(rel))));
12150 :
12151 460 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12152 460 : if (con->contype != CONSTRAINT_FOREIGN &&
12153 144 : con->contype != CONSTRAINT_CHECK)
12154 0 : ereport(ERROR,
12155 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12156 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12157 : constrName, RelationGetRelationName(rel))));
12158 :
12159 460 : if (!con->conenforced)
12160 6 : ereport(ERROR,
12161 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12162 : errmsg("cannot validate NOT ENFORCED constraint")));
12163 :
12164 454 : if (!con->convalidated)
12165 : {
12166 436 : if (con->contype == CONSTRAINT_FOREIGN)
12167 : {
12168 310 : QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12169 : }
12170 126 : else if (con->contype == CONSTRAINT_CHECK)
12171 : {
12172 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12173 : tuple, recurse, recursing, lockmode);
12174 : }
12175 :
12176 436 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12177 : }
12178 : else
12179 18 : address = InvalidObjectAddress; /* already validated */
12180 :
12181 454 : systable_endscan(scan);
12182 :
12183 454 : table_close(conrel, RowExclusiveLock);
12184 :
12185 454 : return address;
12186 : }
12187 :
12188 : /*
12189 : * QueueFKConstraintValidation
12190 : *
12191 : * Add an entry to the wqueue to validate the given foreign key constraint in
12192 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12193 : * for the specified relation and all its children.
12194 : */
12195 : static void
12196 334 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12197 : HeapTuple contuple, LOCKMODE lockmode)
12198 : {
12199 : Form_pg_constraint con;
12200 : AlteredTableInfo *tab;
12201 : HeapTuple copyTuple;
12202 : Form_pg_constraint copy_con;
12203 :
12204 334 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12205 : Assert(con->contype == CONSTRAINT_FOREIGN);
12206 : Assert(!con->convalidated);
12207 :
12208 334 : if (rel->rd_rel->relkind == RELKIND_RELATION)
12209 : {
12210 : NewConstraint *newcon;
12211 : Constraint *fkconstraint;
12212 :
12213 : /* Queue validation for phase 3 */
12214 322 : fkconstraint = makeNode(Constraint);
12215 : /* for now this is all we need */
12216 322 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12217 :
12218 322 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12219 322 : newcon->name = fkconstraint->conname;
12220 322 : newcon->contype = CONSTR_FOREIGN;
12221 322 : newcon->refrelid = con->confrelid;
12222 322 : newcon->refindid = con->conindid;
12223 322 : newcon->conid = con->oid;
12224 322 : newcon->qual = (Node *) fkconstraint;
12225 :
12226 : /* Find or create work queue entry for this table */
12227 322 : tab = ATGetQueueEntry(wqueue, rel);
12228 322 : tab->constraints = lappend(tab->constraints, newcon);
12229 : }
12230 :
12231 : /*
12232 : * If the table at either end of the constraint is partitioned, we need to
12233 : * recurse and handle every constraint that is a child of this constraint.
12234 : */
12235 656 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12236 322 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12237 : {
12238 : ScanKeyData pkey;
12239 : SysScanDesc pscan;
12240 : HeapTuple childtup;
12241 :
12242 18 : ScanKeyInit(&pkey,
12243 : Anum_pg_constraint_conparentid,
12244 : BTEqualStrategyNumber, F_OIDEQ,
12245 : ObjectIdGetDatum(con->oid));
12246 :
12247 18 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12248 : true, NULL, 1, &pkey);
12249 :
12250 36 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12251 : {
12252 : Form_pg_constraint childcon;
12253 : Relation childrel;
12254 :
12255 18 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12256 :
12257 : /*
12258 : * If the child constraint has already been validated, no further
12259 : * action is required for it or its descendants, as they are all
12260 : * valid.
12261 : */
12262 18 : if (childcon->convalidated)
12263 12 : continue;
12264 :
12265 6 : childrel = table_open(childcon->conrelid, lockmode);
12266 :
12267 6 : QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
12268 : lockmode);
12269 6 : table_close(childrel, NoLock);
12270 : }
12271 :
12272 18 : systable_endscan(pscan);
12273 : }
12274 :
12275 : /*
12276 : * Now update the catalog, while we have the door open.
12277 : */
12278 334 : copyTuple = heap_copytuple(contuple);
12279 334 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12280 334 : copy_con->convalidated = true;
12281 334 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12282 :
12283 334 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12284 :
12285 334 : heap_freetuple(copyTuple);
12286 334 : }
12287 :
12288 : /*
12289 : * QueueCheckConstraintValidation
12290 : *
12291 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
12292 : * and update the convalidated field in the pg_constraint catalog for the
12293 : * specified relation and all its inheriting children.
12294 : */
12295 : static void
12296 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12297 : char *constrName, HeapTuple contuple,
12298 : bool recurse, bool recursing, LOCKMODE lockmode)
12299 : {
12300 : Form_pg_constraint con;
12301 : AlteredTableInfo *tab;
12302 : HeapTuple copyTuple;
12303 : Form_pg_constraint copy_con;
12304 :
12305 126 : List *children = NIL;
12306 : ListCell *child;
12307 : NewConstraint *newcon;
12308 : Datum val;
12309 : char *conbin;
12310 :
12311 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12312 : Assert(con->contype == CONSTRAINT_CHECK);
12313 :
12314 : /*
12315 : * If we're recursing, the parent has already done this, so skip it. Also,
12316 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
12317 : * for it in the children.
12318 : */
12319 126 : if (!recursing && !con->connoinherit)
12320 72 : children = find_all_inheritors(RelationGetRelid(rel),
12321 : lockmode, NULL);
12322 :
12323 : /*
12324 : * For CHECK constraints, we must ensure that we only mark the constraint
12325 : * as validated on the parent if it's already validated on the children.
12326 : *
12327 : * We recurse before validating on the parent, to reduce risk of
12328 : * deadlocks.
12329 : */
12330 246 : foreach(child, children)
12331 : {
12332 120 : Oid childoid = lfirst_oid(child);
12333 : Relation childrel;
12334 :
12335 120 : if (childoid == RelationGetRelid(rel))
12336 72 : continue;
12337 :
12338 : /*
12339 : * If we are told not to recurse, there had better not be any child
12340 : * tables, because we can't mark the constraint on the parent valid
12341 : * unless it is valid for all child tables.
12342 : */
12343 48 : if (!recurse)
12344 0 : ereport(ERROR,
12345 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12346 : errmsg("constraint must be validated on child tables too")));
12347 :
12348 : /* find_all_inheritors already got lock */
12349 48 : childrel = table_open(childoid, NoLock);
12350 :
12351 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12352 : true, lockmode);
12353 48 : table_close(childrel, NoLock);
12354 : }
12355 :
12356 : /* Queue validation for phase 3 */
12357 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12358 126 : newcon->name = constrName;
12359 126 : newcon->contype = CONSTR_CHECK;
12360 126 : newcon->refrelid = InvalidOid;
12361 126 : newcon->refindid = InvalidOid;
12362 126 : newcon->conid = con->oid;
12363 :
12364 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
12365 : Anum_pg_constraint_conbin);
12366 126 : conbin = TextDatumGetCString(val);
12367 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
12368 :
12369 : /* Find or create work queue entry for this table */
12370 126 : tab = ATGetQueueEntry(wqueue, rel);
12371 126 : tab->constraints = lappend(tab->constraints, newcon);
12372 :
12373 : /*
12374 : * Invalidate relcache so that others see the new validated constraint.
12375 : */
12376 126 : CacheInvalidateRelcache(rel);
12377 :
12378 : /*
12379 : * Now update the catalog, while we have the door open.
12380 : */
12381 126 : copyTuple = heap_copytuple(contuple);
12382 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12383 126 : copy_con->convalidated = true;
12384 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12385 :
12386 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12387 :
12388 126 : heap_freetuple(copyTuple);
12389 126 : }
12390 :
12391 : /*
12392 : * transformColumnNameList - transform list of column names
12393 : *
12394 : * Lookup each name and return its attnum and, optionally, type and collation
12395 : * OIDs
12396 : *
12397 : * Note: the name of this function suggests that it's general-purpose,
12398 : * but actually it's only used to look up names appearing in foreign-key
12399 : * clauses. The error messages would need work to use it in other cases,
12400 : * and perhaps the validity checks as well.
12401 : */
12402 : static int
12403 6454 : transformColumnNameList(Oid relId, List *colList,
12404 : int16 *attnums, Oid *atttypids, Oid *attcollids)
12405 : {
12406 : ListCell *l;
12407 : int attnum;
12408 :
12409 6454 : attnum = 0;
12410 11790 : foreach(l, colList)
12411 : {
12412 5402 : char *attname = strVal(lfirst(l));
12413 : HeapTuple atttuple;
12414 : Form_pg_attribute attform;
12415 :
12416 5402 : atttuple = SearchSysCacheAttName(relId, attname);
12417 5402 : if (!HeapTupleIsValid(atttuple))
12418 54 : ereport(ERROR,
12419 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12420 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12421 : attname)));
12422 5348 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12423 5348 : if (attform->attnum < 0)
12424 12 : ereport(ERROR,
12425 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12426 : errmsg("system columns cannot be used in foreign keys")));
12427 5336 : if (attnum >= INDEX_MAX_KEYS)
12428 0 : ereport(ERROR,
12429 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12430 : errmsg("cannot have more than %d keys in a foreign key",
12431 : INDEX_MAX_KEYS)));
12432 5336 : attnums[attnum] = attform->attnum;
12433 5336 : if (atttypids != NULL)
12434 5306 : atttypids[attnum] = attform->atttypid;
12435 5336 : if (attcollids != NULL)
12436 5306 : attcollids[attnum] = attform->attcollation;
12437 5336 : ReleaseSysCache(atttuple);
12438 5336 : attnum++;
12439 : }
12440 :
12441 6388 : return attnum;
12442 : }
12443 :
12444 : /*
12445 : * transformFkeyGetPrimaryKey -
12446 : *
12447 : * Look up the names, attnums, types, and collations of the primary key attributes
12448 : * for the pkrel. Also return the index OID and index opclasses of the
12449 : * index supporting the primary key. Also return whether the index has
12450 : * WITHOUT OVERLAPS.
12451 : *
12452 : * All parameters except pkrel are output parameters. Also, the function
12453 : * return value is the number of attributes in the primary key.
12454 : *
12455 : * Used when the column list in the REFERENCES specification is omitted.
12456 : */
12457 : static int
12458 1172 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12459 : List **attnamelist,
12460 : int16 *attnums, Oid *atttypids, Oid *attcollids,
12461 : Oid *opclasses, bool *pk_has_without_overlaps)
12462 : {
12463 : List *indexoidlist;
12464 : ListCell *indexoidscan;
12465 1172 : HeapTuple indexTuple = NULL;
12466 1172 : Form_pg_index indexStruct = NULL;
12467 : Datum indclassDatum;
12468 : oidvector *indclass;
12469 : int i;
12470 :
12471 : /*
12472 : * Get the list of index OIDs for the table from the relcache, and look up
12473 : * each one in the pg_index syscache until we find one marked primary key
12474 : * (hopefully there isn't more than one such). Insist it's valid, too.
12475 : */
12476 1172 : *indexOid = InvalidOid;
12477 :
12478 1172 : indexoidlist = RelationGetIndexList(pkrel);
12479 :
12480 1178 : foreach(indexoidscan, indexoidlist)
12481 : {
12482 1178 : Oid indexoid = lfirst_oid(indexoidscan);
12483 :
12484 1178 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12485 1178 : if (!HeapTupleIsValid(indexTuple))
12486 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12487 1178 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12488 1178 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12489 : {
12490 : /*
12491 : * Refuse to use a deferrable primary key. This is per SQL spec,
12492 : * and there would be a lot of interesting semantic problems if we
12493 : * tried to allow it.
12494 : */
12495 1172 : if (!indexStruct->indimmediate)
12496 0 : ereport(ERROR,
12497 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12498 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12499 : RelationGetRelationName(pkrel))));
12500 :
12501 1172 : *indexOid = indexoid;
12502 1172 : break;
12503 : }
12504 6 : ReleaseSysCache(indexTuple);
12505 : }
12506 :
12507 1172 : list_free(indexoidlist);
12508 :
12509 : /*
12510 : * Check that we found it
12511 : */
12512 1172 : if (!OidIsValid(*indexOid))
12513 0 : ereport(ERROR,
12514 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12515 : errmsg("there is no primary key for referenced table \"%s\"",
12516 : RelationGetRelationName(pkrel))));
12517 :
12518 : /* Must get indclass the hard way */
12519 1172 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12520 : Anum_pg_index_indclass);
12521 1172 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12522 :
12523 : /*
12524 : * Now build the list of PK attributes from the indkey definition (we
12525 : * assume a primary key cannot have expressional elements)
12526 : */
12527 1172 : *attnamelist = NIL;
12528 2768 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12529 : {
12530 1596 : int pkattno = indexStruct->indkey.values[i];
12531 :
12532 1596 : attnums[i] = pkattno;
12533 1596 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12534 1596 : attcollids[i] = attnumCollationId(pkrel, pkattno);
12535 1596 : opclasses[i] = indclass->values[i];
12536 1596 : *attnamelist = lappend(*attnamelist,
12537 1596 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12538 : }
12539 :
12540 1172 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12541 :
12542 1172 : ReleaseSysCache(indexTuple);
12543 :
12544 1172 : return i;
12545 : }
12546 :
12547 : /*
12548 : * transformFkeyCheckAttrs -
12549 : *
12550 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12551 : * reference as part of a foreign key constraint.
12552 : *
12553 : * Returns the OID of the unique index supporting the constraint and
12554 : * populates the caller-provided 'opclasses' array with the opclasses
12555 : * associated with the index columns. Also sets whether the index
12556 : * uses WITHOUT OVERLAPS.
12557 : *
12558 : * Raises an ERROR on validation failure.
12559 : */
12560 : static Oid
12561 1290 : transformFkeyCheckAttrs(Relation pkrel,
12562 : int numattrs, int16 *attnums,
12563 : bool with_period, Oid *opclasses,
12564 : bool *pk_has_without_overlaps)
12565 : {
12566 1290 : Oid indexoid = InvalidOid;
12567 1290 : bool found = false;
12568 1290 : bool found_deferrable = false;
12569 : List *indexoidlist;
12570 : ListCell *indexoidscan;
12571 : int i,
12572 : j;
12573 :
12574 : /*
12575 : * Reject duplicate appearances of columns in the referenced-columns list.
12576 : * Such a case is forbidden by the SQL standard, and even if we thought it
12577 : * useful to allow it, there would be ambiguity about how to match the
12578 : * list to unique indexes (in particular, it'd be unclear which index
12579 : * opclass goes with which FK column).
12580 : */
12581 3016 : for (i = 0; i < numattrs; i++)
12582 : {
12583 2280 : for (j = i + 1; j < numattrs; j++)
12584 : {
12585 554 : if (attnums[i] == attnums[j])
12586 24 : ereport(ERROR,
12587 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12588 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12589 : }
12590 : }
12591 :
12592 : /*
12593 : * Get the list of index OIDs for the table from the relcache, and look up
12594 : * each one in the pg_index syscache, and match unique indexes to the list
12595 : * of attnums we are given.
12596 : */
12597 1266 : indexoidlist = RelationGetIndexList(pkrel);
12598 :
12599 1446 : foreach(indexoidscan, indexoidlist)
12600 : {
12601 : HeapTuple indexTuple;
12602 : Form_pg_index indexStruct;
12603 :
12604 1434 : indexoid = lfirst_oid(indexoidscan);
12605 1434 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12606 1434 : if (!HeapTupleIsValid(indexTuple))
12607 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12608 1434 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12609 :
12610 : /*
12611 : * Must have the right number of columns; must be unique (or if
12612 : * temporal then exclusion instead) and not a partial index; forget it
12613 : * if there are any expressions, too. Invalid indexes are out as well.
12614 : */
12615 2760 : if (indexStruct->indnkeyatts == numattrs &&
12616 1326 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12617 2624 : indexStruct->indisvalid &&
12618 2624 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12619 1312 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12620 : {
12621 : Datum indclassDatum;
12622 : oidvector *indclass;
12623 :
12624 : /* Must get indclass the hard way */
12625 1312 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12626 : Anum_pg_index_indclass);
12627 1312 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12628 :
12629 : /*
12630 : * The given attnum list may match the index columns in any order.
12631 : * Check for a match, and extract the appropriate opclasses while
12632 : * we're at it.
12633 : *
12634 : * We know that attnums[] is duplicate-free per the test at the
12635 : * start of this function, and we checked above that the number of
12636 : * index columns agrees, so if we find a match for each attnums[]
12637 : * entry then we must have a one-to-one match in some order.
12638 : */
12639 3026 : for (i = 0; i < numattrs; i++)
12640 : {
12641 1772 : found = false;
12642 2360 : for (j = 0; j < numattrs; j++)
12643 : {
12644 2302 : if (attnums[i] == indexStruct->indkey.values[j])
12645 : {
12646 1714 : opclasses[i] = indclass->values[j];
12647 1714 : found = true;
12648 1714 : break;
12649 : }
12650 : }
12651 1772 : if (!found)
12652 58 : break;
12653 : }
12654 : /* The last attribute in the index must be the PERIOD FK part */
12655 1312 : if (found && with_period)
12656 : {
12657 130 : int16 periodattnum = attnums[numattrs - 1];
12658 :
12659 130 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12660 : }
12661 :
12662 : /*
12663 : * Refuse to use a deferrable unique/primary key. This is per SQL
12664 : * spec, and there would be a lot of interesting semantic problems
12665 : * if we tried to allow it.
12666 : */
12667 1312 : if (found && !indexStruct->indimmediate)
12668 : {
12669 : /*
12670 : * Remember that we found an otherwise matching index, so that
12671 : * we can generate a more appropriate error message.
12672 : */
12673 0 : found_deferrable = true;
12674 0 : found = false;
12675 : }
12676 :
12677 : /* We need to know whether the index has WITHOUT OVERLAPS */
12678 1312 : if (found)
12679 1254 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12680 : }
12681 1434 : ReleaseSysCache(indexTuple);
12682 1434 : if (found)
12683 1254 : break;
12684 : }
12685 :
12686 1266 : if (!found)
12687 : {
12688 12 : if (found_deferrable)
12689 0 : ereport(ERROR,
12690 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12691 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12692 : RelationGetRelationName(pkrel))));
12693 : else
12694 12 : ereport(ERROR,
12695 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12696 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12697 : RelationGetRelationName(pkrel))));
12698 : }
12699 :
12700 1254 : list_free(indexoidlist);
12701 :
12702 1254 : return indexoid;
12703 : }
12704 :
12705 : /*
12706 : * findFkeyCast -
12707 : *
12708 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12709 : * Caller has equal regard for binary coercibility and for an exact match.
12710 : */
12711 : static CoercionPathType
12712 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12713 : {
12714 : CoercionPathType ret;
12715 :
12716 12 : if (targetTypeId == sourceTypeId)
12717 : {
12718 12 : ret = COERCION_PATH_RELABELTYPE;
12719 12 : *funcid = InvalidOid;
12720 : }
12721 : else
12722 : {
12723 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12724 : COERCION_IMPLICIT, funcid);
12725 0 : if (ret == COERCION_PATH_NONE)
12726 : /* A previously-relied-upon cast is now gone. */
12727 0 : elog(ERROR, "could not find cast from %u to %u",
12728 : sourceTypeId, targetTypeId);
12729 : }
12730 :
12731 12 : return ret;
12732 : }
12733 :
12734 : /*
12735 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12736 : *
12737 : * Note: we have already checked that the user owns the referencing table,
12738 : * else we'd have failed much earlier; no additional checks are needed for it.
12739 : */
12740 : static void
12741 2390 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12742 : {
12743 2390 : Oid roleid = GetUserId();
12744 : AclResult aclresult;
12745 : int i;
12746 :
12747 : /* Okay if we have relation-level REFERENCES permission */
12748 2390 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12749 : ACL_REFERENCES);
12750 2390 : if (aclresult == ACLCHECK_OK)
12751 2390 : return;
12752 : /* Else we must have REFERENCES on each column */
12753 0 : for (i = 0; i < natts; i++)
12754 : {
12755 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12756 : roleid, ACL_REFERENCES);
12757 0 : if (aclresult != ACLCHECK_OK)
12758 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12759 0 : RelationGetRelationName(rel));
12760 : }
12761 : }
12762 :
12763 : /*
12764 : * Scan the existing rows in a table to verify they meet a proposed FK
12765 : * constraint.
12766 : *
12767 : * Caller must have opened and locked both relations appropriately.
12768 : */
12769 : static void
12770 1150 : validateForeignKeyConstraint(char *conname,
12771 : Relation rel,
12772 : Relation pkrel,
12773 : Oid pkindOid,
12774 : Oid constraintOid,
12775 : bool hasperiod)
12776 : {
12777 : TupleTableSlot *slot;
12778 : TableScanDesc scan;
12779 1150 : Trigger trig = {0};
12780 : Snapshot snapshot;
12781 : MemoryContext oldcxt;
12782 : MemoryContext perTupCxt;
12783 :
12784 1150 : ereport(DEBUG1,
12785 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12786 :
12787 : /*
12788 : * Build a trigger call structure; we'll need it either way.
12789 : */
12790 1150 : trig.tgoid = InvalidOid;
12791 1150 : trig.tgname = conname;
12792 1150 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12793 1150 : trig.tgisinternal = true;
12794 1150 : trig.tgconstrrelid = RelationGetRelid(pkrel);
12795 1150 : trig.tgconstrindid = pkindOid;
12796 1150 : trig.tgconstraint = constraintOid;
12797 1150 : trig.tgdeferrable = false;
12798 1150 : trig.tginitdeferred = false;
12799 : /* we needn't fill in remaining fields */
12800 :
12801 : /*
12802 : * See if we can do it with a single LEFT JOIN query. A false result
12803 : * indicates we must proceed with the fire-the-trigger method. We can't do
12804 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12805 : * left joins.
12806 : */
12807 1150 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12808 924 : return;
12809 :
12810 : /*
12811 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12812 : * if that tuple had just been inserted. If any of those fail, it should
12813 : * ereport(ERROR) and that's that.
12814 : */
12815 158 : snapshot = RegisterSnapshot(GetLatestSnapshot());
12816 158 : slot = table_slot_create(rel, NULL);
12817 158 : scan = table_beginscan(rel, snapshot, 0, NULL);
12818 :
12819 158 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12820 : "validateForeignKeyConstraint",
12821 : ALLOCSET_SMALL_SIZES);
12822 158 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12823 :
12824 242 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12825 : {
12826 102 : LOCAL_FCINFO(fcinfo, 0);
12827 102 : TriggerData trigdata = {0};
12828 :
12829 102 : CHECK_FOR_INTERRUPTS();
12830 :
12831 : /*
12832 : * Make a call to the trigger function
12833 : *
12834 : * No parameters are passed, but we do set a context
12835 : */
12836 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12837 :
12838 : /*
12839 : * We assume RI_FKey_check_ins won't look at flinfo...
12840 : */
12841 102 : trigdata.type = T_TriggerData;
12842 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12843 102 : trigdata.tg_relation = rel;
12844 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12845 102 : trigdata.tg_trigslot = slot;
12846 102 : trigdata.tg_trigger = &trig;
12847 :
12848 102 : fcinfo->context = (Node *) &trigdata;
12849 :
12850 102 : RI_FKey_check_ins(fcinfo);
12851 :
12852 84 : MemoryContextReset(perTupCxt);
12853 : }
12854 :
12855 140 : MemoryContextSwitchTo(oldcxt);
12856 140 : MemoryContextDelete(perTupCxt);
12857 140 : table_endscan(scan);
12858 140 : UnregisterSnapshot(snapshot);
12859 140 : ExecDropSingleTupleTableSlot(slot);
12860 : }
12861 :
12862 : /*
12863 : * CreateFKCheckTrigger
12864 : * Creates the insert (on_insert=true) or update "check" trigger that
12865 : * implements a given foreign key
12866 : *
12867 : * Returns the OID of the so created trigger.
12868 : */
12869 : static Oid
12870 5732 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12871 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12872 : bool on_insert)
12873 : {
12874 : ObjectAddress trigAddress;
12875 : CreateTrigStmt *fk_trigger;
12876 :
12877 : /*
12878 : * Note: for a self-referential FK (referencing and referenced tables are
12879 : * the same), it is important that the ON UPDATE action fires before the
12880 : * CHECK action, since both triggers will fire on the same row during an
12881 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12882 : * state of the row. Triggers fire in name order, so we ensure this by
12883 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12884 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12885 : */
12886 5732 : fk_trigger = makeNode(CreateTrigStmt);
12887 5732 : fk_trigger->replace = false;
12888 5732 : fk_trigger->isconstraint = true;
12889 5732 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
12890 5732 : fk_trigger->relation = NULL;
12891 :
12892 : /* Either ON INSERT or ON UPDATE */
12893 5732 : if (on_insert)
12894 : {
12895 2866 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12896 2866 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12897 : }
12898 : else
12899 : {
12900 2866 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12901 2866 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12902 : }
12903 :
12904 5732 : fk_trigger->args = NIL;
12905 5732 : fk_trigger->row = true;
12906 5732 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12907 5732 : fk_trigger->columns = NIL;
12908 5732 : fk_trigger->whenClause = NULL;
12909 5732 : fk_trigger->transitionRels = NIL;
12910 5732 : fk_trigger->deferrable = fkconstraint->deferrable;
12911 5732 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12912 5732 : fk_trigger->constrrel = NULL;
12913 :
12914 5732 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12915 : constraintOid, indexOid, InvalidOid,
12916 : parentTrigOid, NULL, true, false);
12917 :
12918 : /* Make changes-so-far visible */
12919 5732 : CommandCounterIncrement();
12920 :
12921 5732 : return trigAddress.objectId;
12922 : }
12923 :
12924 : /*
12925 : * createForeignKeyActionTriggers
12926 : * Create the referenced-side "action" triggers that implement a foreign
12927 : * key.
12928 : *
12929 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
12930 : * *updateTrigOid.
12931 : */
12932 : static void
12933 3158 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12934 : Oid constraintOid, Oid indexOid,
12935 : Oid parentDelTrigger, Oid parentUpdTrigger,
12936 : Oid *deleteTrigOid, Oid *updateTrigOid)
12937 : {
12938 : CreateTrigStmt *fk_trigger;
12939 : ObjectAddress trigAddress;
12940 :
12941 : /*
12942 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12943 : * DELETE action on the referenced table.
12944 : */
12945 3158 : fk_trigger = makeNode(CreateTrigStmt);
12946 3158 : fk_trigger->replace = false;
12947 3158 : fk_trigger->isconstraint = true;
12948 3158 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12949 3158 : fk_trigger->relation = NULL;
12950 3158 : fk_trigger->args = NIL;
12951 3158 : fk_trigger->row = true;
12952 3158 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12953 3158 : fk_trigger->events = TRIGGER_TYPE_DELETE;
12954 3158 : fk_trigger->columns = NIL;
12955 3158 : fk_trigger->whenClause = NULL;
12956 3158 : fk_trigger->transitionRels = NIL;
12957 3158 : fk_trigger->constrrel = NULL;
12958 :
12959 3158 : switch (fkconstraint->fk_del_action)
12960 : {
12961 2490 : case FKCONSTR_ACTION_NOACTION:
12962 2490 : fk_trigger->deferrable = fkconstraint->deferrable;
12963 2490 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12964 2490 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12965 2490 : break;
12966 82 : case FKCONSTR_ACTION_RESTRICT:
12967 82 : fk_trigger->deferrable = false;
12968 82 : fk_trigger->initdeferred = false;
12969 82 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12970 82 : break;
12971 428 : case FKCONSTR_ACTION_CASCADE:
12972 428 : fk_trigger->deferrable = false;
12973 428 : fk_trigger->initdeferred = false;
12974 428 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12975 428 : break;
12976 98 : case FKCONSTR_ACTION_SETNULL:
12977 98 : fk_trigger->deferrable = false;
12978 98 : fk_trigger->initdeferred = false;
12979 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12980 98 : break;
12981 60 : case FKCONSTR_ACTION_SETDEFAULT:
12982 60 : fk_trigger->deferrable = false;
12983 60 : fk_trigger->initdeferred = false;
12984 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12985 60 : break;
12986 0 : default:
12987 0 : elog(ERROR, "unrecognized FK action type: %d",
12988 : (int) fkconstraint->fk_del_action);
12989 : break;
12990 : }
12991 :
12992 3158 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12993 : RelationGetRelid(rel),
12994 : constraintOid, indexOid, InvalidOid,
12995 : parentDelTrigger, NULL, true, false);
12996 3158 : if (deleteTrigOid)
12997 3158 : *deleteTrigOid = trigAddress.objectId;
12998 :
12999 : /* Make changes-so-far visible */
13000 3158 : CommandCounterIncrement();
13001 :
13002 : /*
13003 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13004 : * UPDATE action on the referenced table.
13005 : */
13006 3158 : fk_trigger = makeNode(CreateTrigStmt);
13007 3158 : fk_trigger->replace = false;
13008 3158 : fk_trigger->isconstraint = true;
13009 3158 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13010 3158 : fk_trigger->relation = NULL;
13011 3158 : fk_trigger->args = NIL;
13012 3158 : fk_trigger->row = true;
13013 3158 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13014 3158 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13015 3158 : fk_trigger->columns = NIL;
13016 3158 : fk_trigger->whenClause = NULL;
13017 3158 : fk_trigger->transitionRels = NIL;
13018 3158 : fk_trigger->constrrel = NULL;
13019 :
13020 3158 : switch (fkconstraint->fk_upd_action)
13021 : {
13022 2724 : case FKCONSTR_ACTION_NOACTION:
13023 2724 : fk_trigger->deferrable = fkconstraint->deferrable;
13024 2724 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13025 2724 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13026 2724 : break;
13027 48 : case FKCONSTR_ACTION_RESTRICT:
13028 48 : fk_trigger->deferrable = false;
13029 48 : fk_trigger->initdeferred = false;
13030 48 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13031 48 : break;
13032 282 : case FKCONSTR_ACTION_CASCADE:
13033 282 : fk_trigger->deferrable = false;
13034 282 : fk_trigger->initdeferred = false;
13035 282 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13036 282 : break;
13037 62 : case FKCONSTR_ACTION_SETNULL:
13038 62 : fk_trigger->deferrable = false;
13039 62 : fk_trigger->initdeferred = false;
13040 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13041 62 : break;
13042 42 : case FKCONSTR_ACTION_SETDEFAULT:
13043 42 : fk_trigger->deferrable = false;
13044 42 : fk_trigger->initdeferred = false;
13045 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13046 42 : break;
13047 0 : default:
13048 0 : elog(ERROR, "unrecognized FK action type: %d",
13049 : (int) fkconstraint->fk_upd_action);
13050 : break;
13051 : }
13052 :
13053 3158 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
13054 : RelationGetRelid(rel),
13055 : constraintOid, indexOid, InvalidOid,
13056 : parentUpdTrigger, NULL, true, false);
13057 3158 : if (updateTrigOid)
13058 3158 : *updateTrigOid = trigAddress.objectId;
13059 3158 : }
13060 :
13061 : /*
13062 : * createForeignKeyCheckTriggers
13063 : * Create the referencing-side "check" triggers that implement a foreign
13064 : * key.
13065 : *
13066 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13067 : * *updateTrigOid.
13068 : */
13069 : static void
13070 2866 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13071 : Constraint *fkconstraint, Oid constraintOid,
13072 : Oid indexOid,
13073 : Oid parentInsTrigger, Oid parentUpdTrigger,
13074 : Oid *insertTrigOid, Oid *updateTrigOid)
13075 : {
13076 2866 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13077 : constraintOid, indexOid,
13078 : parentInsTrigger, true);
13079 2866 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13080 : constraintOid, indexOid,
13081 : parentUpdTrigger, false);
13082 2866 : }
13083 :
13084 : /*
13085 : * ALTER TABLE DROP CONSTRAINT
13086 : *
13087 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13088 : */
13089 : static void
13090 816 : ATExecDropConstraint(Relation rel, const char *constrName,
13091 : DropBehavior behavior, bool recurse,
13092 : bool missing_ok, LOCKMODE lockmode)
13093 : {
13094 : Relation conrel;
13095 : SysScanDesc scan;
13096 : ScanKeyData skey[3];
13097 : HeapTuple tuple;
13098 816 : bool found = false;
13099 :
13100 816 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13101 :
13102 : /*
13103 : * Find and drop the target constraint
13104 : */
13105 816 : ScanKeyInit(&skey[0],
13106 : Anum_pg_constraint_conrelid,
13107 : BTEqualStrategyNumber, F_OIDEQ,
13108 : ObjectIdGetDatum(RelationGetRelid(rel)));
13109 816 : ScanKeyInit(&skey[1],
13110 : Anum_pg_constraint_contypid,
13111 : BTEqualStrategyNumber, F_OIDEQ,
13112 : ObjectIdGetDatum(InvalidOid));
13113 816 : ScanKeyInit(&skey[2],
13114 : Anum_pg_constraint_conname,
13115 : BTEqualStrategyNumber, F_NAMEEQ,
13116 : CStringGetDatum(constrName));
13117 816 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13118 : true, NULL, 3, skey);
13119 :
13120 : /* There can be at most one matching row */
13121 816 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13122 : {
13123 780 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
13124 : missing_ok, lockmode);
13125 594 : found = true;
13126 : }
13127 :
13128 630 : systable_endscan(scan);
13129 :
13130 630 : if (!found)
13131 : {
13132 36 : if (!missing_ok)
13133 24 : ereport(ERROR,
13134 : errcode(ERRCODE_UNDEFINED_OBJECT),
13135 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13136 : constrName, RelationGetRelationName(rel)));
13137 : else
13138 12 : ereport(NOTICE,
13139 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
13140 : constrName, RelationGetRelationName(rel)));
13141 : }
13142 :
13143 606 : table_close(conrel, RowExclusiveLock);
13144 606 : }
13145 :
13146 : /*
13147 : * Remove a constraint, using its pg_constraint tuple
13148 : *
13149 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
13150 : * DROP NOT NULL.
13151 : *
13152 : * Returns the address of the constraint being removed.
13153 : */
13154 : static ObjectAddress
13155 1198 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
13156 : bool recurse, bool recursing, bool missing_ok,
13157 : LOCKMODE lockmode)
13158 : {
13159 : Relation conrel;
13160 : Form_pg_constraint con;
13161 : ObjectAddress conobj;
13162 : List *children;
13163 1198 : bool is_no_inherit_constraint = false;
13164 : char *constrName;
13165 1198 : char *colname = NULL;
13166 :
13167 : /* Guard against stack overflow due to overly deep inheritance tree. */
13168 1198 : check_stack_depth();
13169 :
13170 : /* At top level, permission check was done in ATPrepCmd, else do it */
13171 1198 : if (recursing)
13172 210 : ATSimplePermissions(AT_DropConstraint, rel,
13173 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
13174 :
13175 1192 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13176 :
13177 1192 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
13178 1192 : constrName = NameStr(con->conname);
13179 :
13180 : /* Don't allow drop of inherited constraints */
13181 1192 : if (con->coninhcount > 0 && !recursing)
13182 156 : ereport(ERROR,
13183 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13184 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13185 : constrName, RelationGetRelationName(rel))));
13186 :
13187 : /*
13188 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
13189 : *
13190 : * While doing that, we're in a good position to disallow dropping a not-
13191 : * null constraint underneath a primary key, a replica identity index, or
13192 : * a generated identity column.
13193 : */
13194 1036 : if (con->contype == CONSTRAINT_NOTNULL)
13195 : {
13196 278 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
13197 278 : AttrNumber attnum = extractNotNullColumn(constraintTup);
13198 : Bitmapset *pkattrs;
13199 : Bitmapset *irattrs;
13200 : HeapTuple atttup;
13201 : Form_pg_attribute attForm;
13202 :
13203 : /* save column name for recursion step */
13204 278 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13205 :
13206 : /*
13207 : * Disallow if it's in the primary key. For partitioned tables we
13208 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
13209 : * return NULL if the primary key is invalid; but we still need to
13210 : * protect not-null constraints under such a constraint, so check the
13211 : * slow way.
13212 : */
13213 278 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
13214 :
13215 278 : if (pkattrs == NULL &&
13216 242 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13217 : {
13218 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
13219 :
13220 18 : if (OidIsValid(pkindex))
13221 : {
13222 0 : Relation pk = relation_open(pkindex, AccessShareLock);
13223 :
13224 0 : pkattrs = NULL;
13225 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
13226 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
13227 :
13228 0 : relation_close(pk, AccessShareLock);
13229 : }
13230 : }
13231 :
13232 314 : if (pkattrs &&
13233 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
13234 24 : ereport(ERROR,
13235 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13236 : errmsg("column \"%s\" is in a primary key",
13237 : get_attname(RelationGetRelid(rel), attnum, false)));
13238 :
13239 : /* Disallow if it's in the replica identity */
13240 254 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13241 254 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
13242 12 : ereport(ERROR,
13243 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13244 : errmsg("column \"%s\" is in index used as replica identity",
13245 : get_attname(RelationGetRelid(rel), attnum, false)));
13246 :
13247 : /* Disallow if it's a GENERATED AS IDENTITY column */
13248 242 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
13249 242 : if (!HeapTupleIsValid(atttup))
13250 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13251 : attnum, RelationGetRelid(rel));
13252 242 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13253 242 : if (attForm->attidentity != '\0')
13254 0 : ereport(ERROR,
13255 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13256 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13257 : get_attname(RelationGetRelid(rel), attnum,
13258 : false),
13259 : RelationGetRelationName(rel)));
13260 :
13261 : /* All good -- reset attnotnull if needed */
13262 242 : if (attForm->attnotnull)
13263 : {
13264 242 : attForm->attnotnull = false;
13265 242 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13266 : }
13267 :
13268 242 : table_close(attrel, RowExclusiveLock);
13269 : }
13270 :
13271 1000 : is_no_inherit_constraint = con->connoinherit;
13272 :
13273 : /*
13274 : * If it's a foreign-key constraint, we'd better lock the referenced table
13275 : * and check that that's not in use, just as we've already done for the
13276 : * constrained table (else we might, eg, be dropping a trigger that has
13277 : * unfired events). But we can/must skip that in the self-referential
13278 : * case.
13279 : */
13280 1000 : if (con->contype == CONSTRAINT_FOREIGN &&
13281 216 : con->confrelid != RelationGetRelid(rel))
13282 : {
13283 : Relation frel;
13284 :
13285 : /* Must match lock taken by RemoveTriggerById: */
13286 216 : frel = table_open(con->confrelid, AccessExclusiveLock);
13287 216 : CheckAlterTableIsSafe(frel);
13288 210 : table_close(frel, NoLock);
13289 : }
13290 :
13291 : /*
13292 : * Perform the actual constraint deletion
13293 : */
13294 994 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13295 994 : performDeletion(&conobj, behavior, 0);
13296 :
13297 : /*
13298 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13299 : * are dropped via the dependency mechanism, so we're done here.
13300 : */
13301 958 : if (con->contype != CONSTRAINT_CHECK &&
13302 640 : con->contype != CONSTRAINT_NOTNULL &&
13303 398 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13304 : {
13305 90 : table_close(conrel, RowExclusiveLock);
13306 90 : return conobj;
13307 : }
13308 :
13309 : /*
13310 : * Propagate to children as appropriate. Unlike most other ALTER
13311 : * routines, we have to do this one level of recursion at a time; we can't
13312 : * use find_all_inheritors to do it in one pass.
13313 : */
13314 868 : if (!is_no_inherit_constraint)
13315 554 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13316 : else
13317 314 : children = NIL;
13318 :
13319 2102 : foreach_oid(childrelid, children)
13320 : {
13321 : Relation childrel;
13322 : HeapTuple tuple;
13323 : Form_pg_constraint childcon;
13324 :
13325 : /* find_inheritance_children already got lock */
13326 378 : childrel = table_open(childrelid, NoLock);
13327 378 : CheckAlterTableIsSafe(childrel);
13328 :
13329 : /*
13330 : * We search for not-null constraints by column name, and others by
13331 : * constraint name.
13332 : */
13333 378 : if (con->contype == CONSTRAINT_NOTNULL)
13334 : {
13335 142 : tuple = findNotNullConstraint(childrelid, colname);
13336 142 : if (!HeapTupleIsValid(tuple))
13337 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13338 : colname, RelationGetRelid(childrel));
13339 : }
13340 : else
13341 : {
13342 : SysScanDesc scan;
13343 : ScanKeyData skey[3];
13344 :
13345 236 : ScanKeyInit(&skey[0],
13346 : Anum_pg_constraint_conrelid,
13347 : BTEqualStrategyNumber, F_OIDEQ,
13348 : ObjectIdGetDatum(childrelid));
13349 236 : ScanKeyInit(&skey[1],
13350 : Anum_pg_constraint_contypid,
13351 : BTEqualStrategyNumber, F_OIDEQ,
13352 : ObjectIdGetDatum(InvalidOid));
13353 236 : ScanKeyInit(&skey[2],
13354 : Anum_pg_constraint_conname,
13355 : BTEqualStrategyNumber, F_NAMEEQ,
13356 : CStringGetDatum(constrName));
13357 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13358 : true, NULL, 3, skey);
13359 : /* There can only be one, so no need to loop */
13360 236 : tuple = systable_getnext(scan);
13361 236 : if (!HeapTupleIsValid(tuple))
13362 0 : ereport(ERROR,
13363 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13364 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13365 : constrName,
13366 : RelationGetRelationName(childrel))));
13367 236 : tuple = heap_copytuple(tuple);
13368 236 : systable_endscan(scan);
13369 : }
13370 :
13371 378 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13372 :
13373 : /* Right now only CHECK and not-null constraints can be inherited */
13374 378 : if (childcon->contype != CONSTRAINT_CHECK &&
13375 142 : childcon->contype != CONSTRAINT_NOTNULL)
13376 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13377 :
13378 378 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13379 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13380 : childrelid, NameStr(childcon->conname));
13381 :
13382 378 : if (recurse)
13383 : {
13384 : /*
13385 : * If the child constraint has other definition sources, just
13386 : * decrement its inheritance count; if not, recurse to delete it.
13387 : */
13388 276 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13389 : {
13390 : /* Time to delete this child constraint, too */
13391 210 : dropconstraint_internal(childrel, tuple, behavior,
13392 : recurse, true, missing_ok,
13393 : lockmode);
13394 : }
13395 : else
13396 : {
13397 : /* Child constraint must survive my deletion */
13398 66 : childcon->coninhcount--;
13399 66 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13400 :
13401 : /* Make update visible */
13402 66 : CommandCounterIncrement();
13403 : }
13404 : }
13405 : else
13406 : {
13407 : /*
13408 : * If we were told to drop ONLY in this table (no recursion) and
13409 : * there are no further parents for this constraint, we need to
13410 : * mark the inheritors' constraints as locally defined rather than
13411 : * inherited.
13412 : */
13413 102 : childcon->coninhcount--;
13414 102 : if (childcon->coninhcount == 0)
13415 102 : childcon->conislocal = true;
13416 :
13417 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13418 :
13419 : /* Make update visible */
13420 102 : CommandCounterIncrement();
13421 : }
13422 :
13423 372 : heap_freetuple(tuple);
13424 :
13425 372 : table_close(childrel, NoLock);
13426 : }
13427 :
13428 862 : table_close(conrel, RowExclusiveLock);
13429 :
13430 862 : return conobj;
13431 : }
13432 :
13433 : /*
13434 : * ALTER COLUMN TYPE
13435 : *
13436 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13437 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13438 : * transformed (and must be, because we rely on some transformed fields).
13439 : *
13440 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13441 : * table will be done "in parallel" during phase 3, so all the USING
13442 : * expressions should be parsed assuming the original column types. Also,
13443 : * this allows a USING expression to refer to a field that will be dropped.
13444 : *
13445 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13446 : * the first two execution steps in phase 2; they must not see the effects
13447 : * of any other subcommand types, since the USING expressions are parsed
13448 : * against the unmodified table's state.
13449 : */
13450 : static void
13451 1264 : ATPrepAlterColumnType(List **wqueue,
13452 : AlteredTableInfo *tab, Relation rel,
13453 : bool recurse, bool recursing,
13454 : AlterTableCmd *cmd, LOCKMODE lockmode,
13455 : AlterTableUtilityContext *context)
13456 : {
13457 1264 : char *colName = cmd->name;
13458 1264 : ColumnDef *def = (ColumnDef *) cmd->def;
13459 1264 : TypeName *typeName = def->typeName;
13460 1264 : Node *transform = def->cooked_default;
13461 : HeapTuple tuple;
13462 : Form_pg_attribute attTup;
13463 : AttrNumber attnum;
13464 : Oid targettype;
13465 : int32 targettypmod;
13466 : Oid targetcollid;
13467 : NewColumnValue *newval;
13468 1264 : ParseState *pstate = make_parsestate(NULL);
13469 : AclResult aclresult;
13470 : bool is_expr;
13471 :
13472 1264 : pstate->p_sourcetext = context->queryString;
13473 :
13474 1264 : if (rel->rd_rel->reloftype && !recursing)
13475 6 : ereport(ERROR,
13476 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13477 : errmsg("cannot alter column type of typed table"),
13478 : parser_errposition(pstate, def->location)));
13479 :
13480 : /* lookup the attribute so we can check inheritance status */
13481 1258 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13482 1258 : if (!HeapTupleIsValid(tuple))
13483 0 : ereport(ERROR,
13484 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13485 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13486 : colName, RelationGetRelationName(rel)),
13487 : parser_errposition(pstate, def->location)));
13488 1258 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13489 1258 : attnum = attTup->attnum;
13490 :
13491 : /* Can't alter a system attribute */
13492 1258 : if (attnum <= 0)
13493 6 : ereport(ERROR,
13494 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13495 : errmsg("cannot alter system column \"%s\"", colName),
13496 : parser_errposition(pstate, def->location)));
13497 :
13498 : /*
13499 : * Cannot specify USING when altering type of a generated column, because
13500 : * that would violate the generation expression.
13501 : */
13502 1252 : if (attTup->attgenerated && def->cooked_default)
13503 12 : ereport(ERROR,
13504 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13505 : errmsg("cannot specify USING when altering type of generated column"),
13506 : errdetail("Column \"%s\" is a generated column.", colName),
13507 : parser_errposition(pstate, def->location)));
13508 :
13509 : /*
13510 : * Don't alter inherited columns. At outer level, there had better not be
13511 : * any inherited definition; when recursing, we assume this was checked at
13512 : * the parent level (see below).
13513 : */
13514 1240 : if (attTup->attinhcount > 0 && !recursing)
13515 6 : ereport(ERROR,
13516 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13517 : errmsg("cannot alter inherited column \"%s\"", colName),
13518 : parser_errposition(pstate, def->location)));
13519 :
13520 : /* Don't alter columns used in the partition key */
13521 1234 : if (has_partition_attrs(rel,
13522 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13523 : &is_expr))
13524 18 : ereport(ERROR,
13525 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13526 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13527 : colName, RelationGetRelationName(rel)),
13528 : parser_errposition(pstate, def->location)));
13529 :
13530 : /* Look up the target type */
13531 1216 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
13532 :
13533 1210 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13534 1210 : if (aclresult != ACLCHECK_OK)
13535 12 : aclcheck_error_type(aclresult, targettype);
13536 :
13537 : /* And the collation */
13538 1198 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
13539 :
13540 : /* make sure datatype is legal for a column */
13541 1192 : CheckAttributeType(colName, targettype, targetcollid,
13542 1192 : list_make1_oid(rel->rd_rel->reltype),
13543 : 0);
13544 :
13545 1186 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13546 : {
13547 : /* do nothing */
13548 : }
13549 1150 : else if (tab->relkind == RELKIND_RELATION ||
13550 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13551 : {
13552 : /*
13553 : * Set up an expression to transform the old data value to the new
13554 : * type. If a USING option was given, use the expression as
13555 : * transformed by transformAlterTableStmt, else just take the old
13556 : * value and try to coerce it. We do this first so that type
13557 : * incompatibility can be detected before we waste effort, and because
13558 : * we need the expression to be parsed against the original table row
13559 : * type.
13560 : */
13561 1014 : if (!transform)
13562 : {
13563 792 : transform = (Node *) makeVar(1, attnum,
13564 : attTup->atttypid, attTup->atttypmod,
13565 : attTup->attcollation,
13566 : 0);
13567 : }
13568 :
13569 1014 : transform = coerce_to_target_type(pstate,
13570 : transform, exprType(transform),
13571 : targettype, targettypmod,
13572 : COERCION_ASSIGNMENT,
13573 : COERCE_IMPLICIT_CAST,
13574 : -1);
13575 1014 : if (transform == NULL)
13576 : {
13577 : /* error text depends on whether USING was specified or not */
13578 24 : if (def->cooked_default != NULL)
13579 6 : ereport(ERROR,
13580 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13581 : errmsg("result of USING clause for column \"%s\""
13582 : " cannot be cast automatically to type %s",
13583 : colName, format_type_be(targettype)),
13584 : errhint("You might need to add an explicit cast.")));
13585 : else
13586 18 : ereport(ERROR,
13587 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13588 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13589 : colName, format_type_be(targettype)),
13590 : !attTup->attgenerated ?
13591 : /* translator: USING is SQL, don't translate it */
13592 : errhint("You might need to specify \"USING %s::%s\".",
13593 : quote_identifier(colName),
13594 : format_type_with_typemod(targettype,
13595 : targettypmod)) : 0));
13596 : }
13597 :
13598 : /* Fix collations after all else */
13599 990 : assign_expr_collations(pstate, transform);
13600 :
13601 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13602 990 : transform = (Node *) expression_planner((Expr *) transform);
13603 :
13604 : /*
13605 : * Add a work queue item to make ATRewriteTable update the column
13606 : * contents.
13607 : */
13608 990 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13609 990 : newval->attnum = attnum;
13610 990 : newval->expr = (Expr *) transform;
13611 990 : newval->is_generated = false;
13612 :
13613 990 : tab->newvals = lappend(tab->newvals, newval);
13614 990 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13615 794 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13616 : }
13617 136 : else if (transform)
13618 12 : ereport(ERROR,
13619 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13620 : errmsg("\"%s\" is not a table",
13621 : RelationGetRelationName(rel))));
13622 :
13623 1150 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13624 : {
13625 : /*
13626 : * For relations or columns without storage, do this check now.
13627 : * Regular tables will check it later when the table is being
13628 : * rewritten.
13629 : */
13630 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13631 : }
13632 :
13633 1102 : ReleaseSysCache(tuple);
13634 :
13635 : /*
13636 : * Recurse manually by queueing a new command for each child, if
13637 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13638 : * remap attribute numbers in the USING expression, if any.
13639 : *
13640 : * If we are told not to recurse, there had better not be any child
13641 : * tables; else the alter would put them out of step.
13642 : */
13643 1102 : if (recurse)
13644 : {
13645 844 : Oid relid = RelationGetRelid(rel);
13646 : List *child_oids,
13647 : *child_numparents;
13648 : ListCell *lo,
13649 : *li;
13650 :
13651 844 : child_oids = find_all_inheritors(relid, lockmode,
13652 : &child_numparents);
13653 :
13654 : /*
13655 : * find_all_inheritors does the recursive search of the inheritance
13656 : * hierarchy, so all we have to do is process all of the relids in the
13657 : * list that it returns.
13658 : */
13659 1896 : forboth(lo, child_oids, li, child_numparents)
13660 : {
13661 1076 : Oid childrelid = lfirst_oid(lo);
13662 1076 : int numparents = lfirst_int(li);
13663 : Relation childrel;
13664 : HeapTuple childtuple;
13665 : Form_pg_attribute childattTup;
13666 :
13667 1076 : if (childrelid == relid)
13668 844 : continue;
13669 :
13670 : /* find_all_inheritors already got lock */
13671 232 : childrel = relation_open(childrelid, NoLock);
13672 232 : CheckAlterTableIsSafe(childrel);
13673 :
13674 : /*
13675 : * Verify that the child doesn't have any inherited definitions of
13676 : * this column that came from outside this inheritance hierarchy.
13677 : * (renameatt makes a similar test, though in a different way
13678 : * because of its different recursion mechanism.)
13679 : */
13680 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13681 : colName);
13682 232 : if (!HeapTupleIsValid(childtuple))
13683 0 : ereport(ERROR,
13684 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13685 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13686 : colName, RelationGetRelationName(childrel))));
13687 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13688 :
13689 232 : if (childattTup->attinhcount > numparents)
13690 6 : ereport(ERROR,
13691 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13692 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13693 : colName, RelationGetRelationName(childrel))));
13694 :
13695 226 : ReleaseSysCache(childtuple);
13696 :
13697 : /*
13698 : * Remap the attribute numbers. If no USING expression was
13699 : * specified, there is no need for this step.
13700 : */
13701 226 : if (def->cooked_default)
13702 : {
13703 : AttrMap *attmap;
13704 : bool found_whole_row;
13705 :
13706 : /* create a copy to scribble on */
13707 78 : cmd = copyObject(cmd);
13708 :
13709 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13710 : RelationGetDescr(rel),
13711 : false);
13712 156 : ((ColumnDef *) cmd->def)->cooked_default =
13713 78 : map_variable_attnos(def->cooked_default,
13714 : 1, 0,
13715 : attmap,
13716 : InvalidOid, &found_whole_row);
13717 78 : if (found_whole_row)
13718 6 : ereport(ERROR,
13719 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13720 : errmsg("cannot convert whole-row table reference"),
13721 : errdetail("USING expression contains a whole-row table reference.")));
13722 72 : pfree(attmap);
13723 : }
13724 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13725 208 : relation_close(childrel, NoLock);
13726 : }
13727 : }
13728 308 : else if (!recursing &&
13729 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13730 0 : ereport(ERROR,
13731 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13732 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13733 : colName)));
13734 :
13735 1078 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13736 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13737 1072 : }
13738 :
13739 : /*
13740 : * When the data type of a column is changed, a rewrite might not be required
13741 : * if the new type is sufficiently identical to the old one, and the USING
13742 : * clause isn't trying to insert some other value. It's safe to skip the
13743 : * rewrite in these cases:
13744 : *
13745 : * - the old type is binary coercible to the new type
13746 : * - the new type is an unconstrained domain over the old type
13747 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13748 : *
13749 : * In the case of a constrained domain, we could get by with scanning the
13750 : * table and checking the constraint rather than actually rewriting it, but we
13751 : * don't currently try to do that.
13752 : */
13753 : static bool
13754 1108 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13755 : {
13756 : Assert(expr != NULL);
13757 :
13758 : for (;;)
13759 : {
13760 : /* only one varno, so no need to check that */
13761 1108 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13762 196 : return false;
13763 912 : else if (IsA(expr, RelabelType))
13764 106 : expr = (Node *) ((RelabelType *) expr)->arg;
13765 806 : else if (IsA(expr, CoerceToDomain))
13766 : {
13767 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
13768 :
13769 0 : if (DomainHasConstraints(d->resulttype))
13770 0 : return true;
13771 0 : expr = (Node *) d->arg;
13772 : }
13773 806 : else if (IsA(expr, FuncExpr))
13774 : {
13775 600 : FuncExpr *f = (FuncExpr *) expr;
13776 :
13777 600 : switch (f->funcid)
13778 : {
13779 18 : case F_TIMESTAMPTZ_TIMESTAMP:
13780 : case F_TIMESTAMP_TIMESTAMPTZ:
13781 18 : if (TimestampTimestampTzRequiresRewrite())
13782 6 : return true;
13783 : else
13784 12 : expr = linitial(f->args);
13785 12 : break;
13786 582 : default:
13787 582 : return true;
13788 : }
13789 : }
13790 : else
13791 206 : return true;
13792 : }
13793 : }
13794 :
13795 : /*
13796 : * ALTER COLUMN .. SET DATA TYPE
13797 : *
13798 : * Return the address of the modified column.
13799 : */
13800 : static ObjectAddress
13801 1036 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13802 : AlterTableCmd *cmd, LOCKMODE lockmode)
13803 : {
13804 1036 : char *colName = cmd->name;
13805 1036 : ColumnDef *def = (ColumnDef *) cmd->def;
13806 1036 : TypeName *typeName = def->typeName;
13807 : HeapTuple heapTup;
13808 : Form_pg_attribute attTup,
13809 : attOldTup;
13810 : AttrNumber attnum;
13811 : HeapTuple typeTuple;
13812 : Form_pg_type tform;
13813 : Oid targettype;
13814 : int32 targettypmod;
13815 : Oid targetcollid;
13816 : Node *defaultexpr;
13817 : Relation attrelation;
13818 : Relation depRel;
13819 : ScanKeyData key[3];
13820 : SysScanDesc scan;
13821 : HeapTuple depTup;
13822 : ObjectAddress address;
13823 :
13824 : /*
13825 : * Clear all the missing values if we're rewriting the table, since this
13826 : * renders them pointless.
13827 : */
13828 1036 : if (tab->rewrite)
13829 : {
13830 : Relation newrel;
13831 :
13832 734 : newrel = table_open(RelationGetRelid(rel), NoLock);
13833 734 : RelationClearMissing(newrel);
13834 734 : relation_close(newrel, NoLock);
13835 : /* make sure we don't conflict with later attribute modifications */
13836 734 : CommandCounterIncrement();
13837 : }
13838 :
13839 1036 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13840 :
13841 : /* Look up the target column */
13842 1036 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13843 1036 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13844 0 : ereport(ERROR,
13845 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13846 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13847 : colName, RelationGetRelationName(rel))));
13848 1036 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13849 1036 : attnum = attTup->attnum;
13850 1036 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13851 :
13852 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13853 1036 : if (attTup->atttypid != attOldTup->atttypid ||
13854 1036 : attTup->atttypmod != attOldTup->atttypmod)
13855 0 : ereport(ERROR,
13856 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13857 : errmsg("cannot alter type of column \"%s\" twice",
13858 : colName)));
13859 :
13860 : /* Look up the target type (should not fail, since prep found it) */
13861 1036 : typeTuple = typenameType(NULL, typeName, &targettypmod);
13862 1036 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
13863 1036 : targettype = tform->oid;
13864 : /* And the collation */
13865 1036 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13866 :
13867 : /*
13868 : * If there is a default expression for the column, get it and ensure we
13869 : * can coerce it to the new datatype. (We must do this before changing
13870 : * the column type, because build_column_default itself will try to
13871 : * coerce, and will not issue the error message we want if it fails.)
13872 : *
13873 : * We remove any implicit coercion steps at the top level of the old
13874 : * default expression; this has been agreed to satisfy the principle of
13875 : * least surprise. (The conversion to the new column type should act like
13876 : * it started from what the user sees as the stored expression, and the
13877 : * implicit coercions aren't going to be shown.)
13878 : */
13879 1036 : if (attTup->atthasdef)
13880 : {
13881 92 : defaultexpr = build_column_default(rel, attnum);
13882 : Assert(defaultexpr);
13883 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
13884 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13885 : defaultexpr, exprType(defaultexpr),
13886 : targettype, targettypmod,
13887 : COERCION_ASSIGNMENT,
13888 : COERCE_IMPLICIT_CAST,
13889 : -1);
13890 92 : if (defaultexpr == NULL)
13891 : {
13892 6 : if (attTup->attgenerated)
13893 0 : ereport(ERROR,
13894 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13895 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13896 : colName, format_type_be(targettype))));
13897 : else
13898 6 : ereport(ERROR,
13899 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13900 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13901 : colName, format_type_be(targettype))));
13902 : }
13903 : }
13904 : else
13905 944 : defaultexpr = NULL;
13906 :
13907 : /*
13908 : * Find everything that depends on the column (constraints, indexes, etc),
13909 : * and record enough information to let us recreate the objects.
13910 : *
13911 : * The actual recreation does not happen here, but only after we have
13912 : * performed all the individual ALTER TYPE operations. We have to save
13913 : * the info before executing ALTER TYPE, though, else the deparser will
13914 : * get confused.
13915 : */
13916 1030 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13917 :
13918 : /*
13919 : * Now scan for dependencies of this column on other things. The only
13920 : * things we should find are the dependency on the column datatype and
13921 : * possibly a collation dependency. Those can be removed.
13922 : */
13923 994 : depRel = table_open(DependRelationId, RowExclusiveLock);
13924 :
13925 994 : ScanKeyInit(&key[0],
13926 : Anum_pg_depend_classid,
13927 : BTEqualStrategyNumber, F_OIDEQ,
13928 : ObjectIdGetDatum(RelationRelationId));
13929 994 : ScanKeyInit(&key[1],
13930 : Anum_pg_depend_objid,
13931 : BTEqualStrategyNumber, F_OIDEQ,
13932 : ObjectIdGetDatum(RelationGetRelid(rel)));
13933 994 : ScanKeyInit(&key[2],
13934 : Anum_pg_depend_objsubid,
13935 : BTEqualStrategyNumber, F_INT4EQ,
13936 : Int32GetDatum((int32) attnum));
13937 :
13938 994 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
13939 : NULL, 3, key);
13940 :
13941 998 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13942 : {
13943 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13944 : ObjectAddress foundObject;
13945 :
13946 4 : foundObject.classId = foundDep->refclassid;
13947 4 : foundObject.objectId = foundDep->refobjid;
13948 4 : foundObject.objectSubId = foundDep->refobjsubid;
13949 :
13950 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
13951 0 : elog(ERROR, "found unexpected dependency type '%c'",
13952 : foundDep->deptype);
13953 4 : if (!(foundDep->refclassid == TypeRelationId &&
13954 4 : foundDep->refobjid == attTup->atttypid) &&
13955 0 : !(foundDep->refclassid == CollationRelationId &&
13956 0 : foundDep->refobjid == attTup->attcollation))
13957 0 : elog(ERROR, "found unexpected dependency for column: %s",
13958 : getObjectDescription(&foundObject, false));
13959 :
13960 4 : CatalogTupleDelete(depRel, &depTup->t_self);
13961 : }
13962 :
13963 994 : systable_endscan(scan);
13964 :
13965 994 : table_close(depRel, RowExclusiveLock);
13966 :
13967 : /*
13968 : * Here we go --- change the recorded column type and collation. (Note
13969 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13970 : * fix up the missing value if any.
13971 : */
13972 994 : if (attTup->atthasmissing)
13973 : {
13974 : Datum missingval;
13975 : bool missingNull;
13976 :
13977 : /* if rewrite is true the missing value should already be cleared */
13978 : Assert(tab->rewrite == 0);
13979 :
13980 : /* Get the missing value datum */
13981 6 : missingval = heap_getattr(heapTup,
13982 : Anum_pg_attribute_attmissingval,
13983 : attrelation->rd_att,
13984 : &missingNull);
13985 :
13986 : /* if it's a null array there is nothing to do */
13987 :
13988 6 : if (!missingNull)
13989 : {
13990 : /*
13991 : * Get the datum out of the array and repack it in a new array
13992 : * built with the new type data. We assume that since the table
13993 : * doesn't need rewriting, the actual Datum doesn't need to be
13994 : * changed, only the array metadata.
13995 : */
13996 :
13997 6 : int one = 1;
13998 : bool isNull;
13999 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14000 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14001 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14002 : HeapTuple newTup;
14003 :
14004 12 : missingval = array_get_element(missingval,
14005 : 1,
14006 : &one,
14007 : 0,
14008 6 : attTup->attlen,
14009 6 : attTup->attbyval,
14010 6 : attTup->attalign,
14011 : &isNull);
14012 6 : missingval = PointerGetDatum(construct_array(&missingval,
14013 : 1,
14014 : targettype,
14015 6 : tform->typlen,
14016 6 : tform->typbyval,
14017 6 : tform->typalign));
14018 :
14019 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14020 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14021 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14022 :
14023 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14024 : valuesAtt, nullsAtt, replacesAtt);
14025 6 : heap_freetuple(heapTup);
14026 6 : heapTup = newTup;
14027 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14028 : }
14029 : }
14030 :
14031 994 : attTup->atttypid = targettype;
14032 994 : attTup->atttypmod = targettypmod;
14033 994 : attTup->attcollation = targetcollid;
14034 994 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14035 0 : ereport(ERROR,
14036 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14037 : errmsg("too many array dimensions"));
14038 994 : attTup->attndims = list_length(typeName->arrayBounds);
14039 994 : attTup->attlen = tform->typlen;
14040 994 : attTup->attbyval = tform->typbyval;
14041 994 : attTup->attalign = tform->typalign;
14042 994 : attTup->attstorage = tform->typstorage;
14043 994 : attTup->attcompression = InvalidCompressionMethod;
14044 :
14045 994 : ReleaseSysCache(typeTuple);
14046 :
14047 994 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14048 :
14049 994 : table_close(attrelation, RowExclusiveLock);
14050 :
14051 : /* Install dependencies on new datatype and collation */
14052 994 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14053 994 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14054 :
14055 : /*
14056 : * Drop any pg_statistic entry for the column, since it's now wrong type
14057 : */
14058 994 : RemoveStatistics(RelationGetRelid(rel), attnum);
14059 :
14060 994 : InvokeObjectPostAlterHook(RelationRelationId,
14061 : RelationGetRelid(rel), attnum);
14062 :
14063 : /*
14064 : * Update the default, if present, by brute force --- remove and re-add
14065 : * the default. Probably unsafe to take shortcuts, since the new version
14066 : * may well have additional dependencies. (It's okay to do this now,
14067 : * rather than after other ALTER TYPE commands, since the default won't
14068 : * depend on other column types.)
14069 : */
14070 994 : if (defaultexpr)
14071 : {
14072 : /*
14073 : * If it's a GENERATED default, drop its dependency records, in
14074 : * particular its INTERNAL dependency on the column, which would
14075 : * otherwise cause dependency.c to refuse to perform the deletion.
14076 : */
14077 86 : if (attTup->attgenerated)
14078 : {
14079 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14080 :
14081 36 : if (!OidIsValid(attrdefoid))
14082 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14083 : RelationGetRelid(rel), attnum);
14084 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14085 : }
14086 :
14087 : /*
14088 : * Make updates-so-far visible, particularly the new pg_attribute row
14089 : * which will be updated again.
14090 : */
14091 86 : CommandCounterIncrement();
14092 :
14093 : /*
14094 : * We use RESTRICT here for safety, but at present we do not expect
14095 : * anything to depend on the default.
14096 : */
14097 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14098 : true);
14099 :
14100 86 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
14101 : }
14102 :
14103 994 : ObjectAddressSubSet(address, RelationRelationId,
14104 : RelationGetRelid(rel), attnum);
14105 :
14106 : /* Cleanup */
14107 994 : heap_freetuple(heapTup);
14108 :
14109 994 : return address;
14110 : }
14111 :
14112 : /*
14113 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14114 : * that depends on the column (constraints, indexes, etc), and record enough
14115 : * information to let us recreate the objects.
14116 : */
14117 : static void
14118 1108 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14119 : Relation rel, AttrNumber attnum, const char *colName)
14120 : {
14121 : Relation depRel;
14122 : ScanKeyData key[3];
14123 : SysScanDesc scan;
14124 : HeapTuple depTup;
14125 :
14126 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14127 :
14128 1108 : depRel = table_open(DependRelationId, RowExclusiveLock);
14129 :
14130 1108 : ScanKeyInit(&key[0],
14131 : Anum_pg_depend_refclassid,
14132 : BTEqualStrategyNumber, F_OIDEQ,
14133 : ObjectIdGetDatum(RelationRelationId));
14134 1108 : ScanKeyInit(&key[1],
14135 : Anum_pg_depend_refobjid,
14136 : BTEqualStrategyNumber, F_OIDEQ,
14137 : ObjectIdGetDatum(RelationGetRelid(rel)));
14138 1108 : ScanKeyInit(&key[2],
14139 : Anum_pg_depend_refobjsubid,
14140 : BTEqualStrategyNumber, F_INT4EQ,
14141 : Int32GetDatum((int32) attnum));
14142 :
14143 1108 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14144 : NULL, 3, key);
14145 :
14146 2234 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14147 : {
14148 1162 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14149 : ObjectAddress foundObject;
14150 :
14151 1162 : foundObject.classId = foundDep->classid;
14152 1162 : foundObject.objectId = foundDep->objid;
14153 1162 : foundObject.objectSubId = foundDep->objsubid;
14154 :
14155 1162 : switch (foundObject.classId)
14156 : {
14157 274 : case RelationRelationId:
14158 : {
14159 274 : char relKind = get_rel_relkind(foundObject.objectId);
14160 :
14161 274 : if (relKind == RELKIND_INDEX ||
14162 : relKind == RELKIND_PARTITIONED_INDEX)
14163 : {
14164 : Assert(foundObject.objectSubId == 0);
14165 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
14166 : }
14167 38 : else if (relKind == RELKIND_SEQUENCE)
14168 : {
14169 : /*
14170 : * This must be a SERIAL column's sequence. We need
14171 : * not do anything to it.
14172 : */
14173 : Assert(foundObject.objectSubId == 0);
14174 : }
14175 : else
14176 : {
14177 : /* Not expecting any other direct dependencies... */
14178 0 : elog(ERROR, "unexpected object depending on column: %s",
14179 : getObjectDescription(&foundObject, false));
14180 : }
14181 274 : break;
14182 : }
14183 :
14184 674 : case ConstraintRelationId:
14185 : Assert(foundObject.objectSubId == 0);
14186 674 : RememberConstraintForRebuilding(foundObject.objectId, tab);
14187 674 : break;
14188 :
14189 0 : case ProcedureRelationId:
14190 :
14191 : /*
14192 : * A new-style SQL function can depend on a column, if that
14193 : * column is referenced in the parsed function body. Ideally
14194 : * we'd automatically update the function by deparsing and
14195 : * reparsing it, but that's risky and might well fail anyhow.
14196 : * FIXME someday.
14197 : *
14198 : * This is only a problem for AT_AlterColumnType, not
14199 : * AT_SetExpression.
14200 : */
14201 0 : if (subtype == AT_AlterColumnType)
14202 0 : ereport(ERROR,
14203 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14204 : errmsg("cannot alter type of a column used by a function or procedure"),
14205 : errdetail("%s depends on column \"%s\"",
14206 : getObjectDescription(&foundObject, false),
14207 : colName)));
14208 0 : break;
14209 :
14210 12 : case RewriteRelationId:
14211 :
14212 : /*
14213 : * View/rule bodies have pretty much the same issues as
14214 : * function bodies. FIXME someday.
14215 : */
14216 12 : if (subtype == AT_AlterColumnType)
14217 12 : ereport(ERROR,
14218 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14219 : errmsg("cannot alter type of a column used by a view or rule"),
14220 : errdetail("%s depends on column \"%s\"",
14221 : getObjectDescription(&foundObject, false),
14222 : colName)));
14223 0 : break;
14224 :
14225 0 : case TriggerRelationId:
14226 :
14227 : /*
14228 : * A trigger can depend on a column because the column is
14229 : * specified as an update target, or because the column is
14230 : * used in the trigger's WHEN condition. The first case would
14231 : * not require any extra work, but the second case would
14232 : * require updating the WHEN expression, which has the same
14233 : * issues as above. Since we can't easily tell which case
14234 : * applies, we punt for both. FIXME someday.
14235 : */
14236 0 : if (subtype == AT_AlterColumnType)
14237 0 : ereport(ERROR,
14238 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14239 : errmsg("cannot alter type of a column used in a trigger definition"),
14240 : errdetail("%s depends on column \"%s\"",
14241 : getObjectDescription(&foundObject, false),
14242 : colName)));
14243 0 : break;
14244 :
14245 0 : case PolicyRelationId:
14246 :
14247 : /*
14248 : * A policy can depend on a column because the column is
14249 : * specified in the policy's USING or WITH CHECK qual
14250 : * expressions. It might be possible to rewrite and recheck
14251 : * the policy expression, but punt for now. It's certainly
14252 : * easy enough to remove and recreate the policy; still, FIXME
14253 : * someday.
14254 : */
14255 0 : if (subtype == AT_AlterColumnType)
14256 0 : ereport(ERROR,
14257 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14258 : errmsg("cannot alter type of a column used in a policy definition"),
14259 : errdetail("%s depends on column \"%s\"",
14260 : getObjectDescription(&foundObject, false),
14261 : colName)));
14262 0 : break;
14263 :
14264 188 : case AttrDefaultRelationId:
14265 : {
14266 188 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14267 :
14268 188 : if (col.objectId == RelationGetRelid(rel) &&
14269 188 : col.objectSubId == attnum)
14270 : {
14271 : /*
14272 : * Ignore the column's own default expression. The
14273 : * caller deals with it.
14274 : */
14275 : }
14276 : else
14277 : {
14278 : /*
14279 : * This must be a reference from the expression of a
14280 : * generated column elsewhere in the same table.
14281 : * Changing the type/generated expression of a column
14282 : * that is used by a generated column is not allowed
14283 : * by SQL standard, so just punt for now. It might be
14284 : * doable with some thinking and effort.
14285 : */
14286 24 : if (subtype == AT_AlterColumnType)
14287 24 : ereport(ERROR,
14288 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14289 : errmsg("cannot alter type of a column used by a generated column"),
14290 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14291 : colName,
14292 : get_attname(col.objectId,
14293 : col.objectSubId,
14294 : false))));
14295 : }
14296 164 : break;
14297 : }
14298 :
14299 14 : case StatisticExtRelationId:
14300 :
14301 : /*
14302 : * Give the extended-stats machinery a chance to fix anything
14303 : * that this column type change would break.
14304 : */
14305 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14306 14 : break;
14307 :
14308 0 : case PublicationRelRelationId:
14309 :
14310 : /*
14311 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14312 : * clause. Same issues as above. FIXME someday.
14313 : */
14314 0 : if (subtype == AT_AlterColumnType)
14315 0 : ereport(ERROR,
14316 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14317 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
14318 : errdetail("%s depends on column \"%s\"",
14319 : getObjectDescription(&foundObject, false),
14320 : colName)));
14321 0 : break;
14322 :
14323 0 : default:
14324 :
14325 : /*
14326 : * We don't expect any other sorts of objects to depend on a
14327 : * column.
14328 : */
14329 0 : elog(ERROR, "unexpected object depending on column: %s",
14330 : getObjectDescription(&foundObject, false));
14331 : break;
14332 : }
14333 : }
14334 :
14335 1072 : systable_endscan(scan);
14336 1072 : table_close(depRel, NoLock);
14337 1072 : }
14338 :
14339 : /*
14340 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14341 : * needs to be reset.
14342 : */
14343 : static void
14344 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14345 : {
14346 444 : if (!get_index_isreplident(indoid))
14347 426 : return;
14348 :
14349 18 : if (tab->replicaIdentityIndex)
14350 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14351 :
14352 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14353 : }
14354 :
14355 : /*
14356 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14357 : */
14358 : static void
14359 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14360 : {
14361 444 : if (!get_index_isclustered(indoid))
14362 426 : return;
14363 :
14364 18 : if (tab->clusterOnIndex)
14365 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14366 :
14367 18 : tab->clusterOnIndex = get_rel_name(indoid);
14368 : }
14369 :
14370 : /*
14371 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14372 : * to be rebuilt (which we might already know).
14373 : */
14374 : static void
14375 686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14376 : {
14377 : /*
14378 : * This de-duplication check is critical for two independent reasons: we
14379 : * mustn't try to recreate the same constraint twice, and if a constraint
14380 : * depends on more than one column whose type is to be altered, we must
14381 : * capture its definition string before applying any of the column type
14382 : * changes. ruleutils.c will get confused if we ask again later.
14383 : */
14384 686 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14385 : {
14386 : /* OK, capture the constraint's existing definition string */
14387 596 : char *defstring = pg_get_constraintdef_command(conoid);
14388 : Oid indoid;
14389 :
14390 : /*
14391 : * It is critical to create not-null constraints ahead of primary key
14392 : * indexes; otherwise, the not-null constraint would be created by the
14393 : * primary key, and the constraint name would be wrong.
14394 : */
14395 596 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14396 : {
14397 198 : tab->changedConstraintOids = lcons_oid(conoid,
14398 : tab->changedConstraintOids);
14399 198 : tab->changedConstraintDefs = lcons(defstring,
14400 : tab->changedConstraintDefs);
14401 : }
14402 : else
14403 : {
14404 :
14405 398 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14406 : conoid);
14407 398 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14408 : defstring);
14409 : }
14410 :
14411 : /*
14412 : * For the index of a constraint, if any, remember if it is used for
14413 : * the table's replica identity or if it is a clustered index, so that
14414 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14415 : * those properties.
14416 : */
14417 596 : indoid = get_constraint_index(conoid);
14418 596 : if (OidIsValid(indoid))
14419 : {
14420 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
14421 228 : RememberClusterOnForRebuilding(indoid, tab);
14422 : }
14423 : }
14424 686 : }
14425 :
14426 : /*
14427 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14428 : * to be rebuilt (which we might already know).
14429 : */
14430 : static void
14431 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14432 : {
14433 : /*
14434 : * This de-duplication check is critical for two independent reasons: we
14435 : * mustn't try to recreate the same index twice, and if an index depends
14436 : * on more than one column whose type is to be altered, we must capture
14437 : * its definition string before applying any of the column type changes.
14438 : * ruleutils.c will get confused if we ask again later.
14439 : */
14440 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
14441 : {
14442 : /*
14443 : * Before adding it as an index-to-rebuild, we'd better see if it
14444 : * belongs to a constraint, and if so rebuild the constraint instead.
14445 : * Typically this check fails, because constraint indexes normally
14446 : * have only dependencies on their constraint. But it's possible for
14447 : * such an index to also have direct dependencies on table columns,
14448 : * for example with a partial exclusion constraint.
14449 : */
14450 228 : Oid conoid = get_index_constraint(indoid);
14451 :
14452 228 : if (OidIsValid(conoid))
14453 : {
14454 12 : RememberConstraintForRebuilding(conoid, tab);
14455 : }
14456 : else
14457 : {
14458 : /* OK, capture the index's existing definition string */
14459 216 : char *defstring = pg_get_indexdef_string(indoid);
14460 :
14461 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14462 : indoid);
14463 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14464 : defstring);
14465 :
14466 : /*
14467 : * Remember if this index is used for the table's replica identity
14468 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14469 : * can queue up commands necessary to restore those properties.
14470 : */
14471 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
14472 216 : RememberClusterOnForRebuilding(indoid, tab);
14473 : }
14474 : }
14475 236 : }
14476 :
14477 : /*
14478 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14479 : * needs to be rebuilt (which we might already know).
14480 : */
14481 : static void
14482 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14483 : {
14484 : /*
14485 : * This de-duplication check is critical for two independent reasons: we
14486 : * mustn't try to recreate the same statistics object twice, and if the
14487 : * statistics object depends on more than one column whose type is to be
14488 : * altered, we must capture its definition string before applying any of
14489 : * the type changes. ruleutils.c will get confused if we ask again later.
14490 : */
14491 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14492 : {
14493 : /* OK, capture the statistics object's existing definition string */
14494 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14495 :
14496 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14497 : stxoid);
14498 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14499 : defstring);
14500 : }
14501 14 : }
14502 :
14503 : /*
14504 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14505 : * operations for a particular relation. We have to drop and recreate all the
14506 : * indexes and constraints that depend on the altered columns. We do the
14507 : * actual dropping here, but re-creation is managed by adding work queue
14508 : * entries to do those steps later.
14509 : */
14510 : static void
14511 1084 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14512 : {
14513 : ObjectAddress obj;
14514 : ObjectAddresses *objects;
14515 : ListCell *def_item;
14516 : ListCell *oid_item;
14517 :
14518 : /*
14519 : * Collect all the constraints and indexes to drop so we can process them
14520 : * in a single call. That way we don't have to worry about dependencies
14521 : * among them.
14522 : */
14523 1084 : objects = new_object_addresses();
14524 :
14525 : /*
14526 : * Re-parse the index and constraint definitions, and attach them to the
14527 : * appropriate work queue entries. We do this before dropping because in
14528 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14529 : * lock on the table the constraint is attached to, and we need to get
14530 : * that before reparsing/dropping.
14531 : *
14532 : * We can't rely on the output of deparsing to tell us which relation to
14533 : * operate on, because concurrent activity might have made the name
14534 : * resolve differently. Instead, we've got to use the OID of the
14535 : * constraint or index we're processing to figure out which relation to
14536 : * operate on.
14537 : */
14538 1680 : forboth(oid_item, tab->changedConstraintOids,
14539 : def_item, tab->changedConstraintDefs)
14540 : {
14541 596 : Oid oldId = lfirst_oid(oid_item);
14542 : HeapTuple tup;
14543 : Form_pg_constraint con;
14544 : Oid relid;
14545 : Oid confrelid;
14546 : char contype;
14547 : bool conislocal;
14548 :
14549 596 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14550 596 : if (!HeapTupleIsValid(tup)) /* should not happen */
14551 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14552 596 : con = (Form_pg_constraint) GETSTRUCT(tup);
14553 596 : if (OidIsValid(con->conrelid))
14554 582 : relid = con->conrelid;
14555 : else
14556 : {
14557 : /* must be a domain constraint */
14558 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14559 14 : if (!OidIsValid(relid))
14560 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14561 : }
14562 596 : confrelid = con->confrelid;
14563 596 : contype = con->contype;
14564 596 : conislocal = con->conislocal;
14565 596 : ReleaseSysCache(tup);
14566 :
14567 596 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14568 596 : add_exact_object_address(&obj, objects);
14569 :
14570 : /*
14571 : * If the constraint is inherited (only), we don't want to inject a
14572 : * new definition here; it'll get recreated when
14573 : * ATAddCheckNNConstraint recurses from adding the parent table's
14574 : * constraint. But we had to carry the info this far so that we can
14575 : * drop the constraint below.
14576 : */
14577 596 : if (!conislocal)
14578 28 : continue;
14579 :
14580 : /*
14581 : * When rebuilding an FK constraint that references the table we're
14582 : * modifying, we might not yet have any lock on the FK's table, so get
14583 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14584 : * step, so there's no value in asking for anything weaker.
14585 : */
14586 568 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14587 36 : LockRelationOid(relid, AccessExclusiveLock);
14588 :
14589 568 : ATPostAlterTypeParse(oldId, relid, confrelid,
14590 568 : (char *) lfirst(def_item),
14591 568 : wqueue, lockmode, tab->rewrite);
14592 : }
14593 1300 : forboth(oid_item, tab->changedIndexOids,
14594 : def_item, tab->changedIndexDefs)
14595 : {
14596 216 : Oid oldId = lfirst_oid(oid_item);
14597 : Oid relid;
14598 :
14599 216 : relid = IndexGetRelation(oldId, false);
14600 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14601 216 : (char *) lfirst(def_item),
14602 216 : wqueue, lockmode, tab->rewrite);
14603 :
14604 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
14605 216 : add_exact_object_address(&obj, objects);
14606 : }
14607 :
14608 : /* add dependencies for new statistics */
14609 1098 : forboth(oid_item, tab->changedStatisticsOids,
14610 : def_item, tab->changedStatisticsDefs)
14611 : {
14612 14 : Oid oldId = lfirst_oid(oid_item);
14613 : Oid relid;
14614 :
14615 14 : relid = StatisticsGetRelation(oldId, false);
14616 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14617 14 : (char *) lfirst(def_item),
14618 14 : wqueue, lockmode, tab->rewrite);
14619 :
14620 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14621 14 : add_exact_object_address(&obj, objects);
14622 : }
14623 :
14624 : /*
14625 : * Queue up command to restore replica identity index marking
14626 : */
14627 1084 : if (tab->replicaIdentityIndex)
14628 : {
14629 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14630 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14631 :
14632 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14633 18 : subcmd->name = tab->replicaIdentityIndex;
14634 18 : cmd->subtype = AT_ReplicaIdentity;
14635 18 : cmd->def = (Node *) subcmd;
14636 :
14637 : /* do it after indexes and constraints */
14638 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14639 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14640 : }
14641 :
14642 : /*
14643 : * Queue up command to restore marking of index used for cluster.
14644 : */
14645 1084 : if (tab->clusterOnIndex)
14646 : {
14647 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14648 :
14649 18 : cmd->subtype = AT_ClusterOn;
14650 18 : cmd->name = tab->clusterOnIndex;
14651 :
14652 : /* do it after indexes and constraints */
14653 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14654 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14655 : }
14656 :
14657 : /*
14658 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14659 : * be depending on these objects.
14660 : */
14661 1084 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14662 :
14663 1084 : free_object_addresses(objects);
14664 :
14665 : /*
14666 : * The objects will get recreated during subsequent passes over the work
14667 : * queue.
14668 : */
14669 1084 : }
14670 :
14671 : /*
14672 : * Parse the previously-saved definition string for a constraint, index or
14673 : * statistics object against the newly-established column data type(s), and
14674 : * queue up the resulting command parsetrees for execution.
14675 : *
14676 : * This might fail if, for example, you have a WHERE clause that uses an
14677 : * operator that's not available for the new column type.
14678 : */
14679 : static void
14680 798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14681 : List **wqueue, LOCKMODE lockmode, bool rewrite)
14682 : {
14683 : List *raw_parsetree_list;
14684 : List *querytree_list;
14685 : ListCell *list_item;
14686 : Relation rel;
14687 :
14688 : /*
14689 : * We expect that we will get only ALTER TABLE and CREATE INDEX
14690 : * statements. Hence, there is no need to pass them through
14691 : * parse_analyze_*() or the rewriter, but instead we need to pass them
14692 : * through parse_utilcmd.c to make them ready for execution.
14693 : */
14694 798 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14695 798 : querytree_list = NIL;
14696 1596 : foreach(list_item, raw_parsetree_list)
14697 : {
14698 798 : RawStmt *rs = lfirst_node(RawStmt, list_item);
14699 798 : Node *stmt = rs->stmt;
14700 :
14701 798 : if (IsA(stmt, IndexStmt))
14702 216 : querytree_list = lappend(querytree_list,
14703 216 : transformIndexStmt(oldRelId,
14704 : (IndexStmt *) stmt,
14705 : cmd));
14706 582 : else if (IsA(stmt, AlterTableStmt))
14707 : {
14708 : List *beforeStmts;
14709 : List *afterStmts;
14710 :
14711 554 : stmt = (Node *) transformAlterTableStmt(oldRelId,
14712 : (AlterTableStmt *) stmt,
14713 : cmd,
14714 : &beforeStmts,
14715 : &afterStmts);
14716 554 : querytree_list = list_concat(querytree_list, beforeStmts);
14717 554 : querytree_list = lappend(querytree_list, stmt);
14718 554 : querytree_list = list_concat(querytree_list, afterStmts);
14719 : }
14720 28 : else if (IsA(stmt, CreateStatsStmt))
14721 14 : querytree_list = lappend(querytree_list,
14722 14 : transformStatsStmt(oldRelId,
14723 : (CreateStatsStmt *) stmt,
14724 : cmd));
14725 : else
14726 14 : querytree_list = lappend(querytree_list, stmt);
14727 : }
14728 :
14729 : /* Caller should already have acquired whatever lock we need. */
14730 798 : rel = relation_open(oldRelId, NoLock);
14731 :
14732 : /*
14733 : * Attach each generated command to the proper place in the work queue.
14734 : * Note this could result in creation of entirely new work-queue entries.
14735 : *
14736 : * Also note that we have to tweak the command subtypes, because it turns
14737 : * out that re-creation of indexes and constraints has to act a bit
14738 : * differently from initial creation.
14739 : */
14740 1596 : foreach(list_item, querytree_list)
14741 : {
14742 798 : Node *stm = (Node *) lfirst(list_item);
14743 : AlteredTableInfo *tab;
14744 :
14745 798 : tab = ATGetQueueEntry(wqueue, rel);
14746 :
14747 798 : if (IsA(stm, IndexStmt))
14748 : {
14749 216 : IndexStmt *stmt = (IndexStmt *) stm;
14750 : AlterTableCmd *newcmd;
14751 :
14752 216 : if (!rewrite)
14753 56 : TryReuseIndex(oldId, stmt);
14754 216 : stmt->reset_default_tblspc = true;
14755 : /* keep the index's comment */
14756 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14757 :
14758 216 : newcmd = makeNode(AlterTableCmd);
14759 216 : newcmd->subtype = AT_ReAddIndex;
14760 216 : newcmd->def = (Node *) stmt;
14761 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
14762 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14763 : }
14764 582 : else if (IsA(stm, AlterTableStmt))
14765 : {
14766 554 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14767 : ListCell *lcmd;
14768 :
14769 1108 : foreach(lcmd, stmt->cmds)
14770 : {
14771 554 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14772 :
14773 554 : if (cmd->subtype == AT_AddIndex)
14774 : {
14775 : IndexStmt *indstmt;
14776 : Oid indoid;
14777 :
14778 228 : indstmt = castNode(IndexStmt, cmd->def);
14779 228 : indoid = get_constraint_index(oldId);
14780 :
14781 228 : if (!rewrite)
14782 48 : TryReuseIndex(indoid, indstmt);
14783 : /* keep any comment on the index */
14784 228 : indstmt->idxcomment = GetComment(indoid,
14785 : RelationRelationId, 0);
14786 228 : indstmt->reset_default_tblspc = true;
14787 :
14788 228 : cmd->subtype = AT_ReAddIndex;
14789 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
14790 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14791 :
14792 : /* recreate any comment on the constraint */
14793 228 : RebuildConstraintComment(tab,
14794 : AT_PASS_OLD_INDEX,
14795 : oldId,
14796 : rel,
14797 : NIL,
14798 228 : indstmt->idxname);
14799 : }
14800 326 : else if (cmd->subtype == AT_AddConstraint)
14801 : {
14802 326 : Constraint *con = castNode(Constraint, cmd->def);
14803 :
14804 326 : con->old_pktable_oid = refRelId;
14805 : /* rewriting neither side of a FK */
14806 326 : if (con->contype == CONSTR_FOREIGN &&
14807 72 : !rewrite && tab->rewrite == 0)
14808 6 : TryReuseForeignKey(oldId, con);
14809 326 : con->reset_default_tblspc = true;
14810 326 : cmd->subtype = AT_ReAddConstraint;
14811 326 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14812 326 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14813 :
14814 : /*
14815 : * Recreate any comment on the constraint. If we have
14816 : * recreated a primary key, then transformTableConstraint
14817 : * has added an unnamed not-null constraint here; skip
14818 : * this in that case.
14819 : */
14820 326 : if (con->conname)
14821 326 : RebuildConstraintComment(tab,
14822 : AT_PASS_OLD_CONSTR,
14823 : oldId,
14824 : rel,
14825 : NIL,
14826 326 : con->conname);
14827 : else
14828 : Assert(con->contype == CONSTR_NOTNULL);
14829 : }
14830 : else
14831 0 : elog(ERROR, "unexpected statement subtype: %d",
14832 : (int) cmd->subtype);
14833 : }
14834 : }
14835 28 : else if (IsA(stm, AlterDomainStmt))
14836 : {
14837 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14838 :
14839 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14840 : {
14841 14 : Constraint *con = castNode(Constraint, stmt->def);
14842 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14843 :
14844 14 : cmd->subtype = AT_ReAddDomainConstraint;
14845 14 : cmd->def = (Node *) stmt;
14846 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14847 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14848 :
14849 : /* recreate any comment on the constraint */
14850 14 : RebuildConstraintComment(tab,
14851 : AT_PASS_OLD_CONSTR,
14852 : oldId,
14853 : NULL,
14854 : stmt->typeName,
14855 14 : con->conname);
14856 : }
14857 : else
14858 0 : elog(ERROR, "unexpected statement subtype: %d",
14859 : (int) stmt->subtype);
14860 : }
14861 14 : else if (IsA(stm, CreateStatsStmt))
14862 : {
14863 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14864 : AlterTableCmd *newcmd;
14865 :
14866 : /* keep the statistics object's comment */
14867 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14868 :
14869 14 : newcmd = makeNode(AlterTableCmd);
14870 14 : newcmd->subtype = AT_ReAddStatistics;
14871 14 : newcmd->def = (Node *) stmt;
14872 14 : tab->subcmds[AT_PASS_MISC] =
14873 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14874 : }
14875 : else
14876 0 : elog(ERROR, "unexpected statement type: %d",
14877 : (int) nodeTag(stm));
14878 : }
14879 :
14880 798 : relation_close(rel, NoLock);
14881 798 : }
14882 :
14883 : /*
14884 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14885 : * for a table or domain constraint that is being rebuilt.
14886 : *
14887 : * objid is the OID of the constraint.
14888 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14889 : * as a string list) for a domain constraint.
14890 : * (We could dig that info, as well as the conname, out of the pg_constraint
14891 : * entry; but callers already have them so might as well pass them.)
14892 : */
14893 : static void
14894 568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14895 : Relation rel, List *domname,
14896 : const char *conname)
14897 : {
14898 : CommentStmt *cmd;
14899 : char *comment_str;
14900 : AlterTableCmd *newcmd;
14901 :
14902 : /* Look for comment for object wanted, and leave if none */
14903 568 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14904 568 : if (comment_str == NULL)
14905 478 : return;
14906 :
14907 : /* Build CommentStmt node, copying all input data for safety */
14908 90 : cmd = makeNode(CommentStmt);
14909 90 : if (rel)
14910 : {
14911 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
14912 78 : cmd->object = (Node *)
14913 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14914 : makeString(pstrdup(RelationGetRelationName(rel))),
14915 : makeString(pstrdup(conname)));
14916 : }
14917 : else
14918 : {
14919 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
14920 12 : cmd->object = (Node *)
14921 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
14922 : makeString(pstrdup(conname)));
14923 : }
14924 90 : cmd->comment = comment_str;
14925 :
14926 : /* Append it to list of commands */
14927 90 : newcmd = makeNode(AlterTableCmd);
14928 90 : newcmd->subtype = AT_ReAddComment;
14929 90 : newcmd->def = (Node *) cmd;
14930 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14931 : }
14932 :
14933 : /*
14934 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14935 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14936 : */
14937 : static void
14938 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14939 : {
14940 104 : if (CheckIndexCompatible(oldId,
14941 104 : stmt->accessMethod,
14942 104 : stmt->indexParams,
14943 104 : stmt->excludeOpNames,
14944 104 : stmt->iswithoutoverlaps))
14945 : {
14946 104 : Relation irel = index_open(oldId, NoLock);
14947 :
14948 : /* If it's a partitioned index, there is no storage to share. */
14949 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14950 : {
14951 74 : stmt->oldNumber = irel->rd_locator.relNumber;
14952 74 : stmt->oldCreateSubid = irel->rd_createSubid;
14953 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14954 : }
14955 104 : index_close(irel, NoLock);
14956 : }
14957 104 : }
14958 :
14959 : /*
14960 : * Subroutine for ATPostAlterTypeParse().
14961 : *
14962 : * Stash the old P-F equality operator into the Constraint node, for possible
14963 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14964 : * this constraint can be skipped.
14965 : */
14966 : static void
14967 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
14968 : {
14969 : HeapTuple tup;
14970 : Datum adatum;
14971 : ArrayType *arr;
14972 : Oid *rawarr;
14973 : int numkeys;
14974 : int i;
14975 :
14976 : Assert(con->contype == CONSTR_FOREIGN);
14977 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14978 :
14979 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14980 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
14981 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14982 :
14983 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14984 : Anum_pg_constraint_conpfeqop);
14985 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14986 6 : numkeys = ARR_DIMS(arr)[0];
14987 : /* test follows the one in ri_FetchConstraintInfo() */
14988 6 : if (ARR_NDIM(arr) != 1 ||
14989 6 : ARR_HASNULL(arr) ||
14990 6 : ARR_ELEMTYPE(arr) != OIDOID)
14991 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
14992 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14993 :
14994 : /* stash a List of the operator Oids in our Constraint node */
14995 12 : for (i = 0; i < numkeys; i++)
14996 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14997 :
14998 6 : ReleaseSysCache(tup);
14999 6 : }
15000 :
15001 : /*
15002 : * ALTER COLUMN .. OPTIONS ( ... )
15003 : *
15004 : * Returns the address of the modified column
15005 : */
15006 : static ObjectAddress
15007 172 : ATExecAlterColumnGenericOptions(Relation rel,
15008 : const char *colName,
15009 : List *options,
15010 : LOCKMODE lockmode)
15011 : {
15012 : Relation ftrel;
15013 : Relation attrel;
15014 : ForeignServer *server;
15015 : ForeignDataWrapper *fdw;
15016 : HeapTuple tuple;
15017 : HeapTuple newtuple;
15018 : bool isnull;
15019 : Datum repl_val[Natts_pg_attribute];
15020 : bool repl_null[Natts_pg_attribute];
15021 : bool repl_repl[Natts_pg_attribute];
15022 : Datum datum;
15023 : Form_pg_foreign_table fttableform;
15024 : Form_pg_attribute atttableform;
15025 : AttrNumber attnum;
15026 : ObjectAddress address;
15027 :
15028 172 : if (options == NIL)
15029 0 : return InvalidObjectAddress;
15030 :
15031 : /* First, determine FDW validator associated to the foreign table. */
15032 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15033 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15034 172 : if (!HeapTupleIsValid(tuple))
15035 0 : ereport(ERROR,
15036 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15037 : errmsg("foreign table \"%s\" does not exist",
15038 : RelationGetRelationName(rel))));
15039 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15040 172 : server = GetForeignServer(fttableform->ftserver);
15041 172 : fdw = GetForeignDataWrapper(server->fdwid);
15042 :
15043 172 : table_close(ftrel, AccessShareLock);
15044 172 : ReleaseSysCache(tuple);
15045 :
15046 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15047 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15048 172 : if (!HeapTupleIsValid(tuple))
15049 0 : ereport(ERROR,
15050 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15051 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15052 : colName, RelationGetRelationName(rel))));
15053 :
15054 : /* Prevent them from altering a system attribute */
15055 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15056 172 : attnum = atttableform->attnum;
15057 172 : if (attnum <= 0)
15058 6 : ereport(ERROR,
15059 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15060 : errmsg("cannot alter system column \"%s\"", colName)));
15061 :
15062 :
15063 : /* Initialize buffers for new tuple values */
15064 166 : memset(repl_val, 0, sizeof(repl_val));
15065 166 : memset(repl_null, false, sizeof(repl_null));
15066 166 : memset(repl_repl, false, sizeof(repl_repl));
15067 :
15068 : /* Extract the current options */
15069 166 : datum = SysCacheGetAttr(ATTNAME,
15070 : tuple,
15071 : Anum_pg_attribute_attfdwoptions,
15072 : &isnull);
15073 166 : if (isnull)
15074 156 : datum = PointerGetDatum(NULL);
15075 :
15076 : /* Transform the options */
15077 166 : datum = transformGenericOptions(AttributeRelationId,
15078 : datum,
15079 : options,
15080 : fdw->fdwvalidator);
15081 :
15082 166 : if (PointerIsValid(DatumGetPointer(datum)))
15083 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15084 : else
15085 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15086 :
15087 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15088 :
15089 : /* Everything looks good - update the tuple */
15090 :
15091 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15092 : repl_val, repl_null, repl_repl);
15093 :
15094 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15095 :
15096 166 : InvokeObjectPostAlterHook(RelationRelationId,
15097 : RelationGetRelid(rel),
15098 : atttableform->attnum);
15099 166 : ObjectAddressSubSet(address, RelationRelationId,
15100 : RelationGetRelid(rel), attnum);
15101 :
15102 166 : ReleaseSysCache(tuple);
15103 :
15104 166 : table_close(attrel, RowExclusiveLock);
15105 :
15106 166 : heap_freetuple(newtuple);
15107 :
15108 166 : return address;
15109 : }
15110 :
15111 : /*
15112 : * ALTER TABLE OWNER
15113 : *
15114 : * recursing is true if we are recursing from a table to its indexes,
15115 : * sequences, or toast table. We don't allow the ownership of those things to
15116 : * be changed separately from the parent table. Also, we can skip permission
15117 : * checks (this is necessary not just an optimization, else we'd fail to
15118 : * handle toast tables properly).
15119 : *
15120 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15121 : * free-standing composite type.
15122 : */
15123 : void
15124 2124 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15125 : {
15126 : Relation target_rel;
15127 : Relation class_rel;
15128 : HeapTuple tuple;
15129 : Form_pg_class tuple_class;
15130 :
15131 : /*
15132 : * Get exclusive lock till end of transaction on the target table. Use
15133 : * relation_open so that we can work on indexes and sequences.
15134 : */
15135 2124 : target_rel = relation_open(relationOid, lockmode);
15136 :
15137 : /* Get its pg_class tuple, too */
15138 2124 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
15139 :
15140 2124 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15141 2124 : if (!HeapTupleIsValid(tuple))
15142 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
15143 2124 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15144 :
15145 : /* Can we change the ownership of this tuple? */
15146 2124 : switch (tuple_class->relkind)
15147 : {
15148 1848 : case RELKIND_RELATION:
15149 : case RELKIND_VIEW:
15150 : case RELKIND_MATVIEW:
15151 : case RELKIND_FOREIGN_TABLE:
15152 : case RELKIND_PARTITIONED_TABLE:
15153 : /* ok to change owner */
15154 1848 : break;
15155 96 : case RELKIND_INDEX:
15156 96 : if (!recursing)
15157 : {
15158 : /*
15159 : * Because ALTER INDEX OWNER used to be allowed, and in fact
15160 : * is generated by old versions of pg_dump, we give a warning
15161 : * and do nothing rather than erroring out. Also, to avoid
15162 : * unnecessary chatter while restoring those old dumps, say
15163 : * nothing at all if the command would be a no-op anyway.
15164 : */
15165 0 : if (tuple_class->relowner != newOwnerId)
15166 0 : ereport(WARNING,
15167 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15168 : errmsg("cannot change owner of index \"%s\"",
15169 : NameStr(tuple_class->relname)),
15170 : errhint("Change the ownership of the index's table instead.")));
15171 : /* quick hack to exit via the no-op path */
15172 0 : newOwnerId = tuple_class->relowner;
15173 : }
15174 96 : break;
15175 20 : case RELKIND_PARTITIONED_INDEX:
15176 20 : if (recursing)
15177 20 : break;
15178 0 : ereport(ERROR,
15179 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15180 : errmsg("cannot change owner of index \"%s\"",
15181 : NameStr(tuple_class->relname)),
15182 : errhint("Change the ownership of the index's table instead.")));
15183 : break;
15184 110 : case RELKIND_SEQUENCE:
15185 110 : if (!recursing &&
15186 62 : tuple_class->relowner != newOwnerId)
15187 : {
15188 : /* if it's an owned sequence, disallow changing it by itself */
15189 : Oid tableId;
15190 : int32 colId;
15191 :
15192 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15193 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15194 0 : ereport(ERROR,
15195 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15196 : errmsg("cannot change owner of sequence \"%s\"",
15197 : NameStr(tuple_class->relname)),
15198 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
15199 : NameStr(tuple_class->relname),
15200 : get_rel_name(tableId))));
15201 : }
15202 110 : break;
15203 8 : case RELKIND_COMPOSITE_TYPE:
15204 8 : if (recursing)
15205 8 : break;
15206 0 : ereport(ERROR,
15207 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15208 : errmsg("\"%s\" is a composite type",
15209 : NameStr(tuple_class->relname)),
15210 : /* translator: %s is an SQL ALTER command */
15211 : errhint("Use %s instead.",
15212 : "ALTER TYPE")));
15213 : break;
15214 42 : case RELKIND_TOASTVALUE:
15215 42 : if (recursing)
15216 42 : break;
15217 : /* FALL THRU */
15218 : default:
15219 0 : ereport(ERROR,
15220 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15221 : errmsg("cannot change owner of relation \"%s\"",
15222 : NameStr(tuple_class->relname)),
15223 : errdetail_relkind_not_supported(tuple_class->relkind)));
15224 : }
15225 :
15226 : /*
15227 : * If the new owner is the same as the existing owner, consider the
15228 : * command to have succeeded. This is for dump restoration purposes.
15229 : */
15230 2124 : if (tuple_class->relowner != newOwnerId)
15231 : {
15232 : Datum repl_val[Natts_pg_class];
15233 : bool repl_null[Natts_pg_class];
15234 : bool repl_repl[Natts_pg_class];
15235 : Acl *newAcl;
15236 : Datum aclDatum;
15237 : bool isNull;
15238 : HeapTuple newtuple;
15239 :
15240 : /* skip permission checks when recursing to index or toast table */
15241 498 : if (!recursing)
15242 : {
15243 : /* Superusers can always do it */
15244 280 : if (!superuser())
15245 : {
15246 42 : Oid namespaceOid = tuple_class->relnamespace;
15247 : AclResult aclresult;
15248 :
15249 : /* Otherwise, must be owner of the existing object */
15250 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15251 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15252 0 : RelationGetRelationName(target_rel));
15253 :
15254 : /* Must be able to become new owner */
15255 42 : check_can_set_role(GetUserId(), newOwnerId);
15256 :
15257 : /* New owner must have CREATE privilege on namespace */
15258 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15259 : ACL_CREATE);
15260 30 : if (aclresult != ACLCHECK_OK)
15261 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15262 0 : get_namespace_name(namespaceOid));
15263 : }
15264 : }
15265 :
15266 486 : memset(repl_null, false, sizeof(repl_null));
15267 486 : memset(repl_repl, false, sizeof(repl_repl));
15268 :
15269 486 : repl_repl[Anum_pg_class_relowner - 1] = true;
15270 486 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15271 :
15272 : /*
15273 : * Determine the modified ACL for the new owner. This is only
15274 : * necessary when the ACL is non-null.
15275 : */
15276 486 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15277 : Anum_pg_class_relacl,
15278 : &isNull);
15279 486 : if (!isNull)
15280 : {
15281 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15282 : tuple_class->relowner, newOwnerId);
15283 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
15284 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15285 : }
15286 :
15287 486 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15288 :
15289 486 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15290 :
15291 486 : heap_freetuple(newtuple);
15292 :
15293 : /*
15294 : * We must similarly update any per-column ACLs to reflect the new
15295 : * owner; for neatness reasons that's split out as a subroutine.
15296 : */
15297 486 : change_owner_fix_column_acls(relationOid,
15298 : tuple_class->relowner,
15299 : newOwnerId);
15300 :
15301 : /*
15302 : * Update owner dependency reference, if any. A composite type has
15303 : * none, because it's tracked for the pg_type entry instead of here;
15304 : * indexes and TOAST tables don't have their own entries either.
15305 : */
15306 486 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15307 478 : tuple_class->relkind != RELKIND_INDEX &&
15308 382 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15309 362 : tuple_class->relkind != RELKIND_TOASTVALUE)
15310 320 : changeDependencyOnOwner(RelationRelationId, relationOid,
15311 : newOwnerId);
15312 :
15313 : /*
15314 : * Also change the ownership of the table's row type, if it has one
15315 : */
15316 486 : if (OidIsValid(tuple_class->reltype))
15317 294 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15318 :
15319 : /*
15320 : * If we are operating on a table or materialized view, also change
15321 : * the ownership of any indexes and sequences that belong to the
15322 : * relation, as well as its toast table (if it has one).
15323 : */
15324 486 : if (tuple_class->relkind == RELKIND_RELATION ||
15325 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15326 224 : tuple_class->relkind == RELKIND_MATVIEW ||
15327 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
15328 : {
15329 : List *index_oid_list;
15330 : ListCell *i;
15331 :
15332 : /* Find all the indexes belonging to this relation */
15333 304 : index_oid_list = RelationGetIndexList(target_rel);
15334 :
15335 : /* For each index, recursively change its ownership */
15336 420 : foreach(i, index_oid_list)
15337 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15338 :
15339 304 : list_free(index_oid_list);
15340 : }
15341 :
15342 : /* If it has a toast table, recurse to change its ownership */
15343 486 : if (tuple_class->reltoastrelid != InvalidOid)
15344 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15345 : true, lockmode);
15346 :
15347 : /* If it has dependent sequences, recurse to change them too */
15348 486 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15349 : }
15350 :
15351 2112 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15352 :
15353 2112 : ReleaseSysCache(tuple);
15354 2112 : table_close(class_rel, RowExclusiveLock);
15355 2112 : relation_close(target_rel, NoLock);
15356 2112 : }
15357 :
15358 : /*
15359 : * change_owner_fix_column_acls
15360 : *
15361 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15362 : * and fix any non-null column ACLs to reflect the new owner.
15363 : */
15364 : static void
15365 486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15366 : {
15367 : Relation attRelation;
15368 : SysScanDesc scan;
15369 : ScanKeyData key[1];
15370 : HeapTuple attributeTuple;
15371 :
15372 486 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15373 486 : ScanKeyInit(&key[0],
15374 : Anum_pg_attribute_attrelid,
15375 : BTEqualStrategyNumber, F_OIDEQ,
15376 : ObjectIdGetDatum(relationOid));
15377 486 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15378 : true, NULL, 1, key);
15379 3372 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15380 : {
15381 2886 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15382 : Datum repl_val[Natts_pg_attribute];
15383 : bool repl_null[Natts_pg_attribute];
15384 : bool repl_repl[Natts_pg_attribute];
15385 : Acl *newAcl;
15386 : Datum aclDatum;
15387 : bool isNull;
15388 : HeapTuple newtuple;
15389 :
15390 : /* Ignore dropped columns */
15391 2886 : if (att->attisdropped)
15392 2884 : continue;
15393 :
15394 2886 : aclDatum = heap_getattr(attributeTuple,
15395 : Anum_pg_attribute_attacl,
15396 : RelationGetDescr(attRelation),
15397 : &isNull);
15398 : /* Null ACLs do not require changes */
15399 2886 : if (isNull)
15400 2884 : continue;
15401 :
15402 2 : memset(repl_null, false, sizeof(repl_null));
15403 2 : memset(repl_repl, false, sizeof(repl_repl));
15404 :
15405 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15406 : oldOwnerId, newOwnerId);
15407 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15408 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15409 :
15410 2 : newtuple = heap_modify_tuple(attributeTuple,
15411 : RelationGetDescr(attRelation),
15412 : repl_val, repl_null, repl_repl);
15413 :
15414 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15415 :
15416 2 : heap_freetuple(newtuple);
15417 : }
15418 486 : systable_endscan(scan);
15419 486 : table_close(attRelation, RowExclusiveLock);
15420 486 : }
15421 :
15422 : /*
15423 : * change_owner_recurse_to_sequences
15424 : *
15425 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15426 : * for sequences that are dependent on serial columns, and changes their
15427 : * ownership.
15428 : */
15429 : static void
15430 486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15431 : {
15432 : Relation depRel;
15433 : SysScanDesc scan;
15434 : ScanKeyData key[2];
15435 : HeapTuple tup;
15436 :
15437 : /*
15438 : * SERIAL sequences are those having an auto dependency on one of the
15439 : * table's columns (we don't care *which* column, exactly).
15440 : */
15441 486 : depRel = table_open(DependRelationId, AccessShareLock);
15442 :
15443 486 : ScanKeyInit(&key[0],
15444 : Anum_pg_depend_refclassid,
15445 : BTEqualStrategyNumber, F_OIDEQ,
15446 : ObjectIdGetDatum(RelationRelationId));
15447 486 : ScanKeyInit(&key[1],
15448 : Anum_pg_depend_refobjid,
15449 : BTEqualStrategyNumber, F_OIDEQ,
15450 : ObjectIdGetDatum(relationOid));
15451 : /* we leave refobjsubid unspecified */
15452 :
15453 486 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15454 : NULL, 2, key);
15455 :
15456 1374 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15457 : {
15458 888 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15459 : Relation seqRel;
15460 :
15461 : /* skip dependencies other than auto dependencies on columns */
15462 888 : if (depForm->refobjsubid == 0 ||
15463 352 : depForm->classid != RelationRelationId ||
15464 142 : depForm->objsubid != 0 ||
15465 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15466 746 : continue;
15467 :
15468 : /* Use relation_open just in case it's an index */
15469 142 : seqRel = relation_open(depForm->objid, lockmode);
15470 :
15471 : /* skip non-sequence relations */
15472 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15473 : {
15474 : /* No need to keep the lock */
15475 116 : relation_close(seqRel, lockmode);
15476 116 : continue;
15477 : }
15478 :
15479 : /* We don't need to close the sequence while we alter it. */
15480 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15481 :
15482 : /* Now we can close it. Keep the lock till end of transaction. */
15483 26 : relation_close(seqRel, NoLock);
15484 : }
15485 :
15486 486 : systable_endscan(scan);
15487 :
15488 486 : relation_close(depRel, AccessShareLock);
15489 486 : }
15490 :
15491 : /*
15492 : * ALTER TABLE CLUSTER ON
15493 : *
15494 : * The only thing we have to do is to change the indisclustered bits.
15495 : *
15496 : * Return the address of the new clustering index.
15497 : */
15498 : static ObjectAddress
15499 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15500 : {
15501 : Oid indexOid;
15502 : ObjectAddress address;
15503 :
15504 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15505 :
15506 64 : if (!OidIsValid(indexOid))
15507 0 : ereport(ERROR,
15508 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15509 : errmsg("index \"%s\" for table \"%s\" does not exist",
15510 : indexName, RelationGetRelationName(rel))));
15511 :
15512 : /* Check index is valid to cluster on */
15513 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15514 :
15515 : /* And do the work */
15516 64 : mark_index_clustered(rel, indexOid, false);
15517 :
15518 58 : ObjectAddressSet(address,
15519 : RelationRelationId, indexOid);
15520 :
15521 58 : return address;
15522 : }
15523 :
15524 : /*
15525 : * ALTER TABLE SET WITHOUT CLUSTER
15526 : *
15527 : * We have to find any indexes on the table that have indisclustered bit
15528 : * set and turn it off.
15529 : */
15530 : static void
15531 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15532 : {
15533 18 : mark_index_clustered(rel, InvalidOid, false);
15534 12 : }
15535 :
15536 : /*
15537 : * Preparation phase for SET ACCESS METHOD
15538 : *
15539 : * Check that the access method exists and determine whether a change is
15540 : * actually needed.
15541 : */
15542 : static void
15543 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15544 : {
15545 : Oid amoid;
15546 :
15547 : /*
15548 : * Look up the access method name and check that it differs from the
15549 : * table's current AM. If DEFAULT was specified for a partitioned table
15550 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15551 : */
15552 110 : if (amname != NULL)
15553 74 : amoid = get_table_am_oid(amname, false);
15554 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15555 18 : amoid = InvalidOid;
15556 : else
15557 18 : amoid = get_table_am_oid(default_table_access_method, false);
15558 :
15559 : /* if it's a match, phase 3 doesn't need to do anything */
15560 110 : if (rel->rd_rel->relam == amoid)
15561 12 : return;
15562 :
15563 : /* Save info for Phase 3 to do the real work */
15564 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15565 98 : tab->newAccessMethod = amoid;
15566 98 : tab->chgAccessMethod = true;
15567 : }
15568 :
15569 : /*
15570 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15571 : * storage that have an interest in preserving AM.
15572 : *
15573 : * Since these have no storage, setting the access method is a catalog only
15574 : * operation.
15575 : */
15576 : static void
15577 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15578 : {
15579 : Relation pg_class;
15580 : Oid oldAccessMethodId;
15581 : HeapTuple tuple;
15582 : Form_pg_class rd_rel;
15583 44 : Oid reloid = RelationGetRelid(rel);
15584 :
15585 : /*
15586 : * Shouldn't be called on relations having storage; these are processed in
15587 : * phase 3.
15588 : */
15589 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15590 :
15591 : /* Get a modifiable copy of the relation's pg_class row. */
15592 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15593 :
15594 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15595 44 : if (!HeapTupleIsValid(tuple))
15596 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15597 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15598 :
15599 : /* Update the pg_class row. */
15600 44 : oldAccessMethodId = rd_rel->relam;
15601 44 : rd_rel->relam = newAccessMethodId;
15602 :
15603 : /* Leave if no update required */
15604 44 : if (rd_rel->relam == oldAccessMethodId)
15605 : {
15606 0 : heap_freetuple(tuple);
15607 0 : table_close(pg_class, RowExclusiveLock);
15608 0 : return;
15609 : }
15610 :
15611 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15612 :
15613 : /*
15614 : * Update the dependency on the new access method. No dependency is added
15615 : * if the new access method is InvalidOid (default case). Be very careful
15616 : * that this has to compare the previous value stored in pg_class with the
15617 : * new one.
15618 : */
15619 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15620 20 : {
15621 : ObjectAddress relobj,
15622 : referenced;
15623 :
15624 : /*
15625 : * New access method is defined and there was no dependency
15626 : * previously, so record a new one.
15627 : */
15628 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15629 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15630 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15631 : }
15632 24 : else if (OidIsValid(oldAccessMethodId) &&
15633 24 : !OidIsValid(rd_rel->relam))
15634 : {
15635 : /*
15636 : * There was an access method defined, and no new one, so just remove
15637 : * the existing dependency.
15638 : */
15639 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15640 : AccessMethodRelationId,
15641 : DEPENDENCY_NORMAL);
15642 : }
15643 : else
15644 : {
15645 : Assert(OidIsValid(oldAccessMethodId) &&
15646 : OidIsValid(rd_rel->relam));
15647 :
15648 : /* Both are valid, so update the dependency */
15649 12 : changeDependencyFor(RelationRelationId, reloid,
15650 : AccessMethodRelationId,
15651 : oldAccessMethodId, rd_rel->relam);
15652 : }
15653 :
15654 : /* make the relam and dependency changes visible */
15655 44 : CommandCounterIncrement();
15656 :
15657 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15658 :
15659 44 : heap_freetuple(tuple);
15660 44 : table_close(pg_class, RowExclusiveLock);
15661 : }
15662 :
15663 : /*
15664 : * ALTER TABLE SET TABLESPACE
15665 : */
15666 : static void
15667 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15668 : {
15669 : Oid tablespaceId;
15670 :
15671 : /* Check that the tablespace exists */
15672 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
15673 :
15674 : /* Check permissions except when moving to database's default */
15675 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15676 : {
15677 : AclResult aclresult;
15678 :
15679 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15680 66 : if (aclresult != ACLCHECK_OK)
15681 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15682 : }
15683 :
15684 : /* Save info for Phase 3 to do the real work */
15685 158 : if (OidIsValid(tab->newTableSpace))
15686 0 : ereport(ERROR,
15687 : (errcode(ERRCODE_SYNTAX_ERROR),
15688 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
15689 :
15690 158 : tab->newTableSpace = tablespaceId;
15691 158 : }
15692 :
15693 : /*
15694 : * Set, reset, or replace reloptions.
15695 : */
15696 : static void
15697 946 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15698 : LOCKMODE lockmode)
15699 : {
15700 : Oid relid;
15701 : Relation pgclass;
15702 : HeapTuple tuple;
15703 : HeapTuple newtuple;
15704 : Datum datum;
15705 : bool isnull;
15706 : Datum newOptions;
15707 : Datum repl_val[Natts_pg_class];
15708 : bool repl_null[Natts_pg_class];
15709 : bool repl_repl[Natts_pg_class];
15710 946 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
15711 :
15712 946 : if (defList == NIL && operation != AT_ReplaceRelOptions)
15713 0 : return; /* nothing to do */
15714 :
15715 946 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
15716 :
15717 : /* Fetch heap tuple */
15718 946 : relid = RelationGetRelid(rel);
15719 946 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
15720 946 : if (!HeapTupleIsValid(tuple))
15721 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
15722 :
15723 946 : if (operation == AT_ReplaceRelOptions)
15724 : {
15725 : /*
15726 : * If we're supposed to replace the reloptions list, we just pretend
15727 : * there were none before.
15728 : */
15729 194 : datum = (Datum) 0;
15730 194 : isnull = true;
15731 : }
15732 : else
15733 : {
15734 : /* Get the old reloptions */
15735 752 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15736 : &isnull);
15737 : }
15738 :
15739 : /* Generate new proposed reloptions (text array) */
15740 946 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15741 : defList, NULL, validnsps, false,
15742 : operation == AT_ResetRelOptions);
15743 :
15744 : /* Validate */
15745 940 : switch (rel->rd_rel->relkind)
15746 : {
15747 524 : case RELKIND_RELATION:
15748 : case RELKIND_TOASTVALUE:
15749 : case RELKIND_MATVIEW:
15750 524 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15751 524 : break;
15752 6 : case RELKIND_PARTITIONED_TABLE:
15753 6 : (void) partitioned_table_reloptions(newOptions, true);
15754 0 : break;
15755 296 : case RELKIND_VIEW:
15756 296 : (void) view_reloptions(newOptions, true);
15757 278 : break;
15758 114 : case RELKIND_INDEX:
15759 : case RELKIND_PARTITIONED_INDEX:
15760 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15761 92 : break;
15762 0 : default:
15763 0 : ereport(ERROR,
15764 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15765 : errmsg("cannot set options for relation \"%s\"",
15766 : RelationGetRelationName(rel)),
15767 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15768 : break;
15769 : }
15770 :
15771 : /* Special-case validation of view options */
15772 894 : if (rel->rd_rel->relkind == RELKIND_VIEW)
15773 : {
15774 278 : Query *view_query = get_view_query(rel);
15775 278 : List *view_options = untransformRelOptions(newOptions);
15776 : ListCell *cell;
15777 278 : bool check_option = false;
15778 :
15779 380 : foreach(cell, view_options)
15780 : {
15781 102 : DefElem *defel = (DefElem *) lfirst(cell);
15782 :
15783 102 : if (strcmp(defel->defname, "check_option") == 0)
15784 24 : check_option = true;
15785 : }
15786 :
15787 : /*
15788 : * If the check option is specified, look to see if the view is
15789 : * actually auto-updatable or not.
15790 : */
15791 278 : if (check_option)
15792 : {
15793 : const char *view_updatable_error =
15794 24 : view_query_is_auto_updatable(view_query, true);
15795 :
15796 24 : if (view_updatable_error)
15797 0 : ereport(ERROR,
15798 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15799 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15800 : errhint("%s", _(view_updatable_error))));
15801 : }
15802 : }
15803 :
15804 : /*
15805 : * All we need do here is update the pg_class row; the new options will be
15806 : * propagated into relcaches during post-commit cache inval.
15807 : */
15808 894 : memset(repl_val, 0, sizeof(repl_val));
15809 894 : memset(repl_null, false, sizeof(repl_null));
15810 894 : memset(repl_repl, false, sizeof(repl_repl));
15811 :
15812 894 : if (newOptions != (Datum) 0)
15813 600 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15814 : else
15815 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
15816 :
15817 894 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15818 :
15819 894 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15820 : repl_val, repl_null, repl_repl);
15821 :
15822 894 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15823 894 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
15824 :
15825 894 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15826 :
15827 894 : heap_freetuple(newtuple);
15828 :
15829 894 : ReleaseSysCache(tuple);
15830 :
15831 : /* repeat the whole exercise for the toast table, if there's one */
15832 894 : if (OidIsValid(rel->rd_rel->reltoastrelid))
15833 : {
15834 : Relation toastrel;
15835 256 : Oid toastid = rel->rd_rel->reltoastrelid;
15836 :
15837 256 : toastrel = table_open(toastid, lockmode);
15838 :
15839 : /* Fetch heap tuple */
15840 256 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15841 256 : if (!HeapTupleIsValid(tuple))
15842 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15843 :
15844 256 : if (operation == AT_ReplaceRelOptions)
15845 : {
15846 : /*
15847 : * If we're supposed to replace the reloptions list, we just
15848 : * pretend there were none before.
15849 : */
15850 0 : datum = (Datum) 0;
15851 0 : isnull = true;
15852 : }
15853 : else
15854 : {
15855 : /* Get the old reloptions */
15856 256 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15857 : &isnull);
15858 : }
15859 :
15860 256 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15861 : defList, "toast", validnsps, false,
15862 : operation == AT_ResetRelOptions);
15863 :
15864 256 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15865 :
15866 256 : memset(repl_val, 0, sizeof(repl_val));
15867 256 : memset(repl_null, false, sizeof(repl_null));
15868 256 : memset(repl_repl, false, sizeof(repl_repl));
15869 :
15870 256 : if (newOptions != (Datum) 0)
15871 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15872 : else
15873 214 : repl_null[Anum_pg_class_reloptions - 1] = true;
15874 :
15875 256 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15876 :
15877 256 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15878 : repl_val, repl_null, repl_repl);
15879 :
15880 256 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15881 :
15882 256 : InvokeObjectPostAlterHookArg(RelationRelationId,
15883 : RelationGetRelid(toastrel), 0,
15884 : InvalidOid, true);
15885 :
15886 256 : heap_freetuple(newtuple);
15887 :
15888 256 : ReleaseSysCache(tuple);
15889 :
15890 256 : table_close(toastrel, NoLock);
15891 : }
15892 :
15893 894 : table_close(pgclass, RowExclusiveLock);
15894 : }
15895 :
15896 : /*
15897 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15898 : * rewriting to be done, so we just want to copy the data as fast as possible.
15899 : */
15900 : static void
15901 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15902 : {
15903 : Relation rel;
15904 : Oid reltoastrelid;
15905 : RelFileNumber newrelfilenumber;
15906 : RelFileLocator newrlocator;
15907 162 : List *reltoastidxids = NIL;
15908 : ListCell *lc;
15909 :
15910 : /*
15911 : * Need lock here in case we are recursing to toast table or index
15912 : */
15913 162 : rel = relation_open(tableOid, lockmode);
15914 :
15915 : /* Check first if relation can be moved to new tablespace */
15916 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15917 : {
15918 2 : InvokeObjectPostAlterHook(RelationRelationId,
15919 : RelationGetRelid(rel), 0);
15920 2 : relation_close(rel, NoLock);
15921 2 : return;
15922 : }
15923 :
15924 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
15925 : /* Fetch the list of indexes on toast relation if necessary */
15926 160 : if (OidIsValid(reltoastrelid))
15927 : {
15928 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15929 :
15930 20 : reltoastidxids = RelationGetIndexList(toastRel);
15931 20 : relation_close(toastRel, lockmode);
15932 : }
15933 :
15934 : /*
15935 : * Relfilenumbers are not unique in databases across tablespaces, so we
15936 : * need to allocate a new one in the new tablespace.
15937 : */
15938 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15939 160 : rel->rd_rel->relpersistence);
15940 :
15941 : /* Open old and new relation */
15942 160 : newrlocator = rel->rd_locator;
15943 160 : newrlocator.relNumber = newrelfilenumber;
15944 160 : newrlocator.spcOid = newTableSpace;
15945 :
15946 : /* hand off to AM to actually create new rel storage and copy the data */
15947 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
15948 : {
15949 62 : index_copy_data(rel, newrlocator);
15950 : }
15951 : else
15952 : {
15953 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15954 98 : table_relation_copy_data(rel, &newrlocator);
15955 : }
15956 :
15957 : /*
15958 : * Update the pg_class row.
15959 : *
15960 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15961 : * executed on pg_class or its indexes (the above copy wouldn't contain
15962 : * the updated pg_class entry), but that's forbidden with
15963 : * CheckRelationTableSpaceMove().
15964 : */
15965 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15966 :
15967 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15968 :
15969 160 : RelationAssumeNewRelfilelocator(rel);
15970 :
15971 160 : relation_close(rel, NoLock);
15972 :
15973 : /* Make sure the reltablespace change is visible */
15974 160 : CommandCounterIncrement();
15975 :
15976 : /* Move associated toast relation and/or indexes, too */
15977 160 : if (OidIsValid(reltoastrelid))
15978 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15979 180 : foreach(lc, reltoastidxids)
15980 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15981 :
15982 : /* Clean up */
15983 160 : list_free(reltoastidxids);
15984 : }
15985 :
15986 : /*
15987 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15988 : * storage that have an interest in preserving tablespace.
15989 : *
15990 : * Since these have no storage the tablespace can be updated with a simple
15991 : * metadata only operation to update the tablespace.
15992 : */
15993 : static void
15994 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15995 : {
15996 : /*
15997 : * Shouldn't be called on relations having storage; these are processed in
15998 : * phase 3.
15999 : */
16000 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16001 :
16002 : /* check if relation can be moved to its new tablespace */
16003 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16004 : {
16005 0 : InvokeObjectPostAlterHook(RelationRelationId,
16006 : RelationGetRelid(rel),
16007 : 0);
16008 0 : return;
16009 : }
16010 :
16011 : /* Update can be done, so change reltablespace */
16012 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16013 :
16014 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16015 :
16016 : /* Make sure the reltablespace change is visible */
16017 30 : CommandCounterIncrement();
16018 : }
16019 :
16020 : /*
16021 : * Alter Table ALL ... SET TABLESPACE
16022 : *
16023 : * Allows a user to move all objects of some type in a given tablespace in the
16024 : * current database to another tablespace. Objects can be chosen based on the
16025 : * owner of the object also, to allow users to move only their objects.
16026 : * The user must have CREATE rights on the new tablespace, as usual. The main
16027 : * permissions handling is done by the lower-level table move function.
16028 : *
16029 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16030 : * lock can't be acquired then we ereport(ERROR).
16031 : */
16032 : Oid
16033 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16034 : {
16035 30 : List *relations = NIL;
16036 : ListCell *l;
16037 : ScanKeyData key[1];
16038 : Relation rel;
16039 : TableScanDesc scan;
16040 : HeapTuple tuple;
16041 : Oid orig_tablespaceoid;
16042 : Oid new_tablespaceoid;
16043 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16044 :
16045 : /* Ensure we were not asked to move something we can't */
16046 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16047 12 : stmt->objtype != OBJECT_MATVIEW)
16048 0 : ereport(ERROR,
16049 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16050 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16051 :
16052 : /* Get the orig and new tablespace OIDs */
16053 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16054 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16055 :
16056 : /* Can't move shared relations in to or out of pg_global */
16057 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16058 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16059 : new_tablespaceoid == GLOBALTABLESPACE_OID)
16060 0 : ereport(ERROR,
16061 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16062 : errmsg("cannot move relations in to or out of pg_global tablespace")));
16063 :
16064 : /*
16065 : * Must have CREATE rights on the new tablespace, unless it is the
16066 : * database default tablespace (which all users implicitly have CREATE
16067 : * rights on).
16068 : */
16069 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16070 : {
16071 : AclResult aclresult;
16072 :
16073 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16074 : ACL_CREATE);
16075 0 : if (aclresult != ACLCHECK_OK)
16076 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
16077 0 : get_tablespace_name(new_tablespaceoid));
16078 : }
16079 :
16080 : /*
16081 : * Now that the checks are done, check if we should set either to
16082 : * InvalidOid because it is our database's default tablespace.
16083 : */
16084 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
16085 0 : orig_tablespaceoid = InvalidOid;
16086 :
16087 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
16088 30 : new_tablespaceoid = InvalidOid;
16089 :
16090 : /* no-op */
16091 30 : if (orig_tablespaceoid == new_tablespaceoid)
16092 0 : return new_tablespaceoid;
16093 :
16094 : /*
16095 : * Walk the list of objects in the tablespace and move them. This will
16096 : * only find objects in our database, of course.
16097 : */
16098 30 : ScanKeyInit(&key[0],
16099 : Anum_pg_class_reltablespace,
16100 : BTEqualStrategyNumber, F_OIDEQ,
16101 : ObjectIdGetDatum(orig_tablespaceoid));
16102 :
16103 30 : rel = table_open(RelationRelationId, AccessShareLock);
16104 30 : scan = table_beginscan_catalog(rel, 1, key);
16105 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16106 : {
16107 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16108 102 : Oid relOid = relForm->oid;
16109 :
16110 : /*
16111 : * Do not move objects in pg_catalog as part of this, if an admin
16112 : * really wishes to do so, they can issue the individual ALTER
16113 : * commands directly.
16114 : *
16115 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
16116 : * (TOAST will be moved with the main table).
16117 : */
16118 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
16119 204 : relForm->relisshared ||
16120 204 : isAnyTempNamespace(relForm->relnamespace) ||
16121 102 : IsToastNamespace(relForm->relnamespace))
16122 0 : continue;
16123 :
16124 : /* Only move the object type requested */
16125 102 : if ((stmt->objtype == OBJECT_TABLE &&
16126 60 : relForm->relkind != RELKIND_RELATION &&
16127 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16128 66 : (stmt->objtype == OBJECT_INDEX &&
16129 36 : relForm->relkind != RELKIND_INDEX &&
16130 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16131 60 : (stmt->objtype == OBJECT_MATVIEW &&
16132 6 : relForm->relkind != RELKIND_MATVIEW))
16133 42 : continue;
16134 :
16135 : /* Check if we are only moving objects owned by certain roles */
16136 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16137 0 : continue;
16138 :
16139 : /*
16140 : * Handle permissions-checking here since we are locking the tables
16141 : * and also to avoid doing a bunch of work only to fail part-way. Note
16142 : * that permissions will also be checked by AlterTableInternal().
16143 : *
16144 : * Caller must be considered an owner on the table to move it.
16145 : */
16146 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16147 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16148 0 : NameStr(relForm->relname));
16149 :
16150 60 : if (stmt->nowait &&
16151 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16152 0 : ereport(ERROR,
16153 : (errcode(ERRCODE_OBJECT_IN_USE),
16154 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
16155 : get_namespace_name(relForm->relnamespace),
16156 : NameStr(relForm->relname))));
16157 : else
16158 60 : LockRelationOid(relOid, AccessExclusiveLock);
16159 :
16160 : /* Add to our list of objects to move */
16161 60 : relations = lappend_oid(relations, relOid);
16162 : }
16163 :
16164 30 : table_endscan(scan);
16165 30 : table_close(rel, AccessShareLock);
16166 :
16167 30 : if (relations == NIL)
16168 12 : ereport(NOTICE,
16169 : (errcode(ERRCODE_NO_DATA_FOUND),
16170 : errmsg("no matching relations in tablespace \"%s\" found",
16171 : orig_tablespaceoid == InvalidOid ? "(database default)" :
16172 : get_tablespace_name(orig_tablespaceoid))));
16173 :
16174 : /* Everything is locked, loop through and move all of the relations. */
16175 90 : foreach(l, relations)
16176 : {
16177 60 : List *cmds = NIL;
16178 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16179 :
16180 60 : cmd->subtype = AT_SetTableSpace;
16181 60 : cmd->name = stmt->new_tablespacename;
16182 :
16183 60 : cmds = lappend(cmds, cmd);
16184 :
16185 60 : EventTriggerAlterTableStart((Node *) stmt);
16186 : /* OID is set by AlterTableInternal */
16187 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
16188 60 : EventTriggerAlterTableEnd();
16189 : }
16190 :
16191 30 : return new_tablespaceoid;
16192 : }
16193 :
16194 : static void
16195 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
16196 : {
16197 : SMgrRelation dstrel;
16198 :
16199 : /*
16200 : * Since we copy the file directly without looking at the shared buffers,
16201 : * we'd better first flush out any pages of the source relation that are
16202 : * in shared buffers. We assume no new changes will be made while we are
16203 : * holding exclusive lock on the rel.
16204 : */
16205 62 : FlushRelationBuffers(rel);
16206 :
16207 : /*
16208 : * Create and copy all forks of the relation, and schedule unlinking of
16209 : * old physical files.
16210 : *
16211 : * NOTE: any conflict in relfilenumber value will be caught in
16212 : * RelationCreateStorage().
16213 : */
16214 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16215 :
16216 : /* copy main fork */
16217 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16218 62 : rel->rd_rel->relpersistence);
16219 :
16220 : /* copy those extra forks that exist */
16221 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16222 186 : forkNum <= MAX_FORKNUM; forkNum++)
16223 : {
16224 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
16225 : {
16226 0 : smgrcreate(dstrel, forkNum, false);
16227 :
16228 : /*
16229 : * WAL log creation if the relation is persistent, or this is the
16230 : * init fork of an unlogged relation.
16231 : */
16232 0 : if (RelationIsPermanent(rel) ||
16233 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16234 : forkNum == INIT_FORKNUM))
16235 0 : log_smgrcreate(&newrlocator, forkNum);
16236 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16237 0 : rel->rd_rel->relpersistence);
16238 : }
16239 : }
16240 :
16241 : /* drop old relation, and close new one */
16242 62 : RelationDropStorage(rel);
16243 62 : smgrclose(dstrel);
16244 62 : }
16245 :
16246 : /*
16247 : * ALTER TABLE ENABLE/DISABLE TRIGGER
16248 : *
16249 : * We just pass this off to trigger.c.
16250 : */
16251 : static void
16252 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16253 : char fires_when, bool skip_system, bool recurse,
16254 : LOCKMODE lockmode)
16255 : {
16256 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
16257 : fires_when, skip_system, recurse,
16258 : lockmode);
16259 :
16260 340 : InvokeObjectPostAlterHook(RelationRelationId,
16261 : RelationGetRelid(rel), 0);
16262 340 : }
16263 :
16264 : /*
16265 : * ALTER TABLE ENABLE/DISABLE RULE
16266 : *
16267 : * We just pass this off to rewriteDefine.c.
16268 : */
16269 : static void
16270 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16271 : char fires_when, LOCKMODE lockmode)
16272 : {
16273 46 : EnableDisableRule(rel, rulename, fires_when);
16274 :
16275 46 : InvokeObjectPostAlterHook(RelationRelationId,
16276 : RelationGetRelid(rel), 0);
16277 46 : }
16278 :
16279 : /*
16280 : * ALTER TABLE INHERIT
16281 : *
16282 : * Add a parent to the child's parents. This verifies that all the columns and
16283 : * check constraints of the parent appear in the child and that they have the
16284 : * same data types and expressions.
16285 : */
16286 : static void
16287 386 : ATPrepAddInherit(Relation child_rel)
16288 : {
16289 386 : if (child_rel->rd_rel->reloftype)
16290 6 : ereport(ERROR,
16291 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16292 : errmsg("cannot change inheritance of typed table")));
16293 :
16294 380 : if (child_rel->rd_rel->relispartition)
16295 6 : ereport(ERROR,
16296 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16297 : errmsg("cannot change inheritance of a partition")));
16298 :
16299 374 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16300 6 : ereport(ERROR,
16301 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16302 : errmsg("cannot change inheritance of partitioned table")));
16303 368 : }
16304 :
16305 : /*
16306 : * Return the address of the new parent relation.
16307 : */
16308 : static ObjectAddress
16309 368 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16310 : {
16311 : Relation parent_rel;
16312 : List *children;
16313 : ObjectAddress address;
16314 : const char *trigger_name;
16315 :
16316 : /*
16317 : * A self-exclusive lock is needed here. See the similar case in
16318 : * MergeAttributes() for a full explanation.
16319 : */
16320 368 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16321 :
16322 : /*
16323 : * Must be owner of both parent and child -- child was checked by
16324 : * ATSimplePermissions call in ATPrepCmd
16325 : */
16326 368 : ATSimplePermissions(AT_AddInherit, parent_rel,
16327 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
16328 :
16329 : /* Permanent rels cannot inherit from temporary ones */
16330 368 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16331 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16332 0 : ereport(ERROR,
16333 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16334 : errmsg("cannot inherit from temporary relation \"%s\"",
16335 : RelationGetRelationName(parent_rel))));
16336 :
16337 : /* If parent rel is temp, it must belong to this session */
16338 368 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16339 6 : !parent_rel->rd_islocaltemp)
16340 0 : ereport(ERROR,
16341 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16342 : errmsg("cannot inherit from temporary relation of another session")));
16343 :
16344 : /* Ditto for the child */
16345 368 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16346 6 : !child_rel->rd_islocaltemp)
16347 0 : ereport(ERROR,
16348 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16349 : errmsg("cannot inherit to temporary relation of another session")));
16350 :
16351 : /* Prevent partitioned tables from becoming inheritance parents */
16352 368 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16353 6 : ereport(ERROR,
16354 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16355 : errmsg("cannot inherit from partitioned table \"%s\"",
16356 : parent->relname)));
16357 :
16358 : /* Likewise for partitions */
16359 362 : if (parent_rel->rd_rel->relispartition)
16360 6 : ereport(ERROR,
16361 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16362 : errmsg("cannot inherit from a partition")));
16363 :
16364 : /*
16365 : * Prevent circularity by seeing if proposed parent inherits from child.
16366 : * (In particular, this disallows making a rel inherit from itself.)
16367 : *
16368 : * This is not completely bulletproof because of race conditions: in
16369 : * multi-level inheritance trees, someone else could concurrently be
16370 : * making another inheritance link that closes the loop but does not join
16371 : * either of the rels we have locked. Preventing that seems to require
16372 : * exclusive locks on the entire inheritance tree, which is a cure worse
16373 : * than the disease. find_all_inheritors() will cope with circularity
16374 : * anyway, so don't sweat it too much.
16375 : *
16376 : * We use weakest lock we can on child's children, namely AccessShareLock.
16377 : */
16378 356 : children = find_all_inheritors(RelationGetRelid(child_rel),
16379 : AccessShareLock, NULL);
16380 :
16381 356 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16382 12 : ereport(ERROR,
16383 : (errcode(ERRCODE_DUPLICATE_TABLE),
16384 : errmsg("circular inheritance not allowed"),
16385 : errdetail("\"%s\" is already a child of \"%s\".",
16386 : parent->relname,
16387 : RelationGetRelationName(child_rel))));
16388 :
16389 : /*
16390 : * If child_rel has row-level triggers with transition tables, we
16391 : * currently don't allow it to become an inheritance child. See also
16392 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16393 : */
16394 344 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16395 344 : if (trigger_name != NULL)
16396 6 : ereport(ERROR,
16397 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16398 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16399 : trigger_name, RelationGetRelationName(child_rel)),
16400 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16401 :
16402 : /* OK to create inheritance */
16403 338 : CreateInheritance(child_rel, parent_rel, false);
16404 :
16405 260 : ObjectAddressSet(address, RelationRelationId,
16406 : RelationGetRelid(parent_rel));
16407 :
16408 : /* keep our lock on the parent relation until commit */
16409 260 : table_close(parent_rel, NoLock);
16410 :
16411 260 : return address;
16412 : }
16413 :
16414 : /*
16415 : * CreateInheritance
16416 : * Catalog manipulation portion of creating inheritance between a child
16417 : * table and a parent table.
16418 : *
16419 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16420 : */
16421 : static void
16422 2484 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16423 : {
16424 : Relation catalogRelation;
16425 : SysScanDesc scan;
16426 : ScanKeyData key;
16427 : HeapTuple inheritsTuple;
16428 : int32 inhseqno;
16429 :
16430 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16431 2484 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16432 :
16433 : /*
16434 : * Check for duplicates in the list of parents, and determine the highest
16435 : * inhseqno already present; we'll use the next one for the new parent.
16436 : * Also, if proposed child is a partition, it cannot already be
16437 : * inheriting.
16438 : *
16439 : * Note: we do not reject the case where the child already inherits from
16440 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16441 : */
16442 2484 : ScanKeyInit(&key,
16443 : Anum_pg_inherits_inhrelid,
16444 : BTEqualStrategyNumber, F_OIDEQ,
16445 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16446 2484 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16447 : true, NULL, 1, &key);
16448 :
16449 : /* inhseqno sequences start at 1 */
16450 2484 : inhseqno = 0;
16451 2532 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16452 : {
16453 54 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16454 :
16455 54 : if (inh->inhparent == RelationGetRelid(parent_rel))
16456 6 : ereport(ERROR,
16457 : (errcode(ERRCODE_DUPLICATE_TABLE),
16458 : errmsg("relation \"%s\" would be inherited from more than once",
16459 : RelationGetRelationName(parent_rel))));
16460 :
16461 48 : if (inh->inhseqno > inhseqno)
16462 48 : inhseqno = inh->inhseqno;
16463 : }
16464 2478 : systable_endscan(scan);
16465 :
16466 : /* Match up the columns and bump attinhcount as needed */
16467 2478 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16468 :
16469 : /* Match up the constraints and bump coninhcount as needed */
16470 2352 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16471 :
16472 : /*
16473 : * OK, it looks valid. Make the catalog entries that show inheritance.
16474 : */
16475 2304 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16476 : RelationGetRelid(parent_rel),
16477 : inhseqno + 1,
16478 : catalogRelation,
16479 2304 : parent_rel->rd_rel->relkind ==
16480 : RELKIND_PARTITIONED_TABLE);
16481 :
16482 : /* Now we're done with pg_inherits */
16483 2304 : table_close(catalogRelation, RowExclusiveLock);
16484 2304 : }
16485 :
16486 : /*
16487 : * Obtain the source-text form of the constraint expression for a check
16488 : * constraint, given its pg_constraint tuple
16489 : */
16490 : static char *
16491 184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16492 : {
16493 : Form_pg_constraint con;
16494 : bool isnull;
16495 : Datum attr;
16496 : Datum expr;
16497 :
16498 184 : con = (Form_pg_constraint) GETSTRUCT(contup);
16499 184 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16500 184 : if (isnull)
16501 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16502 :
16503 184 : expr = DirectFunctionCall2(pg_get_expr, attr,
16504 : ObjectIdGetDatum(con->conrelid));
16505 184 : return TextDatumGetCString(expr);
16506 : }
16507 :
16508 : /*
16509 : * Determine whether two check constraints are functionally equivalent
16510 : *
16511 : * The test we apply is to see whether they reverse-compile to the same
16512 : * source string. This insulates us from issues like whether attributes
16513 : * have the same physical column numbers in parent and child relations.
16514 : *
16515 : * Note that we ignore enforceability as there are cases where constraints
16516 : * with differing enforceability are allowed.
16517 : */
16518 : static bool
16519 92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16520 : {
16521 92 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16522 92 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16523 :
16524 92 : if (acon->condeferrable != bcon->condeferrable ||
16525 92 : acon->condeferred != bcon->condeferred ||
16526 92 : strcmp(decompile_conbin(a, tupleDesc),
16527 92 : decompile_conbin(b, tupleDesc)) != 0)
16528 6 : return false;
16529 : else
16530 86 : return true;
16531 : }
16532 :
16533 : /*
16534 : * Check columns in child table match up with columns in parent, and increment
16535 : * their attinhcount.
16536 : *
16537 : * Called by CreateInheritance
16538 : *
16539 : * Currently all parent columns must be found in child. Missing columns are an
16540 : * error. One day we might consider creating new columns like CREATE TABLE
16541 : * does. However, that is widely unpopular --- in the common use case of
16542 : * partitioned tables it's a foot-gun.
16543 : *
16544 : * The data type must match exactly. If the parent column is NOT NULL then
16545 : * the child must be as well. Defaults are not compared, however.
16546 : */
16547 : static void
16548 2478 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16549 : {
16550 : Relation attrrel;
16551 : TupleDesc parent_desc;
16552 :
16553 2478 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16554 2478 : parent_desc = RelationGetDescr(parent_rel);
16555 :
16556 7966 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16557 : {
16558 5614 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16559 5614 : char *parent_attname = NameStr(parent_att->attname);
16560 : HeapTuple tuple;
16561 :
16562 : /* Ignore dropped columns in the parent. */
16563 5614 : if (parent_att->attisdropped)
16564 296 : continue;
16565 :
16566 : /* Find same column in child (matching on column name). */
16567 5318 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16568 5318 : if (HeapTupleIsValid(tuple))
16569 : {
16570 5306 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16571 :
16572 5306 : if (parent_att->atttypid != child_att->atttypid ||
16573 5300 : parent_att->atttypmod != child_att->atttypmod)
16574 12 : ereport(ERROR,
16575 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16576 : errmsg("child table \"%s\" has different type for column \"%s\"",
16577 : RelationGetRelationName(child_rel), parent_attname)));
16578 :
16579 5294 : if (parent_att->attcollation != child_att->attcollation)
16580 6 : ereport(ERROR,
16581 : (errcode(ERRCODE_COLLATION_MISMATCH),
16582 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16583 : RelationGetRelationName(child_rel), parent_attname)));
16584 :
16585 : /*
16586 : * If the parent has a not-null constraint that's not NO INHERIT,
16587 : * make sure the child has one too.
16588 : *
16589 : * Other constraints are checked elsewhere.
16590 : */
16591 5288 : if (parent_att->attnotnull && !child_att->attnotnull)
16592 : {
16593 : HeapTuple contup;
16594 :
16595 30 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16596 30 : parent_att->attnum);
16597 30 : if (HeapTupleIsValid(contup) &&
16598 30 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16599 24 : ereport(ERROR,
16600 : errcode(ERRCODE_DATATYPE_MISMATCH),
16601 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16602 : parent_attname, RelationGetRelationName(child_rel)));
16603 : }
16604 :
16605 : /*
16606 : * Child column must be generated if and only if parent column is.
16607 : */
16608 5264 : if (parent_att->attgenerated && !child_att->attgenerated)
16609 36 : ereport(ERROR,
16610 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16611 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16612 5228 : if (child_att->attgenerated && !parent_att->attgenerated)
16613 24 : ereport(ERROR,
16614 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16615 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16616 :
16617 5204 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
16618 12 : ereport(ERROR,
16619 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16620 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
16621 : errdetail("Parent column is %s, child column is %s.",
16622 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
16623 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
16624 :
16625 : /*
16626 : * Regular inheritance children are independent enough not to
16627 : * inherit identity columns. But partitions are integral part of
16628 : * a partitioned table and inherit identity column.
16629 : */
16630 5192 : if (ispartition)
16631 4586 : child_att->attidentity = parent_att->attidentity;
16632 :
16633 : /*
16634 : * OK, bump the child column's inheritance count. (If we fail
16635 : * later on, this change will just roll back.)
16636 : */
16637 5192 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
16638 : &child_att->attinhcount))
16639 0 : ereport(ERROR,
16640 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16641 : errmsg("too many inheritance parents"));
16642 :
16643 : /*
16644 : * In case of partitions, we must enforce that value of attislocal
16645 : * is same in all partitions. (Note: there are only inherited
16646 : * attributes in partitions)
16647 : */
16648 5192 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16649 : {
16650 : Assert(child_att->attinhcount == 1);
16651 4586 : child_att->attislocal = false;
16652 : }
16653 :
16654 5192 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16655 5192 : heap_freetuple(tuple);
16656 : }
16657 : else
16658 : {
16659 12 : ereport(ERROR,
16660 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16661 : errmsg("child table is missing column \"%s\"", parent_attname)));
16662 : }
16663 : }
16664 :
16665 2352 : table_close(attrrel, RowExclusiveLock);
16666 2352 : }
16667 :
16668 : /*
16669 : * Check constraints in child table match up with constraints in parent,
16670 : * and increment their coninhcount.
16671 : *
16672 : * Constraints that are marked ONLY in the parent are ignored.
16673 : *
16674 : * Called by CreateInheritance
16675 : *
16676 : * Currently all constraints in parent must be present in the child. One day we
16677 : * may consider adding new constraints like CREATE TABLE does.
16678 : *
16679 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
16680 : * constraints. As long as tables have more like 10 constraints it shouldn't be
16681 : * a problem though. Even 100 constraints ought not be the end of the world.
16682 : *
16683 : * XXX See MergeWithExistingConstraint too if you change this code.
16684 : */
16685 : static void
16686 2352 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16687 : {
16688 : Relation constraintrel;
16689 : SysScanDesc parent_scan;
16690 : ScanKeyData parent_key;
16691 : HeapTuple parent_tuple;
16692 2352 : Oid parent_relid = RelationGetRelid(parent_rel);
16693 : AttrMap *attmap;
16694 :
16695 2352 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16696 :
16697 : /* Outer loop scans through the parent's constraint definitions */
16698 2352 : ScanKeyInit(&parent_key,
16699 : Anum_pg_constraint_conrelid,
16700 : BTEqualStrategyNumber, F_OIDEQ,
16701 : ObjectIdGetDatum(parent_relid));
16702 2352 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16703 : true, NULL, 1, &parent_key);
16704 :
16705 2352 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16706 : RelationGetDescr(child_rel),
16707 : true);
16708 :
16709 4106 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16710 : {
16711 1802 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16712 : SysScanDesc child_scan;
16713 : ScanKeyData child_key;
16714 : HeapTuple child_tuple;
16715 : AttrNumber parent_attno;
16716 1802 : bool found = false;
16717 :
16718 1802 : if (parent_con->contype != CONSTRAINT_CHECK &&
16719 1666 : parent_con->contype != CONSTRAINT_NOTNULL)
16720 868 : continue;
16721 :
16722 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16723 966 : if (parent_con->connoinherit)
16724 32 : continue;
16725 :
16726 934 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16727 818 : parent_attno = extractNotNullColumn(parent_tuple);
16728 : else
16729 116 : parent_attno = InvalidAttrNumber;
16730 :
16731 : /* Search for a child constraint matching this one */
16732 934 : ScanKeyInit(&child_key,
16733 : Anum_pg_constraint_conrelid,
16734 : BTEqualStrategyNumber, F_OIDEQ,
16735 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16736 934 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16737 : true, NULL, 1, &child_key);
16738 :
16739 1580 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16740 : {
16741 1556 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16742 : HeapTuple child_copy;
16743 :
16744 1556 : if (child_con->contype != parent_con->contype)
16745 352 : continue;
16746 :
16747 : /*
16748 : * CHECK constraint are matched by constraint name, NOT NULL ones
16749 : * by attribute number.
16750 : */
16751 1204 : if (child_con->contype == CONSTRAINT_CHECK)
16752 : {
16753 122 : if (strcmp(NameStr(parent_con->conname),
16754 122 : NameStr(child_con->conname)) != 0)
16755 30 : continue;
16756 : }
16757 1082 : else if (child_con->contype == CONSTRAINT_NOTNULL)
16758 : {
16759 : Form_pg_attribute parent_attr;
16760 : Form_pg_attribute child_attr;
16761 : AttrNumber child_attno;
16762 :
16763 1082 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
16764 1082 : child_attno = extractNotNullColumn(child_tuple);
16765 1082 : if (parent_attno != attmap->attnums[child_attno - 1])
16766 264 : continue;
16767 :
16768 818 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
16769 : /* there shouldn't be constraints on dropped columns */
16770 818 : if (parent_attr->attisdropped || child_attr->attisdropped)
16771 0 : elog(ERROR, "found not-null constraint on dropped columns");
16772 : }
16773 :
16774 910 : if (child_con->contype == CONSTRAINT_CHECK &&
16775 92 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16776 6 : ereport(ERROR,
16777 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16778 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16779 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16780 :
16781 : /*
16782 : * If the child constraint is "no inherit" then cannot merge
16783 : */
16784 904 : if (child_con->connoinherit)
16785 12 : ereport(ERROR,
16786 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16787 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16788 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16789 :
16790 : /*
16791 : * If the child constraint is "not valid" then cannot merge with a
16792 : * valid parent constraint
16793 : */
16794 892 : if (parent_con->convalidated && child_con->conenforced &&
16795 880 : !child_con->convalidated)
16796 0 : ereport(ERROR,
16797 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16798 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16799 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16800 :
16801 : /*
16802 : * A non-enforced child constraint cannot be merged with an
16803 : * enforced parent constraint. However, the reverse is allowed,
16804 : * where the child constraint is enforced.
16805 : */
16806 892 : if (parent_con->conenforced && !child_con->conenforced)
16807 6 : ereport(ERROR,
16808 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16809 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
16810 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16811 :
16812 : /*
16813 : * OK, bump the child constraint's inheritance count. (If we fail
16814 : * later on, this change will just roll back.)
16815 : */
16816 886 : child_copy = heap_copytuple(child_tuple);
16817 886 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16818 :
16819 886 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
16820 : &child_con->coninhcount))
16821 0 : ereport(ERROR,
16822 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16823 : errmsg("too many inheritance parents"));
16824 :
16825 : /*
16826 : * In case of partitions, an inherited constraint must be
16827 : * inherited only once since it cannot have multiple parents and
16828 : * it is never considered local.
16829 : */
16830 886 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16831 : {
16832 : Assert(child_con->coninhcount == 1);
16833 752 : child_con->conislocal = false;
16834 : }
16835 :
16836 886 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16837 886 : heap_freetuple(child_copy);
16838 :
16839 886 : found = true;
16840 886 : break;
16841 : }
16842 :
16843 910 : systable_endscan(child_scan);
16844 :
16845 910 : if (!found)
16846 : {
16847 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16848 0 : ereport(ERROR,
16849 : errcode(ERRCODE_DATATYPE_MISMATCH),
16850 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16851 : get_attname(parent_relid,
16852 : extractNotNullColumn(parent_tuple),
16853 : false),
16854 : RelationGetRelationName(child_rel)));
16855 :
16856 24 : ereport(ERROR,
16857 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16858 : errmsg("child table is missing constraint \"%s\"",
16859 : NameStr(parent_con->conname))));
16860 : }
16861 : }
16862 :
16863 2304 : systable_endscan(parent_scan);
16864 2304 : table_close(constraintrel, RowExclusiveLock);
16865 2304 : }
16866 :
16867 : /*
16868 : * ALTER TABLE NO INHERIT
16869 : *
16870 : * Return value is the address of the relation that is no longer parent.
16871 : */
16872 : static ObjectAddress
16873 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16874 : {
16875 : ObjectAddress address;
16876 : Relation parent_rel;
16877 :
16878 86 : if (rel->rd_rel->relispartition)
16879 0 : ereport(ERROR,
16880 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16881 : errmsg("cannot change inheritance of a partition")));
16882 :
16883 : /*
16884 : * AccessShareLock on the parent is probably enough, seeing that DROP
16885 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
16886 : * be inspecting the parent's schema.
16887 : */
16888 86 : parent_rel = table_openrv(parent, AccessShareLock);
16889 :
16890 : /*
16891 : * We don't bother to check ownership of the parent table --- ownership of
16892 : * the child is presumed enough rights.
16893 : */
16894 :
16895 : /* Off to RemoveInheritance() where most of the work happens */
16896 86 : RemoveInheritance(rel, parent_rel, false);
16897 :
16898 80 : ObjectAddressSet(address, RelationRelationId,
16899 : RelationGetRelid(parent_rel));
16900 :
16901 : /* keep our lock on the parent relation until commit */
16902 80 : table_close(parent_rel, NoLock);
16903 :
16904 80 : return address;
16905 : }
16906 :
16907 : /*
16908 : * MarkInheritDetached
16909 : *
16910 : * Set inhdetachpending for a partition, for ATExecDetachPartition
16911 : * in concurrent mode. While at it, verify that no other partition is
16912 : * already pending detach.
16913 : */
16914 : static void
16915 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16916 : {
16917 : Relation catalogRelation;
16918 : SysScanDesc scan;
16919 : ScanKeyData key;
16920 : HeapTuple inheritsTuple;
16921 146 : bool found = false;
16922 :
16923 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16924 :
16925 : /*
16926 : * Find pg_inherits entries by inhparent. (We need to scan them all in
16927 : * order to verify that no other partition is pending detach.)
16928 : */
16929 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16930 146 : ScanKeyInit(&key,
16931 : Anum_pg_inherits_inhparent,
16932 : BTEqualStrategyNumber, F_OIDEQ,
16933 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16934 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16935 : true, NULL, 1, &key);
16936 :
16937 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16938 : {
16939 : Form_pg_inherits inhForm;
16940 :
16941 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16942 286 : if (inhForm->inhdetachpending)
16943 2 : ereport(ERROR,
16944 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16945 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16946 : get_rel_name(inhForm->inhrelid),
16947 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16948 : RelationGetRelationName(parent_rel)),
16949 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16950 :
16951 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16952 : {
16953 : HeapTuple newtup;
16954 :
16955 144 : newtup = heap_copytuple(inheritsTuple);
16956 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16957 :
16958 144 : CatalogTupleUpdate(catalogRelation,
16959 : &inheritsTuple->t_self,
16960 : newtup);
16961 144 : found = true;
16962 144 : heap_freetuple(newtup);
16963 : /* keep looking, to ensure we catch others pending detach */
16964 : }
16965 : }
16966 :
16967 : /* Done */
16968 144 : systable_endscan(scan);
16969 144 : table_close(catalogRelation, RowExclusiveLock);
16970 :
16971 144 : if (!found)
16972 0 : ereport(ERROR,
16973 : (errcode(ERRCODE_UNDEFINED_TABLE),
16974 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16975 : RelationGetRelationName(child_rel),
16976 : RelationGetRelationName(parent_rel))));
16977 144 : }
16978 :
16979 : /*
16980 : * RemoveInheritance
16981 : *
16982 : * Drop a parent from the child's parents. This just adjusts the attinhcount
16983 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16984 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16985 : *
16986 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16987 : * up attislocal stays true, which means if a child is ever removed from a
16988 : * parent then its columns will never be automatically dropped which may
16989 : * surprise. But at least we'll never surprise by dropping columns someone
16990 : * isn't expecting to be dropped which would actually mean data loss.
16991 : *
16992 : * coninhcount and conislocal for inherited constraints are adjusted in
16993 : * exactly the same way.
16994 : *
16995 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16996 : */
16997 : static void
16998 578 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16999 : {
17000 : Relation catalogRelation;
17001 : SysScanDesc scan;
17002 : ScanKeyData key[3];
17003 : HeapTuple attributeTuple,
17004 : constraintTuple;
17005 : AttrMap *attmap;
17006 : List *connames;
17007 : List *nncolumns;
17008 : bool found;
17009 : bool is_partitioning;
17010 :
17011 578 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17012 :
17013 578 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17014 : RelationGetRelid(parent_rel),
17015 : expect_detached,
17016 578 : RelationGetRelationName(child_rel));
17017 578 : if (!found)
17018 : {
17019 24 : if (is_partitioning)
17020 18 : ereport(ERROR,
17021 : (errcode(ERRCODE_UNDEFINED_TABLE),
17022 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17023 : RelationGetRelationName(child_rel),
17024 : RelationGetRelationName(parent_rel))));
17025 : else
17026 6 : ereport(ERROR,
17027 : (errcode(ERRCODE_UNDEFINED_TABLE),
17028 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17029 : RelationGetRelationName(parent_rel),
17030 : RelationGetRelationName(child_rel))));
17031 : }
17032 :
17033 : /*
17034 : * Search through child columns looking for ones matching parent rel
17035 : */
17036 554 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17037 554 : ScanKeyInit(&key[0],
17038 : Anum_pg_attribute_attrelid,
17039 : BTEqualStrategyNumber, F_OIDEQ,
17040 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17041 554 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17042 : true, NULL, 1, key);
17043 4946 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17044 : {
17045 4392 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17046 :
17047 : /* Ignore if dropped or not inherited */
17048 4392 : if (att->attisdropped)
17049 6 : continue;
17050 4386 : if (att->attinhcount <= 0)
17051 3354 : continue;
17052 :
17053 1032 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17054 1032 : NameStr(att->attname)))
17055 : {
17056 : /* Decrement inhcount and possibly set islocal to true */
17057 978 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
17058 978 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17059 :
17060 978 : copy_att->attinhcount--;
17061 978 : if (copy_att->attinhcount == 0)
17062 948 : copy_att->attislocal = true;
17063 :
17064 978 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17065 978 : heap_freetuple(copyTuple);
17066 : }
17067 : }
17068 554 : systable_endscan(scan);
17069 554 : table_close(catalogRelation, RowExclusiveLock);
17070 :
17071 : /*
17072 : * Likewise, find inherited check and not-null constraints and disinherit
17073 : * them. To do this, we first need a list of the names of the parent's
17074 : * check constraints. (We cheat a bit by only checking for name matches,
17075 : * assuming that the expressions will match.)
17076 : *
17077 : * For NOT NULL columns, we store column numbers to match, mapping them in
17078 : * to the child rel's attribute numbers.
17079 : */
17080 554 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17081 : RelationGetDescr(parent_rel),
17082 : false);
17083 :
17084 554 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17085 554 : ScanKeyInit(&key[0],
17086 : Anum_pg_constraint_conrelid,
17087 : BTEqualStrategyNumber, F_OIDEQ,
17088 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17089 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17090 : true, NULL, 1, key);
17091 :
17092 554 : connames = NIL;
17093 554 : nncolumns = NIL;
17094 :
17095 1082 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17096 : {
17097 528 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17098 :
17099 528 : if (con->connoinherit)
17100 98 : continue;
17101 :
17102 430 : if (con->contype == CONSTRAINT_CHECK)
17103 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
17104 430 : if (con->contype == CONSTRAINT_NOTNULL)
17105 : {
17106 196 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17107 :
17108 196 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17109 : }
17110 : }
17111 :
17112 554 : systable_endscan(scan);
17113 :
17114 : /* Now scan the child's constraints to find matches */
17115 554 : ScanKeyInit(&key[0],
17116 : Anum_pg_constraint_conrelid,
17117 : BTEqualStrategyNumber, F_OIDEQ,
17118 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17119 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17120 : true, NULL, 1, key);
17121 :
17122 1272 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17123 : {
17124 718 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17125 718 : bool match = false;
17126 :
17127 : /*
17128 : * Match CHECK constraints by name, not-null constraints by column
17129 : * number, and ignore all others.
17130 : */
17131 718 : if (con->contype == CONSTRAINT_CHECK)
17132 : {
17133 350 : foreach_ptr(char, chkname, connames)
17134 : {
17135 18 : if (con->contype == CONSTRAINT_CHECK &&
17136 18 : strcmp(NameStr(con->conname), chkname) == 0)
17137 : {
17138 12 : match = true;
17139 12 : connames = foreach_delete_current(connames, chkname);
17140 12 : break;
17141 : }
17142 : }
17143 : }
17144 546 : else if (con->contype == CONSTRAINT_NOTNULL)
17145 : {
17146 256 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17147 :
17148 518 : foreach_int(prevattno, nncolumns)
17149 : {
17150 202 : if (prevattno == child_attno)
17151 : {
17152 196 : match = true;
17153 196 : nncolumns = foreach_delete_current(nncolumns, prevattno);
17154 196 : break;
17155 : }
17156 : }
17157 : }
17158 : else
17159 290 : continue;
17160 :
17161 428 : if (match)
17162 : {
17163 : /* Decrement inhcount and possibly set islocal to true */
17164 208 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
17165 208 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17166 :
17167 208 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
17168 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17169 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
17170 :
17171 208 : copy_con->coninhcount--;
17172 208 : if (copy_con->coninhcount == 0)
17173 190 : copy_con->conislocal = true;
17174 :
17175 208 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17176 208 : heap_freetuple(copyTuple);
17177 : }
17178 : }
17179 :
17180 : /* We should have matched all constraints */
17181 554 : if (connames != NIL || nncolumns != NIL)
17182 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
17183 : list_length(connames) + list_length(nncolumns),
17184 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
17185 :
17186 554 : systable_endscan(scan);
17187 554 : table_close(catalogRelation, RowExclusiveLock);
17188 :
17189 554 : drop_parent_dependency(RelationGetRelid(child_rel),
17190 : RelationRelationId,
17191 : RelationGetRelid(parent_rel),
17192 : child_dependency_type(is_partitioning));
17193 :
17194 : /*
17195 : * Post alter hook of this inherits. Since object_access_hook doesn't take
17196 : * multiple object identifiers, we relay oid of parent relation using
17197 : * auxiliary_id argument.
17198 : */
17199 554 : InvokeObjectPostAlterHookArg(InheritsRelationId,
17200 : RelationGetRelid(child_rel), 0,
17201 : RelationGetRelid(parent_rel), false);
17202 554 : }
17203 :
17204 : /*
17205 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17206 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17207 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17208 : * be TypeRelationId). There's no convenient way to do this, so go trawling
17209 : * through pg_depend.
17210 : */
17211 : static void
17212 566 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17213 : DependencyType deptype)
17214 : {
17215 : Relation catalogRelation;
17216 : SysScanDesc scan;
17217 : ScanKeyData key[3];
17218 : HeapTuple depTuple;
17219 :
17220 566 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17221 :
17222 566 : ScanKeyInit(&key[0],
17223 : Anum_pg_depend_classid,
17224 : BTEqualStrategyNumber, F_OIDEQ,
17225 : ObjectIdGetDatum(RelationRelationId));
17226 566 : ScanKeyInit(&key[1],
17227 : Anum_pg_depend_objid,
17228 : BTEqualStrategyNumber, F_OIDEQ,
17229 : ObjectIdGetDatum(relid));
17230 566 : ScanKeyInit(&key[2],
17231 : Anum_pg_depend_objsubid,
17232 : BTEqualStrategyNumber, F_INT4EQ,
17233 : Int32GetDatum(0));
17234 :
17235 566 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17236 : NULL, 3, key);
17237 :
17238 1758 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17239 : {
17240 1192 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17241 :
17242 1192 : if (dep->refclassid == refclassid &&
17243 608 : dep->refobjid == refobjid &&
17244 566 : dep->refobjsubid == 0 &&
17245 566 : dep->deptype == deptype)
17246 566 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17247 : }
17248 :
17249 566 : systable_endscan(scan);
17250 566 : table_close(catalogRelation, RowExclusiveLock);
17251 566 : }
17252 :
17253 : /*
17254 : * ALTER TABLE OF
17255 : *
17256 : * Attach a table to a composite type, as though it had been created with CREATE
17257 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17258 : * subject table must not have inheritance parents. These restrictions ensure
17259 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17260 : *
17261 : * The address of the type is returned.
17262 : */
17263 : static ObjectAddress
17264 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17265 : {
17266 66 : Oid relid = RelationGetRelid(rel);
17267 : Type typetuple;
17268 : Form_pg_type typeform;
17269 : Oid typeid;
17270 : Relation inheritsRelation,
17271 : relationRelation;
17272 : SysScanDesc scan;
17273 : ScanKeyData key;
17274 : AttrNumber table_attno,
17275 : type_attno;
17276 : TupleDesc typeTupleDesc,
17277 : tableTupleDesc;
17278 : ObjectAddress tableobj,
17279 : typeobj;
17280 : HeapTuple classtuple;
17281 :
17282 : /* Validate the type. */
17283 66 : typetuple = typenameType(NULL, ofTypename, NULL);
17284 66 : check_of_type(typetuple);
17285 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17286 66 : typeid = typeform->oid;
17287 :
17288 : /* Fail if the table has any inheritance parents. */
17289 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17290 66 : ScanKeyInit(&key,
17291 : Anum_pg_inherits_inhrelid,
17292 : BTEqualStrategyNumber, F_OIDEQ,
17293 : ObjectIdGetDatum(relid));
17294 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17295 : true, NULL, 1, &key);
17296 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17297 6 : ereport(ERROR,
17298 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17299 : errmsg("typed tables cannot inherit")));
17300 60 : systable_endscan(scan);
17301 60 : table_close(inheritsRelation, AccessShareLock);
17302 :
17303 : /*
17304 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17305 : * require that the order also match. However, attnotnull need not match.
17306 : */
17307 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17308 60 : tableTupleDesc = RelationGetDescr(rel);
17309 60 : table_attno = 1;
17310 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17311 : {
17312 : Form_pg_attribute type_attr,
17313 : table_attr;
17314 : const char *type_attname,
17315 : *table_attname;
17316 :
17317 : /* Get the next non-dropped type attribute. */
17318 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17319 154 : if (type_attr->attisdropped)
17320 44 : continue;
17321 110 : type_attname = NameStr(type_attr->attname);
17322 :
17323 : /* Get the next non-dropped table attribute. */
17324 : do
17325 : {
17326 122 : if (table_attno > tableTupleDesc->natts)
17327 6 : ereport(ERROR,
17328 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17329 : errmsg("table is missing column \"%s\"",
17330 : type_attname)));
17331 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17332 116 : table_attno++;
17333 116 : } while (table_attr->attisdropped);
17334 104 : table_attname = NameStr(table_attr->attname);
17335 :
17336 : /* Compare name. */
17337 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17338 6 : ereport(ERROR,
17339 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17340 : errmsg("table has column \"%s\" where type requires \"%s\"",
17341 : table_attname, type_attname)));
17342 :
17343 : /* Compare type. */
17344 98 : if (table_attr->atttypid != type_attr->atttypid ||
17345 92 : table_attr->atttypmod != type_attr->atttypmod ||
17346 86 : table_attr->attcollation != type_attr->attcollation)
17347 12 : ereport(ERROR,
17348 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17349 : errmsg("table \"%s\" has different type for column \"%s\"",
17350 : RelationGetRelationName(rel), type_attname)));
17351 : }
17352 36 : ReleaseTupleDesc(typeTupleDesc);
17353 :
17354 : /* Any remaining columns at the end of the table had better be dropped. */
17355 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17356 : {
17357 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17358 : table_attno - 1);
17359 :
17360 6 : if (!table_attr->attisdropped)
17361 6 : ereport(ERROR,
17362 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17363 : errmsg("table has extra column \"%s\"",
17364 : NameStr(table_attr->attname))));
17365 : }
17366 :
17367 : /* If the table was already typed, drop the existing dependency. */
17368 30 : if (rel->rd_rel->reloftype)
17369 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17370 : DEPENDENCY_NORMAL);
17371 :
17372 : /* Record a dependency on the new type. */
17373 30 : tableobj.classId = RelationRelationId;
17374 30 : tableobj.objectId = relid;
17375 30 : tableobj.objectSubId = 0;
17376 30 : typeobj.classId = TypeRelationId;
17377 30 : typeobj.objectId = typeid;
17378 30 : typeobj.objectSubId = 0;
17379 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17380 :
17381 : /* Update pg_class.reloftype */
17382 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17383 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17384 30 : if (!HeapTupleIsValid(classtuple))
17385 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17386 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17387 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17388 :
17389 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17390 :
17391 30 : heap_freetuple(classtuple);
17392 30 : table_close(relationRelation, RowExclusiveLock);
17393 :
17394 30 : ReleaseSysCache(typetuple);
17395 :
17396 30 : return typeobj;
17397 : }
17398 :
17399 : /*
17400 : * ALTER TABLE NOT OF
17401 : *
17402 : * Detach a typed table from its originating type. Just clear reloftype and
17403 : * remove the dependency.
17404 : */
17405 : static void
17406 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17407 : {
17408 6 : Oid relid = RelationGetRelid(rel);
17409 : Relation relationRelation;
17410 : HeapTuple tuple;
17411 :
17412 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17413 0 : ereport(ERROR,
17414 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17415 : errmsg("\"%s\" is not a typed table",
17416 : RelationGetRelationName(rel))));
17417 :
17418 : /*
17419 : * We don't bother to check ownership of the type --- ownership of the
17420 : * table is presumed enough rights. No lock required on the type, either.
17421 : */
17422 :
17423 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17424 : DEPENDENCY_NORMAL);
17425 :
17426 : /* Clear pg_class.reloftype */
17427 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17428 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17429 6 : if (!HeapTupleIsValid(tuple))
17430 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17431 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17432 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17433 :
17434 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17435 :
17436 6 : heap_freetuple(tuple);
17437 6 : table_close(relationRelation, RowExclusiveLock);
17438 6 : }
17439 :
17440 : /*
17441 : * relation_mark_replica_identity: Update a table's replica identity
17442 : *
17443 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17444 : * index. Otherwise, it must be InvalidOid.
17445 : *
17446 : * Caller had better hold an exclusive lock on the relation, as the results
17447 : * of running two of these concurrently wouldn't be pretty.
17448 : */
17449 : static void
17450 460 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17451 : bool is_internal)
17452 : {
17453 : Relation pg_index;
17454 : Relation pg_class;
17455 : HeapTuple pg_class_tuple;
17456 : HeapTuple pg_index_tuple;
17457 : Form_pg_class pg_class_form;
17458 : Form_pg_index pg_index_form;
17459 : ListCell *index;
17460 :
17461 : /*
17462 : * Check whether relreplident has changed, and update it if so.
17463 : */
17464 460 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17465 460 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17466 : ObjectIdGetDatum(RelationGetRelid(rel)));
17467 460 : if (!HeapTupleIsValid(pg_class_tuple))
17468 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17469 : RelationGetRelationName(rel));
17470 460 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17471 460 : if (pg_class_form->relreplident != ri_type)
17472 : {
17473 410 : pg_class_form->relreplident = ri_type;
17474 410 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17475 : }
17476 460 : table_close(pg_class, RowExclusiveLock);
17477 460 : heap_freetuple(pg_class_tuple);
17478 :
17479 : /*
17480 : * Update the per-index indisreplident flags correctly.
17481 : */
17482 460 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17483 1180 : foreach(index, RelationGetIndexList(rel))
17484 : {
17485 720 : Oid thisIndexOid = lfirst_oid(index);
17486 720 : bool dirty = false;
17487 :
17488 720 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17489 : ObjectIdGetDatum(thisIndexOid));
17490 720 : if (!HeapTupleIsValid(pg_index_tuple))
17491 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17492 720 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17493 :
17494 720 : if (thisIndexOid == indexOid)
17495 : {
17496 : /* Set the bit if not already set. */
17497 240 : if (!pg_index_form->indisreplident)
17498 : {
17499 222 : dirty = true;
17500 222 : pg_index_form->indisreplident = true;
17501 : }
17502 : }
17503 : else
17504 : {
17505 : /* Unset the bit if set. */
17506 480 : if (pg_index_form->indisreplident)
17507 : {
17508 52 : dirty = true;
17509 52 : pg_index_form->indisreplident = false;
17510 : }
17511 : }
17512 :
17513 720 : if (dirty)
17514 : {
17515 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17516 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17517 : InvalidOid, is_internal);
17518 :
17519 : /*
17520 : * Invalidate the relcache for the table, so that after we commit
17521 : * all sessions will refresh the table's replica identity index
17522 : * before attempting any UPDATE or DELETE on the table. (If we
17523 : * changed the table's pg_class row above, then a relcache inval
17524 : * is already queued due to that; but we might not have.)
17525 : */
17526 274 : CacheInvalidateRelcache(rel);
17527 : }
17528 720 : heap_freetuple(pg_index_tuple);
17529 : }
17530 :
17531 460 : table_close(pg_index, RowExclusiveLock);
17532 460 : }
17533 :
17534 : /*
17535 : * ALTER TABLE <name> REPLICA IDENTITY ...
17536 : */
17537 : static void
17538 508 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17539 : {
17540 : Oid indexOid;
17541 : Relation indexRel;
17542 : int key;
17543 :
17544 508 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17545 : {
17546 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17547 6 : return;
17548 : }
17549 502 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17550 : {
17551 166 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17552 166 : return;
17553 : }
17554 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17555 : {
17556 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17557 48 : return;
17558 : }
17559 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17560 : {
17561 : /* fallthrough */ ;
17562 : }
17563 : else
17564 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17565 :
17566 : /* Check that the index exists */
17567 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17568 288 : if (!OidIsValid(indexOid))
17569 0 : ereport(ERROR,
17570 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17571 : errmsg("index \"%s\" for table \"%s\" does not exist",
17572 : stmt->name, RelationGetRelationName(rel))));
17573 :
17574 288 : indexRel = index_open(indexOid, ShareLock);
17575 :
17576 : /* Check that the index is on the relation we're altering. */
17577 288 : if (indexRel->rd_index == NULL ||
17578 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17579 6 : ereport(ERROR,
17580 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17581 : errmsg("\"%s\" is not an index for table \"%s\"",
17582 : RelationGetRelationName(indexRel),
17583 : RelationGetRelationName(rel))));
17584 :
17585 : /*
17586 : * The AM must support uniqueness, and the index must in fact be unique.
17587 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17588 : * exclusion), we can use that too.
17589 : */
17590 282 : if ((!indexRel->rd_indam->amcanunique ||
17591 262 : !indexRel->rd_index->indisunique) &&
17592 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
17593 12 : ereport(ERROR,
17594 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17595 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17596 : RelationGetRelationName(indexRel))));
17597 : /* Deferred indexes are not guaranteed to be always unique. */
17598 270 : if (!indexRel->rd_index->indimmediate)
17599 12 : ereport(ERROR,
17600 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17601 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17602 : RelationGetRelationName(indexRel))));
17603 : /* Expression indexes aren't supported. */
17604 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
17605 6 : ereport(ERROR,
17606 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17607 : errmsg("cannot use expression index \"%s\" as replica identity",
17608 : RelationGetRelationName(indexRel))));
17609 : /* Predicate indexes aren't supported. */
17610 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
17611 6 : ereport(ERROR,
17612 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17613 : errmsg("cannot use partial index \"%s\" as replica identity",
17614 : RelationGetRelationName(indexRel))));
17615 :
17616 : /* Check index for nullable columns. */
17617 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17618 : {
17619 312 : int16 attno = indexRel->rd_index->indkey.values[key];
17620 : Form_pg_attribute attr;
17621 :
17622 : /*
17623 : * Reject any other system columns. (Going forward, we'll disallow
17624 : * indexes containing such columns in the first place, but they might
17625 : * exist in older branches.)
17626 : */
17627 312 : if (attno <= 0)
17628 0 : ereport(ERROR,
17629 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17630 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17631 : RelationGetRelationName(indexRel), attno)));
17632 :
17633 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17634 312 : if (!attr->attnotnull)
17635 6 : ereport(ERROR,
17636 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17637 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17638 : RelationGetRelationName(indexRel),
17639 : NameStr(attr->attname))));
17640 : }
17641 :
17642 : /* This index is suitable for use as a replica identity. Mark it. */
17643 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17644 :
17645 240 : index_close(indexRel, NoLock);
17646 : }
17647 :
17648 : /*
17649 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17650 : */
17651 : static void
17652 300 : ATExecSetRowSecurity(Relation rel, bool rls)
17653 : {
17654 : Relation pg_class;
17655 : Oid relid;
17656 : HeapTuple tuple;
17657 :
17658 300 : relid = RelationGetRelid(rel);
17659 :
17660 : /* Pull the record for this relation and update it */
17661 300 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17662 :
17663 300 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17664 :
17665 300 : if (!HeapTupleIsValid(tuple))
17666 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17667 :
17668 300 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17669 300 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17670 :
17671 300 : InvokeObjectPostAlterHook(RelationRelationId,
17672 : RelationGetRelid(rel), 0);
17673 :
17674 300 : table_close(pg_class, RowExclusiveLock);
17675 300 : heap_freetuple(tuple);
17676 300 : }
17677 :
17678 : /*
17679 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17680 : */
17681 : static void
17682 126 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17683 : {
17684 : Relation pg_class;
17685 : Oid relid;
17686 : HeapTuple tuple;
17687 :
17688 126 : relid = RelationGetRelid(rel);
17689 :
17690 126 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17691 :
17692 126 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17693 :
17694 126 : if (!HeapTupleIsValid(tuple))
17695 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17696 :
17697 126 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17698 126 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17699 :
17700 126 : InvokeObjectPostAlterHook(RelationRelationId,
17701 : RelationGetRelid(rel), 0);
17702 :
17703 126 : table_close(pg_class, RowExclusiveLock);
17704 126 : heap_freetuple(tuple);
17705 126 : }
17706 :
17707 : /*
17708 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
17709 : */
17710 : static void
17711 58 : ATExecGenericOptions(Relation rel, List *options)
17712 : {
17713 : Relation ftrel;
17714 : ForeignServer *server;
17715 : ForeignDataWrapper *fdw;
17716 : HeapTuple tuple;
17717 : bool isnull;
17718 : Datum repl_val[Natts_pg_foreign_table];
17719 : bool repl_null[Natts_pg_foreign_table];
17720 : bool repl_repl[Natts_pg_foreign_table];
17721 : Datum datum;
17722 : Form_pg_foreign_table tableform;
17723 :
17724 58 : if (options == NIL)
17725 0 : return;
17726 :
17727 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17728 :
17729 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17730 : ObjectIdGetDatum(rel->rd_id));
17731 58 : if (!HeapTupleIsValid(tuple))
17732 0 : ereport(ERROR,
17733 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17734 : errmsg("foreign table \"%s\" does not exist",
17735 : RelationGetRelationName(rel))));
17736 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17737 58 : server = GetForeignServer(tableform->ftserver);
17738 58 : fdw = GetForeignDataWrapper(server->fdwid);
17739 :
17740 58 : memset(repl_val, 0, sizeof(repl_val));
17741 58 : memset(repl_null, false, sizeof(repl_null));
17742 58 : memset(repl_repl, false, sizeof(repl_repl));
17743 :
17744 : /* Extract the current options */
17745 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
17746 : tuple,
17747 : Anum_pg_foreign_table_ftoptions,
17748 : &isnull);
17749 58 : if (isnull)
17750 4 : datum = PointerGetDatum(NULL);
17751 :
17752 : /* Transform the options */
17753 58 : datum = transformGenericOptions(ForeignTableRelationId,
17754 : datum,
17755 : options,
17756 : fdw->fdwvalidator);
17757 :
17758 56 : if (PointerIsValid(DatumGetPointer(datum)))
17759 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17760 : else
17761 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17762 :
17763 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17764 :
17765 : /* Everything looks good - update the tuple */
17766 :
17767 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17768 : repl_val, repl_null, repl_repl);
17769 :
17770 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17771 :
17772 : /*
17773 : * Invalidate relcache so that all sessions will refresh any cached plans
17774 : * that might depend on the old options.
17775 : */
17776 56 : CacheInvalidateRelcache(rel);
17777 :
17778 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
17779 : RelationGetRelid(rel), 0);
17780 :
17781 56 : table_close(ftrel, RowExclusiveLock);
17782 :
17783 56 : heap_freetuple(tuple);
17784 : }
17785 :
17786 : /*
17787 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
17788 : *
17789 : * Return value is the address of the modified column
17790 : */
17791 : static ObjectAddress
17792 68 : ATExecSetCompression(Relation rel,
17793 : const char *column,
17794 : Node *newValue,
17795 : LOCKMODE lockmode)
17796 : {
17797 : Relation attrel;
17798 : HeapTuple tuple;
17799 : Form_pg_attribute atttableform;
17800 : AttrNumber attnum;
17801 : char *compression;
17802 : char cmethod;
17803 : ObjectAddress address;
17804 :
17805 68 : compression = strVal(newValue);
17806 :
17807 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
17808 :
17809 : /* copy the cache entry so we can scribble on it below */
17810 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17811 68 : if (!HeapTupleIsValid(tuple))
17812 0 : ereport(ERROR,
17813 : (errcode(ERRCODE_UNDEFINED_COLUMN),
17814 : errmsg("column \"%s\" of relation \"%s\" does not exist",
17815 : column, RelationGetRelationName(rel))));
17816 :
17817 : /* prevent them from altering a system attribute */
17818 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17819 68 : attnum = atttableform->attnum;
17820 68 : if (attnum <= 0)
17821 0 : ereport(ERROR,
17822 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17823 : errmsg("cannot alter system column \"%s\"", column)));
17824 :
17825 : /*
17826 : * Check that column type is compressible, then get the attribute
17827 : * compression method code
17828 : */
17829 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17830 :
17831 : /* update pg_attribute entry */
17832 62 : atttableform->attcompression = cmethod;
17833 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17834 :
17835 62 : InvokeObjectPostAlterHook(RelationRelationId,
17836 : RelationGetRelid(rel),
17837 : attnum);
17838 :
17839 : /*
17840 : * Apply the change to indexes as well (only for simple index columns,
17841 : * matching behavior of index.c ConstructTupleDescriptor()).
17842 : */
17843 62 : SetIndexStorageProperties(rel, attrel, attnum,
17844 : false, 0,
17845 : true, cmethod,
17846 : lockmode);
17847 :
17848 62 : heap_freetuple(tuple);
17849 :
17850 62 : table_close(attrel, RowExclusiveLock);
17851 :
17852 : /* make changes visible */
17853 62 : CommandCounterIncrement();
17854 :
17855 62 : ObjectAddressSubSet(address, RelationRelationId,
17856 : RelationGetRelid(rel), attnum);
17857 62 : return address;
17858 : }
17859 :
17860 :
17861 : /*
17862 : * Preparation phase for SET LOGGED/UNLOGGED
17863 : *
17864 : * This verifies that we're not trying to change a temp table. Also,
17865 : * existing foreign key constraints are checked to avoid ending up with
17866 : * permanent tables referencing unlogged tables.
17867 : */
17868 : static void
17869 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
17870 : {
17871 : Relation pg_constraint;
17872 : HeapTuple tuple;
17873 : SysScanDesc scan;
17874 : ScanKeyData skey[1];
17875 :
17876 : /*
17877 : * Disallow changing status for a temp table. Also verify whether we can
17878 : * get away with doing nothing; in such cases we don't need to run the
17879 : * checks below, either.
17880 : */
17881 100 : switch (rel->rd_rel->relpersistence)
17882 : {
17883 0 : case RELPERSISTENCE_TEMP:
17884 0 : ereport(ERROR,
17885 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17886 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
17887 : RelationGetRelationName(rel)),
17888 : errtable(rel)));
17889 : break;
17890 56 : case RELPERSISTENCE_PERMANENT:
17891 56 : if (toLogged)
17892 : /* nothing to do */
17893 12 : return;
17894 50 : break;
17895 44 : case RELPERSISTENCE_UNLOGGED:
17896 44 : if (!toLogged)
17897 : /* nothing to do */
17898 6 : return;
17899 38 : break;
17900 : }
17901 :
17902 : /*
17903 : * Check that the table is not part of any publication when changing to
17904 : * UNLOGGED, as UNLOGGED tables can't be published.
17905 : */
17906 138 : if (!toLogged &&
17907 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
17908 0 : ereport(ERROR,
17909 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17910 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17911 : RelationGetRelationName(rel)),
17912 : errdetail("Unlogged relations cannot be replicated.")));
17913 :
17914 : /*
17915 : * Check existing foreign key constraints to preserve the invariant that
17916 : * permanent tables cannot reference unlogged ones. Self-referencing
17917 : * foreign keys can safely be ignored.
17918 : */
17919 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17920 :
17921 : /*
17922 : * Scan conrelid if changing to permanent, else confrelid. This also
17923 : * determines whether a useful index exists.
17924 : */
17925 88 : ScanKeyInit(&skey[0],
17926 : toLogged ? Anum_pg_constraint_conrelid :
17927 : Anum_pg_constraint_confrelid,
17928 : BTEqualStrategyNumber, F_OIDEQ,
17929 : ObjectIdGetDatum(RelationGetRelid(rel)));
17930 88 : scan = systable_beginscan(pg_constraint,
17931 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17932 : true, NULL, 1, skey);
17933 :
17934 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17935 : {
17936 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17937 :
17938 66 : if (con->contype == CONSTRAINT_FOREIGN)
17939 : {
17940 : Oid foreignrelid;
17941 : Relation foreignrel;
17942 :
17943 : /* the opposite end of what we used as scankey */
17944 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17945 :
17946 : /* ignore if self-referencing */
17947 30 : if (RelationGetRelid(rel) == foreignrelid)
17948 12 : continue;
17949 :
17950 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17951 :
17952 18 : if (toLogged)
17953 : {
17954 6 : if (!RelationIsPermanent(foreignrel))
17955 6 : ereport(ERROR,
17956 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17957 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17958 : RelationGetRelationName(rel),
17959 : RelationGetRelationName(foreignrel)),
17960 : errtableconstraint(rel, NameStr(con->conname))));
17961 : }
17962 : else
17963 : {
17964 12 : if (RelationIsPermanent(foreignrel))
17965 6 : ereport(ERROR,
17966 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17967 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17968 : RelationGetRelationName(rel),
17969 : RelationGetRelationName(foreignrel)),
17970 : errtableconstraint(rel, NameStr(con->conname))));
17971 : }
17972 :
17973 6 : relation_close(foreignrel, AccessShareLock);
17974 : }
17975 : }
17976 :
17977 76 : systable_endscan(scan);
17978 :
17979 76 : table_close(pg_constraint, AccessShareLock);
17980 :
17981 : /* force rewrite if necessary; see comment in ATRewriteTables */
17982 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
17983 76 : if (toLogged)
17984 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
17985 : else
17986 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
17987 76 : tab->chgPersistence = true;
17988 : }
17989 :
17990 : /*
17991 : * Execute ALTER TABLE SET SCHEMA
17992 : */
17993 : ObjectAddress
17994 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17995 : {
17996 : Relation rel;
17997 : Oid relid;
17998 : Oid oldNspOid;
17999 : Oid nspOid;
18000 : RangeVar *newrv;
18001 : ObjectAddresses *objsMoved;
18002 : ObjectAddress myself;
18003 :
18004 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18005 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18006 : RangeVarCallbackForAlterRelation,
18007 : stmt);
18008 :
18009 102 : if (!OidIsValid(relid))
18010 : {
18011 12 : ereport(NOTICE,
18012 : (errmsg("relation \"%s\" does not exist, skipping",
18013 : stmt->relation->relname)));
18014 12 : return InvalidObjectAddress;
18015 : }
18016 :
18017 90 : rel = relation_open(relid, NoLock);
18018 :
18019 90 : oldNspOid = RelationGetNamespace(rel);
18020 :
18021 : /* If it's an owned sequence, disallow moving it by itself. */
18022 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18023 : {
18024 : Oid tableId;
18025 : int32 colId;
18026 :
18027 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18028 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18029 6 : ereport(ERROR,
18030 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18031 : errmsg("cannot move an owned sequence into another schema"),
18032 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18033 : RelationGetRelationName(rel),
18034 : get_rel_name(tableId))));
18035 : }
18036 :
18037 : /* Get and lock schema OID and check its permissions. */
18038 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18039 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18040 :
18041 : /* common checks on switching namespaces */
18042 84 : CheckSetNamespace(oldNspOid, nspOid);
18043 :
18044 84 : objsMoved = new_object_addresses();
18045 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18046 84 : free_object_addresses(objsMoved);
18047 :
18048 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18049 :
18050 84 : if (oldschema)
18051 84 : *oldschema = oldNspOid;
18052 :
18053 : /* close rel, but keep lock until commit */
18054 84 : relation_close(rel, NoLock);
18055 :
18056 84 : return myself;
18057 : }
18058 :
18059 : /*
18060 : * The guts of relocating a table or materialized view to another namespace:
18061 : * besides moving the relation itself, its dependent objects are relocated to
18062 : * the new schema.
18063 : */
18064 : void
18065 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18066 : ObjectAddresses *objsMoved)
18067 : {
18068 : Relation classRel;
18069 :
18070 : Assert(objsMoved != NULL);
18071 :
18072 : /* OK, modify the pg_class row and pg_depend entry */
18073 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18074 :
18075 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18076 : nspOid, true, objsMoved);
18077 :
18078 : /* Fix the table's row type too, if it has one */
18079 86 : if (OidIsValid(rel->rd_rel->reltype))
18080 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18081 : false, /* isImplicitArray */
18082 : false, /* ignoreDependent */
18083 : false, /* errorOnTableType */
18084 : objsMoved);
18085 :
18086 : /* Fix other dependent stuff */
18087 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18088 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18089 : objsMoved, AccessExclusiveLock);
18090 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18091 : false, objsMoved);
18092 :
18093 86 : table_close(classRel, RowExclusiveLock);
18094 86 : }
18095 :
18096 : /*
18097 : * The guts of relocating a relation to another namespace: fix the pg_class
18098 : * entry, and the pg_depend entry if any. Caller must already have
18099 : * opened and write-locked pg_class.
18100 : */
18101 : void
18102 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18103 : Oid oldNspOid, Oid newNspOid,
18104 : bool hasDependEntry,
18105 : ObjectAddresses *objsMoved)
18106 : {
18107 : HeapTuple classTup;
18108 : Form_pg_class classForm;
18109 : ObjectAddress thisobj;
18110 188 : bool already_done = false;
18111 :
18112 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18113 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18114 188 : if (!HeapTupleIsValid(classTup))
18115 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
18116 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
18117 :
18118 : Assert(classForm->relnamespace == oldNspOid);
18119 :
18120 188 : thisobj.classId = RelationRelationId;
18121 188 : thisobj.objectId = relOid;
18122 188 : thisobj.objectSubId = 0;
18123 :
18124 : /*
18125 : * If the object has already been moved, don't move it again. If it's
18126 : * already in the right place, don't move it, but still fire the object
18127 : * access hook.
18128 : */
18129 188 : already_done = object_address_present(&thisobj, objsMoved);
18130 188 : if (!already_done && oldNspOid != newNspOid)
18131 146 : {
18132 146 : ItemPointerData otid = classTup->t_self;
18133 :
18134 : /* check for duplicate name (more friendly than unique-index failure) */
18135 146 : if (get_relname_relid(NameStr(classForm->relname),
18136 : newNspOid) != InvalidOid)
18137 0 : ereport(ERROR,
18138 : (errcode(ERRCODE_DUPLICATE_TABLE),
18139 : errmsg("relation \"%s\" already exists in schema \"%s\"",
18140 : NameStr(classForm->relname),
18141 : get_namespace_name(newNspOid))));
18142 :
18143 : /* classTup is a copy, so OK to scribble on */
18144 146 : classForm->relnamespace = newNspOid;
18145 :
18146 146 : CatalogTupleUpdate(classRel, &otid, classTup);
18147 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
18148 :
18149 :
18150 : /* Update dependency on schema if caller said so */
18151 250 : if (hasDependEntry &&
18152 104 : changeDependencyFor(RelationRelationId,
18153 : relOid,
18154 : NamespaceRelationId,
18155 : oldNspOid,
18156 : newNspOid) != 1)
18157 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
18158 : NameStr(classForm->relname));
18159 : }
18160 : else
18161 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
18162 188 : if (!already_done)
18163 : {
18164 188 : add_exact_object_address(&thisobj, objsMoved);
18165 :
18166 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18167 : }
18168 :
18169 188 : heap_freetuple(classTup);
18170 188 : }
18171 :
18172 : /*
18173 : * Move all indexes for the specified relation to another namespace.
18174 : *
18175 : * Note: we assume adequate permission checking was done by the caller,
18176 : * and that the caller has a suitable lock on the owning relation.
18177 : */
18178 : static void
18179 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
18180 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18181 : {
18182 : List *indexList;
18183 : ListCell *l;
18184 :
18185 86 : indexList = RelationGetIndexList(rel);
18186 :
18187 132 : foreach(l, indexList)
18188 : {
18189 46 : Oid indexOid = lfirst_oid(l);
18190 : ObjectAddress thisobj;
18191 :
18192 46 : thisobj.classId = RelationRelationId;
18193 46 : thisobj.objectId = indexOid;
18194 46 : thisobj.objectSubId = 0;
18195 :
18196 : /*
18197 : * Note: currently, the index will not have its own dependency on the
18198 : * namespace, so we don't need to do changeDependencyFor(). There's no
18199 : * row type in pg_type, either.
18200 : *
18201 : * XXX this objsMoved test may be pointless -- surely we have a single
18202 : * dependency link from a relation to each index?
18203 : */
18204 46 : if (!object_address_present(&thisobj, objsMoved))
18205 : {
18206 46 : AlterRelationNamespaceInternal(classRel, indexOid,
18207 : oldNspOid, newNspOid,
18208 : false, objsMoved);
18209 46 : add_exact_object_address(&thisobj, objsMoved);
18210 : }
18211 : }
18212 :
18213 86 : list_free(indexList);
18214 86 : }
18215 :
18216 : /*
18217 : * Move all identity and SERIAL-column sequences of the specified relation to another
18218 : * namespace.
18219 : *
18220 : * Note: we assume adequate permission checking was done by the caller,
18221 : * and that the caller has a suitable lock on the owning relation.
18222 : */
18223 : static void
18224 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
18225 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18226 : LOCKMODE lockmode)
18227 : {
18228 : Relation depRel;
18229 : SysScanDesc scan;
18230 : ScanKeyData key[2];
18231 : HeapTuple tup;
18232 :
18233 : /*
18234 : * SERIAL sequences are those having an auto dependency on one of the
18235 : * table's columns (we don't care *which* column, exactly).
18236 : */
18237 86 : depRel = table_open(DependRelationId, AccessShareLock);
18238 :
18239 86 : ScanKeyInit(&key[0],
18240 : Anum_pg_depend_refclassid,
18241 : BTEqualStrategyNumber, F_OIDEQ,
18242 : ObjectIdGetDatum(RelationRelationId));
18243 86 : ScanKeyInit(&key[1],
18244 : Anum_pg_depend_refobjid,
18245 : BTEqualStrategyNumber, F_OIDEQ,
18246 : ObjectIdGetDatum(RelationGetRelid(rel)));
18247 : /* we leave refobjsubid unspecified */
18248 :
18249 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18250 : NULL, 2, key);
18251 :
18252 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
18253 : {
18254 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18255 : Relation seqRel;
18256 :
18257 : /* skip dependencies other than auto dependencies on columns */
18258 530 : if (depForm->refobjsubid == 0 ||
18259 382 : depForm->classid != RelationRelationId ||
18260 42 : depForm->objsubid != 0 ||
18261 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18262 488 : continue;
18263 :
18264 : /* Use relation_open just in case it's an index */
18265 42 : seqRel = relation_open(depForm->objid, lockmode);
18266 :
18267 : /* skip non-sequence relations */
18268 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18269 : {
18270 : /* No need to keep the lock */
18271 0 : relation_close(seqRel, lockmode);
18272 0 : continue;
18273 : }
18274 :
18275 : /* Fix the pg_class and pg_depend entries */
18276 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
18277 : oldNspOid, newNspOid,
18278 : true, objsMoved);
18279 :
18280 : /*
18281 : * Sequences used to have entries in pg_type, but no longer do. If we
18282 : * ever re-instate that, we'll need to move the pg_type entry to the
18283 : * new namespace, too (using AlterTypeNamespaceInternal).
18284 : */
18285 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18286 :
18287 : /* Now we can close it. Keep the lock till end of transaction. */
18288 42 : relation_close(seqRel, NoLock);
18289 : }
18290 :
18291 86 : systable_endscan(scan);
18292 :
18293 86 : relation_close(depRel, AccessShareLock);
18294 86 : }
18295 :
18296 :
18297 : /*
18298 : * This code supports
18299 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18300 : *
18301 : * Because we only support this for TEMP tables, it's sufficient to remember
18302 : * the state in a backend-local data structure.
18303 : */
18304 :
18305 : /*
18306 : * Register a newly-created relation's ON COMMIT action.
18307 : */
18308 : void
18309 166 : register_on_commit_action(Oid relid, OnCommitAction action)
18310 : {
18311 : OnCommitItem *oc;
18312 : MemoryContext oldcxt;
18313 :
18314 : /*
18315 : * We needn't bother registering the relation unless there is an ON COMMIT
18316 : * action we need to take.
18317 : */
18318 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18319 24 : return;
18320 :
18321 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18322 :
18323 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18324 142 : oc->relid = relid;
18325 142 : oc->oncommit = action;
18326 142 : oc->creating_subid = GetCurrentSubTransactionId();
18327 142 : oc->deleting_subid = InvalidSubTransactionId;
18328 :
18329 : /*
18330 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18331 : * order of registration. That might not be essential but it seems
18332 : * reasonable.
18333 : */
18334 142 : on_commits = lcons(oc, on_commits);
18335 :
18336 142 : MemoryContextSwitchTo(oldcxt);
18337 : }
18338 :
18339 : /*
18340 : * Unregister any ON COMMIT action when a relation is deleted.
18341 : *
18342 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18343 : */
18344 : void
18345 47180 : remove_on_commit_action(Oid relid)
18346 : {
18347 : ListCell *l;
18348 :
18349 47314 : foreach(l, on_commits)
18350 : {
18351 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18352 :
18353 264 : if (oc->relid == relid)
18354 : {
18355 130 : oc->deleting_subid = GetCurrentSubTransactionId();
18356 130 : break;
18357 : }
18358 : }
18359 47180 : }
18360 :
18361 : /*
18362 : * Perform ON COMMIT actions.
18363 : *
18364 : * This is invoked just before actually committing, since it's possible
18365 : * to encounter errors.
18366 : */
18367 : void
18368 758666 : PreCommit_on_commit_actions(void)
18369 : {
18370 : ListCell *l;
18371 758666 : List *oids_to_truncate = NIL;
18372 758666 : List *oids_to_drop = NIL;
18373 :
18374 759382 : foreach(l, on_commits)
18375 : {
18376 716 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18377 :
18378 : /* Ignore entry if already dropped in this xact */
18379 716 : if (oc->deleting_subid != InvalidSubTransactionId)
18380 68 : continue;
18381 :
18382 648 : switch (oc->oncommit)
18383 : {
18384 0 : case ONCOMMIT_NOOP:
18385 : case ONCOMMIT_PRESERVE_ROWS:
18386 : /* Do nothing (there shouldn't be such entries, actually) */
18387 0 : break;
18388 598 : case ONCOMMIT_DELETE_ROWS:
18389 :
18390 : /*
18391 : * If this transaction hasn't accessed any temporary
18392 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18393 : * tables, as they must still be empty.
18394 : */
18395 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18396 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18397 598 : break;
18398 50 : case ONCOMMIT_DROP:
18399 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18400 50 : break;
18401 : }
18402 716 : }
18403 :
18404 : /*
18405 : * Truncate relations before dropping so that all dependencies between
18406 : * relations are removed after they are worked on. Doing it like this
18407 : * might be a waste as it is possible that a relation being truncated will
18408 : * be dropped anyway due to its parent being dropped, but this makes the
18409 : * code more robust because of not having to re-check that the relation
18410 : * exists at truncation time.
18411 : */
18412 758666 : if (oids_to_truncate != NIL)
18413 334 : heap_truncate(oids_to_truncate);
18414 :
18415 758660 : if (oids_to_drop != NIL)
18416 : {
18417 44 : ObjectAddresses *targetObjects = new_object_addresses();
18418 :
18419 94 : foreach(l, oids_to_drop)
18420 : {
18421 : ObjectAddress object;
18422 :
18423 50 : object.classId = RelationRelationId;
18424 50 : object.objectId = lfirst_oid(l);
18425 50 : object.objectSubId = 0;
18426 :
18427 : Assert(!object_address_present(&object, targetObjects));
18428 :
18429 50 : add_exact_object_address(&object, targetObjects);
18430 : }
18431 :
18432 : /*
18433 : * Object deletion might involve toast table access (to clean up
18434 : * toasted catalog entries), so ensure we have a valid snapshot.
18435 : */
18436 44 : PushActiveSnapshot(GetTransactionSnapshot());
18437 :
18438 : /*
18439 : * Since this is an automatic drop, rather than one directly initiated
18440 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18441 : */
18442 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18443 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18444 :
18445 44 : PopActiveSnapshot();
18446 :
18447 : #ifdef USE_ASSERT_CHECKING
18448 :
18449 : /*
18450 : * Note that table deletion will call remove_on_commit_action, so the
18451 : * entry should get marked as deleted.
18452 : */
18453 : foreach(l, on_commits)
18454 : {
18455 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18456 :
18457 : if (oc->oncommit != ONCOMMIT_DROP)
18458 : continue;
18459 :
18460 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18461 : }
18462 : #endif
18463 : }
18464 758660 : }
18465 :
18466 : /*
18467 : * Post-commit or post-abort cleanup for ON COMMIT management.
18468 : *
18469 : * All we do here is remove no-longer-needed OnCommitItem entries.
18470 : *
18471 : * During commit, remove entries that were deleted during this transaction;
18472 : * during abort, remove those created during this transaction.
18473 : */
18474 : void
18475 806324 : AtEOXact_on_commit_actions(bool isCommit)
18476 : {
18477 : ListCell *cur_item;
18478 :
18479 807070 : foreach(cur_item, on_commits)
18480 : {
18481 746 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18482 :
18483 848 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18484 102 : oc->creating_subid != InvalidSubTransactionId)
18485 : {
18486 : /* cur_item must be removed */
18487 142 : on_commits = foreach_delete_current(on_commits, cur_item);
18488 142 : pfree(oc);
18489 : }
18490 : else
18491 : {
18492 : /* cur_item must be preserved */
18493 604 : oc->creating_subid = InvalidSubTransactionId;
18494 604 : oc->deleting_subid = InvalidSubTransactionId;
18495 : }
18496 : }
18497 806324 : }
18498 :
18499 : /*
18500 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18501 : *
18502 : * During subabort, we can immediately remove entries created during this
18503 : * subtransaction. During subcommit, just relabel entries marked during
18504 : * this subtransaction as being the parent's responsibility.
18505 : */
18506 : void
18507 20060 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18508 : SubTransactionId parentSubid)
18509 : {
18510 : ListCell *cur_item;
18511 :
18512 20060 : foreach(cur_item, on_commits)
18513 : {
18514 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18515 :
18516 0 : if (!isCommit && oc->creating_subid == mySubid)
18517 : {
18518 : /* cur_item must be removed */
18519 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18520 0 : pfree(oc);
18521 : }
18522 : else
18523 : {
18524 : /* cur_item must be preserved */
18525 0 : if (oc->creating_subid == mySubid)
18526 0 : oc->creating_subid = parentSubid;
18527 0 : if (oc->deleting_subid == mySubid)
18528 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18529 : }
18530 : }
18531 20060 : }
18532 :
18533 : /*
18534 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18535 : * the relation to be locked only if (1) it's a plain or partitioned table,
18536 : * materialized view, or TOAST table and (2) the current user is the owner (or
18537 : * the superuser) or has been granted MAINTAIN. This meets the
18538 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18539 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18540 : */
18541 : void
18542 1014 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18543 : Oid relId, Oid oldRelId, void *arg)
18544 : {
18545 : char relkind;
18546 : AclResult aclresult;
18547 :
18548 : /* Nothing to do if the relation was not found. */
18549 1014 : if (!OidIsValid(relId))
18550 6 : return;
18551 :
18552 : /*
18553 : * If the relation does exist, check whether it's an index. But note that
18554 : * the relation might have been dropped between the time we did the name
18555 : * lookup and now. In that case, there's nothing to do.
18556 : */
18557 1008 : relkind = get_rel_relkind(relId);
18558 1008 : if (!relkind)
18559 0 : return;
18560 1008 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18561 138 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18562 28 : ereport(ERROR,
18563 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18564 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18565 :
18566 : /* Check permissions */
18567 980 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18568 980 : if (aclresult != ACLCHECK_OK)
18569 30 : aclcheck_error(aclresult,
18570 30 : get_relkind_objtype(get_rel_relkind(relId)),
18571 30 : relation->relname);
18572 : }
18573 :
18574 : /*
18575 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18576 : */
18577 : static void
18578 2040 : RangeVarCallbackForTruncate(const RangeVar *relation,
18579 : Oid relId, Oid oldRelId, void *arg)
18580 : {
18581 : HeapTuple tuple;
18582 :
18583 : /* Nothing to do if the relation was not found. */
18584 2040 : if (!OidIsValid(relId))
18585 0 : return;
18586 :
18587 2040 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18588 2040 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18589 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18590 :
18591 2040 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18592 2034 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18593 :
18594 2002 : ReleaseSysCache(tuple);
18595 : }
18596 :
18597 : /*
18598 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18599 : * the owner of the relation, or superuser.
18600 : */
18601 : void
18602 15594 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18603 : Oid relId, Oid oldRelId, void *arg)
18604 : {
18605 : HeapTuple tuple;
18606 :
18607 : /* Nothing to do if the relation was not found. */
18608 15594 : if (!OidIsValid(relId))
18609 14 : return;
18610 :
18611 15580 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18612 15580 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18613 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18614 :
18615 15580 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18616 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18617 6 : relation->relname);
18618 :
18619 31028 : if (!allowSystemTableMods &&
18620 15454 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18621 2 : ereport(ERROR,
18622 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18623 : errmsg("permission denied: \"%s\" is a system catalog",
18624 : relation->relname)));
18625 :
18626 15572 : ReleaseSysCache(tuple);
18627 : }
18628 :
18629 : /*
18630 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18631 : * processing.
18632 : */
18633 : static void
18634 31468 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18635 : void *arg)
18636 : {
18637 31468 : Node *stmt = (Node *) arg;
18638 : ObjectType reltype;
18639 : HeapTuple tuple;
18640 : Form_pg_class classform;
18641 : AclResult aclresult;
18642 : char relkind;
18643 :
18644 31468 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18645 31468 : if (!HeapTupleIsValid(tuple))
18646 220 : return; /* concurrently dropped */
18647 31248 : classform = (Form_pg_class) GETSTRUCT(tuple);
18648 31248 : relkind = classform->relkind;
18649 :
18650 : /* Must own relation. */
18651 31248 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18652 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18653 :
18654 : /* No system table modifications unless explicitly allowed. */
18655 31188 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18656 28 : ereport(ERROR,
18657 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18658 : errmsg("permission denied: \"%s\" is a system catalog",
18659 : rv->relname)));
18660 :
18661 : /*
18662 : * Extract the specified relation type from the statement parse tree.
18663 : *
18664 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
18665 : * have CREATE rights on the containing namespace.
18666 : */
18667 31160 : if (IsA(stmt, RenameStmt))
18668 : {
18669 488 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18670 : GetUserId(), ACL_CREATE);
18671 488 : if (aclresult != ACLCHECK_OK)
18672 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
18673 0 : get_namespace_name(classform->relnamespace));
18674 488 : reltype = ((RenameStmt *) stmt)->renameType;
18675 : }
18676 30672 : else if (IsA(stmt, AlterObjectSchemaStmt))
18677 92 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18678 :
18679 30580 : else if (IsA(stmt, AlterTableStmt))
18680 30580 : reltype = ((AlterTableStmt *) stmt)->objtype;
18681 : else
18682 : {
18683 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18684 : reltype = OBJECT_TABLE; /* placate compiler */
18685 : }
18686 :
18687 : /*
18688 : * For compatibility with prior releases, we allow ALTER TABLE to be used
18689 : * with most other types of relations (but not composite types). We allow
18690 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
18691 : * otherwise. Otherwise, the user must select the correct form of the
18692 : * command for the relation at issue.
18693 : */
18694 31160 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18695 0 : ereport(ERROR,
18696 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18697 : errmsg("\"%s\" is not a sequence", rv->relname)));
18698 :
18699 31160 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18700 0 : ereport(ERROR,
18701 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18702 : errmsg("\"%s\" is not a view", rv->relname)));
18703 :
18704 31160 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18705 0 : ereport(ERROR,
18706 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18707 : errmsg("\"%s\" is not a materialized view", rv->relname)));
18708 :
18709 31160 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18710 0 : ereport(ERROR,
18711 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18712 : errmsg("\"%s\" is not a foreign table", rv->relname)));
18713 :
18714 31160 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18715 0 : ereport(ERROR,
18716 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18717 : errmsg("\"%s\" is not a composite type", rv->relname)));
18718 :
18719 31160 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18720 : relkind != RELKIND_PARTITIONED_INDEX
18721 36 : && !IsA(stmt, RenameStmt))
18722 6 : ereport(ERROR,
18723 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18724 : errmsg("\"%s\" is not an index", rv->relname)));
18725 :
18726 : /*
18727 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18728 : * TYPE for that.
18729 : */
18730 31154 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18731 0 : ereport(ERROR,
18732 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18733 : errmsg("\"%s\" is a composite type", rv->relname),
18734 : /* translator: %s is an SQL ALTER command */
18735 : errhint("Use %s instead.",
18736 : "ALTER TYPE")));
18737 :
18738 : /*
18739 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18740 : * to a different schema, such as indexes and TOAST tables.
18741 : */
18742 31154 : if (IsA(stmt, AlterObjectSchemaStmt))
18743 : {
18744 92 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18745 0 : ereport(ERROR,
18746 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18747 : errmsg("cannot change schema of index \"%s\"",
18748 : rv->relname),
18749 : errhint("Change the schema of the table instead.")));
18750 92 : else if (relkind == RELKIND_COMPOSITE_TYPE)
18751 0 : ereport(ERROR,
18752 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18753 : errmsg("cannot change schema of composite type \"%s\"",
18754 : rv->relname),
18755 : /* translator: %s is an SQL ALTER command */
18756 : errhint("Use %s instead.",
18757 : "ALTER TYPE")));
18758 92 : else if (relkind == RELKIND_TOASTVALUE)
18759 0 : ereport(ERROR,
18760 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18761 : errmsg("cannot change schema of TOAST table \"%s\"",
18762 : rv->relname),
18763 : errhint("Change the schema of the table instead.")));
18764 : }
18765 :
18766 31154 : ReleaseSysCache(tuple);
18767 : }
18768 :
18769 : /*
18770 : * Transform any expressions present in the partition key
18771 : *
18772 : * Returns a transformed PartitionSpec.
18773 : */
18774 : static PartitionSpec *
18775 4880 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18776 : {
18777 : PartitionSpec *newspec;
18778 : ParseState *pstate;
18779 : ParseNamespaceItem *nsitem;
18780 : ListCell *l;
18781 :
18782 4880 : newspec = makeNode(PartitionSpec);
18783 :
18784 4880 : newspec->strategy = partspec->strategy;
18785 4880 : newspec->partParams = NIL;
18786 4880 : newspec->location = partspec->location;
18787 :
18788 : /* Check valid number of columns for strategy */
18789 7348 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18790 2468 : list_length(partspec->partParams) != 1)
18791 6 : ereport(ERROR,
18792 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18793 : errmsg("cannot use \"list\" partition strategy with more than one column")));
18794 :
18795 : /*
18796 : * Create a dummy ParseState and insert the target relation as its sole
18797 : * rangetable entry. We need a ParseState for transformExpr.
18798 : */
18799 4874 : pstate = make_parsestate(NULL);
18800 4874 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18801 : NULL, false, true);
18802 4874 : addNSItemToQuery(pstate, nsitem, true, true, true);
18803 :
18804 : /* take care of any partition expressions */
18805 10174 : foreach(l, partspec->partParams)
18806 : {
18807 5324 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
18808 :
18809 5324 : if (pelem->expr)
18810 : {
18811 : /* Copy, to avoid scribbling on the input */
18812 304 : pelem = copyObject(pelem);
18813 :
18814 : /* Now do parse transformation of the expression */
18815 304 : pelem->expr = transformExpr(pstate, pelem->expr,
18816 : EXPR_KIND_PARTITION_EXPRESSION);
18817 :
18818 : /* we have to fix its collations too */
18819 280 : assign_expr_collations(pstate, pelem->expr);
18820 : }
18821 :
18822 5300 : newspec->partParams = lappend(newspec->partParams, pelem);
18823 : }
18824 :
18825 4850 : return newspec;
18826 : }
18827 :
18828 : /*
18829 : * Compute per-partition-column information from a list of PartitionElems.
18830 : * Expressions in the PartitionElems must be parse-analyzed already.
18831 : */
18832 : static void
18833 4850 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18834 : List **partexprs, Oid *partopclass, Oid *partcollation,
18835 : PartitionStrategy strategy)
18836 : {
18837 : int attn;
18838 : ListCell *lc;
18839 : Oid am_oid;
18840 :
18841 4850 : attn = 0;
18842 10054 : foreach(lc, partParams)
18843 : {
18844 5300 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18845 : Oid atttype;
18846 : Oid attcollation;
18847 :
18848 5300 : if (pelem->name != NULL)
18849 : {
18850 : /* Simple attribute reference */
18851 : HeapTuple atttuple;
18852 : Form_pg_attribute attform;
18853 :
18854 5020 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18855 5020 : pelem->name);
18856 5020 : if (!HeapTupleIsValid(atttuple))
18857 12 : ereport(ERROR,
18858 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18859 : errmsg("column \"%s\" named in partition key does not exist",
18860 : pelem->name),
18861 : parser_errposition(pstate, pelem->location)));
18862 5008 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18863 :
18864 5008 : if (attform->attnum <= 0)
18865 6 : ereport(ERROR,
18866 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18867 : errmsg("cannot use system column \"%s\" in partition key",
18868 : pelem->name),
18869 : parser_errposition(pstate, pelem->location)));
18870 :
18871 : /*
18872 : * Stored generated columns cannot work: They are computed after
18873 : * BEFORE triggers, but partition routing is done before all
18874 : * triggers. Maybe virtual generated columns could be made to
18875 : * work, but then they would need to be handled as an expression
18876 : * below.
18877 : */
18878 5002 : if (attform->attgenerated)
18879 12 : ereport(ERROR,
18880 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18881 : errmsg("cannot use generated column in partition key"),
18882 : errdetail("Column \"%s\" is a generated column.",
18883 : pelem->name),
18884 : parser_errposition(pstate, pelem->location)));
18885 :
18886 4990 : partattrs[attn] = attform->attnum;
18887 4990 : atttype = attform->atttypid;
18888 4990 : attcollation = attform->attcollation;
18889 4990 : ReleaseSysCache(atttuple);
18890 : }
18891 : else
18892 : {
18893 : /* Expression */
18894 280 : Node *expr = pelem->expr;
18895 : char partattname[16];
18896 :
18897 : Assert(expr != NULL);
18898 280 : atttype = exprType(expr);
18899 280 : attcollation = exprCollation(expr);
18900 :
18901 : /*
18902 : * The expression must be of a storable type (e.g., not RECORD).
18903 : * The test is the same as for whether a table column is of a safe
18904 : * type (which is why we needn't check for the non-expression
18905 : * case).
18906 : */
18907 280 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18908 280 : CheckAttributeType(partattname,
18909 : atttype, attcollation,
18910 : NIL, CHKATYPE_IS_PARTKEY);
18911 :
18912 : /*
18913 : * Strip any top-level COLLATE clause. This ensures that we treat
18914 : * "x COLLATE y" and "(x COLLATE y)" alike.
18915 : */
18916 268 : while (IsA(expr, CollateExpr))
18917 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
18918 :
18919 268 : if (IsA(expr, Var) &&
18920 12 : ((Var *) expr)->varattno > 0)
18921 : {
18922 : /*
18923 : * User wrote "(column)" or "(column COLLATE something)".
18924 : * Treat it like simple attribute anyway.
18925 : */
18926 6 : partattrs[attn] = ((Var *) expr)->varattno;
18927 : }
18928 : else
18929 : {
18930 262 : Bitmapset *expr_attrs = NULL;
18931 : int i;
18932 :
18933 262 : partattrs[attn] = 0; /* marks the column as expression */
18934 262 : *partexprs = lappend(*partexprs, expr);
18935 :
18936 : /*
18937 : * transformPartitionSpec() should have already rejected
18938 : * subqueries, aggregates, window functions, and SRFs, based
18939 : * on the EXPR_KIND_ for partition expressions.
18940 : */
18941 :
18942 : /*
18943 : * Cannot allow system column references, since that would
18944 : * make partition routing impossible: their values won't be
18945 : * known yet when we need to do that.
18946 : */
18947 262 : pull_varattnos(expr, 1, &expr_attrs);
18948 2096 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18949 : {
18950 1834 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18951 : expr_attrs))
18952 0 : ereport(ERROR,
18953 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18954 : errmsg("partition key expressions cannot contain system column references")));
18955 : }
18956 :
18957 : /*
18958 : * Stored generated columns cannot work: They are computed
18959 : * after BEFORE triggers, but partition routing is done before
18960 : * all triggers. Virtual generated columns could probably
18961 : * work, but it would require more work elsewhere (for example
18962 : * SET EXPRESSION would need to check whether the column is
18963 : * used in partition keys). Seems safer to prohibit for now.
18964 : */
18965 262 : i = -1;
18966 570 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
18967 : {
18968 320 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18969 :
18970 320 : if (attno > 0 &&
18971 314 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18972 12 : ereport(ERROR,
18973 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18974 : errmsg("cannot use generated column in partition key"),
18975 : errdetail("Column \"%s\" is a generated column.",
18976 : get_attname(RelationGetRelid(rel), attno, false)),
18977 : parser_errposition(pstate, pelem->location)));
18978 : }
18979 :
18980 : /*
18981 : * Preprocess the expression before checking for mutability.
18982 : * This is essential for the reasons described in
18983 : * contain_mutable_functions_after_planning. However, we call
18984 : * expression_planner for ourselves rather than using that
18985 : * function, because if constant-folding reduces the
18986 : * expression to a constant, we'd like to know that so we can
18987 : * complain below.
18988 : *
18989 : * Like contain_mutable_functions_after_planning, assume that
18990 : * expression_planner won't scribble on its input, so this
18991 : * won't affect the partexprs entry we saved above.
18992 : */
18993 250 : expr = (Node *) expression_planner((Expr *) expr);
18994 :
18995 : /*
18996 : * Partition expressions cannot contain mutable functions,
18997 : * because a given row must always map to the same partition
18998 : * as long as there is no change in the partition boundary
18999 : * structure.
19000 : */
19001 250 : if (contain_mutable_functions(expr))
19002 6 : ereport(ERROR,
19003 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19004 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19005 :
19006 : /*
19007 : * While it is not exactly *wrong* for a partition expression
19008 : * to be a constant, it seems better to reject such keys.
19009 : */
19010 244 : if (IsA(expr, Const))
19011 12 : ereport(ERROR,
19012 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19013 : errmsg("cannot use constant expression as partition key")));
19014 : }
19015 : }
19016 :
19017 : /*
19018 : * Apply collation override if any
19019 : */
19020 5228 : if (pelem->collation)
19021 54 : attcollation = get_collation_oid(pelem->collation, false);
19022 :
19023 : /*
19024 : * Check we have a collation iff it's a collatable type. The only
19025 : * expected failures here are (1) COLLATE applied to a noncollatable
19026 : * type, or (2) partition expression had an unresolved collation. But
19027 : * we might as well code this to be a complete consistency check.
19028 : */
19029 5228 : if (type_is_collatable(atttype))
19030 : {
19031 602 : if (!OidIsValid(attcollation))
19032 0 : ereport(ERROR,
19033 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19034 : errmsg("could not determine which collation to use for partition expression"),
19035 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19036 : }
19037 : else
19038 : {
19039 4626 : if (OidIsValid(attcollation))
19040 0 : ereport(ERROR,
19041 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19042 : errmsg("collations are not supported by type %s",
19043 : format_type_be(atttype))));
19044 : }
19045 :
19046 5228 : partcollation[attn] = attcollation;
19047 :
19048 : /*
19049 : * Identify the appropriate operator class. For list and range
19050 : * partitioning, we use a btree operator class; hash partitioning uses
19051 : * a hash operator class.
19052 : */
19053 5228 : if (strategy == PARTITION_STRATEGY_HASH)
19054 294 : am_oid = HASH_AM_OID;
19055 : else
19056 4934 : am_oid = BTREE_AM_OID;
19057 :
19058 5228 : if (!pelem->opclass)
19059 : {
19060 5096 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19061 :
19062 5096 : if (!OidIsValid(partopclass[attn]))
19063 : {
19064 12 : if (strategy == PARTITION_STRATEGY_HASH)
19065 0 : ereport(ERROR,
19066 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19067 : errmsg("data type %s has no default operator class for access method \"%s\"",
19068 : format_type_be(atttype), "hash"),
19069 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19070 : else
19071 12 : ereport(ERROR,
19072 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19073 : errmsg("data type %s has no default operator class for access method \"%s\"",
19074 : format_type_be(atttype), "btree"),
19075 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19076 : }
19077 : }
19078 : else
19079 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19080 : atttype,
19081 : am_oid == HASH_AM_OID ? "hash" : "btree",
19082 : am_oid);
19083 :
19084 5204 : attn++;
19085 : }
19086 4754 : }
19087 :
19088 : /*
19089 : * PartConstraintImpliedByRelConstraint
19090 : * Do scanrel's existing constraints imply the partition constraint?
19091 : *
19092 : * "Existing constraints" include its check constraints and column-level
19093 : * not-null constraints. partConstraint describes the partition constraint,
19094 : * in implicit-AND form.
19095 : */
19096 : bool
19097 3102 : PartConstraintImpliedByRelConstraint(Relation scanrel,
19098 : List *partConstraint)
19099 : {
19100 3102 : List *existConstraint = NIL;
19101 3102 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19102 : int i;
19103 :
19104 3102 : if (constr && constr->has_not_null)
19105 : {
19106 766 : int natts = scanrel->rd_att->natts;
19107 :
19108 2488 : for (i = 1; i <= natts; i++)
19109 : {
19110 1722 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19111 :
19112 1722 : if (att->attnotnull && !att->attisdropped)
19113 : {
19114 1070 : NullTest *ntest = makeNode(NullTest);
19115 :
19116 1070 : ntest->arg = (Expr *) makeVar(1,
19117 : i,
19118 : att->atttypid,
19119 : att->atttypmod,
19120 : att->attcollation,
19121 : 0);
19122 1070 : ntest->nulltesttype = IS_NOT_NULL;
19123 :
19124 : /*
19125 : * argisrow=false is correct even for a composite column,
19126 : * because attnotnull does not represent a SQL-spec IS NOT
19127 : * NULL test in such a case, just IS DISTINCT FROM NULL.
19128 : */
19129 1070 : ntest->argisrow = false;
19130 1070 : ntest->location = -1;
19131 1070 : existConstraint = lappend(existConstraint, ntest);
19132 : }
19133 : }
19134 : }
19135 :
19136 3102 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19137 : }
19138 :
19139 : /*
19140 : * ConstraintImpliedByRelConstraint
19141 : * Do scanrel's existing constraints imply the given constraint?
19142 : *
19143 : * testConstraint is the constraint to validate. provenConstraint is a
19144 : * caller-provided list of conditions which this function may assume
19145 : * to be true. Both provenConstraint and testConstraint must be in
19146 : * implicit-AND form, must only contain immutable clauses, and must
19147 : * contain only Vars with varno = 1.
19148 : */
19149 : bool
19150 4250 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19151 : {
19152 4250 : List *existConstraint = list_copy(provenConstraint);
19153 4250 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19154 : int num_check,
19155 : i;
19156 :
19157 4250 : num_check = (constr != NULL) ? constr->num_check : 0;
19158 4766 : for (i = 0; i < num_check; i++)
19159 : {
19160 : Node *cexpr;
19161 :
19162 : /*
19163 : * If this constraint hasn't been fully validated yet, we must ignore
19164 : * it here.
19165 : */
19166 516 : if (!constr->check[i].ccvalid)
19167 6 : continue;
19168 :
19169 : /*
19170 : * NOT ENFORCED constraints are always marked as invalid, which should
19171 : * have been ignored.
19172 : */
19173 : Assert(constr->check[i].ccenforced);
19174 :
19175 510 : cexpr = stringToNode(constr->check[i].ccbin);
19176 :
19177 : /*
19178 : * Run each expression through const-simplification and
19179 : * canonicalization. It is necessary, because we will be comparing it
19180 : * to similarly-processed partition constraint expressions, and may
19181 : * fail to detect valid matches without this.
19182 : */
19183 510 : cexpr = eval_const_expressions(NULL, cexpr);
19184 510 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19185 :
19186 510 : existConstraint = list_concat(existConstraint,
19187 510 : make_ands_implicit((Expr *) cexpr));
19188 : }
19189 :
19190 : /*
19191 : * Try to make the proof. Since we are comparing CHECK constraints, we
19192 : * need to use weak implication, i.e., we assume existConstraint is
19193 : * not-false and try to prove the same for testConstraint.
19194 : *
19195 : * Note that predicate_implied_by assumes its first argument is known
19196 : * immutable. That should always be true for both NOT NULL and partition
19197 : * constraints, so we don't test it here.
19198 : */
19199 4250 : return predicate_implied_by(testConstraint, existConstraint, true);
19200 : }
19201 :
19202 : /*
19203 : * QueuePartitionConstraintValidation
19204 : *
19205 : * Add an entry to wqueue to have the given partition constraint validated by
19206 : * Phase 3, for the given relation, and all its children.
19207 : *
19208 : * We first verify whether the given constraint is implied by pre-existing
19209 : * relation constraints; if it is, there's no need to scan the table to
19210 : * validate, so don't queue in that case.
19211 : */
19212 : static void
19213 2472 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19214 : List *partConstraint,
19215 : bool validate_default)
19216 : {
19217 : /*
19218 : * Based on the table's existing constraints, determine whether or not we
19219 : * may skip scanning the table.
19220 : */
19221 2472 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19222 : {
19223 90 : if (!validate_default)
19224 68 : ereport(DEBUG1,
19225 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19226 : RelationGetRelationName(scanrel))));
19227 : else
19228 22 : ereport(DEBUG1,
19229 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19230 : RelationGetRelationName(scanrel))));
19231 90 : return;
19232 : }
19233 :
19234 : /*
19235 : * Constraints proved insufficient. For plain relations, queue a
19236 : * validation item now; for partitioned tables, recurse to process each
19237 : * partition.
19238 : */
19239 2382 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19240 : {
19241 : AlteredTableInfo *tab;
19242 :
19243 : /* Grab a work queue entry. */
19244 1986 : tab = ATGetQueueEntry(wqueue, scanrel);
19245 : Assert(tab->partition_constraint == NULL);
19246 1986 : tab->partition_constraint = (Expr *) linitial(partConstraint);
19247 1986 : tab->validate_default = validate_default;
19248 : }
19249 396 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19250 : {
19251 348 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19252 : int i;
19253 :
19254 726 : for (i = 0; i < partdesc->nparts; i++)
19255 : {
19256 : Relation part_rel;
19257 : List *thisPartConstraint;
19258 :
19259 : /*
19260 : * This is the minimum lock we need to prevent deadlocks.
19261 : */
19262 378 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19263 :
19264 : /*
19265 : * Adjust the constraint for scanrel so that it matches this
19266 : * partition's attribute numbers.
19267 : */
19268 : thisPartConstraint =
19269 378 : map_partition_varattnos(partConstraint, 1,
19270 : part_rel, scanrel);
19271 :
19272 378 : QueuePartitionConstraintValidation(wqueue, part_rel,
19273 : thisPartConstraint,
19274 : validate_default);
19275 378 : table_close(part_rel, NoLock); /* keep lock till commit */
19276 : }
19277 : }
19278 : }
19279 :
19280 : /*
19281 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19282 : *
19283 : * Return the address of the newly attached partition.
19284 : */
19285 : static ObjectAddress
19286 2302 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19287 : AlterTableUtilityContext *context)
19288 : {
19289 : Relation attachrel,
19290 : catalog;
19291 : List *attachrel_children;
19292 : List *partConstraint;
19293 : SysScanDesc scan;
19294 : ScanKeyData skey;
19295 : AttrNumber attno;
19296 : int natts;
19297 : TupleDesc tupleDesc;
19298 : ObjectAddress address;
19299 : const char *trigger_name;
19300 : Oid defaultPartOid;
19301 : List *partBoundConstraint;
19302 2302 : ParseState *pstate = make_parsestate(NULL);
19303 :
19304 2302 : pstate->p_sourcetext = context->queryString;
19305 :
19306 : /*
19307 : * We must lock the default partition if one exists, because attaching a
19308 : * new partition will change its partition constraint.
19309 : */
19310 : defaultPartOid =
19311 2302 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19312 2302 : if (OidIsValid(defaultPartOid))
19313 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19314 :
19315 2302 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19316 :
19317 : /*
19318 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19319 : * by FKs at this point also.
19320 : */
19321 :
19322 : /*
19323 : * Must be owner of both parent and source table -- parent was checked by
19324 : * ATSimplePermissions call in ATPrepCmd
19325 : */
19326 2296 : ATSimplePermissions(AT_AttachPartition, attachrel,
19327 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
19328 :
19329 : /* A partition can only have one parent */
19330 2290 : if (attachrel->rd_rel->relispartition)
19331 6 : ereport(ERROR,
19332 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19333 : errmsg("\"%s\" is already a partition",
19334 : RelationGetRelationName(attachrel))));
19335 :
19336 2284 : if (OidIsValid(attachrel->rd_rel->reloftype))
19337 6 : ereport(ERROR,
19338 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19339 : errmsg("cannot attach a typed table as partition")));
19340 :
19341 : /*
19342 : * Table being attached should not already be part of inheritance; either
19343 : * as a child table...
19344 : */
19345 2278 : catalog = table_open(InheritsRelationId, AccessShareLock);
19346 2278 : ScanKeyInit(&skey,
19347 : Anum_pg_inherits_inhrelid,
19348 : BTEqualStrategyNumber, F_OIDEQ,
19349 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19350 2278 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19351 : NULL, 1, &skey);
19352 2278 : if (HeapTupleIsValid(systable_getnext(scan)))
19353 6 : ereport(ERROR,
19354 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19355 : errmsg("cannot attach inheritance child as partition")));
19356 2272 : systable_endscan(scan);
19357 :
19358 : /* ...or as a parent table (except the case when it is partitioned) */
19359 2272 : ScanKeyInit(&skey,
19360 : Anum_pg_inherits_inhparent,
19361 : BTEqualStrategyNumber, F_OIDEQ,
19362 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19363 2272 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19364 : 1, &skey);
19365 2272 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19366 256 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19367 6 : ereport(ERROR,
19368 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19369 : errmsg("cannot attach inheritance parent as partition")));
19370 2266 : systable_endscan(scan);
19371 2266 : table_close(catalog, AccessShareLock);
19372 :
19373 : /*
19374 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19375 : * particular, this disallows making a rel a partition of itself.)
19376 : *
19377 : * We do that by checking if rel is a member of the list of attachrel's
19378 : * partitions provided the latter is partitioned at all. We want to avoid
19379 : * having to construct this list again, so we request the strongest lock
19380 : * on all partitions. We need the strongest lock, because we may decide
19381 : * to scan them if we find out that the table being attached (or its leaf
19382 : * partitions) may contain rows that violate the partition constraint. If
19383 : * the table has a constraint that would prevent such rows, which by
19384 : * definition is present in all the partitions, we need not scan the
19385 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19386 : * weaker lock now and the stronger one only when needed.
19387 : */
19388 2266 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19389 : AccessExclusiveLock, NULL);
19390 2266 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19391 12 : ereport(ERROR,
19392 : (errcode(ERRCODE_DUPLICATE_TABLE),
19393 : errmsg("circular inheritance not allowed"),
19394 : errdetail("\"%s\" is already a child of \"%s\".",
19395 : RelationGetRelationName(rel),
19396 : RelationGetRelationName(attachrel))));
19397 :
19398 : /* If the parent is permanent, so must be all of its partitions. */
19399 2254 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19400 2212 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19401 6 : ereport(ERROR,
19402 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19403 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19404 : RelationGetRelationName(rel))));
19405 :
19406 : /* Temp parent cannot have a partition that is itself not a temp */
19407 2248 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19408 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19409 18 : ereport(ERROR,
19410 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19411 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19412 : RelationGetRelationName(rel))));
19413 :
19414 : /* If the parent is temp, it must belong to this session */
19415 2230 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19416 24 : !rel->rd_islocaltemp)
19417 0 : ereport(ERROR,
19418 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19419 : errmsg("cannot attach as partition of temporary relation of another session")));
19420 :
19421 : /* Ditto for the partition */
19422 2230 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19423 24 : !attachrel->rd_islocaltemp)
19424 0 : ereport(ERROR,
19425 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19426 : errmsg("cannot attach temporary relation of another session as partition")));
19427 :
19428 : /*
19429 : * Check if attachrel has any identity columns or any columns that aren't
19430 : * in the parent.
19431 : */
19432 2230 : tupleDesc = RelationGetDescr(attachrel);
19433 2230 : natts = tupleDesc->natts;
19434 7642 : for (attno = 1; attno <= natts; attno++)
19435 : {
19436 5454 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19437 5454 : char *attributeName = NameStr(attribute->attname);
19438 :
19439 : /* Ignore dropped */
19440 5454 : if (attribute->attisdropped)
19441 580 : continue;
19442 :
19443 4874 : if (attribute->attidentity)
19444 24 : ereport(ERROR,
19445 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19446 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19447 : RelationGetRelationName(attachrel), attributeName),
19448 : errdetail("The new partition may not contain an identity column."));
19449 :
19450 : /* Try to find the column in parent (matching on column name) */
19451 4850 : if (!SearchSysCacheExists2(ATTNAME,
19452 : ObjectIdGetDatum(RelationGetRelid(rel)),
19453 : CStringGetDatum(attributeName)))
19454 18 : ereport(ERROR,
19455 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19456 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19457 : RelationGetRelationName(attachrel), attributeName,
19458 : RelationGetRelationName(rel)),
19459 : errdetail("The new partition may contain only the columns present in parent.")));
19460 : }
19461 :
19462 : /*
19463 : * If child_rel has row-level triggers with transition tables, we
19464 : * currently don't allow it to become a partition. See also prohibitions
19465 : * in ATExecAddInherit() and CreateTrigger().
19466 : */
19467 2188 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19468 2188 : if (trigger_name != NULL)
19469 6 : ereport(ERROR,
19470 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19471 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19472 : trigger_name, RelationGetRelationName(attachrel)),
19473 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19474 :
19475 : /*
19476 : * Check that the new partition's bound is valid and does not overlap any
19477 : * of existing partitions of the parent - note that it does not return on
19478 : * error.
19479 : */
19480 2182 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19481 : cmd->bound, pstate);
19482 :
19483 : /* OK to create inheritance. Rest of the checks performed there */
19484 2146 : CreateInheritance(attachrel, rel, true);
19485 :
19486 : /* Update the pg_class entry. */
19487 2044 : StorePartitionBound(attachrel, rel, cmd->bound);
19488 :
19489 : /* Ensure there exists a correct set of indexes in the partition. */
19490 2044 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19491 :
19492 : /* and triggers */
19493 2014 : CloneRowTriggersToPartition(rel, attachrel);
19494 :
19495 : /*
19496 : * Clone foreign key constraints. Callee is responsible for setting up
19497 : * for phase 3 constraint verification.
19498 : */
19499 2008 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19500 :
19501 : /*
19502 : * Generate partition constraint from the partition bound specification.
19503 : * If the parent itself is a partition, make sure to include its
19504 : * constraint as well.
19505 : */
19506 1996 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19507 :
19508 : /*
19509 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19510 : * since it's needed later to construct the constraint expression for
19511 : * validating against the default partition, if any.
19512 : */
19513 1996 : partConstraint = list_concat_copy(partBoundConstraint,
19514 1996 : RelationGetPartitionQual(rel));
19515 :
19516 : /* Skip validation if there are no constraints to validate. */
19517 1996 : if (partConstraint)
19518 : {
19519 : /*
19520 : * Run the partition quals through const-simplification similar to
19521 : * check constraints. We skip canonicalize_qual, though, because
19522 : * partition quals should be in canonical form already.
19523 : */
19524 : partConstraint =
19525 1948 : (List *) eval_const_expressions(NULL,
19526 : (Node *) partConstraint);
19527 :
19528 : /* XXX this sure looks wrong */
19529 1948 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19530 :
19531 : /*
19532 : * Adjust the generated constraint to match this partition's attribute
19533 : * numbers.
19534 : */
19535 1948 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19536 : rel);
19537 :
19538 : /* Validate partition constraints against the table being attached. */
19539 1948 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19540 : false);
19541 : }
19542 :
19543 : /*
19544 : * If we're attaching a partition other than the default partition and a
19545 : * default one exists, then that partition's partition constraint changes,
19546 : * so add an entry to the work queue to validate it, too. (We must not do
19547 : * this when the partition being attached is the default one; we already
19548 : * did it above!)
19549 : */
19550 1996 : if (OidIsValid(defaultPartOid))
19551 : {
19552 : Relation defaultrel;
19553 : List *defPartConstraint;
19554 :
19555 : Assert(!cmd->bound->is_default);
19556 :
19557 : /* we already hold a lock on the default partition */
19558 146 : defaultrel = table_open(defaultPartOid, NoLock);
19559 : defPartConstraint =
19560 146 : get_proposed_default_constraint(partBoundConstraint);
19561 :
19562 : /*
19563 : * Map the Vars in the constraint expression from rel's attnos to
19564 : * defaultrel's.
19565 : */
19566 : defPartConstraint =
19567 146 : map_partition_varattnos(defPartConstraint,
19568 : 1, defaultrel, rel);
19569 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19570 : defPartConstraint, true);
19571 :
19572 : /* keep our lock until commit. */
19573 146 : table_close(defaultrel, NoLock);
19574 : }
19575 :
19576 1996 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19577 :
19578 : /*
19579 : * If the partition we just attached is partitioned itself, invalidate
19580 : * relcache for all descendent partitions too to ensure that their
19581 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19582 : * the beginning of this function.
19583 : */
19584 1996 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19585 : {
19586 : ListCell *l;
19587 :
19588 984 : foreach(l, attachrel_children)
19589 : {
19590 660 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19591 : }
19592 : }
19593 :
19594 : /* keep our lock until commit */
19595 1996 : table_close(attachrel, NoLock);
19596 :
19597 1996 : return address;
19598 : }
19599 :
19600 : /*
19601 : * AttachPartitionEnsureIndexes
19602 : * subroutine for ATExecAttachPartition to create/match indexes
19603 : *
19604 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19605 : * PARTITION: every partition must have an index attached to each index on the
19606 : * partitioned table.
19607 : */
19608 : static void
19609 2044 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19610 : {
19611 : List *idxes;
19612 : List *attachRelIdxs;
19613 : Relation *attachrelIdxRels;
19614 : IndexInfo **attachInfos;
19615 : ListCell *cell;
19616 : MemoryContext cxt;
19617 : MemoryContext oldcxt;
19618 :
19619 2044 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19620 : "AttachPartitionEnsureIndexes",
19621 : ALLOCSET_DEFAULT_SIZES);
19622 2044 : oldcxt = MemoryContextSwitchTo(cxt);
19623 :
19624 2044 : idxes = RelationGetIndexList(rel);
19625 2044 : attachRelIdxs = RelationGetIndexList(attachrel);
19626 2044 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19627 2044 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19628 :
19629 : /* Build arrays of all existing indexes and their IndexInfos */
19630 4452 : foreach_oid(cldIdxId, attachRelIdxs)
19631 : {
19632 364 : int i = foreach_current_index(cldIdxId);
19633 :
19634 364 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19635 364 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19636 : }
19637 :
19638 : /*
19639 : * If we're attaching a foreign table, we must fail if any of the indexes
19640 : * is a constraint index; otherwise, there's nothing to do here. Do this
19641 : * before starting work, to avoid wasting the effort of building a few
19642 : * non-unique indexes before coming across a unique one.
19643 : */
19644 2044 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19645 : {
19646 86 : foreach(cell, idxes)
19647 : {
19648 36 : Oid idx = lfirst_oid(cell);
19649 36 : Relation idxRel = index_open(idx, AccessShareLock);
19650 :
19651 36 : if (idxRel->rd_index->indisunique ||
19652 24 : idxRel->rd_index->indisprimary)
19653 12 : ereport(ERROR,
19654 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19655 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19656 : RelationGetRelationName(attachrel),
19657 : RelationGetRelationName(rel)),
19658 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19659 : RelationGetRelationName(rel))));
19660 24 : index_close(idxRel, AccessShareLock);
19661 : }
19662 :
19663 50 : goto out;
19664 : }
19665 :
19666 : /*
19667 : * For each index on the partitioned table, find a matching one in the
19668 : * partition-to-be; if one is not found, create one.
19669 : */
19670 2402 : foreach(cell, idxes)
19671 : {
19672 438 : Oid idx = lfirst_oid(cell);
19673 438 : Relation idxRel = index_open(idx, AccessShareLock);
19674 : IndexInfo *info;
19675 : AttrMap *attmap;
19676 438 : bool found = false;
19677 : Oid constraintOid;
19678 :
19679 : /*
19680 : * Ignore indexes in the partitioned table other than partitioned
19681 : * indexes.
19682 : */
19683 438 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19684 : {
19685 0 : index_close(idxRel, AccessShareLock);
19686 0 : continue;
19687 : }
19688 :
19689 : /* construct an indexinfo to compare existing indexes against */
19690 438 : info = BuildIndexInfo(idxRel);
19691 438 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19692 : RelationGetDescr(rel),
19693 : false);
19694 438 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19695 :
19696 : /*
19697 : * Scan the list of existing indexes in the partition-to-be, and mark
19698 : * the first matching, valid, unattached one we find, if any, as
19699 : * partition of the parent index. If we find one, we're done.
19700 : */
19701 498 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19702 : {
19703 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19704 262 : Oid cldConstrOid = InvalidOid;
19705 :
19706 : /* does this index have a parent? if so, can't use it */
19707 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
19708 12 : continue;
19709 :
19710 : /* If this index is invalid, can't use it */
19711 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
19712 6 : continue;
19713 :
19714 244 : if (CompareIndexInfo(attachInfos[i], info,
19715 244 : attachrelIdxRels[i]->rd_indcollation,
19716 244 : idxRel->rd_indcollation,
19717 244 : attachrelIdxRels[i]->rd_opfamily,
19718 244 : idxRel->rd_opfamily,
19719 : attmap))
19720 : {
19721 : /*
19722 : * If this index is being created in the parent because of a
19723 : * constraint, then the child needs to have a constraint also,
19724 : * so look for one. If there is no such constraint, this
19725 : * index is no good, so keep looking.
19726 : */
19727 208 : if (OidIsValid(constraintOid))
19728 : {
19729 : cldConstrOid =
19730 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19731 : cldIdxId);
19732 : /* no dice */
19733 110 : if (!OidIsValid(cldConstrOid))
19734 6 : continue;
19735 :
19736 : /* Ensure they're both the same type of constraint */
19737 208 : if (get_constraint_type(constraintOid) !=
19738 104 : get_constraint_type(cldConstrOid))
19739 0 : continue;
19740 : }
19741 :
19742 : /* bingo. */
19743 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
19744 202 : if (OidIsValid(constraintOid))
19745 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19746 : RelationGetRelid(attachrel));
19747 202 : found = true;
19748 :
19749 202 : CommandCounterIncrement();
19750 202 : break;
19751 : }
19752 : }
19753 :
19754 : /*
19755 : * If no suitable index was found in the partition-to-be, create one
19756 : * now. Note that if this is a PK, not-null constraints must already
19757 : * exist.
19758 : */
19759 438 : if (!found)
19760 : {
19761 : IndexStmt *stmt;
19762 : Oid conOid;
19763 :
19764 236 : stmt = generateClonedIndexStmt(NULL,
19765 : idxRel, attmap,
19766 : &conOid);
19767 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19768 : RelationGetRelid(idxRel),
19769 : conOid,
19770 : -1,
19771 : true, false, false, false, false);
19772 : }
19773 :
19774 420 : index_close(idxRel, AccessShareLock);
19775 : }
19776 :
19777 2014 : out:
19778 : /* Clean up. */
19779 2366 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19780 352 : index_close(attachrelIdxRels[i], AccessShareLock);
19781 2014 : MemoryContextSwitchTo(oldcxt);
19782 2014 : MemoryContextDelete(cxt);
19783 2014 : }
19784 :
19785 : /*
19786 : * CloneRowTriggersToPartition
19787 : * subroutine for ATExecAttachPartition/DefineRelation to create row
19788 : * triggers on partitions
19789 : */
19790 : static void
19791 2434 : CloneRowTriggersToPartition(Relation parent, Relation partition)
19792 : {
19793 : Relation pg_trigger;
19794 : ScanKeyData key;
19795 : SysScanDesc scan;
19796 : HeapTuple tuple;
19797 : MemoryContext perTupCxt;
19798 :
19799 2434 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19800 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19801 2434 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19802 2434 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19803 : true, NULL, 1, &key);
19804 :
19805 2434 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19806 : "clone trig", ALLOCSET_SMALL_SIZES);
19807 :
19808 4062 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19809 : {
19810 1634 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19811 : CreateTrigStmt *trigStmt;
19812 1634 : Node *qual = NULL;
19813 : Datum value;
19814 : bool isnull;
19815 1634 : List *cols = NIL;
19816 1634 : List *trigargs = NIL;
19817 : MemoryContext oldcxt;
19818 :
19819 : /*
19820 : * Ignore statement-level triggers; those are not cloned.
19821 : */
19822 1634 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19823 1478 : continue;
19824 :
19825 : /*
19826 : * Don't clone internal triggers, because the constraint cloning code
19827 : * will.
19828 : */
19829 1634 : if (trigForm->tgisinternal)
19830 1478 : continue;
19831 :
19832 : /*
19833 : * Complain if we find an unexpected trigger type.
19834 : */
19835 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19836 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
19837 0 : elog(ERROR, "unexpected trigger \"%s\" found",
19838 : NameStr(trigForm->tgname));
19839 :
19840 : /* Use short-lived context for CREATE TRIGGER */
19841 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
19842 :
19843 : /*
19844 : * If there is a WHEN clause, generate a 'cooked' version of it that's
19845 : * appropriate for the partition.
19846 : */
19847 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19848 : RelationGetDescr(pg_trigger), &isnull);
19849 156 : if (!isnull)
19850 : {
19851 6 : qual = stringToNode(TextDatumGetCString(value));
19852 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19853 : partition, parent);
19854 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19855 : partition, parent);
19856 : }
19857 :
19858 : /*
19859 : * If there is a column list, transform it to a list of column names.
19860 : * Note we don't need to map this list in any way ...
19861 : */
19862 156 : if (trigForm->tgattr.dim1 > 0)
19863 : {
19864 : int i;
19865 :
19866 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
19867 : {
19868 : Form_pg_attribute col;
19869 :
19870 6 : col = TupleDescAttr(parent->rd_att,
19871 6 : trigForm->tgattr.values[i] - 1);
19872 6 : cols = lappend(cols,
19873 6 : makeString(pstrdup(NameStr(col->attname))));
19874 : }
19875 : }
19876 :
19877 : /* Reconstruct trigger arguments list. */
19878 156 : if (trigForm->tgnargs > 0)
19879 : {
19880 : char *p;
19881 :
19882 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19883 : RelationGetDescr(pg_trigger), &isnull);
19884 12 : if (isnull)
19885 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19886 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
19887 :
19888 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19889 :
19890 36 : for (int i = 0; i < trigForm->tgnargs; i++)
19891 : {
19892 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
19893 24 : p += strlen(p) + 1;
19894 : }
19895 : }
19896 :
19897 156 : trigStmt = makeNode(CreateTrigStmt);
19898 156 : trigStmt->replace = false;
19899 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19900 156 : trigStmt->trigname = NameStr(trigForm->tgname);
19901 156 : trigStmt->relation = NULL;
19902 156 : trigStmt->funcname = NULL; /* passed separately */
19903 156 : trigStmt->args = trigargs;
19904 156 : trigStmt->row = true;
19905 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19906 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19907 156 : trigStmt->columns = cols;
19908 156 : trigStmt->whenClause = NULL; /* passed separately */
19909 156 : trigStmt->transitionRels = NIL; /* not supported at present */
19910 156 : trigStmt->deferrable = trigForm->tgdeferrable;
19911 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
19912 156 : trigStmt->constrrel = NULL; /* passed separately */
19913 :
19914 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19915 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19916 : trigForm->tgfoid, trigForm->oid, qual,
19917 156 : false, true, trigForm->tgenabled);
19918 :
19919 150 : MemoryContextSwitchTo(oldcxt);
19920 150 : MemoryContextReset(perTupCxt);
19921 : }
19922 :
19923 2428 : MemoryContextDelete(perTupCxt);
19924 :
19925 2428 : systable_endscan(scan);
19926 2428 : table_close(pg_trigger, RowExclusiveLock);
19927 2428 : }
19928 :
19929 : /*
19930 : * ALTER TABLE DETACH PARTITION
19931 : *
19932 : * Return the address of the relation that is no longer a partition of rel.
19933 : *
19934 : * If concurrent mode is requested, we run in two transactions. A side-
19935 : * effect is that this command cannot run in a multi-part ALTER TABLE.
19936 : * Currently, that's enforced by the grammar.
19937 : *
19938 : * The strategy for concurrency is to first modify the partition's
19939 : * pg_inherit catalog row to make it visible to everyone that the
19940 : * partition is detached, lock the partition against writes, and commit
19941 : * the transaction; anyone who requests the partition descriptor from
19942 : * that point onwards has to ignore such a partition. In a second
19943 : * transaction, we wait until all transactions that could have seen the
19944 : * partition as attached are gone, then we remove the rest of partition
19945 : * metadata (pg_inherits and pg_class.relpartbounds).
19946 : */
19947 : static ObjectAddress
19948 558 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19949 : RangeVar *name, bool concurrent)
19950 : {
19951 : Relation partRel;
19952 : ObjectAddress address;
19953 : Oid defaultPartOid;
19954 :
19955 : /*
19956 : * We must lock the default partition, because detaching this partition
19957 : * will change its partition constraint.
19958 : */
19959 : defaultPartOid =
19960 558 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19961 558 : if (OidIsValid(defaultPartOid))
19962 : {
19963 : /*
19964 : * Concurrent detaching when a default partition exists is not
19965 : * supported. The main problem is that the default partition
19966 : * constraint would change. And there's a definitional problem: what
19967 : * should happen to the tuples that are being inserted that belong to
19968 : * the partition being detached? Putting them on the partition being
19969 : * detached would be wrong, since they'd become "lost" after the
19970 : * detaching completes but we cannot put them in the default partition
19971 : * either until we alter its partition constraint.
19972 : *
19973 : * I think we could solve this problem if we effected the constraint
19974 : * change before committing the first transaction. But the lock would
19975 : * have to remain AEL and it would cause concurrent query planning to
19976 : * be blocked, so changing it that way would be even worse.
19977 : */
19978 106 : if (concurrent)
19979 12 : ereport(ERROR,
19980 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19981 : errmsg("cannot detach partitions concurrently when a default partition exists")));
19982 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19983 : }
19984 :
19985 : /*
19986 : * In concurrent mode, the partition is locked with share-update-exclusive
19987 : * in the first transaction. This allows concurrent transactions to be
19988 : * doing DML to the partition.
19989 : */
19990 546 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19991 : AccessExclusiveLock);
19992 :
19993 : /*
19994 : * Check inheritance conditions and either delete the pg_inherits row (in
19995 : * non-concurrent mode) or just set the inhdetachpending flag.
19996 : */
19997 534 : if (!concurrent)
19998 388 : RemoveInheritance(partRel, rel, false);
19999 : else
20000 146 : MarkInheritDetached(partRel, rel);
20001 :
20002 : /*
20003 : * Ensure that foreign keys still hold after this detach. This keeps
20004 : * locks on the referencing tables, which prevents concurrent transactions
20005 : * from adding rows that we wouldn't see. For this to work in concurrent
20006 : * mode, it is critical that the partition appears as no longer attached
20007 : * for the RI queries as soon as the first transaction commits.
20008 : */
20009 514 : ATDetachCheckNoForeignKeyRefs(partRel);
20010 :
20011 : /*
20012 : * Concurrent mode has to work harder; first we add a new constraint to
20013 : * the partition that matches the partition constraint. Then we close our
20014 : * existing transaction, and in a new one wait for all processes to catch
20015 : * up on the catalog updates we've done so far; at that point we can
20016 : * complete the operation.
20017 : */
20018 480 : if (concurrent)
20019 : {
20020 : Oid partrelid,
20021 : parentrelid;
20022 : LOCKTAG tag;
20023 : char *parentrelname;
20024 : char *partrelname;
20025 :
20026 : /*
20027 : * Add a new constraint to the partition being detached, which
20028 : * supplants the partition constraint (unless there is one already).
20029 : */
20030 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
20031 :
20032 : /*
20033 : * We're almost done now; the only traces that remain are the
20034 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20035 : * remove those, we need to wait until all transactions that know that
20036 : * this is a partition are gone.
20037 : */
20038 :
20039 : /*
20040 : * Remember relation OIDs to re-acquire them later; and relation names
20041 : * too, for error messages if something is dropped in between.
20042 : */
20043 140 : partrelid = RelationGetRelid(partRel);
20044 140 : parentrelid = RelationGetRelid(rel);
20045 140 : parentrelname = MemoryContextStrdup(PortalContext,
20046 140 : RelationGetRelationName(rel));
20047 140 : partrelname = MemoryContextStrdup(PortalContext,
20048 140 : RelationGetRelationName(partRel));
20049 :
20050 : /* Invalidate relcache entries for the parent -- must be before close */
20051 140 : CacheInvalidateRelcache(rel);
20052 :
20053 140 : table_close(partRel, NoLock);
20054 140 : table_close(rel, NoLock);
20055 140 : tab->rel = NULL;
20056 :
20057 : /* Make updated catalog entry visible */
20058 140 : PopActiveSnapshot();
20059 140 : CommitTransactionCommand();
20060 :
20061 140 : StartTransactionCommand();
20062 :
20063 : /*
20064 : * Now wait. This ensures that all queries that were planned
20065 : * including the partition are finished before we remove the rest of
20066 : * catalog entries. We don't need or indeed want to acquire this
20067 : * lock, though -- that would block later queries.
20068 : *
20069 : * We don't need to concern ourselves with waiting for a lock on the
20070 : * partition itself, since we will acquire AccessExclusiveLock below.
20071 : */
20072 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20073 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20074 :
20075 : /*
20076 : * Now acquire locks in both relations again. Note they may have been
20077 : * removed in the meantime, so care is required.
20078 : */
20079 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20080 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20081 :
20082 : /* If the relations aren't there, something bad happened; bail out */
20083 90 : if (rel == NULL)
20084 : {
20085 0 : if (partRel != NULL) /* shouldn't happen */
20086 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20087 : partrelname);
20088 0 : ereport(ERROR,
20089 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20090 : errmsg("partitioned table \"%s\" was removed concurrently",
20091 : parentrelname)));
20092 : }
20093 90 : if (partRel == NULL)
20094 0 : ereport(ERROR,
20095 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20096 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
20097 :
20098 90 : tab->rel = rel;
20099 : }
20100 :
20101 : /* Do the final part of detaching */
20102 430 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20103 :
20104 428 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20105 :
20106 : /* keep our lock until commit */
20107 428 : table_close(partRel, NoLock);
20108 :
20109 428 : return address;
20110 : }
20111 :
20112 : /*
20113 : * Second part of ALTER TABLE .. DETACH.
20114 : *
20115 : * This is separate so that it can be run independently when the second
20116 : * transaction of the concurrent algorithm fails (crash or abort).
20117 : */
20118 : static void
20119 444 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20120 : Oid defaultPartOid)
20121 : {
20122 : Relation classRel;
20123 : List *fks;
20124 : ListCell *cell;
20125 : List *indexes;
20126 : Datum new_val[Natts_pg_class];
20127 : bool new_null[Natts_pg_class],
20128 : new_repl[Natts_pg_class];
20129 : HeapTuple tuple,
20130 : newtuple;
20131 444 : Relation trigrel = NULL;
20132 444 : List *fkoids = NIL;
20133 :
20134 444 : if (concurrent)
20135 : {
20136 : /*
20137 : * We can remove the pg_inherits row now. (In the non-concurrent case,
20138 : * this was already done).
20139 : */
20140 104 : RemoveInheritance(partRel, rel, true);
20141 : }
20142 :
20143 : /* Drop any triggers that were cloned on creation/attach. */
20144 444 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20145 :
20146 : /*
20147 : * Detach any foreign keys that are inherited. This includes creating
20148 : * additional action triggers.
20149 : */
20150 444 : fks = copyObject(RelationGetFKeyList(partRel));
20151 444 : if (fks != NIL)
20152 72 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20153 :
20154 : /*
20155 : * It's possible that the partition being detached has a foreign key that
20156 : * references a partitioned table. When that happens, there are multiple
20157 : * pg_constraint rows for the partition: one points to the partitioned
20158 : * table itself, while the others point to each of its partitions. Only
20159 : * the topmost one is to be considered here; the child constraints must be
20160 : * left alone, because conceptually those aren't coming from our parent
20161 : * partitioned table, but from this partition itself.
20162 : *
20163 : * We implement this by collecting all the constraint OIDs in a first scan
20164 : * of the FK array, and skipping in the loop below those constraints whose
20165 : * parents are listed here.
20166 : */
20167 1044 : foreach_node(ForeignKeyCacheInfo, fk, fks)
20168 156 : fkoids = lappend_oid(fkoids, fk->conoid);
20169 :
20170 600 : foreach(cell, fks)
20171 : {
20172 156 : ForeignKeyCacheInfo *fk = lfirst(cell);
20173 : HeapTuple contup;
20174 : Form_pg_constraint conform;
20175 : Oid insertTriggerOid,
20176 : updateTriggerOid;
20177 :
20178 156 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20179 156 : if (!HeapTupleIsValid(contup))
20180 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20181 156 : conform = (Form_pg_constraint) GETSTRUCT(contup);
20182 :
20183 : /*
20184 : * Consider only inherited foreign keys, and only if their parents
20185 : * aren't in the list.
20186 : */
20187 156 : if (conform->contype != CONSTRAINT_FOREIGN ||
20188 288 : !OidIsValid(conform->conparentid) ||
20189 132 : list_member_oid(fkoids, conform->conparentid))
20190 : {
20191 66 : ReleaseSysCache(contup);
20192 66 : continue;
20193 : }
20194 :
20195 : /*
20196 : * The constraint on this table must be marked no longer a child of
20197 : * the parent's constraint, as do its check triggers.
20198 : */
20199 90 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20200 :
20201 : /*
20202 : * Also, look up the partition's "check" triggers corresponding to the
20203 : * constraint being detached and detach them from the parent triggers.
20204 : */
20205 90 : GetForeignKeyCheckTriggers(trigrel,
20206 : fk->conoid, fk->confrelid, fk->conrelid,
20207 : &insertTriggerOid, &updateTriggerOid);
20208 : Assert(OidIsValid(insertTriggerOid));
20209 90 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20210 : RelationGetRelid(partRel));
20211 : Assert(OidIsValid(updateTriggerOid));
20212 90 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20213 : RelationGetRelid(partRel));
20214 :
20215 : /*
20216 : * Lastly, create the action triggers on the referenced table, using
20217 : * addFkRecurseReferenced, which requires some elaborate setup (so put
20218 : * it in a separate block). While at it, if the table is partitioned,
20219 : * that function will recurse to create the pg_constraint rows and
20220 : * action triggers for each partition.
20221 : *
20222 : * Note there's no need to do addFkConstraint() here, because the
20223 : * pg_constraint row already exists.
20224 : */
20225 : {
20226 : Constraint *fkconstraint;
20227 : int numfks;
20228 : AttrNumber conkey[INDEX_MAX_KEYS];
20229 : AttrNumber confkey[INDEX_MAX_KEYS];
20230 : Oid conpfeqop[INDEX_MAX_KEYS];
20231 : Oid conppeqop[INDEX_MAX_KEYS];
20232 : Oid conffeqop[INDEX_MAX_KEYS];
20233 : int numfkdelsetcols;
20234 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
20235 : Relation refdRel;
20236 :
20237 90 : DeconstructFkConstraintRow(contup,
20238 : &numfks,
20239 : conkey,
20240 : confkey,
20241 : conpfeqop,
20242 : conppeqop,
20243 : conffeqop,
20244 : &numfkdelsetcols,
20245 : confdelsetcols);
20246 :
20247 : /* Create a synthetic node we'll use throughout */
20248 90 : fkconstraint = makeNode(Constraint);
20249 90 : fkconstraint->contype = CONSTRAINT_FOREIGN;
20250 90 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
20251 90 : fkconstraint->deferrable = conform->condeferrable;
20252 90 : fkconstraint->initdeferred = conform->condeferred;
20253 90 : fkconstraint->skip_validation = true;
20254 90 : fkconstraint->initially_valid = true;
20255 : /* a few irrelevant fields omitted here */
20256 90 : fkconstraint->pktable = NULL;
20257 90 : fkconstraint->fk_attrs = NIL;
20258 90 : fkconstraint->pk_attrs = NIL;
20259 90 : fkconstraint->fk_matchtype = conform->confmatchtype;
20260 90 : fkconstraint->fk_upd_action = conform->confupdtype;
20261 90 : fkconstraint->fk_del_action = conform->confdeltype;
20262 90 : fkconstraint->fk_del_set_cols = NIL;
20263 90 : fkconstraint->old_conpfeqop = NIL;
20264 90 : fkconstraint->old_pktable_oid = InvalidOid;
20265 90 : fkconstraint->location = -1;
20266 :
20267 : /* set up colnames, used to generate the constraint name */
20268 228 : for (int i = 0; i < numfks; i++)
20269 : {
20270 : Form_pg_attribute att;
20271 :
20272 138 : att = TupleDescAttr(RelationGetDescr(partRel),
20273 138 : conkey[i] - 1);
20274 :
20275 138 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
20276 138 : makeString(NameStr(att->attname)));
20277 : }
20278 :
20279 90 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
20280 :
20281 90 : addFkRecurseReferenced(fkconstraint, partRel,
20282 : refdRel,
20283 : conform->conindid,
20284 : fk->conoid,
20285 : numfks,
20286 : confkey,
20287 : conkey,
20288 : conpfeqop,
20289 : conppeqop,
20290 : conffeqop,
20291 : numfkdelsetcols,
20292 : confdelsetcols,
20293 : true,
20294 : InvalidOid, InvalidOid,
20295 90 : conform->conperiod);
20296 90 : table_close(refdRel, NoLock); /* keep lock till end of xact */
20297 : }
20298 :
20299 90 : ReleaseSysCache(contup);
20300 : }
20301 444 : list_free_deep(fks);
20302 444 : if (trigrel)
20303 72 : table_close(trigrel, RowExclusiveLock);
20304 :
20305 : /*
20306 : * Any sub-constraints that are in the referenced-side of a larger
20307 : * constraint have to be removed. This partition is no longer part of the
20308 : * key space of the constraint.
20309 : */
20310 480 : foreach(cell, GetParentedForeignKeyRefs(partRel))
20311 : {
20312 38 : Oid constrOid = lfirst_oid(cell);
20313 : ObjectAddress constraint;
20314 :
20315 38 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20316 38 : deleteDependencyRecordsForClass(ConstraintRelationId,
20317 : constrOid,
20318 : ConstraintRelationId,
20319 : DEPENDENCY_INTERNAL);
20320 38 : CommandCounterIncrement();
20321 :
20322 38 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20323 38 : performDeletion(&constraint, DROP_RESTRICT, 0);
20324 : }
20325 :
20326 : /* Now we can detach indexes */
20327 442 : indexes = RelationGetIndexList(partRel);
20328 624 : foreach(cell, indexes)
20329 : {
20330 182 : Oid idxid = lfirst_oid(cell);
20331 : Oid parentidx;
20332 : Relation idx;
20333 : Oid constrOid;
20334 : Oid parentConstrOid;
20335 :
20336 182 : if (!has_superclass(idxid))
20337 12 : continue;
20338 :
20339 170 : parentidx = get_partition_parent(idxid, false);
20340 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20341 :
20342 170 : idx = index_open(idxid, AccessExclusiveLock);
20343 170 : IndexSetParentIndex(idx, InvalidOid);
20344 :
20345 : /*
20346 : * If there's a constraint associated with the index, detach it too.
20347 : * Careful: it is possible for a constraint index in a partition to be
20348 : * the child of a non-constraint index, so verify whether the parent
20349 : * index does actually have a constraint.
20350 : */
20351 170 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20352 : idxid);
20353 170 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
20354 : parentidx);
20355 170 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20356 72 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20357 :
20358 170 : index_close(idx, NoLock);
20359 : }
20360 :
20361 : /* Update pg_class tuple */
20362 442 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20363 442 : tuple = SearchSysCacheCopy1(RELOID,
20364 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20365 442 : if (!HeapTupleIsValid(tuple))
20366 0 : elog(ERROR, "cache lookup failed for relation %u",
20367 : RelationGetRelid(partRel));
20368 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20369 :
20370 : /* Clear relpartbound and reset relispartition */
20371 442 : memset(new_val, 0, sizeof(new_val));
20372 442 : memset(new_null, false, sizeof(new_null));
20373 442 : memset(new_repl, false, sizeof(new_repl));
20374 442 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20375 442 : new_null[Anum_pg_class_relpartbound - 1] = true;
20376 442 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20377 442 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20378 : new_val, new_null, new_repl);
20379 :
20380 442 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20381 442 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20382 442 : heap_freetuple(newtuple);
20383 442 : table_close(classRel, RowExclusiveLock);
20384 :
20385 : /*
20386 : * Drop identity property from all identity columns of partition.
20387 : */
20388 1258 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20389 : {
20390 816 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20391 :
20392 816 : if (!attr->attisdropped && attr->attidentity)
20393 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20394 : AccessExclusiveLock, true, true);
20395 : }
20396 :
20397 442 : if (OidIsValid(defaultPartOid))
20398 : {
20399 : /*
20400 : * If the relation being detached is the default partition itself,
20401 : * remove it from the parent's pg_partitioned_table entry.
20402 : *
20403 : * If not, we must invalidate default partition's relcache entry, as
20404 : * in StorePartitionBound: its partition constraint depends on every
20405 : * other partition's partition constraint.
20406 : */
20407 46 : if (RelationGetRelid(partRel) == defaultPartOid)
20408 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20409 : else
20410 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20411 : }
20412 :
20413 : /*
20414 : * Invalidate the parent's relcache so that the partition is no longer
20415 : * included in its partition descriptor.
20416 : */
20417 442 : CacheInvalidateRelcache(rel);
20418 :
20419 : /*
20420 : * If the partition we just detached is partitioned itself, invalidate
20421 : * relcache for all descendent partitions too to ensure that their
20422 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20423 : * doing so, using the same lockmode as what partRel has been locked with
20424 : * by the caller.
20425 : */
20426 442 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20427 : {
20428 : List *children;
20429 :
20430 56 : children = find_all_inheritors(RelationGetRelid(partRel),
20431 : AccessExclusiveLock, NULL);
20432 180 : foreach(cell, children)
20433 : {
20434 124 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20435 : }
20436 : }
20437 442 : }
20438 :
20439 : /*
20440 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20441 : *
20442 : * To use when a DETACH PARTITION command previously did not run to
20443 : * completion; this completes the detaching process.
20444 : */
20445 : static ObjectAddress
20446 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20447 : {
20448 : Relation partRel;
20449 : ObjectAddress address;
20450 14 : Snapshot snap = GetActiveSnapshot();
20451 :
20452 14 : partRel = table_openrv(name, AccessExclusiveLock);
20453 :
20454 : /*
20455 : * Wait until existing snapshots are gone. This is important if the
20456 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20457 : * user could immediately run DETACH FINALIZE without actually waiting for
20458 : * existing transactions. We must not complete the detach action until
20459 : * all such queries are complete (otherwise we would present them with an
20460 : * inconsistent view of catalogs).
20461 : */
20462 14 : WaitForOlderSnapshots(snap->xmin, false);
20463 :
20464 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20465 :
20466 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20467 :
20468 14 : table_close(partRel, NoLock);
20469 :
20470 14 : return address;
20471 : }
20472 :
20473 : /*
20474 : * DetachAddConstraintIfNeeded
20475 : * Subroutine for ATExecDetachPartition. Create a constraint that
20476 : * takes the place of the partition constraint, but avoid creating
20477 : * a dupe if a constraint already exists which implies the needed
20478 : * constraint.
20479 : */
20480 : static void
20481 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20482 : {
20483 : List *constraintExpr;
20484 :
20485 140 : constraintExpr = RelationGetPartitionQual(partRel);
20486 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20487 :
20488 : /*
20489 : * Avoid adding a new constraint if the needed constraint is implied by an
20490 : * existing constraint
20491 : */
20492 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20493 : {
20494 : AlteredTableInfo *tab;
20495 : Constraint *n;
20496 :
20497 134 : tab = ATGetQueueEntry(wqueue, partRel);
20498 :
20499 : /* Add constraint on partition, equivalent to the partition constraint */
20500 134 : n = makeNode(Constraint);
20501 134 : n->contype = CONSTR_CHECK;
20502 134 : n->conname = NULL;
20503 134 : n->location = -1;
20504 134 : n->is_no_inherit = false;
20505 134 : n->raw_expr = NULL;
20506 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20507 134 : n->is_enforced = true;
20508 134 : n->initially_valid = true;
20509 134 : n->skip_validation = true;
20510 : /* It's a re-add, since it nominally already exists */
20511 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20512 : true, false, true, ShareUpdateExclusiveLock);
20513 : }
20514 140 : }
20515 :
20516 : /*
20517 : * DropClonedTriggersFromPartition
20518 : * subroutine for ATExecDetachPartition to remove any triggers that were
20519 : * cloned to the partition when it was created-as-partition or attached.
20520 : * This undoes what CloneRowTriggersToPartition did.
20521 : */
20522 : static void
20523 444 : DropClonedTriggersFromPartition(Oid partitionId)
20524 : {
20525 : ScanKeyData skey;
20526 : SysScanDesc scan;
20527 : HeapTuple trigtup;
20528 : Relation tgrel;
20529 : ObjectAddresses *objects;
20530 :
20531 444 : objects = new_object_addresses();
20532 :
20533 : /*
20534 : * Scan pg_trigger to search for all triggers on this rel.
20535 : */
20536 444 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20537 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20538 444 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20539 444 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20540 : true, NULL, 1, &skey);
20541 766 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20542 : {
20543 322 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20544 : ObjectAddress trig;
20545 :
20546 : /* Ignore triggers that weren't cloned */
20547 322 : if (!OidIsValid(pg_trigger->tgparentid))
20548 304 : continue;
20549 :
20550 : /*
20551 : * Ignore internal triggers that are implementation objects of foreign
20552 : * keys, because these will be detached when the foreign keys
20553 : * themselves are.
20554 : */
20555 274 : if (OidIsValid(pg_trigger->tgconstrrelid))
20556 256 : continue;
20557 :
20558 : /*
20559 : * This is ugly, but necessary: remove the dependency markings on the
20560 : * trigger so that it can be removed.
20561 : */
20562 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20563 : TriggerRelationId,
20564 : DEPENDENCY_PARTITION_PRI);
20565 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20566 : RelationRelationId,
20567 : DEPENDENCY_PARTITION_SEC);
20568 :
20569 : /* remember this trigger to remove it below */
20570 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20571 18 : add_exact_object_address(&trig, objects);
20572 : }
20573 :
20574 : /* make the dependency removal visible to the deletion below */
20575 444 : CommandCounterIncrement();
20576 444 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20577 :
20578 : /* done */
20579 444 : free_object_addresses(objects);
20580 444 : systable_endscan(scan);
20581 444 : table_close(tgrel, RowExclusiveLock);
20582 444 : }
20583 :
20584 : /*
20585 : * Before acquiring lock on an index, acquire the same lock on the owning
20586 : * table.
20587 : */
20588 : struct AttachIndexCallbackState
20589 : {
20590 : Oid partitionOid;
20591 : Oid parentTblOid;
20592 : bool lockedParentTbl;
20593 : };
20594 :
20595 : static void
20596 404 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20597 : void *arg)
20598 : {
20599 : struct AttachIndexCallbackState *state;
20600 : Form_pg_class classform;
20601 : HeapTuple tuple;
20602 :
20603 404 : state = (struct AttachIndexCallbackState *) arg;
20604 :
20605 404 : if (!state->lockedParentTbl)
20606 : {
20607 392 : LockRelationOid(state->parentTblOid, AccessShareLock);
20608 392 : state->lockedParentTbl = true;
20609 : }
20610 :
20611 : /*
20612 : * If we previously locked some other heap, and the name we're looking up
20613 : * no longer refers to an index on that relation, release the now-useless
20614 : * lock. XXX maybe we should do *after* we verify whether the index does
20615 : * not actually belong to the same relation ...
20616 : */
20617 404 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20618 : {
20619 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20620 0 : state->partitionOid = InvalidOid;
20621 : }
20622 :
20623 : /* Didn't find a relation, so no need for locking or permission checks. */
20624 404 : if (!OidIsValid(relOid))
20625 6 : return;
20626 :
20627 398 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20628 398 : if (!HeapTupleIsValid(tuple))
20629 0 : return; /* concurrently dropped, so nothing to do */
20630 398 : classform = (Form_pg_class) GETSTRUCT(tuple);
20631 398 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20632 310 : classform->relkind != RELKIND_INDEX)
20633 6 : ereport(ERROR,
20634 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20635 : errmsg("\"%s\" is not an index", rv->relname)));
20636 392 : ReleaseSysCache(tuple);
20637 :
20638 : /*
20639 : * Since we need only examine the heap's tupledesc, an access share lock
20640 : * on it (preventing any DDL) is sufficient.
20641 : */
20642 392 : state->partitionOid = IndexGetRelation(relOid, false);
20643 392 : LockRelationOid(state->partitionOid, AccessShareLock);
20644 : }
20645 :
20646 : /*
20647 : * ALTER INDEX i1 ATTACH PARTITION i2
20648 : */
20649 : static ObjectAddress
20650 392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20651 : {
20652 : Relation partIdx;
20653 : Relation partTbl;
20654 : Relation parentTbl;
20655 : ObjectAddress address;
20656 : Oid partIdxId;
20657 : Oid currParent;
20658 : struct AttachIndexCallbackState state;
20659 :
20660 : /*
20661 : * We need to obtain lock on the index 'name' to modify it, but we also
20662 : * need to read its owning table's tuple descriptor -- so we need to lock
20663 : * both. To avoid deadlocks, obtain lock on the table before doing so on
20664 : * the index. Furthermore, we need to examine the parent table of the
20665 : * partition, so lock that one too.
20666 : */
20667 392 : state.partitionOid = InvalidOid;
20668 392 : state.parentTblOid = parentIdx->rd_index->indrelid;
20669 392 : state.lockedParentTbl = false;
20670 : partIdxId =
20671 392 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20672 : RangeVarCallbackForAttachIndex,
20673 : &state);
20674 : /* Not there? */
20675 380 : if (!OidIsValid(partIdxId))
20676 0 : ereport(ERROR,
20677 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20678 : errmsg("index \"%s\" does not exist", name->relname)));
20679 :
20680 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20681 380 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
20682 :
20683 : /* we already hold locks on both tables, so this is safe: */
20684 380 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20685 380 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20686 :
20687 380 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20688 :
20689 : /* Silently do nothing if already in the right state */
20690 760 : currParent = partIdx->rd_rel->relispartition ?
20691 380 : get_partition_parent(partIdxId, false) : InvalidOid;
20692 380 : if (currParent != RelationGetRelid(parentIdx))
20693 : {
20694 : IndexInfo *childInfo;
20695 : IndexInfo *parentInfo;
20696 : AttrMap *attmap;
20697 : bool found;
20698 : int i;
20699 : PartitionDesc partDesc;
20700 : Oid constraintOid,
20701 356 : cldConstrId = InvalidOid;
20702 :
20703 : /*
20704 : * If this partition already has an index attached, refuse the
20705 : * operation.
20706 : */
20707 356 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20708 :
20709 350 : if (OidIsValid(currParent))
20710 0 : ereport(ERROR,
20711 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20712 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20713 : RelationGetRelationName(partIdx),
20714 : RelationGetRelationName(parentIdx)),
20715 : errdetail("Index \"%s\" is already attached to another index.",
20716 : RelationGetRelationName(partIdx))));
20717 :
20718 : /* Make sure it indexes a partition of the other index's table */
20719 350 : partDesc = RelationGetPartitionDesc(parentTbl, true);
20720 350 : found = false;
20721 552 : for (i = 0; i < partDesc->nparts; i++)
20722 : {
20723 546 : if (partDesc->oids[i] == state.partitionOid)
20724 : {
20725 344 : found = true;
20726 344 : break;
20727 : }
20728 : }
20729 350 : if (!found)
20730 6 : ereport(ERROR,
20731 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20732 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20733 : RelationGetRelationName(partIdx),
20734 : RelationGetRelationName(parentIdx)),
20735 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20736 : RelationGetRelationName(partIdx),
20737 : RelationGetRelationName(parentTbl))));
20738 :
20739 : /* Ensure the indexes are compatible */
20740 344 : childInfo = BuildIndexInfo(partIdx);
20741 344 : parentInfo = BuildIndexInfo(parentIdx);
20742 344 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20743 : RelationGetDescr(parentTbl),
20744 : false);
20745 344 : if (!CompareIndexInfo(childInfo, parentInfo,
20746 344 : partIdx->rd_indcollation,
20747 344 : parentIdx->rd_indcollation,
20748 344 : partIdx->rd_opfamily,
20749 344 : parentIdx->rd_opfamily,
20750 : attmap))
20751 42 : ereport(ERROR,
20752 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20753 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20754 : RelationGetRelationName(partIdx),
20755 : RelationGetRelationName(parentIdx)),
20756 : errdetail("The index definitions do not match.")));
20757 :
20758 : /*
20759 : * If there is a constraint in the parent, make sure there is one in
20760 : * the child too.
20761 : */
20762 302 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20763 : RelationGetRelid(parentIdx));
20764 :
20765 302 : if (OidIsValid(constraintOid))
20766 : {
20767 122 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20768 : partIdxId);
20769 122 : if (!OidIsValid(cldConstrId))
20770 6 : ereport(ERROR,
20771 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20772 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20773 : RelationGetRelationName(partIdx),
20774 : RelationGetRelationName(parentIdx)),
20775 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20776 : RelationGetRelationName(parentIdx),
20777 : RelationGetRelationName(parentTbl),
20778 : RelationGetRelationName(partIdx))));
20779 : }
20780 :
20781 : /*
20782 : * If it's a primary key, make sure the columns in the partition are
20783 : * NOT NULL.
20784 : */
20785 296 : if (parentIdx->rd_index->indisprimary)
20786 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
20787 :
20788 : /* All good -- do it */
20789 296 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20790 296 : if (OidIsValid(constraintOid))
20791 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
20792 : RelationGetRelid(partTbl));
20793 :
20794 296 : free_attrmap(attmap);
20795 :
20796 296 : validatePartitionedIndex(parentIdx, parentTbl);
20797 : }
20798 :
20799 320 : relation_close(parentTbl, AccessShareLock);
20800 : /* keep these locks till commit */
20801 320 : relation_close(partTbl, NoLock);
20802 320 : relation_close(partIdx, NoLock);
20803 :
20804 320 : return address;
20805 : }
20806 :
20807 : /*
20808 : * Verify whether the given partition already contains an index attached
20809 : * to the given partitioned index. If so, raise an error.
20810 : */
20811 : static void
20812 356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20813 : {
20814 : Oid existingIdx;
20815 :
20816 356 : existingIdx = index_get_partition(partitionTbl,
20817 : RelationGetRelid(parentIdx));
20818 356 : if (OidIsValid(existingIdx))
20819 6 : ereport(ERROR,
20820 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20821 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20822 : RelationGetRelationName(partIdx),
20823 : RelationGetRelationName(parentIdx)),
20824 : errdetail("Another index is already attached for partition \"%s\".",
20825 : RelationGetRelationName(partitionTbl))));
20826 350 : }
20827 :
20828 : /*
20829 : * Verify whether the set of attached partition indexes to a parent index on
20830 : * a partitioned table is complete. If it is, mark the parent index valid.
20831 : *
20832 : * This should be called each time a partition index is attached.
20833 : */
20834 : static void
20835 338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20836 : {
20837 : Relation inheritsRel;
20838 : SysScanDesc scan;
20839 : ScanKeyData key;
20840 338 : int tuples = 0;
20841 : HeapTuple inhTup;
20842 338 : bool updated = false;
20843 :
20844 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20845 :
20846 : /*
20847 : * Scan pg_inherits for this parent index. Count each valid index we find
20848 : * (verifying the pg_index entry for each), and if we reach the total
20849 : * amount we expect, we can mark this parent index as valid.
20850 : */
20851 338 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20852 338 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20853 : BTEqualStrategyNumber, F_OIDEQ,
20854 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20855 338 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20856 : NULL, 1, &key);
20857 880 : while ((inhTup = systable_getnext(scan)) != NULL)
20858 : {
20859 542 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20860 : HeapTuple indTup;
20861 : Form_pg_index indexForm;
20862 :
20863 542 : indTup = SearchSysCache1(INDEXRELID,
20864 : ObjectIdGetDatum(inhForm->inhrelid));
20865 542 : if (!HeapTupleIsValid(indTup))
20866 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20867 542 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20868 542 : if (indexForm->indisvalid)
20869 484 : tuples += 1;
20870 542 : ReleaseSysCache(indTup);
20871 : }
20872 :
20873 : /* Done with pg_inherits */
20874 338 : systable_endscan(scan);
20875 338 : table_close(inheritsRel, AccessShareLock);
20876 :
20877 : /*
20878 : * If we found as many inherited indexes as the partitioned table has
20879 : * partitions, we're good; update pg_index to set indisvalid.
20880 : */
20881 338 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20882 : {
20883 : Relation idxRel;
20884 : HeapTuple indTup;
20885 : Form_pg_index indexForm;
20886 :
20887 168 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
20888 168 : indTup = SearchSysCacheCopy1(INDEXRELID,
20889 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20890 168 : if (!HeapTupleIsValid(indTup))
20891 0 : elog(ERROR, "cache lookup failed for index %u",
20892 : RelationGetRelid(partedIdx));
20893 168 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20894 :
20895 168 : indexForm->indisvalid = true;
20896 168 : updated = true;
20897 :
20898 168 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20899 :
20900 168 : table_close(idxRel, RowExclusiveLock);
20901 168 : heap_freetuple(indTup);
20902 : }
20903 :
20904 : /*
20905 : * If this index is in turn a partition of a larger index, validating it
20906 : * might cause the parent to become valid also. Try that.
20907 : */
20908 338 : if (updated && partedIdx->rd_rel->relispartition)
20909 : {
20910 : Oid parentIdxId,
20911 : parentTblId;
20912 : Relation parentIdx,
20913 : parentTbl;
20914 :
20915 : /* make sure we see the validation we just did */
20916 42 : CommandCounterIncrement();
20917 :
20918 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20919 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20920 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20921 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20922 : Assert(!parentIdx->rd_index->indisvalid);
20923 :
20924 42 : validatePartitionedIndex(parentIdx, parentTbl);
20925 :
20926 42 : relation_close(parentIdx, AccessExclusiveLock);
20927 42 : relation_close(parentTbl, AccessExclusiveLock);
20928 : }
20929 338 : }
20930 :
20931 : /*
20932 : * When attaching an index as a partition of a partitioned index which is a
20933 : * primary key, verify that all the columns in the partition are marked NOT
20934 : * NULL.
20935 : */
20936 : static void
20937 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20938 : {
20939 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20940 : {
20941 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20942 94 : iinfo->ii_IndexAttrNumbers[i] - 1);
20943 :
20944 94 : if (!att->attnotnull)
20945 0 : ereport(ERROR,
20946 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20947 : errmsg("invalid primary key definition"),
20948 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20949 : NameStr(att->attname),
20950 : RelationGetRelationName(partition)));
20951 : }
20952 92 : }
20953 :
20954 : /*
20955 : * Return an OID list of constraints that reference the given relation
20956 : * that are marked as having a parent constraints.
20957 : */
20958 : static List *
20959 958 : GetParentedForeignKeyRefs(Relation partition)
20960 : {
20961 : Relation pg_constraint;
20962 : HeapTuple tuple;
20963 : SysScanDesc scan;
20964 : ScanKeyData key[2];
20965 958 : List *constraints = NIL;
20966 :
20967 : /*
20968 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
20969 : * scan.
20970 : */
20971 1362 : if (RelationGetIndexList(partition) == NIL ||
20972 404 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
20973 : INDEX_ATTR_BITMAP_KEY)))
20974 710 : return NIL;
20975 :
20976 : /* Search for constraints referencing this table */
20977 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20978 248 : ScanKeyInit(&key[0],
20979 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20980 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20981 248 : ScanKeyInit(&key[1],
20982 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
20983 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20984 :
20985 : /* XXX This is a seqscan, as we don't have a usable index */
20986 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20987 372 : while ((tuple = systable_getnext(scan)) != NULL)
20988 : {
20989 124 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20990 :
20991 : /*
20992 : * We only need to process constraints that are part of larger ones.
20993 : */
20994 124 : if (!OidIsValid(constrForm->conparentid))
20995 0 : continue;
20996 :
20997 124 : constraints = lappend_oid(constraints, constrForm->oid);
20998 : }
20999 :
21000 248 : systable_endscan(scan);
21001 248 : table_close(pg_constraint, AccessShareLock);
21002 :
21003 248 : return constraints;
21004 : }
21005 :
21006 : /*
21007 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21008 : * partitioned table would not become invalid. An error is raised if any
21009 : * referenced values exist.
21010 : */
21011 : static void
21012 514 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21013 : {
21014 : List *constraints;
21015 : ListCell *cell;
21016 :
21017 514 : constraints = GetParentedForeignKeyRefs(partition);
21018 :
21019 566 : foreach(cell, constraints)
21020 : {
21021 86 : Oid constrOid = lfirst_oid(cell);
21022 : HeapTuple tuple;
21023 : Form_pg_constraint constrForm;
21024 : Relation rel;
21025 86 : Trigger trig = {0};
21026 :
21027 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21028 86 : if (!HeapTupleIsValid(tuple))
21029 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21030 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21031 :
21032 : Assert(OidIsValid(constrForm->conparentid));
21033 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21034 :
21035 : /* prevent data changes into the referencing table until commit */
21036 86 : rel = table_open(constrForm->conrelid, ShareLock);
21037 :
21038 86 : trig.tgoid = InvalidOid;
21039 86 : trig.tgname = NameStr(constrForm->conname);
21040 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21041 86 : trig.tgisinternal = true;
21042 86 : trig.tgconstrrelid = RelationGetRelid(partition);
21043 86 : trig.tgconstrindid = constrForm->conindid;
21044 86 : trig.tgconstraint = constrForm->oid;
21045 86 : trig.tgdeferrable = false;
21046 86 : trig.tginitdeferred = false;
21047 : /* we needn't fill in remaining fields */
21048 :
21049 86 : RI_PartitionRemove_Check(&trig, rel, partition);
21050 :
21051 52 : ReleaseSysCache(tuple);
21052 :
21053 52 : table_close(rel, NoLock);
21054 : }
21055 480 : }
21056 :
21057 : /*
21058 : * resolve column compression specification to compression method.
21059 : */
21060 : static char
21061 235600 : GetAttributeCompression(Oid atttypid, const char *compression)
21062 : {
21063 : char cmethod;
21064 :
21065 235600 : if (compression == NULL || strcmp(compression, "default") == 0)
21066 235444 : return InvalidCompressionMethod;
21067 :
21068 : /*
21069 : * To specify a nondefault method, the column data type must be toastable.
21070 : * Note this says nothing about whether the column's attstorage setting
21071 : * permits compression; we intentionally allow attstorage and
21072 : * attcompression to be independent. But with a non-toastable type,
21073 : * attstorage could not be set to a value that would permit compression.
21074 : *
21075 : * We don't actually need to enforce this, since nothing bad would happen
21076 : * if attcompression were non-default; it would never be consulted. But
21077 : * it seems more user-friendly to complain about a certainly-useless
21078 : * attempt to set the property.
21079 : */
21080 156 : if (!TypeIsToastable(atttypid))
21081 6 : ereport(ERROR,
21082 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21083 : errmsg("column data type %s does not support compression",
21084 : format_type_be(atttypid))));
21085 :
21086 150 : cmethod = CompressionNameToMethod(compression);
21087 150 : if (!CompressionMethodIsValid(cmethod))
21088 12 : ereport(ERROR,
21089 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21090 : errmsg("invalid compression method \"%s\"", compression)));
21091 :
21092 138 : return cmethod;
21093 : }
21094 :
21095 : /*
21096 : * resolve column storage specification
21097 : */
21098 : static char
21099 248 : GetAttributeStorage(Oid atttypid, const char *storagemode)
21100 : {
21101 248 : char cstorage = 0;
21102 :
21103 248 : if (pg_strcasecmp(storagemode, "plain") == 0)
21104 50 : cstorage = TYPSTORAGE_PLAIN;
21105 198 : else if (pg_strcasecmp(storagemode, "external") == 0)
21106 156 : cstorage = TYPSTORAGE_EXTERNAL;
21107 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
21108 16 : cstorage = TYPSTORAGE_EXTENDED;
21109 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
21110 20 : cstorage = TYPSTORAGE_MAIN;
21111 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
21112 6 : cstorage = get_typstorage(atttypid);
21113 : else
21114 0 : ereport(ERROR,
21115 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21116 : errmsg("invalid storage type \"%s\"",
21117 : storagemode)));
21118 :
21119 : /*
21120 : * safety check: do not allow toasted storage modes unless column datatype
21121 : * is TOAST-aware.
21122 : */
21123 248 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21124 6 : ereport(ERROR,
21125 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21126 : errmsg("column data type %s can only have storage PLAIN",
21127 : format_type_be(atttypid))));
21128 :
21129 242 : return cstorage;
21130 : }
|