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, AlterTableCmd *cmd,
393 : bool recurse, bool recursing, LOCKMODE lockmode);
394 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
395 : Relation rel, HeapTuple contuple, List **otherrelids,
396 : LOCKMODE lockmode);
397 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
398 : bool deferrable, bool initdeferred,
399 : List **otherrelids);
400 : static void ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
401 : Relation rel, HeapTuple contuple, List **otherrelids,
402 : LOCKMODE lockmode);
403 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
404 : Relation rel, char *constrName,
405 : bool recurse, bool recursing, LOCKMODE lockmode);
406 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
407 : HeapTuple contuple, LOCKMODE lockmode);
408 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
409 : char *constrName, HeapTuple contuple,
410 : bool recurse, bool recursing, LOCKMODE lockmode);
411 : static int transformColumnNameList(Oid relId, List *colList,
412 : int16 *attnums, Oid *atttypids, Oid *attcollids);
413 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
414 : List **attnamelist,
415 : int16 *attnums, Oid *atttypids, Oid *attcollids,
416 : Oid *opclasses, bool *pk_has_without_overlaps);
417 : static Oid transformFkeyCheckAttrs(Relation pkrel,
418 : int numattrs, int16 *attnums,
419 : bool with_period, Oid *opclasses,
420 : bool *pk_has_without_overlaps);
421 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
422 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
423 : Oid *funcid);
424 : static void validateForeignKeyConstraint(char *conname,
425 : Relation rel, Relation pkrel,
426 : Oid pkindOid, Oid constraintOid, bool hasperiod);
427 : static void CheckAlterTableIsSafe(Relation rel);
428 : static void ATController(AlterTableStmt *parsetree,
429 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
430 : AlterTableUtilityContext *context);
431 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
432 : bool recurse, bool recursing, LOCKMODE lockmode,
433 : AlterTableUtilityContext *context);
434 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
435 : AlterTableUtilityContext *context);
436 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
437 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
438 : AlterTableUtilityContext *context);
439 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
440 : Relation rel, AlterTableCmd *cmd,
441 : bool recurse, LOCKMODE lockmode,
442 : AlterTablePass cur_pass,
443 : AlterTableUtilityContext *context);
444 : static void ATRewriteTables(AlterTableStmt *parsetree,
445 : List **wqueue, LOCKMODE lockmode,
446 : AlterTableUtilityContext *context);
447 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
448 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
449 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
450 : static void ATSimpleRecursion(List **wqueue, Relation rel,
451 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
452 : AlterTableUtilityContext *context);
453 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
454 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
455 : LOCKMODE lockmode,
456 : AlterTableUtilityContext *context);
457 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
458 : DropBehavior behavior);
459 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
460 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
461 : AlterTableUtilityContext *context);
462 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
463 : Relation rel, AlterTableCmd **cmd,
464 : bool recurse, bool recursing,
465 : LOCKMODE lockmode, AlterTablePass cur_pass,
466 : AlterTableUtilityContext *context);
467 : static bool check_for_column_name_collision(Relation rel, const char *colname,
468 : bool if_not_exists);
469 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
470 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
471 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
472 : LOCKMODE lockmode);
473 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
474 : LOCKMODE lockmode);
475 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
476 : char *constrname, char *colName,
477 : bool recurse, bool recursing,
478 : LOCKMODE lockmode);
479 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
480 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
481 : List *testConstraint, List *provenConstraint);
482 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
483 : Node *newDefault, LOCKMODE lockmode);
484 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
485 : Node *newDefault);
486 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
487 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
488 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
489 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
490 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
491 : bool recurse, bool recursing);
492 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
493 : Node *newExpr, LOCKMODE lockmode);
494 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
495 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
496 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
497 : Node *newValue, LOCKMODE lockmode);
498 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
499 : Node *options, bool isReset, LOCKMODE lockmode);
500 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
501 : Node *newValue, LOCKMODE lockmode);
502 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
503 : AlterTableCmd *cmd, LOCKMODE lockmode,
504 : AlterTableUtilityContext *context);
505 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
506 : DropBehavior behavior,
507 : bool recurse, bool recursing,
508 : bool missing_ok, LOCKMODE lockmode,
509 : ObjectAddresses *addrs);
510 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
511 : bool recurse, LOCKMODE lockmode,
512 : AlterTableUtilityContext *context);
513 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
514 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
515 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
516 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
517 : static ObjectAddress ATExecAddConstraint(List **wqueue,
518 : AlteredTableInfo *tab, Relation rel,
519 : Constraint *newConstraint, bool recurse, bool is_readd,
520 : LOCKMODE lockmode);
521 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
522 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
523 : IndexStmt *stmt, LOCKMODE lockmode);
524 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
525 : AlteredTableInfo *tab, Relation rel,
526 : Constraint *constr,
527 : bool recurse, bool recursing, bool is_readd,
528 : LOCKMODE lockmode);
529 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
530 : Relation rel, Constraint *fkconstraint,
531 : bool recurse, bool recursing,
532 : LOCKMODE lockmode);
533 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
534 : int numfksetcols, const int16 *fksetcolsattnums,
535 : List *fksetcols);
536 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
537 : char *constraintname,
538 : Constraint *fkconstraint, Relation rel,
539 : Relation pkrel, Oid indexOid,
540 : Oid parentConstr,
541 : int numfks, int16 *pkattnum, int16 *fkattnum,
542 : Oid *pfeqoperators, Oid *ppeqoperators,
543 : Oid *ffeqoperators, int numfkdelsetcols,
544 : int16 *fkdelsetcols, bool is_internal,
545 : bool with_period);
546 : static void addFkRecurseReferenced(Constraint *fkconstraint,
547 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
548 : int numfks, int16 *pkattnum, int16 *fkattnum,
549 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
550 : int numfkdelsetcols, int16 *fkdelsetcols,
551 : bool old_check_ok,
552 : Oid parentDelTrigger, Oid parentUpdTrigger,
553 : bool with_period);
554 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
555 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
556 : int numfks, int16 *pkattnum, int16 *fkattnum,
557 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
558 : int numfkdelsetcols, int16 *fkdelsetcols,
559 : bool old_check_ok, LOCKMODE lockmode,
560 : Oid parentInsTrigger, Oid parentUpdTrigger,
561 : bool with_period);
562 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
563 : Relation partitionRel);
564 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
565 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
566 : Relation partRel);
567 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
568 : Constraint *fkconstraint, Oid constraintOid,
569 : Oid indexOid,
570 : Oid parentInsTrigger, Oid parentUpdTrigger,
571 : Oid *insertTrigOid, Oid *updateTrigOid);
572 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
573 : Constraint *fkconstraint, Oid constraintOid,
574 : Oid indexOid,
575 : Oid parentDelTrigger, Oid parentUpdTrigger,
576 : Oid *deleteTrigOid, Oid *updateTrigOid);
577 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
578 : Oid partRelid,
579 : Oid parentConstrOid, int numfks,
580 : AttrNumber *mapped_conkey, AttrNumber *confkey,
581 : Oid *conpfeqop,
582 : Oid parentInsTrigger,
583 : Oid parentUpdTrigger,
584 : Relation trigrel);
585 : static void GetForeignKeyActionTriggers(Relation trigrel,
586 : Oid conoid, Oid confrelid, Oid conrelid,
587 : Oid *deleteTriggerOid,
588 : Oid *updateTriggerOid);
589 : static void GetForeignKeyCheckTriggers(Relation trigrel,
590 : Oid conoid, Oid confrelid, Oid conrelid,
591 : Oid *insertTriggerOid,
592 : Oid *updateTriggerOid);
593 : static void ATExecDropConstraint(Relation rel, const char *constrName,
594 : DropBehavior behavior, bool recurse,
595 : bool missing_ok, LOCKMODE lockmode);
596 : static ObjectAddress dropconstraint_internal(Relation rel,
597 : HeapTuple constraintTup, DropBehavior behavior,
598 : bool recurse, bool recursing,
599 : bool missing_ok, LOCKMODE lockmode);
600 : static void ATPrepAlterColumnType(List **wqueue,
601 : AlteredTableInfo *tab, Relation rel,
602 : bool recurse, bool recursing,
603 : AlterTableCmd *cmd, LOCKMODE lockmode,
604 : AlterTableUtilityContext *context);
605 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
606 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
607 : AlterTableCmd *cmd, LOCKMODE lockmode);
608 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
609 : Relation rel, AttrNumber attnum, const char *colName);
610 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
611 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
612 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
613 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
614 : LOCKMODE lockmode);
615 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
616 : char *cmd, List **wqueue, LOCKMODE lockmode,
617 : bool rewrite);
618 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
619 : Oid objid, Relation rel, List *domname,
620 : const char *conname);
621 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
622 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
623 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
624 : List *options, LOCKMODE lockmode);
625 : static void change_owner_fix_column_acls(Oid relationOid,
626 : Oid oldOwnerId, Oid newOwnerId);
627 : static void change_owner_recurse_to_sequences(Oid relationOid,
628 : Oid newOwnerId, LOCKMODE lockmode);
629 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
630 : LOCKMODE lockmode);
631 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
632 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
633 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
634 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
635 : bool toLogged);
636 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
637 : const char *tablespacename, LOCKMODE lockmode);
638 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
639 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
640 : static void ATExecSetRelOptions(Relation rel, List *defList,
641 : AlterTableType operation,
642 : LOCKMODE lockmode);
643 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
644 : char fires_when, bool skip_system, bool recurse,
645 : LOCKMODE lockmode);
646 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
647 : char fires_when, LOCKMODE lockmode);
648 : static void ATPrepAddInherit(Relation child_rel);
649 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
650 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
651 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
652 : DependencyType deptype);
653 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
654 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
655 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
656 : static void ATExecGenericOptions(Relation rel, List *options);
657 : static void ATExecSetRowSecurity(Relation rel, bool rls);
658 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
659 : static ObjectAddress ATExecSetCompression(Relation rel,
660 : const char *column, Node *newValue, LOCKMODE lockmode);
661 :
662 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
663 : static const char *storage_name(char c);
664 :
665 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
666 : Oid oldRelOid, void *arg);
667 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
668 : Oid oldrelid, void *arg);
669 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
670 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
671 : List **partexprs, Oid *partopclass, Oid *partcollation,
672 : PartitionStrategy strategy);
673 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
674 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
675 : bool expect_detached);
676 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
677 : PartitionCmd *cmd,
678 : AlterTableUtilityContext *context);
679 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
680 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
681 : List *partConstraint,
682 : bool validate_default);
683 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
684 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
685 : static void DropClonedTriggersFromPartition(Oid partitionId);
686 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
687 : Relation rel, RangeVar *name,
688 : bool concurrent);
689 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
690 : bool concurrent, Oid defaultPartOid);
691 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
692 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
693 : RangeVar *name);
694 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
695 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
696 : Relation partitionTbl);
697 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
698 : static List *GetParentedForeignKeyRefs(Relation partition);
699 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
700 : static char GetAttributeCompression(Oid atttypid, const char *compression);
701 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
702 :
703 :
704 : /* ----------------------------------------------------------------
705 : * DefineRelation
706 : * Creates a new relation.
707 : *
708 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
709 : * The other arguments are used to extend the behavior for other cases:
710 : * relkind: relkind to assign to the new relation
711 : * ownerId: if not InvalidOid, use this as the new relation's owner.
712 : * typaddress: if not null, it's set to the pg_type entry's address.
713 : * queryString: for error reporting
714 : *
715 : * Note that permissions checks are done against current user regardless of
716 : * ownerId. A nonzero ownerId is used when someone is creating a relation
717 : * "on behalf of" someone else, so we still want to see that the current user
718 : * has permissions to do it.
719 : *
720 : * If successful, returns the address of the new relation.
721 : * ----------------------------------------------------------------
722 : */
723 : ObjectAddress
724 57328 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
725 : ObjectAddress *typaddress, const char *queryString)
726 : {
727 : char relname[NAMEDATALEN];
728 : Oid namespaceId;
729 : Oid relationId;
730 : Oid tablespaceId;
731 : Relation rel;
732 : TupleDesc descriptor;
733 : List *inheritOids;
734 : List *old_constraints;
735 : List *old_notnulls;
736 : List *rawDefaults;
737 : List *cookedDefaults;
738 : List *nncols;
739 : Datum reloptions;
740 : ListCell *listptr;
741 : AttrNumber attnum;
742 : bool partitioned;
743 57328 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
744 : Oid ofTypeId;
745 : ObjectAddress address;
746 : LOCKMODE parentLockmode;
747 57328 : Oid accessMethodId = InvalidOid;
748 :
749 : /*
750 : * Truncate relname to appropriate length (probably a waste of time, as
751 : * parser should have done this already).
752 : */
753 57328 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
754 :
755 : /*
756 : * Check consistency of arguments
757 : */
758 57328 : if (stmt->oncommit != ONCOMMIT_NOOP
759 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
760 12 : ereport(ERROR,
761 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
762 : errmsg("ON COMMIT can only be used on temporary tables")));
763 :
764 57316 : if (stmt->partspec != NULL)
765 : {
766 4796 : if (relkind != RELKIND_RELATION)
767 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
768 :
769 4796 : relkind = RELKIND_PARTITIONED_TABLE;
770 4796 : partitioned = true;
771 : }
772 : else
773 52520 : partitioned = false;
774 :
775 57316 : if (relkind == RELKIND_PARTITIONED_TABLE &&
776 4796 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
777 6 : ereport(ERROR,
778 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
779 : errmsg("partitioned tables cannot be unlogged")));
780 :
781 : /*
782 : * Look up the namespace in which we are supposed to create the relation,
783 : * check we have permission to create there, lock it against concurrent
784 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
785 : * namespace is selected.
786 : */
787 : namespaceId =
788 57310 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
789 :
790 : /*
791 : * Security check: disallow creating temp tables from security-restricted
792 : * code. This is needed because calling code might not expect untrusted
793 : * tables to appear in pg_temp at the front of its search path.
794 : */
795 57310 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
796 3054 : && InSecurityRestrictedOperation())
797 0 : ereport(ERROR,
798 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
799 : errmsg("cannot create temporary table within security-restricted operation")));
800 :
801 : /*
802 : * Determine the lockmode to use when scanning parents. A self-exclusive
803 : * lock is needed here.
804 : *
805 : * For regular inheritance, if two backends attempt to add children to the
806 : * same parent simultaneously, and that parent has no pre-existing
807 : * children, then both will attempt to update the parent's relhassubclass
808 : * field, leading to a "tuple concurrently updated" error. Also, this
809 : * interlocks against a concurrent ANALYZE on the parent table, which
810 : * might otherwise be attempting to clear the parent's relhassubclass
811 : * field, if its previous children were recently dropped.
812 : *
813 : * If the child table is a partition, then we instead grab an exclusive
814 : * lock on the parent because its partition descriptor will be changed by
815 : * addition of the new partition.
816 : */
817 57310 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
818 : ShareUpdateExclusiveLock);
819 :
820 : /* Determine the list of OIDs of the parents. */
821 57310 : inheritOids = NIL;
822 67310 : foreach(listptr, stmt->inhRelations)
823 : {
824 10000 : RangeVar *rv = (RangeVar *) lfirst(listptr);
825 : Oid parentOid;
826 :
827 10000 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
828 :
829 : /*
830 : * Reject duplications in the list of parents.
831 : */
832 10000 : if (list_member_oid(inheritOids, parentOid))
833 0 : ereport(ERROR,
834 : (errcode(ERRCODE_DUPLICATE_TABLE),
835 : errmsg("relation \"%s\" would be inherited from more than once",
836 : get_rel_name(parentOid))));
837 :
838 10000 : inheritOids = lappend_oid(inheritOids, parentOid);
839 : }
840 :
841 : /*
842 : * Select tablespace to use: an explicitly indicated one, or (in the case
843 : * of a partitioned table) the parent's, if it has one.
844 : */
845 57310 : if (stmt->tablespacename)
846 : {
847 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
848 :
849 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
850 6 : ereport(ERROR,
851 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
852 : errmsg("cannot specify default tablespace for partitioned relations")));
853 : }
854 57192 : else if (stmt->partbound)
855 : {
856 : Assert(list_length(inheritOids) == 1);
857 7772 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
858 : }
859 : else
860 49420 : tablespaceId = InvalidOid;
861 :
862 : /* still nothing? use the default */
863 57298 : if (!OidIsValid(tablespaceId))
864 57170 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
865 : partitioned);
866 :
867 : /* Check permissions except when using database's default */
868 57292 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
869 : {
870 : AclResult aclresult;
871 :
872 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
873 : ACL_CREATE);
874 146 : if (aclresult != ACLCHECK_OK)
875 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
876 6 : get_tablespace_name(tablespaceId));
877 : }
878 :
879 : /* In all cases disallow placing user relations in pg_global */
880 57286 : if (tablespaceId == GLOBALTABLESPACE_OID)
881 18 : ereport(ERROR,
882 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
883 : errmsg("only shared relations can be placed in pg_global tablespace")));
884 :
885 : /* Identify user ID that will own the table */
886 57268 : if (!OidIsValid(ownerId))
887 57028 : ownerId = GetUserId();
888 :
889 : /*
890 : * Parse and validate reloptions, if any.
891 : */
892 57268 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
893 : true, false);
894 :
895 57250 : switch (relkind)
896 : {
897 14884 : case RELKIND_VIEW:
898 14884 : (void) view_reloptions(reloptions, true);
899 14866 : break;
900 4772 : case RELKIND_PARTITIONED_TABLE:
901 4772 : (void) partitioned_table_reloptions(reloptions, true);
902 4766 : break;
903 37594 : default:
904 37594 : (void) heap_reloptions(relkind, reloptions, true);
905 : }
906 :
907 57130 : if (stmt->ofTypename)
908 : {
909 : AclResult aclresult;
910 :
911 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
912 :
913 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
914 86 : if (aclresult != ACLCHECK_OK)
915 6 : aclcheck_error_type(aclresult, ofTypeId);
916 : }
917 : else
918 57044 : ofTypeId = InvalidOid;
919 :
920 : /*
921 : * Look up inheritance ancestors and generate relation schema, including
922 : * inherited attributes. (Note that stmt->tableElts is destructively
923 : * modified by MergeAttributes.)
924 : */
925 56956 : stmt->tableElts =
926 57124 : MergeAttributes(stmt->tableElts, inheritOids,
927 57124 : stmt->relation->relpersistence,
928 57124 : stmt->partbound != NULL,
929 : &old_constraints, &old_notnulls);
930 :
931 : /*
932 : * Create a tuple descriptor from the relation schema. Note that this
933 : * deals with column names, types, and in-descriptor NOT NULL flags, but
934 : * not default values, NOT NULL or CHECK constraints; we handle those
935 : * below.
936 : */
937 56956 : descriptor = BuildDescForRelation(stmt->tableElts);
938 :
939 : /*
940 : * Find columns with default values and prepare for insertion of the
941 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
942 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
943 : * while raw defaults go into a list of RawColumnDefault structs that will
944 : * be processed by AddRelationNewConstraints. (We can't deal with raw
945 : * expressions until we can do transformExpr.)
946 : *
947 : * We can set the atthasdef flags now in the tuple descriptor; this just
948 : * saves StoreAttrDefault from having to do an immediate update of the
949 : * pg_attribute rows.
950 : */
951 56908 : rawDefaults = NIL;
952 56908 : cookedDefaults = NIL;
953 56908 : attnum = 0;
954 :
955 285534 : foreach(listptr, stmt->tableElts)
956 : {
957 228626 : ColumnDef *colDef = lfirst(listptr);
958 : Form_pg_attribute attr;
959 :
960 228626 : attnum++;
961 228626 : attr = TupleDescAttr(descriptor, attnum - 1);
962 :
963 228626 : if (colDef->raw_default != NULL)
964 : {
965 : RawColumnDefault *rawEnt;
966 :
967 : Assert(colDef->cooked_default == NULL);
968 :
969 2428 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
970 2428 : rawEnt->attnum = attnum;
971 2428 : rawEnt->raw_default = colDef->raw_default;
972 2428 : rawEnt->missingMode = false;
973 2428 : rawEnt->generated = colDef->generated;
974 2428 : rawDefaults = lappend(rawDefaults, rawEnt);
975 2428 : attr->atthasdef = true;
976 : }
977 226198 : else if (colDef->cooked_default != NULL)
978 : {
979 : CookedConstraint *cooked;
980 :
981 344 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
982 344 : cooked->contype = CONSTR_DEFAULT;
983 344 : cooked->conoid = InvalidOid; /* until created */
984 344 : cooked->name = NULL;
985 344 : cooked->attnum = attnum;
986 344 : cooked->expr = colDef->cooked_default;
987 344 : cooked->is_enforced = true;
988 344 : cooked->skip_validation = false;
989 344 : cooked->is_local = true; /* not used for defaults */
990 344 : cooked->inhcount = 0; /* ditto */
991 344 : cooked->is_no_inherit = false;
992 344 : cookedDefaults = lappend(cookedDefaults, cooked);
993 344 : attr->atthasdef = true;
994 : }
995 :
996 228626 : populate_compact_attribute(descriptor, attnum - 1);
997 : }
998 :
999 : /*
1000 : * For relations with table AM and partitioned tables, select access
1001 : * method to use: an explicitly indicated one, or (in the case of a
1002 : * partitioned table) the parent's, if it has one.
1003 : */
1004 56908 : if (stmt->accessMethod != NULL)
1005 : {
1006 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1007 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1008 : }
1009 56786 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1010 : {
1011 36288 : if (stmt->partbound)
1012 : {
1013 : Assert(list_length(inheritOids) == 1);
1014 7614 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1015 : }
1016 :
1017 36288 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1018 31508 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1019 : }
1020 :
1021 : /*
1022 : * Create the relation. Inherited defaults and CHECK constraints are
1023 : * passed in for immediate handling --- since they don't need parsing,
1024 : * they can be stored immediately.
1025 : */
1026 56890 : relationId = heap_create_with_catalog(relname,
1027 : namespaceId,
1028 : tablespaceId,
1029 : InvalidOid,
1030 : InvalidOid,
1031 : ofTypeId,
1032 : ownerId,
1033 : accessMethodId,
1034 : descriptor,
1035 : list_concat(cookedDefaults,
1036 : old_constraints),
1037 : relkind,
1038 56890 : stmt->relation->relpersistence,
1039 : false,
1040 : false,
1041 : stmt->oncommit,
1042 : reloptions,
1043 : true,
1044 : allowSystemTableMods,
1045 : false,
1046 : InvalidOid,
1047 : typaddress);
1048 :
1049 : /*
1050 : * We must bump the command counter to make the newly-created relation
1051 : * tuple visible for opening.
1052 : */
1053 56866 : CommandCounterIncrement();
1054 :
1055 : /*
1056 : * Open the new relation and acquire exclusive lock on it. This isn't
1057 : * really necessary for locking out other backends (since they can't see
1058 : * the new rel anyway until we commit), but it keeps the lock manager from
1059 : * complaining about deadlock risks.
1060 : */
1061 56866 : rel = relation_open(relationId, AccessExclusiveLock);
1062 :
1063 : /*
1064 : * Now add any newly specified column default and generation expressions
1065 : * to the new relation. These are passed to us in the form of raw
1066 : * parsetrees; we need to transform them to executable expression trees
1067 : * before they can be added. The most convenient way to do that is to
1068 : * apply the parser's transformExpr routine, but transformExpr doesn't
1069 : * work unless we have a pre-existing relation. So, the transformation has
1070 : * to be postponed to this final step of CREATE TABLE.
1071 : *
1072 : * This needs to be before processing the partitioning clauses because
1073 : * those could refer to generated columns.
1074 : */
1075 56866 : if (rawDefaults)
1076 2064 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1077 : true, true, false, queryString);
1078 :
1079 : /*
1080 : * Make column generation expressions visible for use by partitioning.
1081 : */
1082 56764 : CommandCounterIncrement();
1083 :
1084 : /* Process and store partition bound, if any. */
1085 56764 : if (stmt->partbound)
1086 : {
1087 : PartitionBoundSpec *bound;
1088 : ParseState *pstate;
1089 7718 : Oid parentId = linitial_oid(inheritOids),
1090 : defaultPartOid;
1091 : Relation parent,
1092 7718 : defaultRel = NULL;
1093 : ParseNamespaceItem *nsitem;
1094 :
1095 : /* Already have strong enough lock on the parent */
1096 7718 : parent = table_open(parentId, NoLock);
1097 :
1098 : /*
1099 : * We are going to try to validate the partition bound specification
1100 : * against the partition key of parentRel, so it better have one.
1101 : */
1102 7718 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1103 18 : ereport(ERROR,
1104 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1105 : errmsg("\"%s\" is not partitioned",
1106 : RelationGetRelationName(parent))));
1107 :
1108 : /*
1109 : * The partition constraint of the default partition depends on the
1110 : * partition bounds of every other partition. It is possible that
1111 : * another backend might be about to execute a query on the default
1112 : * partition table, and that the query relies on previously cached
1113 : * default partition constraints. We must therefore take a table lock
1114 : * strong enough to prevent all queries on the default partition from
1115 : * proceeding until we commit and send out a shared-cache-inval notice
1116 : * that will make them update their index lists.
1117 : *
1118 : * Order of locking: The relation being added won't be visible to
1119 : * other backends until it is committed, hence here in
1120 : * DefineRelation() the order of locking the default partition and the
1121 : * relation being added does not matter. But at all other places we
1122 : * need to lock the default relation before we lock the relation being
1123 : * added or removed i.e. we should take the lock in same order at all
1124 : * the places such that lock parent, lock default partition and then
1125 : * lock the partition so as to avoid a deadlock.
1126 : */
1127 : defaultPartOid =
1128 7700 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1129 : true));
1130 7700 : if (OidIsValid(defaultPartOid))
1131 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1132 :
1133 : /* Transform the bound values */
1134 7700 : pstate = make_parsestate(NULL);
1135 7700 : pstate->p_sourcetext = queryString;
1136 :
1137 : /*
1138 : * Add an nsitem containing this relation, so that transformExpr
1139 : * called on partition bound expressions is able to report errors
1140 : * using a proper context.
1141 : */
1142 7700 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1143 : NULL, false, false);
1144 7700 : addNSItemToQuery(pstate, nsitem, false, true, true);
1145 :
1146 7700 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1147 :
1148 : /*
1149 : * Check first that the new partition's bound is valid and does not
1150 : * overlap with any of existing partitions of the parent.
1151 : */
1152 7496 : check_new_partition_bound(relname, parent, bound, pstate);
1153 :
1154 : /*
1155 : * If the default partition exists, its partition constraints will
1156 : * change after the addition of this new partition such that it won't
1157 : * allow any row that qualifies for this new partition. So, check that
1158 : * the existing data in the default partition satisfies the constraint
1159 : * as it will exist after adding this partition.
1160 : */
1161 7382 : if (OidIsValid(defaultPartOid))
1162 : {
1163 348 : check_default_partition_contents(parent, defaultRel, bound);
1164 : /* Keep the lock until commit. */
1165 330 : table_close(defaultRel, NoLock);
1166 : }
1167 :
1168 : /* Update the pg_class entry. */
1169 7364 : StorePartitionBound(rel, parent, bound);
1170 :
1171 7364 : table_close(parent, NoLock);
1172 : }
1173 :
1174 : /* Store inheritance information for new rel. */
1175 56410 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1176 :
1177 : /*
1178 : * Process the partitioning specification (if any) and store the partition
1179 : * key information into the catalog.
1180 : */
1181 56410 : if (partitioned)
1182 : {
1183 : ParseState *pstate;
1184 : int partnatts;
1185 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1186 : Oid partopclass[PARTITION_MAX_KEYS];
1187 : Oid partcollation[PARTITION_MAX_KEYS];
1188 4766 : List *partexprs = NIL;
1189 :
1190 4766 : pstate = make_parsestate(NULL);
1191 4766 : pstate->p_sourcetext = queryString;
1192 :
1193 4766 : partnatts = list_length(stmt->partspec->partParams);
1194 :
1195 : /* Protect fixed-size arrays here and in executor */
1196 4766 : if (partnatts > PARTITION_MAX_KEYS)
1197 0 : ereport(ERROR,
1198 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1199 : errmsg("cannot partition using more than %d columns",
1200 : PARTITION_MAX_KEYS)));
1201 :
1202 : /*
1203 : * We need to transform the raw parsetrees corresponding to partition
1204 : * expressions into executable expression trees. Like column defaults
1205 : * and CHECK constraints, we could not have done the transformation
1206 : * earlier.
1207 : */
1208 4766 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1209 :
1210 4736 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1211 : partattrs, &partexprs, partopclass,
1212 4736 : partcollation, stmt->partspec->strategy);
1213 :
1214 4652 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1215 : partexprs,
1216 : partopclass, partcollation);
1217 :
1218 : /* make it all visible */
1219 4652 : CommandCounterIncrement();
1220 : }
1221 :
1222 : /*
1223 : * If we're creating a partition, create now all the indexes, triggers,
1224 : * FKs defined in the parent.
1225 : *
1226 : * We can't do it earlier, because DefineIndex wants to know the partition
1227 : * key which we just stored.
1228 : */
1229 56296 : if (stmt->partbound)
1230 : {
1231 7358 : Oid parentId = linitial_oid(inheritOids);
1232 : Relation parent;
1233 : List *idxlist;
1234 : ListCell *cell;
1235 :
1236 : /* Already have strong enough lock on the parent */
1237 7358 : parent = table_open(parentId, NoLock);
1238 7358 : idxlist = RelationGetIndexList(parent);
1239 :
1240 : /*
1241 : * For each index in the parent table, create one in the partition
1242 : */
1243 8702 : foreach(cell, idxlist)
1244 : {
1245 1362 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1246 : AttrMap *attmap;
1247 : IndexStmt *idxstmt;
1248 : Oid constraintOid;
1249 :
1250 1362 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1251 : {
1252 36 : if (idxRel->rd_index->indisunique)
1253 12 : ereport(ERROR,
1254 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1255 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1256 : RelationGetRelationName(parent)),
1257 : errdetail("Table \"%s\" contains indexes that are unique.",
1258 : RelationGetRelationName(parent))));
1259 : else
1260 : {
1261 24 : index_close(idxRel, AccessShareLock);
1262 24 : continue;
1263 : }
1264 : }
1265 :
1266 1326 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1267 : RelationGetDescr(parent),
1268 : false);
1269 : idxstmt =
1270 1326 : generateClonedIndexStmt(NULL, idxRel,
1271 : attmap, &constraintOid);
1272 1326 : DefineIndex(RelationGetRelid(rel),
1273 : idxstmt,
1274 : InvalidOid,
1275 : RelationGetRelid(idxRel),
1276 : constraintOid,
1277 : -1,
1278 : false, false, false, false, false);
1279 :
1280 1320 : index_close(idxRel, AccessShareLock);
1281 : }
1282 :
1283 7340 : list_free(idxlist);
1284 :
1285 : /*
1286 : * If there are any row-level triggers, clone them to the new
1287 : * partition.
1288 : */
1289 7340 : if (parent->trigdesc != NULL)
1290 408 : CloneRowTriggersToPartition(parent, rel);
1291 :
1292 : /*
1293 : * And foreign keys too. Note that because we're freshly creating the
1294 : * table, there is no need to verify these new constraints.
1295 : */
1296 7340 : CloneForeignKeyConstraints(NULL, parent, rel);
1297 :
1298 7340 : table_close(parent, NoLock);
1299 : }
1300 :
1301 : /*
1302 : * Now add any newly specified CHECK constraints to the new relation. Same
1303 : * as for defaults above, but these need to come after partitioning is set
1304 : * up.
1305 : */
1306 56278 : if (stmt->constraints)
1307 700 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1308 : true, true, false, queryString);
1309 :
1310 : /*
1311 : * Finally, merge the not-null constraints that are declared directly with
1312 : * those that come from parent relations (making sure to count inheritance
1313 : * appropriately for each), create them, and set the attnotnull flag on
1314 : * columns that don't yet have it.
1315 : */
1316 56248 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1317 : old_notnulls);
1318 126520 : foreach_int(attrnum, nncols)
1319 14180 : set_attnotnull(NULL, rel, attrnum, NoLock);
1320 :
1321 56170 : ObjectAddressSet(address, RelationRelationId, relationId);
1322 :
1323 : /*
1324 : * Clean up. We keep lock on new relation (although it shouldn't be
1325 : * visible to anyone else anyway, until commit).
1326 : */
1327 56170 : relation_close(rel, NoLock);
1328 :
1329 56170 : return address;
1330 : }
1331 :
1332 : /*
1333 : * BuildDescForRelation
1334 : *
1335 : * Given a list of ColumnDef nodes, build a TupleDesc.
1336 : *
1337 : * Note: This is only for the limited purpose of table and view creation. Not
1338 : * everything is filled in. A real tuple descriptor should be obtained from
1339 : * the relcache.
1340 : */
1341 : TupleDesc
1342 59668 : BuildDescForRelation(const List *columns)
1343 : {
1344 : int natts;
1345 : AttrNumber attnum;
1346 : ListCell *l;
1347 : TupleDesc desc;
1348 : char *attname;
1349 : Oid atttypid;
1350 : int32 atttypmod;
1351 : Oid attcollation;
1352 : int attdim;
1353 :
1354 : /*
1355 : * allocate a new tuple descriptor
1356 : */
1357 59668 : natts = list_length(columns);
1358 59668 : desc = CreateTemplateTupleDesc(natts);
1359 :
1360 59668 : attnum = 0;
1361 :
1362 291270 : foreach(l, columns)
1363 : {
1364 231662 : ColumnDef *entry = lfirst(l);
1365 : AclResult aclresult;
1366 : Form_pg_attribute att;
1367 :
1368 : /*
1369 : * for each entry in the list, get the name and type information from
1370 : * the list and have TupleDescInitEntry fill in the attribute
1371 : * information we need.
1372 : */
1373 231662 : attnum++;
1374 :
1375 231662 : attname = entry->colname;
1376 231662 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1377 :
1378 231662 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1379 231662 : if (aclresult != ACLCHECK_OK)
1380 42 : aclcheck_error_type(aclresult, atttypid);
1381 :
1382 231620 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1383 231620 : attdim = list_length(entry->typeName->arrayBounds);
1384 231620 : if (attdim > PG_INT16_MAX)
1385 0 : ereport(ERROR,
1386 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1387 : errmsg("too many array dimensions"));
1388 :
1389 231620 : if (entry->typeName->setof)
1390 0 : ereport(ERROR,
1391 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1392 : errmsg("column \"%s\" cannot be declared SETOF",
1393 : attname)));
1394 :
1395 231620 : TupleDescInitEntry(desc, attnum, attname,
1396 : atttypid, atttypmod, attdim);
1397 231620 : att = TupleDescAttr(desc, attnum - 1);
1398 :
1399 : /* Override TupleDescInitEntry's settings as requested */
1400 231620 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1401 :
1402 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1403 231620 : att->attnotnull = entry->is_not_null;
1404 231620 : att->attislocal = entry->is_local;
1405 231620 : att->attinhcount = entry->inhcount;
1406 231620 : att->attidentity = entry->identity;
1407 231620 : att->attgenerated = entry->generated;
1408 231620 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1409 231608 : if (entry->storage)
1410 19342 : att->attstorage = entry->storage;
1411 212266 : else if (entry->storage_name)
1412 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1413 :
1414 231602 : populate_compact_attribute(desc, attnum - 1);
1415 : }
1416 :
1417 59608 : return desc;
1418 : }
1419 :
1420 : /*
1421 : * Emit the right error or warning message for a "DROP" command issued on a
1422 : * non-existent relation
1423 : */
1424 : static void
1425 1060 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1426 : {
1427 : const struct dropmsgstrings *rentry;
1428 :
1429 1180 : if (rel->schemaname != NULL &&
1430 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1431 : {
1432 42 : if (!missing_ok)
1433 : {
1434 0 : ereport(ERROR,
1435 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1436 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1437 : }
1438 : else
1439 : {
1440 42 : ereport(NOTICE,
1441 : (errmsg("schema \"%s\" does not exist, skipping",
1442 : rel->schemaname)));
1443 : }
1444 42 : return;
1445 : }
1446 :
1447 1338 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1448 : {
1449 1338 : if (rentry->kind == rightkind)
1450 : {
1451 1018 : if (!missing_ok)
1452 : {
1453 138 : ereport(ERROR,
1454 : (errcode(rentry->nonexistent_code),
1455 : errmsg(rentry->nonexistent_msg, rel->relname)));
1456 : }
1457 : else
1458 : {
1459 880 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1460 880 : break;
1461 : }
1462 : }
1463 : }
1464 :
1465 : Assert(rentry->kind != '\0'); /* Should be impossible */
1466 : }
1467 :
1468 : /*
1469 : * Emit the right error message for a "DROP" command issued on a
1470 : * relation of the wrong type
1471 : */
1472 : static void
1473 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1474 : {
1475 : const struct dropmsgstrings *rentry;
1476 : const struct dropmsgstrings *wentry;
1477 :
1478 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1479 0 : if (rentry->kind == rightkind)
1480 0 : break;
1481 : Assert(rentry->kind != '\0');
1482 :
1483 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1484 0 : if (wentry->kind == wrongkind)
1485 0 : break;
1486 : /* wrongkind could be something we don't have in our table... */
1487 :
1488 0 : ereport(ERROR,
1489 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1490 : errmsg(rentry->nota_msg, relname),
1491 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1492 : }
1493 :
1494 : /*
1495 : * RemoveRelations
1496 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1497 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1498 : */
1499 : void
1500 16494 : RemoveRelations(DropStmt *drop)
1501 : {
1502 : ObjectAddresses *objects;
1503 : char relkind;
1504 : ListCell *cell;
1505 16494 : int flags = 0;
1506 16494 : LOCKMODE lockmode = AccessExclusiveLock;
1507 :
1508 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1509 16494 : if (drop->concurrent)
1510 : {
1511 : /*
1512 : * Note that for temporary relations this lock may get upgraded later
1513 : * on, but as no other session can access a temporary relation, this
1514 : * is actually fine.
1515 : */
1516 150 : lockmode = ShareUpdateExclusiveLock;
1517 : Assert(drop->removeType == OBJECT_INDEX);
1518 150 : if (list_length(drop->objects) != 1)
1519 6 : ereport(ERROR,
1520 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1521 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1522 144 : if (drop->behavior == DROP_CASCADE)
1523 0 : ereport(ERROR,
1524 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1525 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1526 : }
1527 :
1528 : /*
1529 : * First we identify all the relations, then we delete them in a single
1530 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1531 : * RESTRICT errors if one of the relations depends on another.
1532 : */
1533 :
1534 : /* Determine required relkind */
1535 16488 : switch (drop->removeType)
1536 : {
1537 14308 : case OBJECT_TABLE:
1538 14308 : relkind = RELKIND_RELATION;
1539 14308 : break;
1540 :
1541 820 : case OBJECT_INDEX:
1542 820 : relkind = RELKIND_INDEX;
1543 820 : break;
1544 :
1545 176 : case OBJECT_SEQUENCE:
1546 176 : relkind = RELKIND_SEQUENCE;
1547 176 : break;
1548 :
1549 914 : case OBJECT_VIEW:
1550 914 : relkind = RELKIND_VIEW;
1551 914 : break;
1552 :
1553 120 : case OBJECT_MATVIEW:
1554 120 : relkind = RELKIND_MATVIEW;
1555 120 : break;
1556 :
1557 150 : case OBJECT_FOREIGN_TABLE:
1558 150 : relkind = RELKIND_FOREIGN_TABLE;
1559 150 : break;
1560 :
1561 0 : default:
1562 0 : elog(ERROR, "unrecognized drop object type: %d",
1563 : (int) drop->removeType);
1564 : relkind = 0; /* keep compiler quiet */
1565 : break;
1566 : }
1567 :
1568 : /* Lock and validate each relation; build a list of object addresses */
1569 16488 : objects = new_object_addresses();
1570 :
1571 36670 : foreach(cell, drop->objects)
1572 : {
1573 20346 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1574 : Oid relOid;
1575 : ObjectAddress obj;
1576 : struct DropRelationCallbackState state;
1577 :
1578 : /*
1579 : * These next few steps are a great deal like relation_openrv, but we
1580 : * don't bother building a relcache entry since we don't need it.
1581 : *
1582 : * Check for shared-cache-inval messages before trying to access the
1583 : * relation. This is needed to cover the case where the name
1584 : * identifies a rel that has been dropped and recreated since the
1585 : * start of our transaction: if we don't flush the old syscache entry,
1586 : * then we'll latch onto that entry and suffer an error later.
1587 : */
1588 20346 : AcceptInvalidationMessages();
1589 :
1590 : /* Look up the appropriate relation using namespace search. */
1591 20346 : state.expected_relkind = relkind;
1592 40692 : state.heap_lockmode = drop->concurrent ?
1593 20346 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1594 : /* We must initialize these fields to show that no locks are held: */
1595 20346 : state.heapOid = InvalidOid;
1596 20346 : state.partParentOid = InvalidOid;
1597 :
1598 20346 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1599 : RangeVarCallbackForDropRelation,
1600 : &state);
1601 :
1602 : /* Not there? */
1603 20326 : if (!OidIsValid(relOid))
1604 : {
1605 1060 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1606 922 : continue;
1607 : }
1608 :
1609 : /*
1610 : * Decide if concurrent mode needs to be used here or not. The
1611 : * callback retrieved the rel's persistence for us.
1612 : */
1613 19266 : if (drop->concurrent &&
1614 138 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1615 : {
1616 : Assert(list_length(drop->objects) == 1 &&
1617 : drop->removeType == OBJECT_INDEX);
1618 120 : flags |= PERFORM_DELETION_CONCURRENTLY;
1619 : }
1620 :
1621 : /*
1622 : * Concurrent index drop cannot be used with partitioned indexes,
1623 : * either.
1624 : */
1625 19266 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1626 120 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1627 6 : ereport(ERROR,
1628 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1629 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1630 : rel->relname)));
1631 :
1632 : /*
1633 : * If we're told to drop a partitioned index, we must acquire lock on
1634 : * all the children of its parent partitioned table before proceeding.
1635 : * Otherwise we'd try to lock the child index partitions before their
1636 : * tables, leading to potential deadlock against other sessions that
1637 : * will lock those objects in the other order.
1638 : */
1639 19260 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1640 70 : (void) find_all_inheritors(state.heapOid,
1641 : state.heap_lockmode,
1642 : NULL);
1643 :
1644 : /* OK, we're ready to delete this one */
1645 19260 : obj.classId = RelationRelationId;
1646 19260 : obj.objectId = relOid;
1647 19260 : obj.objectSubId = 0;
1648 :
1649 19260 : add_exact_object_address(&obj, objects);
1650 : }
1651 :
1652 16324 : performMultipleDeletions(objects, drop->behavior, flags);
1653 :
1654 16188 : free_object_addresses(objects);
1655 16188 : }
1656 :
1657 : /*
1658 : * Before acquiring a table lock, check whether we have sufficient rights.
1659 : * In the case of DROP INDEX, also try to lock the table before the index.
1660 : * Also, if the table to be dropped is a partition, we try to lock the parent
1661 : * first.
1662 : */
1663 : static void
1664 20652 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1665 : void *arg)
1666 : {
1667 : HeapTuple tuple;
1668 : struct DropRelationCallbackState *state;
1669 : char expected_relkind;
1670 : bool is_partition;
1671 : Form_pg_class classform;
1672 : LOCKMODE heap_lockmode;
1673 20652 : bool invalid_system_index = false;
1674 :
1675 20652 : state = (struct DropRelationCallbackState *) arg;
1676 20652 : heap_lockmode = state->heap_lockmode;
1677 :
1678 : /*
1679 : * If we previously locked some other index's heap, and the name we're
1680 : * looking up no longer refers to that relation, release the now-useless
1681 : * lock.
1682 : */
1683 20652 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1684 : {
1685 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1686 0 : state->heapOid = InvalidOid;
1687 : }
1688 :
1689 : /*
1690 : * Similarly, if we previously locked some other partition's heap, and the
1691 : * name we're looking up no longer refers to that relation, release the
1692 : * now-useless lock.
1693 : */
1694 20652 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1695 : {
1696 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1697 0 : state->partParentOid = InvalidOid;
1698 : }
1699 :
1700 : /* Didn't find a relation, so no need for locking or permission checks. */
1701 20652 : if (!OidIsValid(relOid))
1702 1066 : return;
1703 :
1704 19586 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1705 19586 : if (!HeapTupleIsValid(tuple))
1706 0 : return; /* concurrently dropped, so nothing to do */
1707 19586 : classform = (Form_pg_class) GETSTRUCT(tuple);
1708 19586 : is_partition = classform->relispartition;
1709 :
1710 : /* Pass back some data to save lookups in RemoveRelations */
1711 19586 : state->actual_relkind = classform->relkind;
1712 19586 : state->actual_relpersistence = classform->relpersistence;
1713 :
1714 : /*
1715 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1716 : * but RemoveRelations() can only pass one relkind for a given relation.
1717 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1718 : * That means we must be careful before giving the wrong type error when
1719 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1720 : * exists with indexes.
1721 : */
1722 19586 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1723 2832 : expected_relkind = RELKIND_RELATION;
1724 16754 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1725 84 : expected_relkind = RELKIND_INDEX;
1726 : else
1727 16670 : expected_relkind = classform->relkind;
1728 :
1729 19586 : if (state->expected_relkind != expected_relkind)
1730 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1731 0 : state->expected_relkind);
1732 :
1733 : /* Allow DROP to either table owner or schema owner */
1734 19586 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1735 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1736 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1737 18 : get_relkind_objtype(classform->relkind),
1738 18 : rel->relname);
1739 :
1740 : /*
1741 : * Check the case of a system index that might have been invalidated by a
1742 : * failed concurrent process and allow its drop. For the time being, this
1743 : * only concerns indexes of toast relations that became invalid during a
1744 : * REINDEX CONCURRENTLY process.
1745 : */
1746 19568 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1747 : {
1748 : HeapTuple locTuple;
1749 : Form_pg_index indexform;
1750 : bool indisvalid;
1751 :
1752 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1753 0 : if (!HeapTupleIsValid(locTuple))
1754 : {
1755 0 : ReleaseSysCache(tuple);
1756 0 : return;
1757 : }
1758 :
1759 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1760 0 : indisvalid = indexform->indisvalid;
1761 0 : ReleaseSysCache(locTuple);
1762 :
1763 : /* Mark object as being an invalid index of system catalogs */
1764 0 : if (!indisvalid)
1765 0 : invalid_system_index = true;
1766 : }
1767 :
1768 : /* In the case of an invalid index, it is fine to bypass this check */
1769 19568 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1770 2 : ereport(ERROR,
1771 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1772 : errmsg("permission denied: \"%s\" is a system catalog",
1773 : rel->relname)));
1774 :
1775 19566 : ReleaseSysCache(tuple);
1776 :
1777 : /*
1778 : * In DROP INDEX, attempt to acquire lock on the parent table before
1779 : * locking the index. index_drop() will need this anyway, and since
1780 : * regular queries lock tables before their indexes, we risk deadlock if
1781 : * we do it the other way around. No error if we don't find a pg_index
1782 : * entry, though --- the relation may have been dropped. Note that this
1783 : * code will execute for either plain or partitioned indexes.
1784 : */
1785 19566 : if (expected_relkind == RELKIND_INDEX &&
1786 : relOid != oldRelOid)
1787 : {
1788 796 : state->heapOid = IndexGetRelation(relOid, true);
1789 796 : if (OidIsValid(state->heapOid))
1790 796 : LockRelationOid(state->heapOid, heap_lockmode);
1791 : }
1792 :
1793 : /*
1794 : * Similarly, if the relation is a partition, we must acquire lock on its
1795 : * parent before locking the partition. That's because queries lock the
1796 : * parent before its partitions, so we risk deadlock if we do it the other
1797 : * way around.
1798 : */
1799 19566 : if (is_partition && relOid != oldRelOid)
1800 : {
1801 600 : state->partParentOid = get_partition_parent(relOid, true);
1802 600 : if (OidIsValid(state->partParentOid))
1803 600 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1804 : }
1805 : }
1806 :
1807 : /*
1808 : * ExecuteTruncate
1809 : * Executes a TRUNCATE command.
1810 : *
1811 : * This is a multi-relation truncate. We first open and grab exclusive
1812 : * lock on all relations involved, checking permissions and otherwise
1813 : * verifying that the relation is OK for truncation. Note that if relations
1814 : * are foreign tables, at this stage, we have not yet checked that their
1815 : * foreign data in external data sources are OK for truncation. These are
1816 : * checked when foreign data are actually truncated later. In CASCADE mode,
1817 : * relations having FK references to the targeted relations are automatically
1818 : * added to the group; in RESTRICT mode, we check that all FK references are
1819 : * internal to the group that's being truncated. Finally all the relations
1820 : * are truncated and reindexed.
1821 : */
1822 : void
1823 1618 : ExecuteTruncate(TruncateStmt *stmt)
1824 : {
1825 1618 : List *rels = NIL;
1826 1618 : List *relids = NIL;
1827 1618 : List *relids_logged = NIL;
1828 : ListCell *cell;
1829 :
1830 : /*
1831 : * Open, exclusive-lock, and check all the explicitly-specified relations
1832 : */
1833 3484 : foreach(cell, stmt->relations)
1834 : {
1835 1916 : RangeVar *rv = lfirst(cell);
1836 : Relation rel;
1837 1916 : bool recurse = rv->inh;
1838 : Oid myrelid;
1839 1916 : LOCKMODE lockmode = AccessExclusiveLock;
1840 :
1841 1916 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1842 : 0, RangeVarCallbackForTruncate,
1843 : NULL);
1844 :
1845 : /* don't throw error for "TRUNCATE foo, foo" */
1846 1878 : if (list_member_oid(relids, myrelid))
1847 2 : continue;
1848 :
1849 : /* open the relation, we already hold a lock on it */
1850 1876 : rel = table_open(myrelid, NoLock);
1851 :
1852 : /*
1853 : * RangeVarGetRelidExtended() has done most checks with its callback,
1854 : * but other checks with the now-opened Relation remain.
1855 : */
1856 1876 : truncate_check_activity(rel);
1857 :
1858 1876 : rels = lappend(rels, rel);
1859 1876 : relids = lappend_oid(relids, myrelid);
1860 :
1861 : /* Log this relation only if needed for logical decoding */
1862 1876 : if (RelationIsLogicallyLogged(rel))
1863 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1864 :
1865 1876 : if (recurse)
1866 : {
1867 : ListCell *child;
1868 : List *children;
1869 :
1870 1814 : children = find_all_inheritors(myrelid, lockmode, NULL);
1871 :
1872 5456 : foreach(child, children)
1873 : {
1874 3642 : Oid childrelid = lfirst_oid(child);
1875 :
1876 3642 : if (list_member_oid(relids, childrelid))
1877 1814 : continue;
1878 :
1879 : /* find_all_inheritors already got lock */
1880 1828 : rel = table_open(childrelid, NoLock);
1881 :
1882 : /*
1883 : * It is possible that the parent table has children that are
1884 : * temp tables of other backends. We cannot safely access
1885 : * such tables (because of buffering issues), and the best
1886 : * thing to do is to silently ignore them. Note that this
1887 : * check is the same as one of the checks done in
1888 : * truncate_check_activity() called below, still it is kept
1889 : * here for simplicity.
1890 : */
1891 1828 : if (RELATION_IS_OTHER_TEMP(rel))
1892 : {
1893 8 : table_close(rel, lockmode);
1894 8 : continue;
1895 : }
1896 :
1897 : /*
1898 : * Inherited TRUNCATE commands perform access permission
1899 : * checks on the parent table only. So we skip checking the
1900 : * children's permissions and don't call
1901 : * truncate_check_perms() here.
1902 : */
1903 1820 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1904 1820 : truncate_check_activity(rel);
1905 :
1906 1820 : rels = lappend(rels, rel);
1907 1820 : relids = lappend_oid(relids, childrelid);
1908 :
1909 : /* Log this relation only if needed for logical decoding */
1910 1820 : if (RelationIsLogicallyLogged(rel))
1911 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1912 : }
1913 : }
1914 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1915 12 : ereport(ERROR,
1916 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1917 : errmsg("cannot truncate only a partitioned table"),
1918 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1919 : }
1920 :
1921 1568 : ExecuteTruncateGuts(rels, relids, relids_logged,
1922 1568 : stmt->behavior, stmt->restart_seqs, false);
1923 :
1924 : /* And close the rels */
1925 5016 : foreach(cell, rels)
1926 : {
1927 3530 : Relation rel = (Relation) lfirst(cell);
1928 :
1929 3530 : table_close(rel, NoLock);
1930 : }
1931 1486 : }
1932 :
1933 : /*
1934 : * ExecuteTruncateGuts
1935 : *
1936 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1937 : * command (see above) as well as replication subscribers that execute a
1938 : * replicated TRUNCATE action.
1939 : *
1940 : * explicit_rels is the list of Relations to truncate that the command
1941 : * specified. relids is the list of Oids corresponding to explicit_rels.
1942 : * relids_logged is the list of Oids (a subset of relids) that require
1943 : * WAL-logging. This is all a bit redundant, but the existing callers have
1944 : * this information handy in this form.
1945 : */
1946 : void
1947 1606 : ExecuteTruncateGuts(List *explicit_rels,
1948 : List *relids,
1949 : List *relids_logged,
1950 : DropBehavior behavior, bool restart_seqs,
1951 : bool run_as_table_owner)
1952 : {
1953 : List *rels;
1954 1606 : List *seq_relids = NIL;
1955 1606 : HTAB *ft_htab = NULL;
1956 : EState *estate;
1957 : ResultRelInfo *resultRelInfos;
1958 : ResultRelInfo *resultRelInfo;
1959 : SubTransactionId mySubid;
1960 : ListCell *cell;
1961 : Oid *logrelids;
1962 :
1963 : /*
1964 : * Check the explicitly-specified relations.
1965 : *
1966 : * In CASCADE mode, suck in all referencing relations as well. This
1967 : * requires multiple iterations to find indirectly-dependent relations. At
1968 : * each phase, we need to exclusive-lock new rels before looking for their
1969 : * dependencies, else we might miss something. Also, we check each rel as
1970 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1971 : * time on a rel we have no permissions for.
1972 : */
1973 1606 : rels = list_copy(explicit_rels);
1974 1606 : if (behavior == DROP_CASCADE)
1975 : {
1976 : for (;;)
1977 40 : {
1978 : List *newrelids;
1979 :
1980 80 : newrelids = heap_truncate_find_FKs(relids);
1981 80 : if (newrelids == NIL)
1982 40 : break; /* nothing else to add */
1983 :
1984 134 : foreach(cell, newrelids)
1985 : {
1986 94 : Oid relid = lfirst_oid(cell);
1987 : Relation rel;
1988 :
1989 94 : rel = table_open(relid, AccessExclusiveLock);
1990 94 : ereport(NOTICE,
1991 : (errmsg("truncate cascades to table \"%s\"",
1992 : RelationGetRelationName(rel))));
1993 94 : truncate_check_rel(relid, rel->rd_rel);
1994 94 : truncate_check_perms(relid, rel->rd_rel);
1995 94 : truncate_check_activity(rel);
1996 94 : rels = lappend(rels, rel);
1997 94 : relids = lappend_oid(relids, relid);
1998 :
1999 : /* Log this relation only if needed for logical decoding */
2000 94 : if (RelationIsLogicallyLogged(rel))
2001 0 : relids_logged = lappend_oid(relids_logged, relid);
2002 : }
2003 : }
2004 : }
2005 :
2006 : /*
2007 : * Check foreign key references. In CASCADE mode, this should be
2008 : * unnecessary since we just pulled in all the references; but as a
2009 : * cross-check, do it anyway if in an Assert-enabled build.
2010 : */
2011 : #ifdef USE_ASSERT_CHECKING
2012 : heap_truncate_check_FKs(rels, false);
2013 : #else
2014 1606 : if (behavior == DROP_RESTRICT)
2015 1566 : heap_truncate_check_FKs(rels, false);
2016 : #endif
2017 :
2018 : /*
2019 : * If we are asked to restart sequences, find all the sequences, lock them
2020 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2021 : * We want to do this early since it's pointless to do all the truncation
2022 : * work only to fail on sequence permissions.
2023 : */
2024 1532 : if (restart_seqs)
2025 : {
2026 52 : foreach(cell, rels)
2027 : {
2028 26 : Relation rel = (Relation) lfirst(cell);
2029 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2030 : ListCell *seqcell;
2031 :
2032 62 : foreach(seqcell, seqlist)
2033 : {
2034 36 : Oid seq_relid = lfirst_oid(seqcell);
2035 : Relation seq_rel;
2036 :
2037 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2038 :
2039 : /* This check must match AlterSequence! */
2040 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2041 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2042 0 : RelationGetRelationName(seq_rel));
2043 :
2044 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2045 :
2046 36 : relation_close(seq_rel, NoLock);
2047 : }
2048 : }
2049 : }
2050 :
2051 : /* Prepare to catch AFTER triggers. */
2052 1532 : AfterTriggerBeginQuery();
2053 :
2054 : /*
2055 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2056 : * each relation. We don't need to call ExecOpenIndices, though.
2057 : *
2058 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2059 : * though we don't have a range table and don't populate the
2060 : * es_result_relations array. That's a bit bogus, but it's enough to make
2061 : * ExecGetTriggerResultRel() find them.
2062 : */
2063 1532 : estate = CreateExecutorState();
2064 : resultRelInfos = (ResultRelInfo *)
2065 1532 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2066 1532 : resultRelInfo = resultRelInfos;
2067 5234 : foreach(cell, rels)
2068 : {
2069 3702 : Relation rel = (Relation) lfirst(cell);
2070 :
2071 3702 : InitResultRelInfo(resultRelInfo,
2072 : rel,
2073 : 0, /* dummy rangetable index */
2074 : NULL,
2075 : 0);
2076 3702 : estate->es_opened_result_relations =
2077 3702 : lappend(estate->es_opened_result_relations, resultRelInfo);
2078 3702 : resultRelInfo++;
2079 : }
2080 :
2081 : /*
2082 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2083 : * truncating (this is because one of them might throw an error). Also, if
2084 : * we were to allow them to prevent statement execution, that would need
2085 : * to be handled here.
2086 : */
2087 1532 : resultRelInfo = resultRelInfos;
2088 5234 : foreach(cell, rels)
2089 : {
2090 : UserContext ucxt;
2091 :
2092 3702 : if (run_as_table_owner)
2093 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2094 : &ucxt);
2095 3702 : ExecBSTruncateTriggers(estate, resultRelInfo);
2096 3702 : if (run_as_table_owner)
2097 70 : RestoreUserContext(&ucxt);
2098 3702 : resultRelInfo++;
2099 : }
2100 :
2101 : /*
2102 : * OK, truncate each table.
2103 : */
2104 1532 : mySubid = GetCurrentSubTransactionId();
2105 :
2106 5234 : foreach(cell, rels)
2107 : {
2108 3702 : Relation rel = (Relation) lfirst(cell);
2109 :
2110 : /* Skip partitioned tables as there is nothing to do */
2111 3702 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2112 740 : continue;
2113 :
2114 : /*
2115 : * Build the lists of foreign tables belonging to each foreign server
2116 : * and pass each list to the foreign data wrapper's callback function,
2117 : * so that each server can truncate its all foreign tables in bulk.
2118 : * Each list is saved as a single entry in a hash table that uses the
2119 : * server OID as lookup key.
2120 : */
2121 2962 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2122 : {
2123 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2124 : bool found;
2125 : ForeignTruncateInfo *ft_info;
2126 :
2127 : /* First time through, initialize hashtable for foreign tables */
2128 34 : if (!ft_htab)
2129 : {
2130 : HASHCTL hctl;
2131 :
2132 30 : memset(&hctl, 0, sizeof(HASHCTL));
2133 30 : hctl.keysize = sizeof(Oid);
2134 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2135 30 : hctl.hcxt = CurrentMemoryContext;
2136 :
2137 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2138 : 32, /* start small and extend */
2139 : &hctl,
2140 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2141 : }
2142 :
2143 : /* Find or create cached entry for the foreign table */
2144 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2145 34 : if (!found)
2146 30 : ft_info->rels = NIL;
2147 :
2148 : /*
2149 : * Save the foreign table in the entry of the server that the
2150 : * foreign table belongs to.
2151 : */
2152 34 : ft_info->rels = lappend(ft_info->rels, rel);
2153 34 : continue;
2154 : }
2155 :
2156 : /*
2157 : * Normally, we need a transaction-safe truncation here. However, if
2158 : * the table was either created in the current (sub)transaction or has
2159 : * a new relfilenumber in the current (sub)transaction, then we can
2160 : * just truncate it in-place, because a rollback would cause the whole
2161 : * table or the current physical file to be thrown away anyway.
2162 : */
2163 2928 : if (rel->rd_createSubid == mySubid ||
2164 2902 : rel->rd_newRelfilelocatorSubid == mySubid)
2165 : {
2166 : /* Immediate, non-rollbackable truncation is OK */
2167 90 : heap_truncate_one_rel(rel);
2168 : }
2169 : else
2170 : {
2171 : Oid heap_relid;
2172 : Oid toast_relid;
2173 2838 : ReindexParams reindex_params = {0};
2174 :
2175 : /*
2176 : * This effectively deletes all rows in the table, and may be done
2177 : * in a serializable transaction. In that case we must record a
2178 : * rw-conflict in to this transaction from each transaction
2179 : * holding a predicate lock on the table.
2180 : */
2181 2838 : CheckTableForSerializableConflictIn(rel);
2182 :
2183 : /*
2184 : * Need the full transaction-safe pushups.
2185 : *
2186 : * Create a new empty storage file for the relation, and assign it
2187 : * as the relfilenumber value. The old storage file is scheduled
2188 : * for deletion at commit.
2189 : */
2190 2838 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2191 :
2192 2838 : heap_relid = RelationGetRelid(rel);
2193 :
2194 : /*
2195 : * The same for the toast table, if any.
2196 : */
2197 2838 : toast_relid = rel->rd_rel->reltoastrelid;
2198 2838 : if (OidIsValid(toast_relid))
2199 : {
2200 1842 : Relation toastrel = relation_open(toast_relid,
2201 : AccessExclusiveLock);
2202 :
2203 1842 : RelationSetNewRelfilenumber(toastrel,
2204 1842 : toastrel->rd_rel->relpersistence);
2205 1842 : table_close(toastrel, NoLock);
2206 : }
2207 :
2208 : /*
2209 : * Reconstruct the indexes to match, and we're done.
2210 : */
2211 2838 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2212 : &reindex_params);
2213 : }
2214 :
2215 2928 : pgstat_count_truncate(rel);
2216 : }
2217 :
2218 : /* Now go through the hash table, and truncate foreign tables */
2219 1532 : if (ft_htab)
2220 : {
2221 : ForeignTruncateInfo *ft_info;
2222 : HASH_SEQ_STATUS seq;
2223 :
2224 30 : hash_seq_init(&seq, ft_htab);
2225 :
2226 30 : PG_TRY();
2227 : {
2228 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2229 : {
2230 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2231 :
2232 : /* truncate_check_rel() has checked that already */
2233 : Assert(routine->ExecForeignTruncate != NULL);
2234 :
2235 30 : routine->ExecForeignTruncate(ft_info->rels,
2236 : behavior,
2237 : restart_seqs);
2238 : }
2239 : }
2240 8 : PG_FINALLY();
2241 : {
2242 30 : hash_destroy(ft_htab);
2243 : }
2244 30 : PG_END_TRY();
2245 : }
2246 :
2247 : /*
2248 : * Restart owned sequences if we were asked to.
2249 : */
2250 1560 : foreach(cell, seq_relids)
2251 : {
2252 36 : Oid seq_relid = lfirst_oid(cell);
2253 :
2254 36 : ResetSequence(seq_relid);
2255 : }
2256 :
2257 : /*
2258 : * Write a WAL record to allow this set of actions to be logically
2259 : * decoded.
2260 : *
2261 : * Assemble an array of relids so we can write a single WAL record for the
2262 : * whole action.
2263 : */
2264 1524 : if (relids_logged != NIL)
2265 : {
2266 : xl_heap_truncate xlrec;
2267 54 : int i = 0;
2268 :
2269 : /* should only get here if wal_level >= logical */
2270 : Assert(XLogLogicalInfoActive());
2271 :
2272 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2273 144 : foreach(cell, relids_logged)
2274 90 : logrelids[i++] = lfirst_oid(cell);
2275 :
2276 54 : xlrec.dbId = MyDatabaseId;
2277 54 : xlrec.nrelids = list_length(relids_logged);
2278 54 : xlrec.flags = 0;
2279 54 : if (behavior == DROP_CASCADE)
2280 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2281 54 : if (restart_seqs)
2282 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2283 :
2284 54 : XLogBeginInsert();
2285 54 : XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2286 54 : XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2287 :
2288 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2289 :
2290 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2291 : }
2292 :
2293 : /*
2294 : * Process all AFTER STATEMENT TRUNCATE triggers.
2295 : */
2296 1524 : resultRelInfo = resultRelInfos;
2297 5218 : foreach(cell, rels)
2298 : {
2299 : UserContext ucxt;
2300 :
2301 3694 : if (run_as_table_owner)
2302 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2303 : &ucxt);
2304 3694 : ExecASTruncateTriggers(estate, resultRelInfo);
2305 3694 : if (run_as_table_owner)
2306 70 : RestoreUserContext(&ucxt);
2307 3694 : resultRelInfo++;
2308 : }
2309 :
2310 : /* Handle queued AFTER triggers */
2311 1524 : AfterTriggerEndQuery(estate);
2312 :
2313 : /* We can clean up the EState now */
2314 1524 : FreeExecutorState(estate);
2315 :
2316 : /*
2317 : * Close any rels opened by CASCADE (can't do this while EState still
2318 : * holds refs)
2319 : */
2320 1524 : rels = list_difference_ptr(rels, explicit_rels);
2321 1618 : foreach(cell, rels)
2322 : {
2323 94 : Relation rel = (Relation) lfirst(cell);
2324 :
2325 94 : table_close(rel, NoLock);
2326 : }
2327 1524 : }
2328 :
2329 : /*
2330 : * Check that a given relation is safe to truncate. Subroutine for
2331 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2332 : */
2333 : static void
2334 3966 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2335 : {
2336 3966 : char *relname = NameStr(reltuple->relname);
2337 :
2338 : /*
2339 : * Only allow truncate on regular tables, foreign tables using foreign
2340 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2341 : * latter are only being included here for the following checks; no
2342 : * physical truncation will occur in their case.).
2343 : */
2344 3966 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2345 : {
2346 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2347 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2348 :
2349 36 : if (!fdwroutine->ExecForeignTruncate)
2350 2 : ereport(ERROR,
2351 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2352 : errmsg("cannot truncate foreign table \"%s\"",
2353 : relname)));
2354 : }
2355 3928 : else if (reltuple->relkind != RELKIND_RELATION &&
2356 756 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2357 0 : ereport(ERROR,
2358 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2359 : errmsg("\"%s\" is not a table", relname)));
2360 :
2361 : /*
2362 : * Most system catalogs can't be truncated at all, or at least not unless
2363 : * allow_system_table_mods=on. As an exception, however, we allow
2364 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2365 : * to change its relfilenode to match the old cluster, and allowing a
2366 : * TRUNCATE command to be executed is the easiest way of doing that.
2367 : */
2368 3962 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2369 22 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2370 2 : ereport(ERROR,
2371 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2372 : errmsg("permission denied: \"%s\" is a system catalog",
2373 : relname)));
2374 :
2375 3960 : InvokeObjectTruncateHook(relid);
2376 3960 : }
2377 :
2378 : /*
2379 : * Check that current user has the permission to truncate given relation.
2380 : */
2381 : static void
2382 2140 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2383 : {
2384 2140 : char *relname = NameStr(reltuple->relname);
2385 : AclResult aclresult;
2386 :
2387 : /* Permissions checks */
2388 2140 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2389 2140 : if (aclresult != ACLCHECK_OK)
2390 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2391 : relname);
2392 2108 : }
2393 :
2394 : /*
2395 : * Set of extra sanity checks to check if a given relation is safe to
2396 : * truncate. This is split with truncate_check_rel() as
2397 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2398 : */
2399 : static void
2400 3790 : truncate_check_activity(Relation rel)
2401 : {
2402 : /*
2403 : * Don't allow truncate on temp tables of other backends ... their local
2404 : * buffer manager is not going to cope.
2405 : */
2406 3790 : if (RELATION_IS_OTHER_TEMP(rel))
2407 0 : ereport(ERROR,
2408 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2409 : errmsg("cannot truncate temporary tables of other sessions")));
2410 :
2411 : /*
2412 : * Also check for active uses of the relation in the current transaction,
2413 : * including open scans and pending AFTER trigger events.
2414 : */
2415 3790 : CheckTableNotInUse(rel, "TRUNCATE");
2416 3790 : }
2417 :
2418 : /*
2419 : * storage_name
2420 : * returns the name corresponding to a typstorage/attstorage enum value
2421 : */
2422 : static const char *
2423 24 : storage_name(char c)
2424 : {
2425 24 : switch (c)
2426 : {
2427 0 : case TYPSTORAGE_PLAIN:
2428 0 : return "PLAIN";
2429 0 : case TYPSTORAGE_EXTERNAL:
2430 0 : return "EXTERNAL";
2431 12 : case TYPSTORAGE_EXTENDED:
2432 12 : return "EXTENDED";
2433 12 : case TYPSTORAGE_MAIN:
2434 12 : return "MAIN";
2435 0 : default:
2436 0 : return "???";
2437 : }
2438 : }
2439 :
2440 : /*----------
2441 : * MergeAttributes
2442 : * Returns new schema given initial schema and superclasses.
2443 : *
2444 : * Input arguments:
2445 : * 'columns' is the column/attribute definition for the table. (It's a list
2446 : * of ColumnDef's.) It is destructively changed.
2447 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2448 : * 'relpersistence' is the persistence type of the table.
2449 : * 'is_partition' tells if the table is a partition.
2450 : *
2451 : * Output arguments:
2452 : * 'supconstr' receives a list of CookedConstraint representing
2453 : * CHECK constraints belonging to parent relations, updated as
2454 : * necessary to be valid for the child.
2455 : * 'supnotnulls' receives a list of CookedConstraint representing
2456 : * not-null constraints based on those from parent relations.
2457 : *
2458 : * Return value:
2459 : * Completed schema list.
2460 : *
2461 : * Notes:
2462 : * The order in which the attributes are inherited is very important.
2463 : * Intuitively, the inherited attributes should come first. If a table
2464 : * inherits from multiple parents, the order of those attributes are
2465 : * according to the order of the parents specified in CREATE TABLE.
2466 : *
2467 : * Here's an example:
2468 : *
2469 : * create table person (name text, age int4, location point);
2470 : * create table emp (salary int4, manager text) inherits(person);
2471 : * create table student (gpa float8) inherits (person);
2472 : * create table stud_emp (percent int4) inherits (emp, student);
2473 : *
2474 : * The order of the attributes of stud_emp is:
2475 : *
2476 : * person {1:name, 2:age, 3:location}
2477 : * / \
2478 : * {6:gpa} student emp {4:salary, 5:manager}
2479 : * \ /
2480 : * stud_emp {7:percent}
2481 : *
2482 : * If the same attribute name appears multiple times, then it appears
2483 : * in the result table in the proper location for its first appearance.
2484 : *
2485 : * Constraints (including not-null constraints) for the child table
2486 : * are the union of all relevant constraints, from both the child schema
2487 : * and parent tables. In addition, in legacy inheritance, each column that
2488 : * appears in a primary key in any of the parents also gets a NOT NULL
2489 : * constraint (partitioning doesn't need this, because the PK itself gets
2490 : * inherited.)
2491 : *
2492 : * The default value for a child column is defined as:
2493 : * (1) If the child schema specifies a default, that value is used.
2494 : * (2) If neither the child nor any parent specifies a default, then
2495 : * the column will not have a default.
2496 : * (3) If conflicting defaults are inherited from different parents
2497 : * (and not overridden by the child), an error is raised.
2498 : * (4) Otherwise the inherited default is used.
2499 : *
2500 : * Note that the default-value infrastructure is used for generated
2501 : * columns' expressions too, so most of the preceding paragraph applies
2502 : * to generation expressions too. We insist that a child column be
2503 : * generated if and only if its parent(s) are, but it need not have
2504 : * the same generation expression.
2505 : *----------
2506 : */
2507 : static List *
2508 57124 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2509 : bool is_partition, List **supconstr, List **supnotnulls)
2510 : {
2511 57124 : List *inh_columns = NIL;
2512 57124 : List *constraints = NIL;
2513 57124 : List *nnconstraints = NIL;
2514 57124 : bool have_bogus_defaults = false;
2515 : int child_attno;
2516 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2517 57124 : List *saved_columns = NIL;
2518 : ListCell *lc;
2519 :
2520 : /*
2521 : * Check for and reject tables with too many columns. We perform this
2522 : * check relatively early for two reasons: (a) we don't run the risk of
2523 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2524 : * okay if we're processing <= 1600 columns, but could take minutes to
2525 : * execute if the user attempts to create a table with hundreds of
2526 : * thousands of columns.
2527 : *
2528 : * Note that we also need to check that we do not exceed this figure after
2529 : * including columns from inherited relations.
2530 : */
2531 57124 : if (list_length(columns) > MaxHeapAttributeNumber)
2532 0 : ereport(ERROR,
2533 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2534 : errmsg("tables can have at most %d columns",
2535 : MaxHeapAttributeNumber)));
2536 :
2537 : /*
2538 : * Check for duplicate names in the explicit list of attributes.
2539 : *
2540 : * Although we might consider merging such entries in the same way that we
2541 : * handle name conflicts for inherited attributes, it seems to make more
2542 : * sense to assume such conflicts are errors.
2543 : *
2544 : * We don't use foreach() here because we have two nested loops over the
2545 : * columns list, with possible element deletions in the inner one. If we
2546 : * used foreach_delete_current() it could only fix up the state of one of
2547 : * the loops, so it seems cleaner to use looping over list indexes for
2548 : * both loops. Note that any deletion will happen beyond where the outer
2549 : * loop is, so its index never needs adjustment.
2550 : */
2551 267182 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2552 : {
2553 210082 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2554 :
2555 210082 : if (!is_partition && coldef->typeName == NULL)
2556 : {
2557 : /*
2558 : * Typed table column option that does not belong to a column from
2559 : * the type. This works because the columns from the type come
2560 : * first in the list. (We omit this check for partition column
2561 : * lists; those are processed separately below.)
2562 : */
2563 6 : ereport(ERROR,
2564 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2565 : errmsg("column \"%s\" does not exist",
2566 : coldef->colname)));
2567 : }
2568 :
2569 : /* restpos scans all entries beyond coldef; incr is in loop body */
2570 6333472 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2571 : {
2572 6123414 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2573 :
2574 6123414 : if (strcmp(coldef->colname, restdef->colname) == 0)
2575 : {
2576 50 : if (coldef->is_from_type)
2577 : {
2578 : /*
2579 : * merge the column options into the column from the type
2580 : */
2581 32 : coldef->is_not_null = restdef->is_not_null;
2582 32 : coldef->raw_default = restdef->raw_default;
2583 32 : coldef->cooked_default = restdef->cooked_default;
2584 32 : coldef->constraints = restdef->constraints;
2585 32 : coldef->is_from_type = false;
2586 32 : columns = list_delete_nth_cell(columns, restpos);
2587 : }
2588 : else
2589 18 : ereport(ERROR,
2590 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2591 : errmsg("column \"%s\" specified more than once",
2592 : coldef->colname)));
2593 : }
2594 : else
2595 6123364 : restpos++;
2596 : }
2597 : }
2598 :
2599 : /*
2600 : * In case of a partition, there are no new column definitions, only dummy
2601 : * ColumnDefs created for column constraints. Set them aside for now and
2602 : * process them at the end.
2603 : */
2604 57100 : if (is_partition)
2605 : {
2606 7760 : saved_columns = columns;
2607 7760 : columns = NIL;
2608 : }
2609 :
2610 : /*
2611 : * Scan the parents left-to-right, and merge their attributes to form a
2612 : * list of inherited columns (inh_columns).
2613 : */
2614 57100 : child_attno = 0;
2615 67004 : foreach(lc, supers)
2616 : {
2617 9976 : Oid parent = lfirst_oid(lc);
2618 : Relation relation;
2619 : TupleDesc tupleDesc;
2620 : TupleConstr *constr;
2621 : AttrMap *newattmap;
2622 : List *inherited_defaults;
2623 : List *cols_with_defaults;
2624 : List *nnconstrs;
2625 : ListCell *lc1;
2626 : ListCell *lc2;
2627 9976 : Bitmapset *nncols = NULL;
2628 :
2629 : /* caller already got lock */
2630 9976 : relation = table_open(parent, NoLock);
2631 :
2632 : /*
2633 : * Check for active uses of the parent partitioned table in the
2634 : * current transaction, such as being used in some manner by an
2635 : * enclosing command.
2636 : */
2637 9976 : if (is_partition)
2638 7760 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2639 :
2640 : /*
2641 : * We do not allow partitioned tables and partitions to participate in
2642 : * regular inheritance.
2643 : */
2644 9970 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2645 6 : ereport(ERROR,
2646 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2647 : errmsg("cannot inherit from partitioned table \"%s\"",
2648 : RelationGetRelationName(relation))));
2649 9964 : if (relation->rd_rel->relispartition && !is_partition)
2650 6 : ereport(ERROR,
2651 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2652 : errmsg("cannot inherit from partition \"%s\"",
2653 : RelationGetRelationName(relation))));
2654 :
2655 9958 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2656 7756 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2657 7736 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2658 0 : ereport(ERROR,
2659 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2660 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2661 : RelationGetRelationName(relation))));
2662 :
2663 : /*
2664 : * If the parent is permanent, so must be all of its partitions. Note
2665 : * that inheritance allows that case.
2666 : */
2667 9958 : if (is_partition &&
2668 7754 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2669 : relpersistence == RELPERSISTENCE_TEMP)
2670 6 : ereport(ERROR,
2671 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2672 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2673 : RelationGetRelationName(relation))));
2674 :
2675 : /* Permanent rels cannot inherit from temporary ones */
2676 9952 : if (relpersistence != RELPERSISTENCE_TEMP &&
2677 9604 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2678 24 : ereport(ERROR,
2679 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2680 : errmsg(!is_partition
2681 : ? "cannot inherit from temporary relation \"%s\""
2682 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2683 : RelationGetRelationName(relation))));
2684 :
2685 : /* If existing rel is temp, it must belong to this session */
2686 9928 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2687 294 : !relation->rd_islocaltemp)
2688 0 : ereport(ERROR,
2689 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2690 : errmsg(!is_partition
2691 : ? "cannot inherit from temporary relation of another session"
2692 : : "cannot create as partition of temporary relation of another session")));
2693 :
2694 : /*
2695 : * We should have an UNDER permission flag for this, but for now,
2696 : * demand that creator of a child table own the parent.
2697 : */
2698 9928 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2699 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2700 0 : RelationGetRelationName(relation));
2701 :
2702 9928 : tupleDesc = RelationGetDescr(relation);
2703 9928 : constr = tupleDesc->constr;
2704 :
2705 : /*
2706 : * newattmap->attnums[] will contain the child-table attribute numbers
2707 : * for the attributes of this parent table. (They are not the same
2708 : * for parents after the first one, nor if we have dropped columns.)
2709 : */
2710 9928 : newattmap = make_attrmap(tupleDesc->natts);
2711 :
2712 : /* We can't process inherited defaults until newattmap is complete. */
2713 9928 : inherited_defaults = cols_with_defaults = NIL;
2714 :
2715 : /*
2716 : * Request attnotnull on columns that have a not-null constraint
2717 : * that's not marked NO INHERIT.
2718 : */
2719 9928 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2720 : true, false);
2721 22032 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2722 2176 : nncols = bms_add_member(nncols, cc->attnum);
2723 :
2724 29824 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2725 19896 : parent_attno++)
2726 : {
2727 19920 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2728 : parent_attno - 1);
2729 19920 : char *attributeName = NameStr(attribute->attname);
2730 : int exist_attno;
2731 : ColumnDef *newdef;
2732 : ColumnDef *mergeddef;
2733 :
2734 : /*
2735 : * Ignore dropped columns in the parent.
2736 : */
2737 19920 : if (attribute->attisdropped)
2738 192 : continue; /* leave newattmap->attnums entry as zero */
2739 :
2740 : /*
2741 : * Create new column definition
2742 : */
2743 19728 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2744 : attribute->atttypmod, attribute->attcollation);
2745 19728 : newdef->storage = attribute->attstorage;
2746 19728 : newdef->generated = attribute->attgenerated;
2747 19728 : if (CompressionMethodIsValid(attribute->attcompression))
2748 30 : newdef->compression =
2749 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2750 :
2751 : /*
2752 : * Regular inheritance children are independent enough not to
2753 : * inherit identity columns. But partitions are integral part of
2754 : * a partitioned table and inherit identity column.
2755 : */
2756 19728 : if (is_partition)
2757 15652 : newdef->identity = attribute->attidentity;
2758 :
2759 : /*
2760 : * Does it match some previously considered column from another
2761 : * parent?
2762 : */
2763 19728 : exist_attno = findAttrByName(attributeName, inh_columns);
2764 19728 : if (exist_attno > 0)
2765 : {
2766 : /*
2767 : * Yes, try to merge the two column definitions.
2768 : */
2769 314 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2770 :
2771 290 : newattmap->attnums[parent_attno - 1] = exist_attno;
2772 :
2773 : /*
2774 : * Partitions have only one parent, so conflict should never
2775 : * occur.
2776 : */
2777 : Assert(!is_partition);
2778 : }
2779 : else
2780 : {
2781 : /*
2782 : * No, create a new inherited column
2783 : */
2784 19414 : newdef->inhcount = 1;
2785 19414 : newdef->is_local = false;
2786 19414 : inh_columns = lappend(inh_columns, newdef);
2787 :
2788 19414 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2789 19414 : mergeddef = newdef;
2790 : }
2791 :
2792 : /*
2793 : * mark attnotnull if parent has it
2794 : */
2795 19704 : if (bms_is_member(parent_attno, nncols))
2796 2176 : mergeddef->is_not_null = true;
2797 :
2798 : /*
2799 : * Locate default/generation expression if any
2800 : */
2801 19704 : if (attribute->atthasdef)
2802 : {
2803 : Node *this_default;
2804 :
2805 556 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2806 556 : if (this_default == NULL)
2807 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2808 : parent_attno, RelationGetRelationName(relation));
2809 :
2810 : /*
2811 : * If it's a GENERATED default, it might contain Vars that
2812 : * need to be mapped to the inherited column(s)' new numbers.
2813 : * We can't do that till newattmap is ready, so just remember
2814 : * all the inherited default expressions for the moment.
2815 : */
2816 556 : inherited_defaults = lappend(inherited_defaults, this_default);
2817 556 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2818 : }
2819 : }
2820 :
2821 : /*
2822 : * Now process any inherited default expressions, adjusting attnos
2823 : * using the completed newattmap map.
2824 : */
2825 10460 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2826 : {
2827 556 : Node *this_default = (Node *) lfirst(lc1);
2828 556 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2829 : bool found_whole_row;
2830 :
2831 : /* Adjust Vars to match new table's column numbering */
2832 556 : this_default = map_variable_attnos(this_default,
2833 : 1, 0,
2834 : newattmap,
2835 : InvalidOid, &found_whole_row);
2836 :
2837 : /*
2838 : * For the moment we have to reject whole-row variables. We could
2839 : * convert them, if we knew the new table's rowtype OID, but that
2840 : * hasn't been assigned yet. (A variable could only appear in a
2841 : * generation expression, so the error message is correct.)
2842 : */
2843 556 : if (found_whole_row)
2844 0 : ereport(ERROR,
2845 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2846 : errmsg("cannot convert whole-row table reference"),
2847 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2848 : def->colname,
2849 : RelationGetRelationName(relation))));
2850 :
2851 : /*
2852 : * If we already had a default from some prior parent, check to
2853 : * see if they are the same. If so, no problem; if not, mark the
2854 : * column as having a bogus default. Below, we will complain if
2855 : * the bogus default isn't overridden by the child columns.
2856 : */
2857 : Assert(def->raw_default == NULL);
2858 556 : if (def->cooked_default == NULL)
2859 526 : def->cooked_default = this_default;
2860 30 : else if (!equal(def->cooked_default, this_default))
2861 : {
2862 24 : def->cooked_default = &bogus_marker;
2863 24 : have_bogus_defaults = true;
2864 : }
2865 : }
2866 :
2867 : /*
2868 : * Now copy the CHECK constraints of this parent, adjusting attnos
2869 : * using the completed newattmap map. Identically named constraints
2870 : * are merged if possible, else we throw error.
2871 : */
2872 9904 : if (constr && constr->num_check > 0)
2873 : {
2874 328 : ConstrCheck *check = constr->check;
2875 :
2876 956 : for (int i = 0; i < constr->num_check; i++)
2877 : {
2878 628 : char *name = check[i].ccname;
2879 : Node *expr;
2880 : bool found_whole_row;
2881 :
2882 : /* ignore if the constraint is non-inheritable */
2883 628 : if (check[i].ccnoinherit)
2884 48 : continue;
2885 :
2886 : /* Adjust Vars to match new table's column numbering */
2887 580 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2888 : 1, 0,
2889 : newattmap,
2890 : InvalidOid, &found_whole_row);
2891 :
2892 : /*
2893 : * For the moment we have to reject whole-row variables. We
2894 : * could convert them, if we knew the new table's rowtype OID,
2895 : * but that hasn't been assigned yet.
2896 : */
2897 580 : if (found_whole_row)
2898 0 : ereport(ERROR,
2899 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2900 : errmsg("cannot convert whole-row table reference"),
2901 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2902 : name,
2903 : RelationGetRelationName(relation))));
2904 :
2905 580 : constraints = MergeCheckConstraint(constraints, name, expr,
2906 580 : check[i].ccenforced);
2907 : }
2908 : }
2909 :
2910 : /*
2911 : * Also copy the not-null constraints from this parent. The
2912 : * attnotnull markings were already installed above.
2913 : */
2914 21984 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2915 : {
2916 : Assert(nn->contype == CONSTR_NOTNULL);
2917 :
2918 2176 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2919 :
2920 2176 : nnconstraints = lappend(nnconstraints, nn);
2921 : }
2922 :
2923 9904 : free_attrmap(newattmap);
2924 :
2925 : /*
2926 : * Close the parent rel, but keep our lock on it until xact commit.
2927 : * That will prevent someone else from deleting or ALTERing the parent
2928 : * before the child is committed.
2929 : */
2930 9904 : table_close(relation, NoLock);
2931 : }
2932 :
2933 : /*
2934 : * If we had no inherited attributes, the result columns are just the
2935 : * explicitly declared columns. Otherwise, we need to merge the declared
2936 : * columns into the inherited column list. Although, we never have any
2937 : * explicitly declared columns if the table is a partition.
2938 : */
2939 57028 : if (inh_columns != NIL)
2940 : {
2941 9530 : int newcol_attno = 0;
2942 :
2943 10386 : foreach(lc, columns)
2944 : {
2945 904 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2946 904 : char *attributeName = newdef->colname;
2947 : int exist_attno;
2948 :
2949 : /*
2950 : * Partitions have only one parent and have no column definitions
2951 : * of their own, so conflict should never occur.
2952 : */
2953 : Assert(!is_partition);
2954 :
2955 904 : newcol_attno++;
2956 :
2957 : /*
2958 : * Does it match some inherited column?
2959 : */
2960 904 : exist_attno = findAttrByName(attributeName, inh_columns);
2961 904 : if (exist_attno > 0)
2962 : {
2963 : /*
2964 : * Yes, try to merge the two column definitions.
2965 : */
2966 304 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2967 : }
2968 : else
2969 : {
2970 : /*
2971 : * No, attach new column unchanged to result columns.
2972 : */
2973 600 : inh_columns = lappend(inh_columns, newdef);
2974 : }
2975 : }
2976 :
2977 9482 : columns = inh_columns;
2978 :
2979 : /*
2980 : * Check that we haven't exceeded the legal # of columns after merging
2981 : * in inherited columns.
2982 : */
2983 9482 : if (list_length(columns) > MaxHeapAttributeNumber)
2984 0 : ereport(ERROR,
2985 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2986 : errmsg("tables can have at most %d columns",
2987 : MaxHeapAttributeNumber)));
2988 : }
2989 :
2990 : /*
2991 : * Now that we have the column definition list for a partition, we can
2992 : * check whether the columns referenced in the column constraint specs
2993 : * actually exist. Also, merge column defaults.
2994 : */
2995 56980 : if (is_partition)
2996 : {
2997 7930 : foreach(lc, saved_columns)
2998 : {
2999 212 : ColumnDef *restdef = lfirst(lc);
3000 212 : bool found = false;
3001 : ListCell *l;
3002 :
3003 804 : foreach(l, columns)
3004 : {
3005 604 : ColumnDef *coldef = lfirst(l);
3006 :
3007 604 : if (strcmp(coldef->colname, restdef->colname) == 0)
3008 : {
3009 212 : found = true;
3010 :
3011 : /*
3012 : * Check for conflicts related to generated columns.
3013 : *
3014 : * Same rules as above: generated-ness has to match the
3015 : * parent, but the contents of the generation expression
3016 : * can be different.
3017 : */
3018 212 : if (coldef->generated)
3019 : {
3020 110 : if (restdef->raw_default && !restdef->generated)
3021 6 : ereport(ERROR,
3022 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3023 : errmsg("column \"%s\" inherits from generated column but specifies default",
3024 : restdef->colname)));
3025 104 : if (restdef->identity)
3026 0 : ereport(ERROR,
3027 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3028 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3029 : restdef->colname)));
3030 : }
3031 : else
3032 : {
3033 102 : if (restdef->generated)
3034 6 : ereport(ERROR,
3035 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3036 : errmsg("child column \"%s\" specifies generation expression",
3037 : restdef->colname),
3038 : errhint("A child table column cannot be generated unless its parent column is.")));
3039 : }
3040 :
3041 : /*
3042 : * Override the parent's default value for this column
3043 : * (coldef->cooked_default) with the partition's local
3044 : * definition (restdef->raw_default), if there's one. It
3045 : * should be physically impossible to get a cooked default
3046 : * in the local definition or a raw default in the
3047 : * inherited definition, but make sure they're nulls, for
3048 : * future-proofing.
3049 : */
3050 : Assert(restdef->cooked_default == NULL);
3051 : Assert(coldef->raw_default == NULL);
3052 200 : if (restdef->raw_default)
3053 : {
3054 128 : coldef->raw_default = restdef->raw_default;
3055 128 : coldef->cooked_default = NULL;
3056 : }
3057 : }
3058 : }
3059 :
3060 : /* complain for constraints on columns not in parent */
3061 200 : if (!found)
3062 0 : ereport(ERROR,
3063 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3064 : errmsg("column \"%s\" does not exist",
3065 : restdef->colname)));
3066 : }
3067 : }
3068 :
3069 : /*
3070 : * If we found any conflicting parent default values, check to make sure
3071 : * they were overridden by the child.
3072 : */
3073 56968 : if (have_bogus_defaults)
3074 : {
3075 54 : foreach(lc, columns)
3076 : {
3077 42 : ColumnDef *def = lfirst(lc);
3078 :
3079 42 : if (def->cooked_default == &bogus_marker)
3080 : {
3081 12 : if (def->generated)
3082 6 : ereport(ERROR,
3083 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3084 : errmsg("column \"%s\" inherits conflicting generation expressions",
3085 : def->colname),
3086 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3087 : else
3088 6 : ereport(ERROR,
3089 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3090 : errmsg("column \"%s\" inherits conflicting default values",
3091 : def->colname),
3092 : errhint("To resolve the conflict, specify a default explicitly.")));
3093 : }
3094 : }
3095 : }
3096 :
3097 56956 : *supconstr = constraints;
3098 56956 : *supnotnulls = nnconstraints;
3099 :
3100 56956 : return columns;
3101 : }
3102 :
3103 :
3104 : /*
3105 : * MergeCheckConstraint
3106 : * Try to merge an inherited CHECK constraint with previous ones
3107 : *
3108 : * If we inherit identically-named constraints from multiple parents, we must
3109 : * merge them, or throw an error if they don't have identical definitions.
3110 : *
3111 : * constraints is a list of CookedConstraint structs for previous constraints.
3112 : *
3113 : * If the new constraint matches an existing one, then the existing
3114 : * constraint's inheritance count is updated. If there is a conflict (same
3115 : * name but different expression), throw an error. If the constraint neither
3116 : * matches nor conflicts with an existing one, a new constraint is appended to
3117 : * the list.
3118 : */
3119 : static List *
3120 580 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3121 : {
3122 : ListCell *lc;
3123 : CookedConstraint *newcon;
3124 :
3125 1480 : foreach(lc, constraints)
3126 : {
3127 1026 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3128 :
3129 : Assert(ccon->contype == CONSTR_CHECK);
3130 :
3131 : /* Non-matching names never conflict */
3132 1026 : if (strcmp(ccon->name, name) != 0)
3133 900 : continue;
3134 :
3135 126 : if (equal(expr, ccon->expr))
3136 : {
3137 : /* OK to merge constraint with existing */
3138 126 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3139 : &ccon->inhcount))
3140 0 : ereport(ERROR,
3141 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3142 : errmsg("too many inheritance parents"));
3143 :
3144 : /*
3145 : * When enforceability differs, the merged constraint should be
3146 : * marked as ENFORCED because one of the parents is ENFORCED.
3147 : */
3148 126 : if (!ccon->is_enforced && is_enforced)
3149 : {
3150 24 : ccon->is_enforced = true;
3151 24 : ccon->skip_validation = false;
3152 : }
3153 :
3154 126 : return constraints;
3155 : }
3156 :
3157 0 : ereport(ERROR,
3158 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3159 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3160 : name)));
3161 : }
3162 :
3163 : /*
3164 : * Constraint couldn't be merged with an existing one and also didn't
3165 : * conflict with an existing one, so add it as a new one to the list.
3166 : */
3167 454 : newcon = palloc0_object(CookedConstraint);
3168 454 : newcon->contype = CONSTR_CHECK;
3169 454 : newcon->name = pstrdup(name);
3170 454 : newcon->expr = expr;
3171 454 : newcon->inhcount = 1;
3172 454 : newcon->is_enforced = is_enforced;
3173 454 : newcon->skip_validation = !is_enforced;
3174 454 : return lappend(constraints, newcon);
3175 : }
3176 :
3177 : /*
3178 : * MergeChildAttribute
3179 : * Merge given child attribute definition into given inherited attribute.
3180 : *
3181 : * Input arguments:
3182 : * 'inh_columns' is the list of inherited ColumnDefs.
3183 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3184 : * 'newcol_attno' is the attribute number in child table's schema definition
3185 : * 'newdef' is the column/attribute definition from the child table.
3186 : *
3187 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3188 : * ColumnDef remains unchanged.
3189 : *
3190 : * Notes:
3191 : * - The attribute is merged according to the rules laid out in the prologue
3192 : * of MergeAttributes().
3193 : * - If matching inherited attribute exists but the child attribute can not be
3194 : * merged into it, the function throws respective errors.
3195 : * - A partition can not have its own column definitions. Hence this function
3196 : * is applicable only to a regular inheritance child.
3197 : */
3198 : static void
3199 304 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3200 : {
3201 304 : char *attributeName = newdef->colname;
3202 : ColumnDef *inhdef;
3203 : Oid inhtypeid,
3204 : newtypeid;
3205 : int32 inhtypmod,
3206 : newtypmod;
3207 : Oid inhcollid,
3208 : newcollid;
3209 :
3210 304 : if (exist_attno == newcol_attno)
3211 276 : ereport(NOTICE,
3212 : (errmsg("merging column \"%s\" with inherited definition",
3213 : attributeName)));
3214 : else
3215 28 : ereport(NOTICE,
3216 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3217 : errdetail("User-specified column moved to the position of the inherited column.")));
3218 :
3219 304 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3220 :
3221 : /*
3222 : * Must have the same type and typmod
3223 : */
3224 304 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3225 304 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3226 304 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3227 12 : ereport(ERROR,
3228 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3229 : errmsg("column \"%s\" has a type conflict",
3230 : attributeName),
3231 : errdetail("%s versus %s",
3232 : format_type_with_typemod(inhtypeid, inhtypmod),
3233 : format_type_with_typemod(newtypeid, newtypmod))));
3234 :
3235 : /*
3236 : * Must have the same collation
3237 : */
3238 292 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3239 292 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3240 292 : if (inhcollid != newcollid)
3241 6 : ereport(ERROR,
3242 : (errcode(ERRCODE_COLLATION_MISMATCH),
3243 : errmsg("column \"%s\" has a collation conflict",
3244 : attributeName),
3245 : errdetail("\"%s\" versus \"%s\"",
3246 : get_collation_name(inhcollid),
3247 : get_collation_name(newcollid))));
3248 :
3249 : /*
3250 : * Identity is never inherited by a regular inheritance child. Pick
3251 : * child's identity definition if there's one.
3252 : */
3253 286 : inhdef->identity = newdef->identity;
3254 :
3255 : /*
3256 : * Copy storage parameter
3257 : */
3258 286 : if (inhdef->storage == 0)
3259 0 : inhdef->storage = newdef->storage;
3260 286 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3261 6 : ereport(ERROR,
3262 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3263 : errmsg("column \"%s\" has a storage parameter conflict",
3264 : attributeName),
3265 : errdetail("%s versus %s",
3266 : storage_name(inhdef->storage),
3267 : storage_name(newdef->storage))));
3268 :
3269 : /*
3270 : * Copy compression parameter
3271 : */
3272 280 : if (inhdef->compression == NULL)
3273 274 : inhdef->compression = newdef->compression;
3274 6 : else if (newdef->compression != NULL)
3275 : {
3276 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3277 6 : ereport(ERROR,
3278 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3279 : errmsg("column \"%s\" has a compression method conflict",
3280 : attributeName),
3281 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3282 : }
3283 :
3284 : /*
3285 : * Merge of not-null constraints = OR 'em together
3286 : */
3287 274 : inhdef->is_not_null |= newdef->is_not_null;
3288 :
3289 : /*
3290 : * Check for conflicts related to generated columns.
3291 : *
3292 : * If the parent column is generated, the child column will be made a
3293 : * generated column if it isn't already. If it is a generated column,
3294 : * we'll take its generation expression in preference to the parent's. We
3295 : * must check that the child column doesn't specify a default value or
3296 : * identity, which matches the rules for a single column in
3297 : * parse_utilcmd.c.
3298 : *
3299 : * Conversely, if the parent column is not generated, the child column
3300 : * can't be either. (We used to allow that, but it results in being able
3301 : * to override the generation expression via UPDATEs through the parent.)
3302 : */
3303 274 : if (inhdef->generated)
3304 : {
3305 26 : if (newdef->raw_default && !newdef->generated)
3306 6 : ereport(ERROR,
3307 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3308 : errmsg("column \"%s\" inherits from generated column but specifies default",
3309 : inhdef->colname)));
3310 20 : if (newdef->identity)
3311 6 : ereport(ERROR,
3312 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3313 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3314 : inhdef->colname)));
3315 : }
3316 : else
3317 : {
3318 248 : if (newdef->generated)
3319 6 : ereport(ERROR,
3320 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3321 : errmsg("child column \"%s\" specifies generation expression",
3322 : inhdef->colname),
3323 : errhint("A child table column cannot be generated unless its parent column is.")));
3324 : }
3325 :
3326 : /*
3327 : * If new def has a default, override previous default
3328 : */
3329 256 : if (newdef->raw_default != NULL)
3330 : {
3331 18 : inhdef->raw_default = newdef->raw_default;
3332 18 : inhdef->cooked_default = newdef->cooked_default;
3333 : }
3334 :
3335 : /* Mark the column as locally defined */
3336 256 : inhdef->is_local = true;
3337 256 : }
3338 :
3339 : /*
3340 : * MergeInheritedAttribute
3341 : * Merge given parent attribute definition into specified attribute
3342 : * inherited from the previous parents.
3343 : *
3344 : * Input arguments:
3345 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3346 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3347 : * 'newdef' is the new parent column/attribute definition to be merged.
3348 : *
3349 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3350 : *
3351 : * Notes:
3352 : * - The attribute is merged according to the rules laid out in the prologue
3353 : * of MergeAttributes().
3354 : * - If matching inherited attribute exists but the new attribute can not be
3355 : * merged into it, the function throws respective errors.
3356 : * - A partition inherits from only a single parent. Hence this function is
3357 : * applicable only to a regular inheritance.
3358 : */
3359 : static ColumnDef *
3360 314 : MergeInheritedAttribute(List *inh_columns,
3361 : int exist_attno,
3362 : const ColumnDef *newdef)
3363 : {
3364 314 : char *attributeName = newdef->colname;
3365 : ColumnDef *prevdef;
3366 : Oid prevtypeid,
3367 : newtypeid;
3368 : int32 prevtypmod,
3369 : newtypmod;
3370 : Oid prevcollid,
3371 : newcollid;
3372 :
3373 314 : ereport(NOTICE,
3374 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3375 : attributeName)));
3376 314 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3377 :
3378 : /*
3379 : * Must have the same type and typmod
3380 : */
3381 314 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3382 314 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3383 314 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3384 0 : ereport(ERROR,
3385 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3386 : errmsg("inherited column \"%s\" has a type conflict",
3387 : attributeName),
3388 : errdetail("%s versus %s",
3389 : format_type_with_typemod(prevtypeid, prevtypmod),
3390 : format_type_with_typemod(newtypeid, newtypmod))));
3391 :
3392 : /*
3393 : * Must have the same collation
3394 : */
3395 314 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3396 314 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3397 314 : if (prevcollid != newcollid)
3398 0 : ereport(ERROR,
3399 : (errcode(ERRCODE_COLLATION_MISMATCH),
3400 : errmsg("inherited column \"%s\" has a collation conflict",
3401 : attributeName),
3402 : errdetail("\"%s\" versus \"%s\"",
3403 : get_collation_name(prevcollid),
3404 : get_collation_name(newcollid))));
3405 :
3406 : /*
3407 : * Copy/check storage parameter
3408 : */
3409 314 : if (prevdef->storage == 0)
3410 0 : prevdef->storage = newdef->storage;
3411 314 : else if (prevdef->storage != newdef->storage)
3412 6 : ereport(ERROR,
3413 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3414 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3415 : attributeName),
3416 : errdetail("%s versus %s",
3417 : storage_name(prevdef->storage),
3418 : storage_name(newdef->storage))));
3419 :
3420 : /*
3421 : * Copy/check compression parameter
3422 : */
3423 308 : if (prevdef->compression == NULL)
3424 296 : prevdef->compression = newdef->compression;
3425 12 : else if (newdef->compression != NULL)
3426 : {
3427 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3428 6 : ereport(ERROR,
3429 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3430 : errmsg("column \"%s\" has a compression method conflict",
3431 : attributeName),
3432 : errdetail("%s versus %s",
3433 : prevdef->compression, newdef->compression)));
3434 : }
3435 :
3436 : /*
3437 : * Check for GENERATED conflicts
3438 : */
3439 302 : if (prevdef->generated != newdef->generated)
3440 12 : ereport(ERROR,
3441 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3442 : errmsg("inherited column \"%s\" has a generation conflict",
3443 : attributeName)));
3444 :
3445 : /*
3446 : * Default and other constraints are handled by the caller.
3447 : */
3448 :
3449 290 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3450 : &prevdef->inhcount))
3451 0 : ereport(ERROR,
3452 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3453 : errmsg("too many inheritance parents"));
3454 :
3455 290 : return prevdef;
3456 : }
3457 :
3458 : /*
3459 : * StoreCatalogInheritance
3460 : * Updates the system catalogs with proper inheritance information.
3461 : *
3462 : * supers is a list of the OIDs of the new relation's direct ancestors.
3463 : */
3464 : static void
3465 56410 : StoreCatalogInheritance(Oid relationId, List *supers,
3466 : bool child_is_partition)
3467 : {
3468 : Relation relation;
3469 : int32 seqNumber;
3470 : ListCell *entry;
3471 :
3472 : /*
3473 : * sanity checks
3474 : */
3475 : Assert(OidIsValid(relationId));
3476 :
3477 56410 : if (supers == NIL)
3478 47264 : return;
3479 :
3480 : /*
3481 : * Store INHERITS information in pg_inherits using direct ancestors only.
3482 : * Also enter dependencies on the direct ancestors, and make sure they are
3483 : * marked with relhassubclass = true.
3484 : *
3485 : * (Once upon a time, both direct and indirect ancestors were found here
3486 : * and then entered into pg_ipl. Since that catalog doesn't exist
3487 : * anymore, there's no need to look for indirect ancestors.)
3488 : */
3489 9146 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3490 :
3491 9146 : seqNumber = 1;
3492 18588 : foreach(entry, supers)
3493 : {
3494 9442 : Oid parentOid = lfirst_oid(entry);
3495 :
3496 9442 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3497 : child_is_partition);
3498 9442 : seqNumber++;
3499 : }
3500 :
3501 9146 : table_close(relation, RowExclusiveLock);
3502 : }
3503 :
3504 : /*
3505 : * Make catalog entries showing relationId as being an inheritance child
3506 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3507 : */
3508 : static void
3509 11656 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3510 : int32 seqNumber, Relation inhRelation,
3511 : bool child_is_partition)
3512 : {
3513 : ObjectAddress childobject,
3514 : parentobject;
3515 :
3516 : /* store the pg_inherits row */
3517 11656 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3518 :
3519 : /*
3520 : * Store a dependency too
3521 : */
3522 11656 : parentobject.classId = RelationRelationId;
3523 11656 : parentobject.objectId = parentOid;
3524 11656 : parentobject.objectSubId = 0;
3525 11656 : childobject.classId = RelationRelationId;
3526 11656 : childobject.objectId = relationId;
3527 11656 : childobject.objectSubId = 0;
3528 :
3529 11656 : recordDependencyOn(&childobject, &parentobject,
3530 : child_dependency_type(child_is_partition));
3531 :
3532 : /*
3533 : * Post creation hook of this inheritance. Since object_access_hook
3534 : * doesn't take multiple object identifiers, we relay oid of parent
3535 : * relation using auxiliary_id argument.
3536 : */
3537 11656 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3538 : relationId, 0,
3539 : parentOid, false);
3540 :
3541 : /*
3542 : * Mark the parent as having subclasses.
3543 : */
3544 11656 : SetRelationHasSubclass(parentOid, true);
3545 11656 : }
3546 :
3547 : /*
3548 : * Look for an existing column entry with the given name.
3549 : *
3550 : * Returns the index (starting with 1) if attribute already exists in columns,
3551 : * 0 if it doesn't.
3552 : */
3553 : static int
3554 20632 : findAttrByName(const char *attributeName, const List *columns)
3555 : {
3556 : ListCell *lc;
3557 20632 : int i = 1;
3558 :
3559 36778 : foreach(lc, columns)
3560 : {
3561 16764 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3562 618 : return i;
3563 :
3564 16146 : i++;
3565 : }
3566 20014 : return 0;
3567 : }
3568 :
3569 :
3570 : /*
3571 : * SetRelationHasSubclass
3572 : * Set the value of the relation's relhassubclass field in pg_class.
3573 : *
3574 : * It's always safe to set this field to true, because all SQL commands are
3575 : * ready to see true and then find no children. On the other hand, commands
3576 : * generally assume zero children if this is false.
3577 : *
3578 : * Caller must hold any self-exclusive lock until end of transaction. If the
3579 : * new value is false, caller must have acquired that lock before reading the
3580 : * evidence that justified the false value. That way, it properly waits if
3581 : * another backend is simultaneously concluding no need to change the tuple
3582 : * (new and old values are true).
3583 : *
3584 : * NOTE: an important side-effect of this operation is that an SI invalidation
3585 : * message is sent out to all backends --- including me --- causing plans
3586 : * referencing the relation to be rebuilt with the new list of children.
3587 : * This must happen even if we find that no change is needed in the pg_class
3588 : * row.
3589 : */
3590 : void
3591 14580 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3592 : {
3593 : Relation relationRelation;
3594 : HeapTuple tuple;
3595 : Form_pg_class classtuple;
3596 :
3597 : Assert(CheckRelationOidLockedByMe(relationId,
3598 : ShareUpdateExclusiveLock, false) ||
3599 : CheckRelationOidLockedByMe(relationId,
3600 : ShareRowExclusiveLock, true));
3601 :
3602 : /*
3603 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3604 : */
3605 14580 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3606 14580 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3607 14580 : if (!HeapTupleIsValid(tuple))
3608 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3609 14580 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3610 :
3611 14580 : if (classtuple->relhassubclass != relhassubclass)
3612 : {
3613 7358 : classtuple->relhassubclass = relhassubclass;
3614 7358 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3615 : }
3616 : else
3617 : {
3618 : /* no need to change tuple, but force relcache rebuild anyway */
3619 7222 : CacheInvalidateRelcacheByTuple(tuple);
3620 : }
3621 :
3622 14580 : heap_freetuple(tuple);
3623 14580 : table_close(relationRelation, RowExclusiveLock);
3624 14580 : }
3625 :
3626 : /*
3627 : * CheckRelationTableSpaceMove
3628 : * Check if relation can be moved to new tablespace.
3629 : *
3630 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3631 : *
3632 : * Returns true if the relation can be moved to the new tablespace; raises
3633 : * an error if it is not possible to do the move; returns false if the move
3634 : * would have no effect.
3635 : */
3636 : bool
3637 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3638 : {
3639 : Oid oldTableSpaceId;
3640 :
3641 : /*
3642 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3643 : * stored as 0.
3644 : */
3645 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3646 226 : if (newTableSpaceId == oldTableSpaceId ||
3647 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3648 10 : return false;
3649 :
3650 : /*
3651 : * We cannot support moving mapped relations into different tablespaces.
3652 : * (In particular this eliminates all shared catalogs.)
3653 : */
3654 216 : if (RelationIsMapped(rel))
3655 0 : ereport(ERROR,
3656 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3657 : errmsg("cannot move system relation \"%s\"",
3658 : RelationGetRelationName(rel))));
3659 :
3660 : /* Cannot move a non-shared relation into pg_global */
3661 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3662 12 : ereport(ERROR,
3663 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3664 : errmsg("only shared relations can be placed in pg_global tablespace")));
3665 :
3666 : /*
3667 : * Do not allow moving temp tables of other backends ... their local
3668 : * buffer manager is not going to cope.
3669 : */
3670 204 : if (RELATION_IS_OTHER_TEMP(rel))
3671 0 : ereport(ERROR,
3672 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3673 : errmsg("cannot move temporary tables of other sessions")));
3674 :
3675 204 : return true;
3676 : }
3677 :
3678 : /*
3679 : * SetRelationTableSpace
3680 : * Set new reltablespace and relfilenumber in pg_class entry.
3681 : *
3682 : * newTableSpaceId is the new tablespace for the relation, and
3683 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3684 : * InvalidRelFileNumber, this field is not updated.
3685 : *
3686 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3687 : *
3688 : * The caller of this routine had better check if a relation can be
3689 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3690 : * first, and is responsible for making the change visible with
3691 : * CommandCounterIncrement().
3692 : */
3693 : void
3694 204 : SetRelationTableSpace(Relation rel,
3695 : Oid newTableSpaceId,
3696 : RelFileNumber newRelFilenumber)
3697 : {
3698 : Relation pg_class;
3699 : HeapTuple tuple;
3700 : ItemPointerData otid;
3701 : Form_pg_class rd_rel;
3702 204 : Oid reloid = RelationGetRelid(rel);
3703 :
3704 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3705 :
3706 : /* Get a modifiable copy of the relation's pg_class row. */
3707 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3708 :
3709 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3710 204 : if (!HeapTupleIsValid(tuple))
3711 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3712 204 : otid = tuple->t_self;
3713 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3714 :
3715 : /* Update the pg_class row. */
3716 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3717 204 : InvalidOid : newTableSpaceId;
3718 204 : if (RelFileNumberIsValid(newRelFilenumber))
3719 160 : rd_rel->relfilenode = newRelFilenumber;
3720 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3721 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3722 :
3723 : /*
3724 : * Record dependency on tablespace. This is only required for relations
3725 : * that have no physical storage.
3726 : */
3727 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3728 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3729 : rd_rel->reltablespace);
3730 :
3731 204 : heap_freetuple(tuple);
3732 204 : table_close(pg_class, RowExclusiveLock);
3733 204 : }
3734 :
3735 : /*
3736 : * renameatt_check - basic sanity checks before attribute rename
3737 : */
3738 : static void
3739 1026 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3740 : {
3741 1026 : char relkind = classform->relkind;
3742 :
3743 1026 : if (classform->reloftype && !recursing)
3744 6 : ereport(ERROR,
3745 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3746 : errmsg("cannot rename column of typed table")));
3747 :
3748 : /*
3749 : * Renaming the columns of sequences or toast tables doesn't actually
3750 : * break anything from the system's point of view, since internal
3751 : * references are by attnum. But it doesn't seem right to allow users to
3752 : * change names that are hardcoded into the system, hence the following
3753 : * restriction.
3754 : */
3755 1020 : if (relkind != RELKIND_RELATION &&
3756 84 : relkind != RELKIND_VIEW &&
3757 84 : relkind != RELKIND_MATVIEW &&
3758 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3759 36 : relkind != RELKIND_INDEX &&
3760 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3761 0 : relkind != RELKIND_FOREIGN_TABLE &&
3762 : relkind != RELKIND_PARTITIONED_TABLE)
3763 0 : ereport(ERROR,
3764 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3765 : errmsg("cannot rename columns of relation \"%s\"",
3766 : NameStr(classform->relname)),
3767 : errdetail_relkind_not_supported(relkind)));
3768 :
3769 : /*
3770 : * permissions checking. only the owner of a class can change its schema.
3771 : */
3772 1020 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3773 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3774 0 : NameStr(classform->relname));
3775 1020 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3776 2 : ereport(ERROR,
3777 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3778 : errmsg("permission denied: \"%s\" is a system catalog",
3779 : NameStr(classform->relname))));
3780 1018 : }
3781 :
3782 : /*
3783 : * renameatt_internal - workhorse for renameatt
3784 : *
3785 : * Return value is the attribute number in the 'myrelid' relation.
3786 : */
3787 : static AttrNumber
3788 552 : renameatt_internal(Oid myrelid,
3789 : const char *oldattname,
3790 : const char *newattname,
3791 : bool recurse,
3792 : bool recursing,
3793 : int expected_parents,
3794 : DropBehavior behavior)
3795 : {
3796 : Relation targetrelation;
3797 : Relation attrelation;
3798 : HeapTuple atttup;
3799 : Form_pg_attribute attform;
3800 : AttrNumber attnum;
3801 :
3802 : /*
3803 : * Grab an exclusive lock on the target table, which we will NOT release
3804 : * until end of transaction.
3805 : */
3806 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3807 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3808 :
3809 : /*
3810 : * if the 'recurse' flag is set then we are supposed to rename this
3811 : * attribute in all classes that inherit from 'relname' (as well as in
3812 : * 'relname').
3813 : *
3814 : * any permissions or problems with duplicate attributes will cause the
3815 : * whole transaction to abort, which is what we want -- all or nothing.
3816 : */
3817 552 : if (recurse)
3818 : {
3819 : List *child_oids,
3820 : *child_numparents;
3821 : ListCell *lo,
3822 : *li;
3823 :
3824 : /*
3825 : * we need the number of parents for each child so that the recursive
3826 : * calls to renameatt() can determine whether there are any parents
3827 : * outside the inheritance hierarchy being processed.
3828 : */
3829 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3830 : &child_numparents);
3831 :
3832 : /*
3833 : * find_all_inheritors does the recursive search of the inheritance
3834 : * hierarchy, so all we have to do is process all of the relids in the
3835 : * list that it returns.
3836 : */
3837 734 : forboth(lo, child_oids, li, child_numparents)
3838 : {
3839 516 : Oid childrelid = lfirst_oid(lo);
3840 516 : int numparents = lfirst_int(li);
3841 :
3842 516 : if (childrelid == myrelid)
3843 248 : continue;
3844 : /* note we need not recurse again */
3845 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3846 : }
3847 : }
3848 : else
3849 : {
3850 : /*
3851 : * If we are told not to recurse, there had better not be any child
3852 : * tables; else the rename would put them out of step.
3853 : *
3854 : * expected_parents will only be 0 if we are not already recursing.
3855 : */
3856 340 : if (expected_parents == 0 &&
3857 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3858 12 : ereport(ERROR,
3859 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3860 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3861 : oldattname)));
3862 : }
3863 :
3864 : /* rename attributes in typed tables of composite type */
3865 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3866 : {
3867 : List *child_oids;
3868 : ListCell *lo;
3869 :
3870 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3871 24 : RelationGetRelationName(targetrelation),
3872 : behavior);
3873 :
3874 24 : foreach(lo, child_oids)
3875 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3876 : }
3877 :
3878 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3879 :
3880 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3881 504 : if (!HeapTupleIsValid(atttup))
3882 24 : ereport(ERROR,
3883 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3884 : errmsg("column \"%s\" does not exist",
3885 : oldattname)));
3886 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3887 :
3888 480 : attnum = attform->attnum;
3889 480 : if (attnum <= 0)
3890 0 : ereport(ERROR,
3891 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3892 : errmsg("cannot rename system column \"%s\"",
3893 : oldattname)));
3894 :
3895 : /*
3896 : * if the attribute is inherited, forbid the renaming. if this is a
3897 : * top-level call to renameatt(), then expected_parents will be 0, so the
3898 : * effect of this code will be to prohibit the renaming if the attribute
3899 : * is inherited at all. if this is a recursive call to renameatt(),
3900 : * expected_parents will be the number of parents the current relation has
3901 : * within the inheritance hierarchy being processed, so we'll prohibit the
3902 : * renaming only if there are additional parents from elsewhere.
3903 : */
3904 480 : if (attform->attinhcount > expected_parents)
3905 30 : ereport(ERROR,
3906 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3907 : errmsg("cannot rename inherited column \"%s\"",
3908 : oldattname)));
3909 :
3910 : /* new name should not already exist */
3911 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3912 :
3913 : /* apply the update */
3914 438 : namestrcpy(&(attform->attname), newattname);
3915 :
3916 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3917 :
3918 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3919 :
3920 438 : heap_freetuple(atttup);
3921 :
3922 438 : table_close(attrelation, RowExclusiveLock);
3923 :
3924 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3925 :
3926 438 : return attnum;
3927 : }
3928 :
3929 : /*
3930 : * Perform permissions and integrity checks before acquiring a relation lock.
3931 : */
3932 : static void
3933 430 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3934 : void *arg)
3935 : {
3936 : HeapTuple tuple;
3937 : Form_pg_class form;
3938 :
3939 430 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3940 430 : if (!HeapTupleIsValid(tuple))
3941 40 : return; /* concurrently dropped */
3942 390 : form = (Form_pg_class) GETSTRUCT(tuple);
3943 390 : renameatt_check(relid, form, false);
3944 382 : ReleaseSysCache(tuple);
3945 : }
3946 :
3947 : /*
3948 : * renameatt - changes the name of an attribute in a relation
3949 : *
3950 : * The returned ObjectAddress is that of the renamed column.
3951 : */
3952 : ObjectAddress
3953 316 : renameatt(RenameStmt *stmt)
3954 : {
3955 : Oid relid;
3956 : AttrNumber attnum;
3957 : ObjectAddress address;
3958 :
3959 : /* lock level taken here should match renameatt_internal */
3960 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3961 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3962 : RangeVarCallbackForRenameAttribute,
3963 : NULL);
3964 :
3965 302 : if (!OidIsValid(relid))
3966 : {
3967 24 : ereport(NOTICE,
3968 : (errmsg("relation \"%s\" does not exist, skipping",
3969 : stmt->relation->relname)));
3970 24 : return InvalidObjectAddress;
3971 : }
3972 :
3973 : attnum =
3974 278 : renameatt_internal(relid,
3975 278 : stmt->subname, /* old att name */
3976 278 : stmt->newname, /* new att name */
3977 278 : stmt->relation->inh, /* recursive? */
3978 : false, /* recursing? */
3979 : 0, /* expected inhcount */
3980 : stmt->behavior);
3981 :
3982 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3983 :
3984 194 : return address;
3985 : }
3986 :
3987 : /*
3988 : * same logic as renameatt_internal
3989 : */
3990 : static ObjectAddress
3991 90 : rename_constraint_internal(Oid myrelid,
3992 : Oid mytypid,
3993 : const char *oldconname,
3994 : const char *newconname,
3995 : bool recurse,
3996 : bool recursing,
3997 : int expected_parents)
3998 : {
3999 90 : Relation targetrelation = NULL;
4000 : Oid constraintOid;
4001 : HeapTuple tuple;
4002 : Form_pg_constraint con;
4003 : ObjectAddress address;
4004 :
4005 : Assert(!myrelid || !mytypid);
4006 :
4007 90 : if (mytypid)
4008 : {
4009 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4010 : }
4011 : else
4012 : {
4013 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4014 :
4015 : /*
4016 : * don't tell it whether we're recursing; we allow changing typed
4017 : * tables here
4018 : */
4019 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4020 :
4021 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4022 : }
4023 :
4024 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4025 90 : if (!HeapTupleIsValid(tuple))
4026 0 : elog(ERROR, "cache lookup failed for constraint %u",
4027 : constraintOid);
4028 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4029 :
4030 90 : if (myrelid &&
4031 84 : (con->contype == CONSTRAINT_CHECK ||
4032 24 : con->contype == CONSTRAINT_NOTNULL) &&
4033 66 : !con->connoinherit)
4034 : {
4035 54 : if (recurse)
4036 : {
4037 : List *child_oids,
4038 : *child_numparents;
4039 : ListCell *lo,
4040 : *li;
4041 :
4042 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4043 : &child_numparents);
4044 :
4045 84 : forboth(lo, child_oids, li, child_numparents)
4046 : {
4047 48 : Oid childrelid = lfirst_oid(lo);
4048 48 : int numparents = lfirst_int(li);
4049 :
4050 48 : if (childrelid == myrelid)
4051 36 : continue;
4052 :
4053 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4054 : }
4055 : }
4056 : else
4057 : {
4058 24 : if (expected_parents == 0 &&
4059 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4060 6 : ereport(ERROR,
4061 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4062 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4063 : oldconname)));
4064 : }
4065 :
4066 48 : if (con->coninhcount > expected_parents)
4067 6 : ereport(ERROR,
4068 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4069 : errmsg("cannot rename inherited constraint \"%s\"",
4070 : oldconname)));
4071 : }
4072 :
4073 78 : if (con->conindid
4074 18 : && (con->contype == CONSTRAINT_PRIMARY
4075 6 : || con->contype == CONSTRAINT_UNIQUE
4076 0 : || con->contype == CONSTRAINT_EXCLUSION))
4077 : /* rename the index; this renames the constraint as well */
4078 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4079 : else
4080 60 : RenameConstraintById(constraintOid, newconname);
4081 :
4082 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4083 :
4084 78 : ReleaseSysCache(tuple);
4085 :
4086 78 : if (targetrelation)
4087 : {
4088 : /*
4089 : * Invalidate relcache so as others can see the new constraint name.
4090 : */
4091 72 : CacheInvalidateRelcache(targetrelation);
4092 :
4093 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4094 : }
4095 :
4096 78 : return address;
4097 : }
4098 :
4099 : ObjectAddress
4100 84 : RenameConstraint(RenameStmt *stmt)
4101 : {
4102 84 : Oid relid = InvalidOid;
4103 84 : Oid typid = InvalidOid;
4104 :
4105 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4106 : {
4107 : Relation rel;
4108 : HeapTuple tup;
4109 :
4110 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4111 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4112 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4113 6 : if (!HeapTupleIsValid(tup))
4114 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4115 6 : checkDomainOwner(tup);
4116 6 : ReleaseSysCache(tup);
4117 6 : table_close(rel, NoLock);
4118 : }
4119 : else
4120 : {
4121 : /* lock level taken here should match rename_constraint_internal */
4122 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4123 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4124 : RangeVarCallbackForRenameAttribute,
4125 : NULL);
4126 78 : if (!OidIsValid(relid))
4127 : {
4128 6 : ereport(NOTICE,
4129 : (errmsg("relation \"%s\" does not exist, skipping",
4130 : stmt->relation->relname)));
4131 6 : return InvalidObjectAddress;
4132 : }
4133 : }
4134 :
4135 : return
4136 78 : rename_constraint_internal(relid, typid,
4137 78 : stmt->subname,
4138 78 : stmt->newname,
4139 150 : (stmt->relation &&
4140 72 : stmt->relation->inh), /* recursive? */
4141 : false, /* recursing? */
4142 : 0 /* expected inhcount */ );
4143 : }
4144 :
4145 : /*
4146 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4147 : * RENAME
4148 : */
4149 : ObjectAddress
4150 510 : RenameRelation(RenameStmt *stmt)
4151 : {
4152 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4153 : Oid relid;
4154 : ObjectAddress address;
4155 :
4156 : /*
4157 : * Grab an exclusive lock on the target table, index, sequence, view,
4158 : * materialized view, or foreign table, which we will NOT release until
4159 : * end of transaction.
4160 : *
4161 : * Lock level used here should match RenameRelationInternal, to avoid lock
4162 : * escalation. However, because ALTER INDEX can be used with any relation
4163 : * type, we mustn't believe without verification.
4164 : */
4165 : for (;;)
4166 12 : {
4167 : LOCKMODE lockmode;
4168 : char relkind;
4169 : bool obj_is_index;
4170 :
4171 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4172 :
4173 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4174 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4175 : RangeVarCallbackForAlterRelation,
4176 : stmt);
4177 :
4178 472 : if (!OidIsValid(relid))
4179 : {
4180 18 : ereport(NOTICE,
4181 : (errmsg("relation \"%s\" does not exist, skipping",
4182 : stmt->relation->relname)));
4183 18 : return InvalidObjectAddress;
4184 : }
4185 :
4186 : /*
4187 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4188 : * to rename a table), but we might've used the wrong lock level. If
4189 : * that happens, retry with the correct lock level. We don't bother
4190 : * if we already acquired AccessExclusiveLock with an index, however.
4191 : */
4192 454 : relkind = get_rel_relkind(relid);
4193 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4194 : relkind == RELKIND_PARTITIONED_INDEX);
4195 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4196 : break;
4197 :
4198 12 : UnlockRelationOid(relid, lockmode);
4199 12 : is_index_stmt = obj_is_index;
4200 : }
4201 :
4202 : /* Do the work */
4203 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4204 :
4205 430 : ObjectAddressSet(address, RelationRelationId, relid);
4206 :
4207 430 : return address;
4208 : }
4209 :
4210 : /*
4211 : * RenameRelationInternal - change the name of a relation
4212 : */
4213 : void
4214 1638 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4215 : {
4216 : Relation targetrelation;
4217 : Relation relrelation; /* for RELATION relation */
4218 : ItemPointerData otid;
4219 : HeapTuple reltup;
4220 : Form_pg_class relform;
4221 : Oid namespaceId;
4222 :
4223 : /*
4224 : * Grab a lock on the target relation, which we will NOT release until end
4225 : * of transaction. We need at least a self-exclusive lock so that
4226 : * concurrent DDL doesn't overwrite the rename if they start updating
4227 : * while still seeing the old version. The lock also guards against
4228 : * triggering relcache reloads in concurrent sessions, which might not
4229 : * handle this information changing under them. For indexes, we can use a
4230 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4231 : * specially.
4232 : */
4233 1638 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4234 1638 : namespaceId = RelationGetNamespace(targetrelation);
4235 :
4236 : /*
4237 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4238 : */
4239 1638 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4240 :
4241 1638 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4242 1638 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4243 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4244 1638 : otid = reltup->t_self;
4245 1638 : relform = (Form_pg_class) GETSTRUCT(reltup);
4246 :
4247 1638 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4248 12 : ereport(ERROR,
4249 : (errcode(ERRCODE_DUPLICATE_TABLE),
4250 : errmsg("relation \"%s\" already exists",
4251 : newrelname)));
4252 :
4253 : /*
4254 : * RenameRelation is careful not to believe the caller's idea of the
4255 : * relation kind being handled. We don't have to worry about this, but
4256 : * let's not be totally oblivious to it. We can process an index as
4257 : * not-an-index, but not the other way around.
4258 : */
4259 : Assert(!is_index ||
4260 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4261 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4262 :
4263 : /*
4264 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4265 : * because it's a copy...)
4266 : */
4267 1626 : namestrcpy(&(relform->relname), newrelname);
4268 :
4269 1626 : CatalogTupleUpdate(relrelation, &otid, reltup);
4270 1626 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4271 :
4272 1626 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4273 : InvalidOid, is_internal);
4274 :
4275 1626 : heap_freetuple(reltup);
4276 1626 : table_close(relrelation, RowExclusiveLock);
4277 :
4278 : /*
4279 : * Also rename the associated type, if any.
4280 : */
4281 1626 : if (OidIsValid(targetrelation->rd_rel->reltype))
4282 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4283 : newrelname, namespaceId);
4284 :
4285 : /*
4286 : * Also rename the associated constraint, if any.
4287 : */
4288 1626 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4289 854 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4290 : {
4291 790 : Oid constraintId = get_index_constraint(myrelid);
4292 :
4293 790 : if (OidIsValid(constraintId))
4294 36 : RenameConstraintById(constraintId, newrelname);
4295 : }
4296 :
4297 : /*
4298 : * Close rel, but keep lock!
4299 : */
4300 1626 : relation_close(targetrelation, NoLock);
4301 1626 : }
4302 :
4303 : /*
4304 : * ResetRelRewrite - reset relrewrite
4305 : */
4306 : void
4307 578 : ResetRelRewrite(Oid myrelid)
4308 : {
4309 : Relation relrelation; /* for RELATION relation */
4310 : HeapTuple reltup;
4311 : Form_pg_class relform;
4312 :
4313 : /*
4314 : * Find relation's pg_class tuple.
4315 : */
4316 578 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4317 :
4318 578 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4319 578 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4320 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4321 578 : relform = (Form_pg_class) GETSTRUCT(reltup);
4322 :
4323 : /*
4324 : * Update pg_class tuple.
4325 : */
4326 578 : relform->relrewrite = InvalidOid;
4327 :
4328 578 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4329 :
4330 578 : heap_freetuple(reltup);
4331 578 : table_close(relrelation, RowExclusiveLock);
4332 578 : }
4333 :
4334 : /*
4335 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4336 : * any open reference to the target table besides the one just acquired by
4337 : * the calling command; this implies there's an open cursor or active plan.
4338 : * We need this check because our lock doesn't protect us against stomping
4339 : * on our own foot, only other people's feet!
4340 : *
4341 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4342 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4343 : * possibly be relaxed to only error out for certain types of alterations.
4344 : * But the use-case for allowing any of these things is not obvious, so we
4345 : * won't work hard at it for now.
4346 : *
4347 : * We also reject these commands if there are any pending AFTER trigger events
4348 : * for the rel. This is certainly necessary for the rewriting variants of
4349 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4350 : * events would try to fetch the wrong tuples. It might be overly cautious
4351 : * in other cases, but again it seems better to err on the side of paranoia.
4352 : *
4353 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4354 : * we are worried about active indexscans on the index. The trigger-event
4355 : * check can be skipped, since we are doing no damage to the parent table.
4356 : *
4357 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4358 : */
4359 : void
4360 158712 : CheckTableNotInUse(Relation rel, const char *stmt)
4361 : {
4362 : int expected_refcnt;
4363 :
4364 158712 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4365 158712 : if (rel->rd_refcnt != expected_refcnt)
4366 30 : ereport(ERROR,
4367 : (errcode(ERRCODE_OBJECT_IN_USE),
4368 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4369 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4370 : stmt, RelationGetRelationName(rel))));
4371 :
4372 158682 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4373 256566 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4374 127260 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4375 18 : ereport(ERROR,
4376 : (errcode(ERRCODE_OBJECT_IN_USE),
4377 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4378 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4379 : stmt, RelationGetRelationName(rel))));
4380 158664 : }
4381 :
4382 : /*
4383 : * CheckAlterTableIsSafe
4384 : * Verify that it's safe to allow ALTER TABLE on this relation.
4385 : *
4386 : * This consists of CheckTableNotInUse() plus a check that the relation
4387 : * isn't another session's temp table. We must split out the temp-table
4388 : * check because there are callers of CheckTableNotInUse() that don't want
4389 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4390 : * an orphaned temp schema.) Compare truncate_check_activity().
4391 : */
4392 : static void
4393 55708 : CheckAlterTableIsSafe(Relation rel)
4394 : {
4395 : /*
4396 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4397 : * manager is not going to cope if we need to change the table's contents.
4398 : * Even if we don't, there may be optimizations that assume temp tables
4399 : * aren't subject to such interference.
4400 : */
4401 55708 : if (RELATION_IS_OTHER_TEMP(rel))
4402 0 : ereport(ERROR,
4403 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4404 : errmsg("cannot alter temporary tables of other sessions")));
4405 :
4406 : /*
4407 : * Also check for active uses of the relation in the current transaction,
4408 : * including open scans and pending AFTER trigger events.
4409 : */
4410 55708 : CheckTableNotInUse(rel, "ALTER TABLE");
4411 55672 : }
4412 :
4413 : /*
4414 : * AlterTableLookupRelation
4415 : * Look up, and lock, the OID for the relation named by an alter table
4416 : * statement.
4417 : */
4418 : Oid
4419 29284 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4420 : {
4421 58482 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4422 29284 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4423 : RangeVarCallbackForAlterRelation,
4424 : stmt);
4425 : }
4426 :
4427 : /*
4428 : * AlterTable
4429 : * Execute ALTER TABLE, which can be a list of subcommands
4430 : *
4431 : * ALTER TABLE is performed in three phases:
4432 : * 1. Examine subcommands and perform pre-transformation checking.
4433 : * 2. Validate and transform subcommands, and update system catalogs.
4434 : * 3. Scan table(s) to check new constraints, and optionally recopy
4435 : * the data into new table(s).
4436 : * Phase 3 is not performed unless one or more of the subcommands requires
4437 : * it. The intention of this design is to allow multiple independent
4438 : * updates of the table schema to be performed with only one pass over the
4439 : * data.
4440 : *
4441 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4442 : * each table to be affected (there may be multiple affected tables if the
4443 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4444 : * validation of the subcommands. Because earlier subcommands may change
4445 : * the catalog state seen by later commands, there are limits to what can
4446 : * be done in this phase. Generally, this phase acquires table locks,
4447 : * checks permissions and relkind, and recurses to find child tables.
4448 : *
4449 : * ATRewriteCatalogs performs phase 2 for each affected table.
4450 : * Certain subcommands need to be performed before others to avoid
4451 : * unnecessary conflicts; for example, DROP COLUMN should come before
4452 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4453 : * lists, one for each logical "pass" of phase 2.
4454 : *
4455 : * ATRewriteTables performs phase 3 for those tables that need it.
4456 : *
4457 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4458 : * since phase 1 already does it. However, for certain subcommand types
4459 : * it is only possible to determine how to recurse at phase 2 time; for
4460 : * those cases, phase 1 sets the cmd->recurse flag.
4461 : *
4462 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4463 : * the whole operation; we don't have to do anything special to clean up.
4464 : *
4465 : * The caller must lock the relation, with an appropriate lock level
4466 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4467 : * or higher. We pass the lock level down
4468 : * so that we can apply it recursively to inherited tables. Note that the
4469 : * lock level we want as we recurse might well be higher than required for
4470 : * that specific subcommand. So we pass down the overall lock requirement,
4471 : * rather than reassess it at lower levels.
4472 : *
4473 : * The caller also provides a "context" which is to be passed back to
4474 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4475 : * Some of the fields therein, such as the relid, are used here as well.
4476 : */
4477 : void
4478 29060 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4479 : AlterTableUtilityContext *context)
4480 : {
4481 : Relation rel;
4482 :
4483 : /* Caller is required to provide an adequate lock. */
4484 29060 : rel = relation_open(context->relid, NoLock);
4485 :
4486 29060 : CheckAlterTableIsSafe(rel);
4487 :
4488 29042 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4489 25816 : }
4490 :
4491 : /*
4492 : * AlterTableInternal
4493 : *
4494 : * ALTER TABLE with target specified by OID
4495 : *
4496 : * We do not reject if the relation is already open, because it's quite
4497 : * likely that one or more layers of caller have it open. That means it
4498 : * is unsafe to use this entry point for alterations that could break
4499 : * existing query plans. On the assumption it's not used for such, we
4500 : * don't have to reject pending AFTER triggers, either.
4501 : *
4502 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4503 : * used for any subcommand types that require parse transformation or
4504 : * could generate subcommands that have to be passed to ProcessUtility.
4505 : */
4506 : void
4507 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4508 : {
4509 : Relation rel;
4510 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4511 :
4512 278 : rel = relation_open(relid, lockmode);
4513 :
4514 278 : EventTriggerAlterTableRelid(relid);
4515 :
4516 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4517 278 : }
4518 :
4519 : /*
4520 : * AlterTableGetLockLevel
4521 : *
4522 : * Sets the overall lock level required for the supplied list of subcommands.
4523 : * Policy for doing this set according to needs of AlterTable(), see
4524 : * comments there for overall explanation.
4525 : *
4526 : * Function is called before and after parsing, so it must give same
4527 : * answer each time it is called. Some subcommands are transformed
4528 : * into other subcommand types, so the transform must never be made to a
4529 : * lower lock level than previously assigned. All transforms are noted below.
4530 : *
4531 : * Since this is called before we lock the table we cannot use table metadata
4532 : * to influence the type of lock we acquire.
4533 : *
4534 : * There should be no lockmodes hardcoded into the subcommand functions. All
4535 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4536 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4537 : * and does not travel through this section of code and cannot be combined with
4538 : * any of the subcommands given here.
4539 : *
4540 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4541 : * so any changes that might affect SELECTs running on standbys need to use
4542 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4543 : * have a solution for that also.
4544 : *
4545 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4546 : * that takes a lock less than AccessExclusiveLock can change object definitions
4547 : * while pg_dump is running. Be careful to check that the appropriate data is
4548 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4549 : * otherwise we might end up with an inconsistent dump that can't restore.
4550 : */
4551 : LOCKMODE
4552 29562 : AlterTableGetLockLevel(List *cmds)
4553 : {
4554 : /*
4555 : * This only works if we read catalog tables using MVCC snapshots.
4556 : */
4557 : ListCell *lcmd;
4558 29562 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4559 :
4560 60194 : foreach(lcmd, cmds)
4561 : {
4562 30632 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4563 30632 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4564 :
4565 30632 : switch (cmd->subtype)
4566 : {
4567 : /*
4568 : * These subcommands rewrite the heap, so require full locks.
4569 : */
4570 3278 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4571 : * to SELECT */
4572 : case AT_SetAccessMethod: /* must rewrite heap */
4573 : case AT_SetTableSpace: /* must rewrite heap */
4574 : case AT_AlterColumnType: /* must rewrite heap */
4575 3278 : cmd_lockmode = AccessExclusiveLock;
4576 3278 : break;
4577 :
4578 : /*
4579 : * These subcommands may require addition of toast tables. If
4580 : * we add a toast table to a table currently being scanned, we
4581 : * might miss data added to the new toast table by concurrent
4582 : * insert transactions.
4583 : */
4584 212 : case AT_SetStorage: /* may add toast tables, see
4585 : * ATRewriteCatalogs() */
4586 212 : cmd_lockmode = AccessExclusiveLock;
4587 212 : break;
4588 :
4589 : /*
4590 : * Removing constraints can affect SELECTs that have been
4591 : * optimized assuming the constraint holds true. See also
4592 : * CloneFkReferenced.
4593 : */
4594 1122 : case AT_DropConstraint: /* as DROP INDEX */
4595 : case AT_DropNotNull: /* may change some SQL plans */
4596 1122 : cmd_lockmode = AccessExclusiveLock;
4597 1122 : break;
4598 :
4599 : /*
4600 : * Subcommands that may be visible to concurrent SELECTs
4601 : */
4602 1704 : case AT_DropColumn: /* change visible to SELECT */
4603 : case AT_AddColumnToView: /* CREATE VIEW */
4604 : case AT_DropOids: /* used to equiv to DropColumn */
4605 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4606 : case AT_EnableReplicaRule: /* may change SELECT rules */
4607 : case AT_EnableRule: /* may change SELECT rules */
4608 : case AT_DisableRule: /* may change SELECT rules */
4609 1704 : cmd_lockmode = AccessExclusiveLock;
4610 1704 : break;
4611 :
4612 : /*
4613 : * Changing owner may remove implicit SELECT privileges
4614 : */
4615 1820 : case AT_ChangeOwner: /* change visible to SELECT */
4616 1820 : cmd_lockmode = AccessExclusiveLock;
4617 1820 : break;
4618 :
4619 : /*
4620 : * Changing foreign table options may affect optimization.
4621 : */
4622 254 : case AT_GenericOptions:
4623 : case AT_AlterColumnGenericOptions:
4624 254 : cmd_lockmode = AccessExclusiveLock;
4625 254 : break;
4626 :
4627 : /*
4628 : * These subcommands affect write operations only.
4629 : */
4630 340 : case AT_EnableTrig:
4631 : case AT_EnableAlwaysTrig:
4632 : case AT_EnableReplicaTrig:
4633 : case AT_EnableTrigAll:
4634 : case AT_EnableTrigUser:
4635 : case AT_DisableTrig:
4636 : case AT_DisableTrigAll:
4637 : case AT_DisableTrigUser:
4638 340 : cmd_lockmode = ShareRowExclusiveLock;
4639 340 : break;
4640 :
4641 : /*
4642 : * These subcommands affect write operations only. XXX
4643 : * Theoretically, these could be ShareRowExclusiveLock.
4644 : */
4645 2474 : case AT_ColumnDefault:
4646 : case AT_CookedColumnDefault:
4647 : case AT_AlterConstraint:
4648 : case AT_AddIndex: /* from ADD CONSTRAINT */
4649 : case AT_AddIndexConstraint:
4650 : case AT_ReplicaIdentity:
4651 : case AT_SetNotNull:
4652 : case AT_EnableRowSecurity:
4653 : case AT_DisableRowSecurity:
4654 : case AT_ForceRowSecurity:
4655 : case AT_NoForceRowSecurity:
4656 : case AT_AddIdentity:
4657 : case AT_DropIdentity:
4658 : case AT_SetIdentity:
4659 : case AT_SetExpression:
4660 : case AT_DropExpression:
4661 : case AT_SetCompression:
4662 2474 : cmd_lockmode = AccessExclusiveLock;
4663 2474 : break;
4664 :
4665 14016 : case AT_AddConstraint:
4666 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4667 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4668 14016 : if (IsA(cmd->def, Constraint))
4669 : {
4670 14016 : Constraint *con = (Constraint *) cmd->def;
4671 :
4672 14016 : switch (con->contype)
4673 : {
4674 10742 : case CONSTR_EXCLUSION:
4675 : case CONSTR_PRIMARY:
4676 : case CONSTR_UNIQUE:
4677 :
4678 : /*
4679 : * Cases essentially the same as CREATE INDEX. We
4680 : * could reduce the lock strength to ShareLock if
4681 : * we can work out how to allow concurrent catalog
4682 : * updates. XXX Might be set down to
4683 : * ShareRowExclusiveLock but requires further
4684 : * analysis.
4685 : */
4686 10742 : cmd_lockmode = AccessExclusiveLock;
4687 10742 : break;
4688 2440 : case CONSTR_FOREIGN:
4689 :
4690 : /*
4691 : * We add triggers to both tables when we add a
4692 : * Foreign Key, so the lock level must be at least
4693 : * as strong as CREATE TRIGGER.
4694 : */
4695 2440 : cmd_lockmode = ShareRowExclusiveLock;
4696 2440 : break;
4697 :
4698 834 : default:
4699 834 : cmd_lockmode = AccessExclusiveLock;
4700 : }
4701 0 : }
4702 14016 : break;
4703 :
4704 : /*
4705 : * These subcommands affect inheritance behaviour. Queries
4706 : * started before us will continue to see the old inheritance
4707 : * behaviour, while queries started after we commit will see
4708 : * new behaviour. No need to prevent reads or writes to the
4709 : * subtable while we hook it up though. Changing the TupDesc
4710 : * may be a problem, so keep highest lock.
4711 : */
4712 432 : case AT_AddInherit:
4713 : case AT_DropInherit:
4714 432 : cmd_lockmode = AccessExclusiveLock;
4715 432 : break;
4716 :
4717 : /*
4718 : * These subcommands affect implicit row type conversion. They
4719 : * have affects similar to CREATE/DROP CAST on queries. don't
4720 : * provide for invalidating parse trees as a result of such
4721 : * changes, so we keep these at AccessExclusiveLock.
4722 : */
4723 72 : case AT_AddOf:
4724 : case AT_DropOf:
4725 72 : cmd_lockmode = AccessExclusiveLock;
4726 72 : break;
4727 :
4728 : /*
4729 : * Only used by CREATE OR REPLACE VIEW which must conflict
4730 : * with an SELECTs currently using the view.
4731 : */
4732 194 : case AT_ReplaceRelOptions:
4733 194 : cmd_lockmode = AccessExclusiveLock;
4734 194 : break;
4735 :
4736 : /*
4737 : * These subcommands affect general strategies for performance
4738 : * and maintenance, though don't change the semantic results
4739 : * from normal data reads and writes. Delaying an ALTER TABLE
4740 : * behind currently active writes only delays the point where
4741 : * the new strategy begins to take effect, so there is no
4742 : * benefit in waiting. In this case the minimum restriction
4743 : * applies: we don't currently allow concurrent catalog
4744 : * updates.
4745 : */
4746 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4747 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4748 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4749 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4750 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4751 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4752 234 : break;
4753 :
4754 112 : case AT_SetLogged:
4755 : case AT_SetUnLogged:
4756 112 : cmd_lockmode = AccessExclusiveLock;
4757 112 : break;
4758 :
4759 394 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4760 394 : cmd_lockmode = ShareUpdateExclusiveLock;
4761 394 : break;
4762 :
4763 : /*
4764 : * Rel options are more complex than first appears. Options
4765 : * are set here for tables, views and indexes; for historical
4766 : * reasons these can all be used with ALTER TABLE, so we can't
4767 : * decide between them using the basic grammar.
4768 : */
4769 752 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4770 : * getTables() */
4771 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4772 : * getTables() */
4773 752 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4774 752 : break;
4775 :
4776 2638 : case AT_AttachPartition:
4777 2638 : cmd_lockmode = ShareUpdateExclusiveLock;
4778 2638 : break;
4779 :
4780 564 : case AT_DetachPartition:
4781 564 : if (((PartitionCmd *) cmd->def)->concurrent)
4782 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4783 : else
4784 400 : cmd_lockmode = AccessExclusiveLock;
4785 564 : break;
4786 :
4787 20 : case AT_DetachPartitionFinalize:
4788 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4789 20 : break;
4790 :
4791 0 : default: /* oops */
4792 0 : elog(ERROR, "unrecognized alter table type: %d",
4793 : (int) cmd->subtype);
4794 : break;
4795 : }
4796 :
4797 : /*
4798 : * Take the greatest lockmode from any subcommand
4799 : */
4800 30632 : if (cmd_lockmode > lockmode)
4801 25546 : lockmode = cmd_lockmode;
4802 : }
4803 :
4804 29562 : return lockmode;
4805 : }
4806 :
4807 : /*
4808 : * ATController provides top level control over the phases.
4809 : *
4810 : * parsetree is passed in to allow it to be passed to event triggers
4811 : * when requested.
4812 : */
4813 : static void
4814 29320 : ATController(AlterTableStmt *parsetree,
4815 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4816 : AlterTableUtilityContext *context)
4817 : {
4818 29320 : List *wqueue = NIL;
4819 : ListCell *lcmd;
4820 :
4821 : /* Phase 1: preliminary examination of commands, create work queue */
4822 59366 : foreach(lcmd, cmds)
4823 : {
4824 30384 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4825 :
4826 30384 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4827 : }
4828 :
4829 : /* Close the relation, but keep lock until commit */
4830 28982 : relation_close(rel, NoLock);
4831 :
4832 : /* Phase 2: update system catalogs */
4833 28982 : ATRewriteCatalogs(&wqueue, lockmode, context);
4834 :
4835 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4836 26424 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4837 26094 : }
4838 :
4839 : /*
4840 : * ATPrepCmd
4841 : *
4842 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4843 : * recursion and permission checks.
4844 : *
4845 : * Caller must have acquired appropriate lock type on relation already.
4846 : * This lock should be held until commit.
4847 : */
4848 : static void
4849 31746 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4850 : bool recurse, bool recursing, LOCKMODE lockmode,
4851 : AlterTableUtilityContext *context)
4852 : {
4853 : AlteredTableInfo *tab;
4854 31746 : AlterTablePass pass = AT_PASS_UNSET;
4855 :
4856 : /* Find or create work queue entry for this table */
4857 31746 : tab = ATGetQueueEntry(wqueue, rel);
4858 :
4859 : /*
4860 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4861 : * partitions that are pending detach.
4862 : */
4863 31746 : if (rel->rd_rel->relispartition &&
4864 2712 : cmd->subtype != AT_DetachPartitionFinalize &&
4865 1356 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4866 2 : ereport(ERROR,
4867 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4868 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4869 : RelationGetRelationName(rel)),
4870 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4871 :
4872 : /*
4873 : * Copy the original subcommand for each table, so we can scribble on it.
4874 : * This avoids conflicts when different child tables need to make
4875 : * different parse transformations (for example, the same column may have
4876 : * different column numbers in different children).
4877 : */
4878 31744 : cmd = copyObject(cmd);
4879 :
4880 : /*
4881 : * Do permissions and relkind checking, recursion to child tables if
4882 : * needed, and any additional phase-1 processing needed. (But beware of
4883 : * adding any processing that looks at table details that another
4884 : * subcommand could change. In some cases we reject multiple subcommands
4885 : * that could try to change the same state in contrary ways.)
4886 : */
4887 31744 : switch (cmd->subtype)
4888 : {
4889 1990 : case AT_AddColumn: /* ADD COLUMN */
4890 1990 : ATSimplePermissions(cmd->subtype, rel,
4891 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4892 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4893 1990 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4894 : lockmode, context);
4895 : /* Recursion occurs during execution phase */
4896 1978 : pass = AT_PASS_ADD_COL;
4897 1978 : break;
4898 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4899 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4900 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4901 : lockmode, context);
4902 : /* Recursion occurs during execution phase */
4903 24 : pass = AT_PASS_ADD_COL;
4904 24 : break;
4905 598 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4906 :
4907 : /*
4908 : * We allow defaults on views so that INSERT into a view can have
4909 : * default-ish behavior. This works because the rewriter
4910 : * substitutes default values into INSERTs before it expands
4911 : * rules.
4912 : */
4913 598 : ATSimplePermissions(cmd->subtype, rel,
4914 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4915 : ATT_FOREIGN_TABLE);
4916 598 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4917 : /* No command-specific prep needed */
4918 598 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4919 598 : break;
4920 56 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4921 : /* This is currently used only in CREATE TABLE */
4922 : /* (so the permission check really isn't necessary) */
4923 56 : ATSimplePermissions(cmd->subtype, rel,
4924 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4925 : /* This command never recurses */
4926 56 : pass = AT_PASS_ADD_OTHERCONSTR;
4927 56 : break;
4928 166 : case AT_AddIdentity:
4929 166 : ATSimplePermissions(cmd->subtype, rel,
4930 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4931 : ATT_FOREIGN_TABLE);
4932 : /* Set up recursion for phase 2; no other prep needed */
4933 166 : if (recurse)
4934 160 : cmd->recurse = true;
4935 166 : pass = AT_PASS_ADD_OTHERCONSTR;
4936 166 : break;
4937 62 : case AT_SetIdentity:
4938 62 : ATSimplePermissions(cmd->subtype, rel,
4939 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4940 : ATT_FOREIGN_TABLE);
4941 : /* Set up recursion for phase 2; no other prep needed */
4942 62 : if (recurse)
4943 56 : cmd->recurse = true;
4944 : /* This should run after AddIdentity, so do it in MISC pass */
4945 62 : pass = AT_PASS_MISC;
4946 62 : break;
4947 56 : case AT_DropIdentity:
4948 56 : ATSimplePermissions(cmd->subtype, rel,
4949 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4950 : ATT_FOREIGN_TABLE);
4951 : /* Set up recursion for phase 2; no other prep needed */
4952 56 : if (recurse)
4953 50 : cmd->recurse = true;
4954 56 : pass = AT_PASS_DROP;
4955 56 : break;
4956 262 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4957 262 : ATSimplePermissions(cmd->subtype, rel,
4958 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4959 : /* Set up recursion for phase 2; no other prep needed */
4960 256 : if (recurse)
4961 238 : cmd->recurse = true;
4962 256 : pass = AT_PASS_DROP;
4963 256 : break;
4964 384 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4965 384 : ATSimplePermissions(cmd->subtype, rel,
4966 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4967 : /* Set up recursion for phase 2; no other prep needed */
4968 378 : if (recurse)
4969 354 : cmd->recurse = true;
4970 378 : pass = AT_PASS_COL_ATTRS;
4971 378 : break;
4972 84 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4973 84 : ATSimplePermissions(cmd->subtype, rel,
4974 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4975 84 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4976 84 : pass = AT_PASS_SET_EXPRESSION;
4977 84 : break;
4978 44 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4979 44 : ATSimplePermissions(cmd->subtype, rel,
4980 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4981 44 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4982 44 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4983 32 : pass = AT_PASS_DROP;
4984 32 : break;
4985 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4986 164 : ATSimplePermissions(cmd->subtype, rel,
4987 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
4988 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4989 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4990 : /* No command-specific prep needed */
4991 164 : pass = AT_PASS_MISC;
4992 164 : break;
4993 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4994 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4995 44 : ATSimplePermissions(cmd->subtype, rel,
4996 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4997 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
4998 : /* This command never recurses */
4999 32 : pass = AT_PASS_MISC;
5000 32 : break;
5001 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5002 234 : ATSimplePermissions(cmd->subtype, rel,
5003 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5004 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5005 234 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5006 : /* No command-specific prep needed */
5007 234 : pass = AT_PASS_MISC;
5008 234 : break;
5009 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5010 68 : ATSimplePermissions(cmd->subtype, rel,
5011 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5012 : /* This command never recurses */
5013 : /* No command-specific prep needed */
5014 68 : pass = AT_PASS_MISC;
5015 68 : break;
5016 1610 : case AT_DropColumn: /* DROP COLUMN */
5017 1610 : ATSimplePermissions(cmd->subtype, rel,
5018 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5019 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5020 1604 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5021 : lockmode, context);
5022 : /* Recursion occurs during execution phase */
5023 1592 : pass = AT_PASS_DROP;
5024 1592 : break;
5025 0 : case AT_AddIndex: /* ADD INDEX */
5026 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5027 : /* This command never recurses */
5028 : /* No command-specific prep needed */
5029 0 : pass = AT_PASS_ADD_INDEX;
5030 0 : break;
5031 14938 : case AT_AddConstraint: /* ADD CONSTRAINT */
5032 14938 : ATSimplePermissions(cmd->subtype, rel,
5033 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5034 14938 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5035 14932 : if (recurse)
5036 : {
5037 : /* recurses at exec time; lock descendants and set flag */
5038 14584 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5039 14584 : cmd->recurse = true;
5040 : }
5041 14932 : pass = AT_PASS_ADD_CONSTR;
5042 14932 : break;
5043 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5044 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5045 : /* This command never recurses */
5046 : /* No command-specific prep needed */
5047 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5048 0 : break;
5049 822 : case AT_DropConstraint: /* DROP CONSTRAINT */
5050 822 : ATSimplePermissions(cmd->subtype, rel,
5051 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5052 822 : ATCheckPartitionsNotInUse(rel, lockmode);
5053 : /* Other recursion occurs during execution phase */
5054 : /* No command-specific prep needed except saving recurse flag */
5055 816 : if (recurse)
5056 780 : cmd->recurse = true;
5057 816 : pass = AT_PASS_DROP;
5058 816 : break;
5059 1174 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5060 1174 : ATSimplePermissions(cmd->subtype, rel,
5061 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5062 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5063 : /* See comments for ATPrepAlterColumnType */
5064 1174 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5065 : AT_PASS_UNSET, context);
5066 : Assert(cmd != NULL);
5067 : /* Performs own recursion */
5068 1168 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5069 : lockmode, context);
5070 1000 : pass = AT_PASS_ALTER_TYPE;
5071 1000 : break;
5072 172 : case AT_AlterColumnGenericOptions:
5073 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5074 : /* This command never recurses */
5075 : /* No command-specific prep needed */
5076 172 : pass = AT_PASS_MISC;
5077 172 : break;
5078 1796 : case AT_ChangeOwner: /* ALTER OWNER */
5079 : /* This command never recurses */
5080 : /* No command-specific prep needed */
5081 1796 : pass = AT_PASS_MISC;
5082 1796 : break;
5083 64 : case AT_ClusterOn: /* CLUSTER ON */
5084 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5085 64 : ATSimplePermissions(cmd->subtype, rel,
5086 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5087 : /* These commands never recurse */
5088 : /* No command-specific prep needed */
5089 64 : pass = AT_PASS_MISC;
5090 64 : break;
5091 112 : case AT_SetLogged: /* SET LOGGED */
5092 : case AT_SetUnLogged: /* SET UNLOGGED */
5093 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5094 100 : if (tab->chgPersistence)
5095 0 : ereport(ERROR,
5096 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5097 : errmsg("cannot change persistence setting twice")));
5098 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5099 88 : pass = AT_PASS_MISC;
5100 88 : break;
5101 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5102 6 : ATSimplePermissions(cmd->subtype, rel,
5103 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5104 6 : pass = AT_PASS_DROP;
5105 6 : break;
5106 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5107 128 : ATSimplePermissions(cmd->subtype, rel,
5108 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5109 :
5110 : /* check if another access method change was already requested */
5111 128 : if (tab->chgAccessMethod)
5112 18 : ereport(ERROR,
5113 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5114 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5115 :
5116 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5117 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5118 110 : break;
5119 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5120 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5121 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5122 : /* This command never recurses */
5123 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5124 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5125 158 : break;
5126 946 : case AT_SetRelOptions: /* SET (...) */
5127 : case AT_ResetRelOptions: /* RESET (...) */
5128 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5129 946 : ATSimplePermissions(cmd->subtype, rel,
5130 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5131 : ATT_MATVIEW | ATT_INDEX);
5132 : /* This command never recurses */
5133 : /* No command-specific prep needed */
5134 946 : pass = AT_PASS_MISC;
5135 946 : break;
5136 346 : case AT_AddInherit: /* INHERIT */
5137 346 : ATSimplePermissions(cmd->subtype, rel,
5138 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5139 : /* This command never recurses */
5140 346 : ATPrepAddInherit(rel);
5141 328 : pass = AT_PASS_MISC;
5142 328 : break;
5143 86 : case AT_DropInherit: /* NO INHERIT */
5144 86 : ATSimplePermissions(cmd->subtype, rel,
5145 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5146 : /* This command never recurses */
5147 : /* No command-specific prep needed */
5148 86 : pass = AT_PASS_MISC;
5149 86 : break;
5150 132 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5151 132 : ATSimplePermissions(cmd->subtype, rel,
5152 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5153 : /* Recursion occurs during execution phase */
5154 126 : pass = AT_PASS_MISC;
5155 126 : break;
5156 394 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5157 394 : ATSimplePermissions(cmd->subtype, rel,
5158 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5159 : /* Recursion occurs during execution phase */
5160 : /* No command-specific prep needed except saving recurse flag */
5161 394 : if (recurse)
5162 394 : cmd->recurse = true;
5163 394 : pass = AT_PASS_MISC;
5164 394 : break;
5165 484 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5166 484 : ATSimplePermissions(cmd->subtype, rel,
5167 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5168 484 : pass = AT_PASS_MISC;
5169 : /* This command never recurses */
5170 : /* No command-specific prep needed */
5171 484 : break;
5172 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5173 : case AT_EnableAlwaysTrig:
5174 : case AT_EnableReplicaTrig:
5175 : case AT_EnableTrigAll:
5176 : case AT_EnableTrigUser:
5177 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5178 : case AT_DisableTrigAll:
5179 : case AT_DisableTrigUser:
5180 340 : ATSimplePermissions(cmd->subtype, rel,
5181 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5182 : /* Set up recursion for phase 2; no other prep needed */
5183 340 : if (recurse)
5184 312 : cmd->recurse = true;
5185 340 : pass = AT_PASS_MISC;
5186 340 : break;
5187 532 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5188 : case AT_EnableAlwaysRule:
5189 : case AT_EnableReplicaRule:
5190 : case AT_DisableRule:
5191 : case AT_AddOf: /* OF */
5192 : case AT_DropOf: /* NOT OF */
5193 : case AT_EnableRowSecurity:
5194 : case AT_DisableRowSecurity:
5195 : case AT_ForceRowSecurity:
5196 : case AT_NoForceRowSecurity:
5197 532 : ATSimplePermissions(cmd->subtype, rel,
5198 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5199 : /* These commands never recurse */
5200 : /* No command-specific prep needed */
5201 532 : pass = AT_PASS_MISC;
5202 532 : break;
5203 58 : case AT_GenericOptions:
5204 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5205 : /* No command-specific prep needed */
5206 58 : pass = AT_PASS_MISC;
5207 58 : break;
5208 2626 : case AT_AttachPartition:
5209 2626 : ATSimplePermissions(cmd->subtype, rel,
5210 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5211 : /* No command-specific prep needed */
5212 2620 : pass = AT_PASS_MISC;
5213 2620 : break;
5214 564 : case AT_DetachPartition:
5215 564 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5216 : /* No command-specific prep needed */
5217 546 : pass = AT_PASS_MISC;
5218 546 : break;
5219 20 : case AT_DetachPartitionFinalize:
5220 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5221 : /* No command-specific prep needed */
5222 14 : pass = AT_PASS_MISC;
5223 14 : break;
5224 0 : default: /* oops */
5225 0 : elog(ERROR, "unrecognized alter table type: %d",
5226 : (int) cmd->subtype);
5227 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5228 : break;
5229 : }
5230 : Assert(pass > AT_PASS_UNSET);
5231 :
5232 : /* Add the subcommand to the appropriate list for phase 2 */
5233 31396 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5234 31396 : }
5235 :
5236 : /*
5237 : * ATRewriteCatalogs
5238 : *
5239 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5240 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5241 : * conflicts).
5242 : */
5243 : static void
5244 28982 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5245 : AlterTableUtilityContext *context)
5246 : {
5247 : ListCell *ltab;
5248 :
5249 : /*
5250 : * We process all the tables "in parallel", one pass at a time. This is
5251 : * needed because we may have to propagate work from one table to another
5252 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5253 : * re-adding of the foreign key constraint to the other table). Work can
5254 : * only be propagated into later passes, however.
5255 : */
5256 365346 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5257 : {
5258 : /* Go through each table that needs to be processed */
5259 689402 : foreach(ltab, *wqueue)
5260 : {
5261 353038 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5262 353038 : List *subcmds = tab->subcmds[pass];
5263 : ListCell *lcmd;
5264 :
5265 353038 : if (subcmds == NIL)
5266 302416 : continue;
5267 :
5268 : /*
5269 : * Open the relation and store it in tab. This allows subroutines
5270 : * close and reopen, if necessary. Appropriate lock was obtained
5271 : * by phase 1, needn't get it again.
5272 : */
5273 50622 : tab->rel = relation_open(tab->relid, NoLock);
5274 :
5275 102626 : foreach(lcmd, subcmds)
5276 54562 : ATExecCmd(wqueue, tab,
5277 54562 : lfirst_node(AlterTableCmd, lcmd),
5278 : lockmode, pass, context);
5279 :
5280 : /*
5281 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5282 : * (this is not done in ATExecAlterColumnType since it should be
5283 : * done only once if multiple columns of a table are altered).
5284 : */
5285 48064 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5286 988 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5287 :
5288 48064 : if (tab->rel)
5289 : {
5290 48064 : relation_close(tab->rel, NoLock);
5291 48064 : tab->rel = NULL;
5292 : }
5293 : }
5294 : }
5295 :
5296 : /* Check to see if a toast table must be added. */
5297 56630 : foreach(ltab, *wqueue)
5298 : {
5299 30206 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5300 :
5301 : /*
5302 : * If the table is source table of ATTACH PARTITION command, we did
5303 : * not modify anything about it that will change its toasting
5304 : * requirement, so no need to check.
5305 : */
5306 30206 : if (((tab->relkind == RELKIND_RELATION ||
5307 5762 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5308 28334 : tab->partition_constraint == NULL) ||
5309 3796 : tab->relkind == RELKIND_MATVIEW)
5310 26460 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5311 : }
5312 26424 : }
5313 :
5314 : /*
5315 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5316 : */
5317 : static void
5318 54562 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5319 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5320 : AlterTableUtilityContext *context)
5321 : {
5322 54562 : ObjectAddress address = InvalidObjectAddress;
5323 54562 : Relation rel = tab->rel;
5324 :
5325 54562 : switch (cmd->subtype)
5326 : {
5327 1996 : case AT_AddColumn: /* ADD COLUMN */
5328 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5329 1996 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5330 1996 : cmd->recurse, false,
5331 : lockmode, cur_pass, context);
5332 1876 : break;
5333 562 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5334 562 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5335 502 : break;
5336 56 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5337 56 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5338 56 : break;
5339 166 : case AT_AddIdentity:
5340 166 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5341 : cur_pass, context);
5342 : Assert(cmd != NULL);
5343 154 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5344 106 : break;
5345 62 : case AT_SetIdentity:
5346 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5347 : cur_pass, context);
5348 : Assert(cmd != NULL);
5349 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5350 38 : break;
5351 56 : case AT_DropIdentity:
5352 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5353 38 : break;
5354 256 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5355 256 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5356 154 : break;
5357 378 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5358 378 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5359 378 : cmd->recurse, false, lockmode);
5360 348 : break;
5361 84 : case AT_SetExpression:
5362 84 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5363 78 : break;
5364 32 : case AT_DropExpression:
5365 32 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5366 26 : break;
5367 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5368 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5369 116 : break;
5370 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5371 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5372 26 : break;
5373 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5374 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5375 6 : break;
5376 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5377 234 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5378 222 : break;
5379 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5380 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5381 : lockmode);
5382 62 : break;
5383 1592 : case AT_DropColumn: /* DROP COLUMN */
5384 1592 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5385 1592 : cmd->behavior, cmd->recurse, false,
5386 1592 : cmd->missing_ok, lockmode,
5387 : NULL);
5388 1418 : break;
5389 1144 : case AT_AddIndex: /* ADD INDEX */
5390 1144 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5391 : lockmode);
5392 974 : break;
5393 444 : case AT_ReAddIndex: /* ADD INDEX */
5394 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5395 : lockmode);
5396 444 : break;
5397 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5398 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5399 : true, lockmode);
5400 14 : break;
5401 26592 : case AT_AddConstraint: /* ADD CONSTRAINT */
5402 : /* Transform the command only during initial examination */
5403 26592 : if (cur_pass == AT_PASS_ADD_CONSTR)
5404 14902 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5405 14932 : cmd->recurse, lockmode,
5406 : cur_pass, context);
5407 : /* Depending on constraint type, might be no more work to do now */
5408 26562 : if (cmd != NULL)
5409 : address =
5410 11660 : ATExecAddConstraint(wqueue, tab, rel,
5411 11660 : (Constraint *) cmd->def,
5412 11660 : cmd->recurse, false, lockmode);
5413 25936 : break;
5414 326 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5415 : address =
5416 326 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5417 : true, true, lockmode);
5418 314 : break;
5419 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5420 : * constraint */
5421 : address =
5422 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5423 14 : ((AlterDomainStmt *) cmd->def)->def,
5424 : NULL);
5425 8 : break;
5426 78 : case AT_ReAddComment: /* Re-add existing comment */
5427 78 : address = CommentObject((CommentStmt *) cmd->def);
5428 78 : break;
5429 9540 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5430 9540 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5431 : lockmode);
5432 9528 : break;
5433 126 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5434 126 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5435 114 : break;
5436 394 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5437 394 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5438 : false, lockmode);
5439 388 : break;
5440 816 : case AT_DropConstraint: /* DROP CONSTRAINT */
5441 816 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5442 816 : cmd->recurse,
5443 816 : cmd->missing_ok, lockmode);
5444 606 : break;
5445 970 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5446 : /* parse transformation was done earlier */
5447 970 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5448 940 : break;
5449 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5450 : address =
5451 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5452 172 : (List *) cmd->def, lockmode);
5453 166 : break;
5454 1796 : case AT_ChangeOwner: /* ALTER OWNER */
5455 1790 : ATExecChangeOwner(RelationGetRelid(rel),
5456 1796 : get_rolespec_oid(cmd->newowner, false),
5457 : false, lockmode);
5458 1778 : break;
5459 64 : case AT_ClusterOn: /* CLUSTER ON */
5460 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5461 58 : break;
5462 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5463 18 : ATExecDropCluster(rel, lockmode);
5464 12 : break;
5465 88 : case AT_SetLogged: /* SET LOGGED */
5466 : case AT_SetUnLogged: /* SET UNLOGGED */
5467 88 : break;
5468 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5469 : /* nothing to do here, oid columns don't exist anymore */
5470 6 : break;
5471 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5472 :
5473 : /*
5474 : * Only do this for partitioned tables, for which this is just a
5475 : * catalog change. Tables with storage are handled by Phase 3.
5476 : */
5477 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5478 50 : tab->chgAccessMethod)
5479 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5480 92 : break;
5481 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5482 :
5483 : /*
5484 : * Only do this for partitioned tables and indexes, for which this
5485 : * is just a catalog change. Other relation types which have
5486 : * storage are handled by Phase 3.
5487 : */
5488 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5489 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5490 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5491 :
5492 152 : break;
5493 946 : case AT_SetRelOptions: /* SET (...) */
5494 : case AT_ResetRelOptions: /* RESET (...) */
5495 : case AT_ReplaceRelOptions: /* replace entire option list */
5496 946 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5497 894 : break;
5498 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5499 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5500 : TRIGGER_FIRES_ON_ORIGIN, false,
5501 122 : cmd->recurse,
5502 : lockmode);
5503 122 : break;
5504 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5505 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5506 : TRIGGER_FIRES_ALWAYS, false,
5507 40 : cmd->recurse,
5508 : lockmode);
5509 40 : break;
5510 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5511 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5512 : TRIGGER_FIRES_ON_REPLICA, false,
5513 16 : cmd->recurse,
5514 : lockmode);
5515 16 : break;
5516 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5517 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5518 : TRIGGER_DISABLED, false,
5519 138 : cmd->recurse,
5520 : lockmode);
5521 138 : break;
5522 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5523 0 : ATExecEnableDisableTrigger(rel, NULL,
5524 : TRIGGER_FIRES_ON_ORIGIN, false,
5525 0 : cmd->recurse,
5526 : lockmode);
5527 0 : break;
5528 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5529 12 : ATExecEnableDisableTrigger(rel, NULL,
5530 : TRIGGER_DISABLED, false,
5531 12 : cmd->recurse,
5532 : lockmode);
5533 12 : break;
5534 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5535 0 : ATExecEnableDisableTrigger(rel, NULL,
5536 : TRIGGER_FIRES_ON_ORIGIN, true,
5537 0 : cmd->recurse,
5538 : lockmode);
5539 0 : break;
5540 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5541 12 : ATExecEnableDisableTrigger(rel, NULL,
5542 : TRIGGER_DISABLED, true,
5543 12 : cmd->recurse,
5544 : lockmode);
5545 12 : break;
5546 :
5547 8 : case AT_EnableRule: /* ENABLE RULE name */
5548 8 : ATExecEnableDisableRule(rel, cmd->name,
5549 : RULE_FIRES_ON_ORIGIN, lockmode);
5550 8 : break;
5551 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5552 0 : ATExecEnableDisableRule(rel, cmd->name,
5553 : RULE_FIRES_ALWAYS, lockmode);
5554 0 : break;
5555 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5556 6 : ATExecEnableDisableRule(rel, cmd->name,
5557 : RULE_FIRES_ON_REPLICA, lockmode);
5558 6 : break;
5559 32 : case AT_DisableRule: /* DISABLE RULE name */
5560 32 : ATExecEnableDisableRule(rel, cmd->name,
5561 : RULE_DISABLED, lockmode);
5562 32 : break;
5563 :
5564 328 : case AT_AddInherit:
5565 328 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5566 232 : break;
5567 86 : case AT_DropInherit:
5568 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5569 80 : break;
5570 66 : case AT_AddOf:
5571 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5572 30 : break;
5573 6 : case AT_DropOf:
5574 6 : ATExecDropOf(rel, lockmode);
5575 6 : break;
5576 502 : case AT_ReplicaIdentity:
5577 502 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5578 454 : break;
5579 284 : case AT_EnableRowSecurity:
5580 284 : ATExecSetRowSecurity(rel, true);
5581 284 : break;
5582 10 : case AT_DisableRowSecurity:
5583 10 : ATExecSetRowSecurity(rel, false);
5584 10 : break;
5585 88 : case AT_ForceRowSecurity:
5586 88 : ATExecForceNoForceRowSecurity(rel, true);
5587 88 : break;
5588 32 : case AT_NoForceRowSecurity:
5589 32 : ATExecForceNoForceRowSecurity(rel, false);
5590 32 : break;
5591 58 : case AT_GenericOptions:
5592 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5593 56 : break;
5594 2620 : case AT_AttachPartition:
5595 2620 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5596 : cur_pass, context);
5597 : Assert(cmd != NULL);
5598 2596 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5599 2204 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5600 : context);
5601 : else
5602 392 : address = ATExecAttachPartitionIdx(wqueue, rel,
5603 392 : ((PartitionCmd *) cmd->def)->name);
5604 2254 : break;
5605 546 : case AT_DetachPartition:
5606 546 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5607 : cur_pass, context);
5608 : Assert(cmd != NULL);
5609 : /* ATPrepCmd ensures it must be a table */
5610 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5611 546 : address = ATExecDetachPartition(wqueue, tab, rel,
5612 546 : ((PartitionCmd *) cmd->def)->name,
5613 546 : ((PartitionCmd *) cmd->def)->concurrent);
5614 416 : break;
5615 14 : case AT_DetachPartitionFinalize:
5616 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5617 14 : break;
5618 0 : default: /* oops */
5619 0 : elog(ERROR, "unrecognized alter table type: %d",
5620 : (int) cmd->subtype);
5621 : break;
5622 : }
5623 :
5624 : /*
5625 : * Report the subcommand to interested event triggers.
5626 : */
5627 52004 : if (cmd)
5628 37102 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5629 :
5630 : /*
5631 : * Bump the command counter to ensure the next subcommand in the sequence
5632 : * can see the changes so far
5633 : */
5634 52004 : CommandCounterIncrement();
5635 52004 : }
5636 :
5637 : /*
5638 : * ATParseTransformCmd: perform parse transformation for one subcommand
5639 : *
5640 : * Returns the transformed subcommand tree, if there is one, else NULL.
5641 : *
5642 : * The parser may hand back additional AlterTableCmd(s) and/or other
5643 : * utility statements, either before or after the original subcommand.
5644 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5645 : * AlteredTableInfo (they had better be for later passes than the current one).
5646 : * Utility statements that are supposed to happen before the AlterTableCmd
5647 : * are executed immediately. Those that are supposed to happen afterwards
5648 : * are added to the tab->afterStmts list to be done at the very end.
5649 : */
5650 : static AlterTableCmd *
5651 21376 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5652 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5653 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5654 : {
5655 21376 : AlterTableCmd *newcmd = NULL;
5656 21376 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5657 : List *beforeStmts;
5658 : List *afterStmts;
5659 : ListCell *lc;
5660 :
5661 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5662 21376 : atstmt->relation =
5663 21376 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5664 21376 : pstrdup(RelationGetRelationName(rel)),
5665 : -1);
5666 21376 : atstmt->relation->inh = recurse;
5667 21376 : atstmt->cmds = list_make1(cmd);
5668 21376 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5669 21376 : atstmt->missing_ok = false;
5670 :
5671 : /* Transform the AlterTableStmt */
5672 21376 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5673 : atstmt,
5674 : context->queryString,
5675 : &beforeStmts,
5676 : &afterStmts);
5677 :
5678 : /* Execute any statements that should happen before these subcommand(s) */
5679 21784 : foreach(lc, beforeStmts)
5680 : {
5681 486 : Node *stmt = (Node *) lfirst(lc);
5682 :
5683 486 : ProcessUtilityForAlterTable(stmt, context);
5684 474 : CommandCounterIncrement();
5685 : }
5686 :
5687 : /* Examine the transformed subcommands and schedule them appropriately */
5688 50074 : foreach(lc, atstmt->cmds)
5689 : {
5690 28776 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5691 : AlterTablePass pass;
5692 :
5693 : /*
5694 : * This switch need only cover the subcommand types that can be added
5695 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5696 : * executing the subcommand immediately, as a substitute for the
5697 : * original subcommand. (Note, however, that this does cause
5698 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5699 : * which is important for index and foreign key constraints.)
5700 : *
5701 : * We assume we needn't do any phase-1 checks for added subcommands.
5702 : */
5703 28776 : switch (cmd2->subtype)
5704 : {
5705 1168 : case AT_AddIndex:
5706 1168 : pass = AT_PASS_ADD_INDEX;
5707 1168 : break;
5708 9540 : case AT_AddIndexConstraint:
5709 9540 : pass = AT_PASS_ADD_INDEXCONSTR;
5710 9540 : break;
5711 11672 : case AT_AddConstraint:
5712 : /* Recursion occurs during execution phase */
5713 11672 : if (recurse)
5714 11622 : cmd2->recurse = true;
5715 11672 : switch (castNode(Constraint, cmd2->def)->contype)
5716 : {
5717 8422 : case CONSTR_NOTNULL:
5718 8422 : pass = AT_PASS_COL_ATTRS;
5719 8422 : break;
5720 0 : case CONSTR_PRIMARY:
5721 : case CONSTR_UNIQUE:
5722 : case CONSTR_EXCLUSION:
5723 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5724 0 : break;
5725 3250 : default:
5726 3250 : pass = AT_PASS_ADD_OTHERCONSTR;
5727 3250 : break;
5728 : }
5729 11672 : break;
5730 0 : case AT_AlterColumnGenericOptions:
5731 : /* This command never recurses */
5732 : /* No command-specific prep needed */
5733 0 : pass = AT_PASS_MISC;
5734 0 : break;
5735 6396 : default:
5736 6396 : pass = cur_pass;
5737 6396 : break;
5738 : }
5739 :
5740 28776 : if (pass < cur_pass)
5741 : {
5742 : /* Cannot schedule into a pass we already finished */
5743 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5744 : pass);
5745 : }
5746 28776 : else if (pass > cur_pass)
5747 : {
5748 : /* OK, queue it up for later */
5749 22380 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5750 : }
5751 : else
5752 : {
5753 : /*
5754 : * We should see at most one subcommand for the current pass,
5755 : * which is the transformed version of the original subcommand.
5756 : */
5757 6396 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5758 : {
5759 : /* Found the transformed version of our subcommand */
5760 6396 : newcmd = cmd2;
5761 : }
5762 : else
5763 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5764 : pass);
5765 : }
5766 : }
5767 :
5768 : /* Queue up any after-statements to happen at the end */
5769 21298 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5770 :
5771 21298 : return newcmd;
5772 : }
5773 :
5774 : /*
5775 : * ATRewriteTables: ALTER TABLE phase 3
5776 : */
5777 : static void
5778 26424 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5779 : AlterTableUtilityContext *context)
5780 : {
5781 : ListCell *ltab;
5782 :
5783 : /* Go through each table that needs to be checked or rewritten */
5784 56368 : foreach(ltab, *wqueue)
5785 : {
5786 30194 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5787 :
5788 : /* Relations without storage may be ignored here */
5789 30194 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5790 5474 : continue;
5791 :
5792 : /*
5793 : * If we change column data types, the operation has to be propagated
5794 : * to tables that use this table's rowtype as a column type.
5795 : * tab->newvals will also be non-NULL in the case where we're adding a
5796 : * column with a default. We choose to forbid that case as well,
5797 : * since composite types might eventually support defaults.
5798 : *
5799 : * (Eventually we'll probably need to check for composite type
5800 : * dependencies even when we're just scanning the table without a
5801 : * rewrite, but at the moment a composite type does not enforce any
5802 : * constraints, so it's not necessary/appropriate to enforce them just
5803 : * during ALTER.)
5804 : */
5805 24720 : if (tab->newvals != NIL || tab->rewrite > 0)
5806 : {
5807 : Relation rel;
5808 :
5809 1494 : rel = table_open(tab->relid, NoLock);
5810 1494 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5811 1482 : table_close(rel, NoLock);
5812 : }
5813 :
5814 : /*
5815 : * We only need to rewrite the table if at least one column needs to
5816 : * be recomputed, or we are changing its persistence or access method.
5817 : *
5818 : * There are two reasons for requiring a rewrite when changing
5819 : * persistence: on one hand, we need to ensure that the buffers
5820 : * belonging to each of the two relations are marked with or without
5821 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5822 : * and assigns a new relfilenumber, we automatically create or drop an
5823 : * init fork for the relation as appropriate.
5824 : */
5825 24708 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5826 856 : {
5827 : /* Build a temporary relation and copy data */
5828 : Relation OldHeap;
5829 : Oid OIDNewHeap;
5830 : Oid NewAccessMethod;
5831 : Oid NewTableSpace;
5832 : char persistence;
5833 :
5834 894 : OldHeap = table_open(tab->relid, NoLock);
5835 :
5836 : /*
5837 : * We don't support rewriting of system catalogs; there are too
5838 : * many corner cases and too little benefit. In particular this
5839 : * is certainly not going to work for mapped catalogs.
5840 : */
5841 894 : if (IsSystemRelation(OldHeap))
5842 0 : ereport(ERROR,
5843 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5844 : errmsg("cannot rewrite system relation \"%s\"",
5845 : RelationGetRelationName(OldHeap))));
5846 :
5847 894 : if (RelationIsUsedAsCatalogTable(OldHeap))
5848 2 : ereport(ERROR,
5849 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5850 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5851 : RelationGetRelationName(OldHeap))));
5852 :
5853 : /*
5854 : * Don't allow rewrite on temp tables of other backends ... their
5855 : * local buffer manager is not going to cope. (This is redundant
5856 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5857 : * check here too.)
5858 : */
5859 892 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5860 0 : ereport(ERROR,
5861 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5862 : errmsg("cannot rewrite temporary tables of other sessions")));
5863 :
5864 : /*
5865 : * Select destination tablespace (same as original unless user
5866 : * requested a change)
5867 : */
5868 892 : if (tab->newTableSpace)
5869 0 : NewTableSpace = tab->newTableSpace;
5870 : else
5871 892 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5872 :
5873 : /*
5874 : * Select destination access method (same as original unless user
5875 : * requested a change)
5876 : */
5877 892 : if (tab->chgAccessMethod)
5878 36 : NewAccessMethod = tab->newAccessMethod;
5879 : else
5880 856 : NewAccessMethod = OldHeap->rd_rel->relam;
5881 :
5882 : /*
5883 : * Select persistence of transient table (same as original unless
5884 : * user requested a change)
5885 : */
5886 892 : persistence = tab->chgPersistence ?
5887 840 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5888 :
5889 892 : table_close(OldHeap, NoLock);
5890 :
5891 : /*
5892 : * Fire off an Event Trigger now, before actually rewriting the
5893 : * table.
5894 : *
5895 : * We don't support Event Trigger for nested commands anywhere,
5896 : * here included, and parsetree is given NULL when coming from
5897 : * AlterTableInternal.
5898 : *
5899 : * And fire it only once.
5900 : */
5901 892 : if (parsetree)
5902 892 : EventTriggerTableRewrite((Node *) parsetree,
5903 : tab->relid,
5904 : tab->rewrite);
5905 :
5906 : /*
5907 : * Create transient table that will receive the modified data.
5908 : *
5909 : * Ensure it is marked correctly as logged or unlogged. We have
5910 : * to do this here so that buffers for the new relfilenumber will
5911 : * have the right persistence set, and at the same time ensure
5912 : * that the original filenumbers's buffers will get read in with
5913 : * the correct setting (i.e. the original one). Otherwise a
5914 : * rollback after the rewrite would possibly result with buffers
5915 : * for the original filenumbers having the wrong persistence
5916 : * setting.
5917 : *
5918 : * NB: This relies on swap_relation_files() also swapping the
5919 : * persistence. That wouldn't work for pg_class, but that can't be
5920 : * unlogged anyway.
5921 : */
5922 886 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5923 : persistence, lockmode);
5924 :
5925 : /*
5926 : * Copy the heap data into the new table with the desired
5927 : * modifications, and test the current data within the table
5928 : * against new constraints generated by ALTER TABLE commands.
5929 : */
5930 886 : ATRewriteTable(tab, OIDNewHeap);
5931 :
5932 : /*
5933 : * Swap the physical files of the old and new heaps, then rebuild
5934 : * indexes and discard the old heap. We can use RecentXmin for
5935 : * the table's new relfrozenxid because we rewrote all the tuples
5936 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5937 : * we never try to swap toast tables by content, since we have no
5938 : * interest in letting this code work on system catalogs.
5939 : */
5940 862 : finish_heap_swap(tab->relid, OIDNewHeap,
5941 : false, false, true,
5942 862 : !OidIsValid(tab->newTableSpace),
5943 : RecentXmin,
5944 : ReadNextMultiXactId(),
5945 : persistence);
5946 :
5947 856 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5948 : }
5949 23814 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5950 : {
5951 24 : if (tab->chgPersistence)
5952 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5953 : }
5954 : else
5955 : {
5956 : /*
5957 : * If required, test the current data within the table against new
5958 : * constraints generated by ALTER TABLE commands, but don't
5959 : * rebuild data.
5960 : */
5961 23790 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5962 21232 : tab->partition_constraint != NULL)
5963 4334 : ATRewriteTable(tab, InvalidOid);
5964 :
5965 : /*
5966 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5967 : * just do a block-by-block copy.
5968 : */
5969 23590 : if (tab->newTableSpace)
5970 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5971 : }
5972 :
5973 : /*
5974 : * Also change persistence of owned sequences, so that it matches the
5975 : * table persistence.
5976 : */
5977 24470 : if (tab->chgPersistence)
5978 : {
5979 76 : List *seqlist = getOwnedSequences(tab->relid);
5980 : ListCell *lc;
5981 :
5982 124 : foreach(lc, seqlist)
5983 : {
5984 48 : Oid seq_relid = lfirst_oid(lc);
5985 :
5986 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5987 : }
5988 : }
5989 : }
5990 :
5991 : /*
5992 : * Foreign key constraints are checked in a final pass, since (a) it's
5993 : * generally best to examine each one separately, and (b) it's at least
5994 : * theoretically possible that we have changed both relations of the
5995 : * foreign key, and we'd better have finished both rewrites before we try
5996 : * to read the tables.
5997 : */
5998 55908 : foreach(ltab, *wqueue)
5999 : {
6000 29814 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6001 29814 : Relation rel = NULL;
6002 : ListCell *lcon;
6003 :
6004 : /* Relations without storage may be ignored here too */
6005 29814 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6006 5394 : continue;
6007 :
6008 26130 : foreach(lcon, tab->constraints)
6009 : {
6010 1790 : NewConstraint *con = lfirst(lcon);
6011 :
6012 1790 : if (con->contype == CONSTR_FOREIGN)
6013 : {
6014 1108 : Constraint *fkconstraint = (Constraint *) con->qual;
6015 : Relation refrel;
6016 :
6017 1108 : if (rel == NULL)
6018 : {
6019 : /* Long since locked, no need for another */
6020 1096 : rel = table_open(tab->relid, NoLock);
6021 : }
6022 :
6023 1108 : refrel = table_open(con->refrelid, RowShareLock);
6024 :
6025 1108 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6026 : con->refindid,
6027 : con->conid,
6028 1108 : con->conwithperiod);
6029 :
6030 : /*
6031 : * No need to mark the constraint row as validated, we did
6032 : * that when we inserted the row earlier.
6033 : */
6034 :
6035 1028 : table_close(refrel, NoLock);
6036 : }
6037 : }
6038 :
6039 24340 : if (rel)
6040 1016 : table_close(rel, NoLock);
6041 : }
6042 :
6043 : /* Finally, run any afterStmts that were queued up */
6044 55790 : foreach(ltab, *wqueue)
6045 : {
6046 29696 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6047 : ListCell *lc;
6048 :
6049 29782 : foreach(lc, tab->afterStmts)
6050 : {
6051 86 : Node *stmt = (Node *) lfirst(lc);
6052 :
6053 86 : ProcessUtilityForAlterTable(stmt, context);
6054 86 : CommandCounterIncrement();
6055 : }
6056 : }
6057 26094 : }
6058 :
6059 : /*
6060 : * ATRewriteTable: scan or rewrite one table
6061 : *
6062 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6063 : * must already hold AccessExclusiveLock on it.
6064 : */
6065 : static void
6066 5220 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6067 : {
6068 : Relation oldrel;
6069 : Relation newrel;
6070 : TupleDesc oldTupDesc;
6071 : TupleDesc newTupDesc;
6072 5220 : bool needscan = false;
6073 : List *notnull_attrs;
6074 : int i;
6075 : ListCell *l;
6076 : EState *estate;
6077 : CommandId mycid;
6078 : BulkInsertState bistate;
6079 : int ti_options;
6080 5220 : ExprState *partqualstate = NULL;
6081 :
6082 : /*
6083 : * Open the relation(s). We have surely already locked the existing
6084 : * table.
6085 : */
6086 5220 : oldrel = table_open(tab->relid, NoLock);
6087 5220 : oldTupDesc = tab->oldDesc;
6088 5220 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6089 :
6090 5220 : if (OidIsValid(OIDNewHeap))
6091 : {
6092 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6093 : false));
6094 886 : newrel = table_open(OIDNewHeap, NoLock);
6095 : }
6096 : else
6097 4334 : newrel = NULL;
6098 :
6099 : /*
6100 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6101 : * is empty, so don't bother using it.
6102 : */
6103 5220 : if (newrel)
6104 : {
6105 886 : mycid = GetCurrentCommandId(true);
6106 886 : bistate = GetBulkInsertState();
6107 886 : ti_options = TABLE_INSERT_SKIP_FSM;
6108 : }
6109 : else
6110 : {
6111 : /* keep compiler quiet about using these uninitialized */
6112 4334 : mycid = 0;
6113 4334 : bistate = NULL;
6114 4334 : ti_options = 0;
6115 : }
6116 :
6117 : /*
6118 : * Generate the constraint and default execution states
6119 : */
6120 :
6121 5220 : estate = CreateExecutorState();
6122 :
6123 : /* Build the needed expression execution states */
6124 7106 : foreach(l, tab->constraints)
6125 : {
6126 1886 : NewConstraint *con = lfirst(l);
6127 :
6128 1886 : switch (con->contype)
6129 : {
6130 772 : case CONSTR_CHECK:
6131 772 : needscan = true;
6132 772 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6133 772 : break;
6134 1114 : case CONSTR_FOREIGN:
6135 : /* Nothing to do here */
6136 1114 : break;
6137 0 : default:
6138 0 : elog(ERROR, "unrecognized constraint type: %d",
6139 : (int) con->contype);
6140 : }
6141 : }
6142 :
6143 : /* Build expression execution states for partition check quals */
6144 5220 : if (tab->partition_constraint)
6145 : {
6146 1918 : needscan = true;
6147 1918 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6148 : }
6149 :
6150 6102 : foreach(l, tab->newvals)
6151 : {
6152 882 : NewColumnValue *ex = lfirst(l);
6153 :
6154 : /* expr already planned */
6155 882 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6156 : }
6157 :
6158 5220 : notnull_attrs = NIL;
6159 5220 : if (newrel || tab->verify_new_notnull)
6160 : {
6161 : /*
6162 : * If we are rebuilding the tuples OR if we added any new but not
6163 : * verified not-null constraints, check all not-null constraints. This
6164 : * is a bit of overkill but it minimizes risk of bugs.
6165 : */
6166 6264 : for (i = 0; i < newTupDesc->natts; i++)
6167 : {
6168 4560 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6169 :
6170 4560 : if (attr->attnotnull && !attr->attisdropped)
6171 1808 : notnull_attrs = lappend_int(notnull_attrs, i);
6172 : }
6173 1704 : if (notnull_attrs)
6174 1288 : needscan = true;
6175 : }
6176 :
6177 5220 : if (newrel || needscan)
6178 : {
6179 : ExprContext *econtext;
6180 : TupleTableSlot *oldslot;
6181 : TupleTableSlot *newslot;
6182 : TableScanDesc scan;
6183 : MemoryContext oldCxt;
6184 4302 : List *dropped_attrs = NIL;
6185 : ListCell *lc;
6186 : Snapshot snapshot;
6187 :
6188 4302 : if (newrel)
6189 886 : ereport(DEBUG1,
6190 : (errmsg_internal("rewriting table \"%s\"",
6191 : RelationGetRelationName(oldrel))));
6192 : else
6193 3416 : ereport(DEBUG1,
6194 : (errmsg_internal("verifying table \"%s\"",
6195 : RelationGetRelationName(oldrel))));
6196 :
6197 4302 : if (newrel)
6198 : {
6199 : /*
6200 : * All predicate locks on the tuples or pages are about to be made
6201 : * invalid, because we move tuples around. Promote them to
6202 : * relation locks.
6203 : */
6204 886 : TransferPredicateLocksToHeapRelation(oldrel);
6205 : }
6206 :
6207 4302 : econtext = GetPerTupleExprContext(estate);
6208 :
6209 : /*
6210 : * Create necessary tuple slots. When rewriting, two slots are needed,
6211 : * otherwise one suffices. In the case where one slot suffices, we
6212 : * need to use the new tuple descriptor, otherwise some constraints
6213 : * can't be evaluated. Note that even when the tuple layout is the
6214 : * same and no rewrite is required, the tupDescs might not be
6215 : * (consider ADD COLUMN without a default).
6216 : */
6217 4302 : if (tab->rewrite)
6218 : {
6219 : Assert(newrel != NULL);
6220 886 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6221 : table_slot_callbacks(oldrel));
6222 886 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6223 : table_slot_callbacks(newrel));
6224 :
6225 : /*
6226 : * Set all columns in the new slot to NULL initially, to ensure
6227 : * columns added as part of the rewrite are initialized to NULL.
6228 : * That is necessary as tab->newvals will not contain an
6229 : * expression for columns with a NULL default, e.g. when adding a
6230 : * column without a default together with a column with a default
6231 : * requiring an actual rewrite.
6232 : */
6233 886 : ExecStoreAllNullTuple(newslot);
6234 : }
6235 : else
6236 : {
6237 3416 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6238 : table_slot_callbacks(oldrel));
6239 3416 : newslot = NULL;
6240 : }
6241 :
6242 : /*
6243 : * Any attributes that are dropped according to the new tuple
6244 : * descriptor can be set to NULL. We precompute the list of dropped
6245 : * attributes to avoid needing to do so in the per-tuple loop.
6246 : */
6247 15114 : for (i = 0; i < newTupDesc->natts; i++)
6248 : {
6249 10812 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6250 778 : dropped_attrs = lappend_int(dropped_attrs, i);
6251 : }
6252 :
6253 : /*
6254 : * Scan through the rows, generating a new row if needed and then
6255 : * checking all the constraints.
6256 : */
6257 4302 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6258 4302 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6259 :
6260 : /*
6261 : * Switch to per-tuple memory context and reset it for each tuple
6262 : * produced, so we don't leak memory.
6263 : */
6264 4302 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6265 :
6266 768838 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6267 : {
6268 : TupleTableSlot *insertslot;
6269 :
6270 764760 : if (tab->rewrite > 0)
6271 : {
6272 : /* Extract data from old tuple */
6273 99570 : slot_getallattrs(oldslot);
6274 99570 : ExecClearTuple(newslot);
6275 :
6276 : /* copy attributes */
6277 99570 : memcpy(newslot->tts_values, oldslot->tts_values,
6278 99570 : sizeof(Datum) * oldslot->tts_nvalid);
6279 99570 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6280 99570 : sizeof(bool) * oldslot->tts_nvalid);
6281 :
6282 : /* Set dropped attributes to null in new tuple */
6283 99656 : foreach(lc, dropped_attrs)
6284 86 : newslot->tts_isnull[lfirst_int(lc)] = true;
6285 :
6286 : /*
6287 : * Constraints and GENERATED expressions might reference the
6288 : * tableoid column, so fill tts_tableOid with the desired
6289 : * value. (We must do this each time, because it gets
6290 : * overwritten with newrel's OID during storing.)
6291 : */
6292 99570 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6293 :
6294 : /*
6295 : * Process supplied expressions to replace selected columns.
6296 : *
6297 : * First, evaluate expressions whose inputs come from the old
6298 : * tuple.
6299 : */
6300 99570 : econtext->ecxt_scantuple = oldslot;
6301 :
6302 204960 : foreach(l, tab->newvals)
6303 : {
6304 105402 : NewColumnValue *ex = lfirst(l);
6305 :
6306 105402 : if (ex->is_generated)
6307 150 : continue;
6308 :
6309 105252 : newslot->tts_values[ex->attnum - 1]
6310 105240 : = ExecEvalExpr(ex->exprstate,
6311 : econtext,
6312 105252 : &newslot->tts_isnull[ex->attnum - 1]);
6313 : }
6314 :
6315 99558 : ExecStoreVirtualTuple(newslot);
6316 :
6317 : /*
6318 : * Now, evaluate any expressions whose inputs come from the
6319 : * new tuple. We assume these columns won't reference each
6320 : * other, so that there's no ordering dependency.
6321 : */
6322 99558 : econtext->ecxt_scantuple = newslot;
6323 :
6324 204948 : foreach(l, tab->newvals)
6325 : {
6326 105390 : NewColumnValue *ex = lfirst(l);
6327 :
6328 105390 : if (!ex->is_generated)
6329 105240 : continue;
6330 :
6331 150 : newslot->tts_values[ex->attnum - 1]
6332 150 : = ExecEvalExpr(ex->exprstate,
6333 : econtext,
6334 150 : &newslot->tts_isnull[ex->attnum - 1]);
6335 : }
6336 :
6337 99558 : insertslot = newslot;
6338 : }
6339 : else
6340 : {
6341 : /*
6342 : * If there's no rewrite, old and new table are guaranteed to
6343 : * have the same AM, so we can just use the old slot to verify
6344 : * new constraints etc.
6345 : */
6346 665190 : insertslot = oldslot;
6347 : }
6348 :
6349 : /* Now check any constraints on the possibly-changed tuple */
6350 764748 : econtext->ecxt_scantuple = insertslot;
6351 :
6352 3341030 : foreach(l, notnull_attrs)
6353 : {
6354 2576348 : int attn = lfirst_int(l);
6355 :
6356 2576348 : if (slot_attisnull(insertslot, attn + 1))
6357 : {
6358 66 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6359 :
6360 66 : ereport(ERROR,
6361 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6362 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6363 : NameStr(attr->attname),
6364 : RelationGetRelationName(oldrel)),
6365 : errtablecol(oldrel, attn + 1)));
6366 : }
6367 : }
6368 :
6369 772782 : foreach(l, tab->constraints)
6370 : {
6371 8172 : NewConstraint *con = lfirst(l);
6372 :
6373 8172 : switch (con->contype)
6374 : {
6375 8072 : case CONSTR_CHECK:
6376 8072 : if (!ExecCheck(con->qualstate, econtext))
6377 72 : ereport(ERROR,
6378 : (errcode(ERRCODE_CHECK_VIOLATION),
6379 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6380 : con->name,
6381 : RelationGetRelationName(oldrel)),
6382 : errtableconstraint(oldrel, con->name)));
6383 8000 : break;
6384 100 : case CONSTR_NOTNULL:
6385 : case CONSTR_FOREIGN:
6386 : /* Nothing to do here */
6387 100 : break;
6388 0 : default:
6389 0 : elog(ERROR, "unrecognized constraint type: %d",
6390 : (int) con->contype);
6391 : }
6392 : }
6393 :
6394 764610 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6395 : {
6396 74 : if (tab->validate_default)
6397 26 : ereport(ERROR,
6398 : (errcode(ERRCODE_CHECK_VIOLATION),
6399 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6400 : RelationGetRelationName(oldrel)),
6401 : errtable(oldrel)));
6402 : else
6403 48 : ereport(ERROR,
6404 : (errcode(ERRCODE_CHECK_VIOLATION),
6405 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6406 : RelationGetRelationName(oldrel)),
6407 : errtable(oldrel)));
6408 : }
6409 :
6410 : /* Write the tuple out to the new relation */
6411 764536 : if (newrel)
6412 99546 : table_tuple_insert(newrel, insertslot, mycid,
6413 : ti_options, bistate);
6414 :
6415 764536 : ResetExprContext(econtext);
6416 :
6417 764536 : CHECK_FOR_INTERRUPTS();
6418 : }
6419 :
6420 4078 : MemoryContextSwitchTo(oldCxt);
6421 4078 : table_endscan(scan);
6422 4078 : UnregisterSnapshot(snapshot);
6423 :
6424 4078 : ExecDropSingleTupleTableSlot(oldslot);
6425 4078 : if (newslot)
6426 862 : ExecDropSingleTupleTableSlot(newslot);
6427 : }
6428 :
6429 4996 : FreeExecutorState(estate);
6430 :
6431 4996 : table_close(oldrel, NoLock);
6432 4996 : if (newrel)
6433 : {
6434 862 : FreeBulkInsertState(bistate);
6435 :
6436 862 : table_finish_bulk_insert(newrel, ti_options);
6437 :
6438 862 : table_close(newrel, NoLock);
6439 : }
6440 4996 : }
6441 :
6442 : /*
6443 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6444 : */
6445 : static AlteredTableInfo *
6446 38260 : ATGetQueueEntry(List **wqueue, Relation rel)
6447 : {
6448 38260 : Oid relid = RelationGetRelid(rel);
6449 : AlteredTableInfo *tab;
6450 : ListCell *ltab;
6451 :
6452 47230 : foreach(ltab, *wqueue)
6453 : {
6454 13944 : tab = (AlteredTableInfo *) lfirst(ltab);
6455 13944 : if (tab->relid == relid)
6456 4974 : return tab;
6457 : }
6458 :
6459 : /*
6460 : * Not there, so add it. Note that we make a copy of the relation's
6461 : * existing descriptor before anything interesting can happen to it.
6462 : */
6463 33286 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6464 33286 : tab->relid = relid;
6465 33286 : tab->rel = NULL; /* set later */
6466 33286 : tab->relkind = rel->rd_rel->relkind;
6467 33286 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6468 33286 : tab->newAccessMethod = InvalidOid;
6469 33286 : tab->chgAccessMethod = false;
6470 33286 : tab->newTableSpace = InvalidOid;
6471 33286 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6472 33286 : tab->chgPersistence = false;
6473 :
6474 33286 : *wqueue = lappend(*wqueue, tab);
6475 :
6476 33286 : return tab;
6477 : }
6478 :
6479 : static const char *
6480 78 : alter_table_type_to_string(AlterTableType cmdtype)
6481 : {
6482 78 : switch (cmdtype)
6483 : {
6484 0 : case AT_AddColumn:
6485 : case AT_AddColumnToView:
6486 0 : return "ADD COLUMN";
6487 0 : case AT_ColumnDefault:
6488 : case AT_CookedColumnDefault:
6489 0 : return "ALTER COLUMN ... SET DEFAULT";
6490 6 : case AT_DropNotNull:
6491 6 : return "ALTER COLUMN ... DROP NOT NULL";
6492 6 : case AT_SetNotNull:
6493 6 : return "ALTER COLUMN ... SET NOT NULL";
6494 0 : case AT_SetExpression:
6495 0 : return "ALTER COLUMN ... SET EXPRESSION";
6496 0 : case AT_DropExpression:
6497 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6498 0 : case AT_SetStatistics:
6499 0 : return "ALTER COLUMN ... SET STATISTICS";
6500 12 : case AT_SetOptions:
6501 12 : return "ALTER COLUMN ... SET";
6502 0 : case AT_ResetOptions:
6503 0 : return "ALTER COLUMN ... RESET";
6504 0 : case AT_SetStorage:
6505 0 : return "ALTER COLUMN ... SET STORAGE";
6506 0 : case AT_SetCompression:
6507 0 : return "ALTER COLUMN ... SET COMPRESSION";
6508 6 : case AT_DropColumn:
6509 6 : return "DROP COLUMN";
6510 0 : case AT_AddIndex:
6511 : case AT_ReAddIndex:
6512 0 : return NULL; /* not real grammar */
6513 0 : case AT_AddConstraint:
6514 : case AT_ReAddConstraint:
6515 : case AT_ReAddDomainConstraint:
6516 : case AT_AddIndexConstraint:
6517 0 : return "ADD CONSTRAINT";
6518 6 : case AT_AlterConstraint:
6519 6 : return "ALTER CONSTRAINT";
6520 0 : case AT_ValidateConstraint:
6521 0 : return "VALIDATE CONSTRAINT";
6522 0 : case AT_DropConstraint:
6523 0 : return "DROP CONSTRAINT";
6524 0 : case AT_ReAddComment:
6525 0 : return NULL; /* not real grammar */
6526 0 : case AT_AlterColumnType:
6527 0 : return "ALTER COLUMN ... SET DATA TYPE";
6528 0 : case AT_AlterColumnGenericOptions:
6529 0 : return "ALTER COLUMN ... OPTIONS";
6530 0 : case AT_ChangeOwner:
6531 0 : return "OWNER TO";
6532 0 : case AT_ClusterOn:
6533 0 : return "CLUSTER ON";
6534 0 : case AT_DropCluster:
6535 0 : return "SET WITHOUT CLUSTER";
6536 0 : case AT_SetAccessMethod:
6537 0 : return "SET ACCESS METHOD";
6538 6 : case AT_SetLogged:
6539 6 : return "SET LOGGED";
6540 6 : case AT_SetUnLogged:
6541 6 : return "SET UNLOGGED";
6542 0 : case AT_DropOids:
6543 0 : return "SET WITHOUT OIDS";
6544 0 : case AT_SetTableSpace:
6545 0 : return "SET TABLESPACE";
6546 0 : case AT_SetRelOptions:
6547 0 : return "SET";
6548 0 : case AT_ResetRelOptions:
6549 0 : return "RESET";
6550 0 : case AT_ReplaceRelOptions:
6551 0 : return NULL; /* not real grammar */
6552 0 : case AT_EnableTrig:
6553 0 : return "ENABLE TRIGGER";
6554 0 : case AT_EnableAlwaysTrig:
6555 0 : return "ENABLE ALWAYS TRIGGER";
6556 0 : case AT_EnableReplicaTrig:
6557 0 : return "ENABLE REPLICA TRIGGER";
6558 0 : case AT_DisableTrig:
6559 0 : return "DISABLE TRIGGER";
6560 0 : case AT_EnableTrigAll:
6561 0 : return "ENABLE TRIGGER ALL";
6562 0 : case AT_DisableTrigAll:
6563 0 : return "DISABLE TRIGGER ALL";
6564 0 : case AT_EnableTrigUser:
6565 0 : return "ENABLE TRIGGER USER";
6566 0 : case AT_DisableTrigUser:
6567 0 : return "DISABLE TRIGGER USER";
6568 0 : case AT_EnableRule:
6569 0 : return "ENABLE RULE";
6570 0 : case AT_EnableAlwaysRule:
6571 0 : return "ENABLE ALWAYS RULE";
6572 0 : case AT_EnableReplicaRule:
6573 0 : return "ENABLE REPLICA RULE";
6574 0 : case AT_DisableRule:
6575 0 : return "DISABLE RULE";
6576 0 : case AT_AddInherit:
6577 0 : return "INHERIT";
6578 0 : case AT_DropInherit:
6579 0 : return "NO INHERIT";
6580 0 : case AT_AddOf:
6581 0 : return "OF";
6582 0 : case AT_DropOf:
6583 0 : return "NOT OF";
6584 0 : case AT_ReplicaIdentity:
6585 0 : return "REPLICA IDENTITY";
6586 0 : case AT_EnableRowSecurity:
6587 0 : return "ENABLE ROW SECURITY";
6588 0 : case AT_DisableRowSecurity:
6589 0 : return "DISABLE ROW SECURITY";
6590 0 : case AT_ForceRowSecurity:
6591 0 : return "FORCE ROW SECURITY";
6592 0 : case AT_NoForceRowSecurity:
6593 0 : return "NO FORCE ROW SECURITY";
6594 0 : case AT_GenericOptions:
6595 0 : return "OPTIONS";
6596 6 : case AT_AttachPartition:
6597 6 : return "ATTACH PARTITION";
6598 18 : case AT_DetachPartition:
6599 18 : return "DETACH PARTITION";
6600 6 : case AT_DetachPartitionFinalize:
6601 6 : return "DETACH PARTITION ... FINALIZE";
6602 0 : case AT_AddIdentity:
6603 0 : return "ALTER COLUMN ... ADD IDENTITY";
6604 0 : case AT_SetIdentity:
6605 0 : return "ALTER COLUMN ... SET";
6606 0 : case AT_DropIdentity:
6607 0 : return "ALTER COLUMN ... DROP IDENTITY";
6608 0 : case AT_ReAddStatistics:
6609 0 : return NULL; /* not real grammar */
6610 : }
6611 :
6612 0 : return NULL;
6613 : }
6614 :
6615 : /*
6616 : * ATSimplePermissions
6617 : *
6618 : * - Ensure that it is a relation (or possibly a view)
6619 : * - Ensure this user is the owner
6620 : * - Ensure that it is not a system table
6621 : */
6622 : static void
6623 34768 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6624 : {
6625 : int actual_target;
6626 :
6627 34768 : switch (rel->rd_rel->relkind)
6628 : {
6629 27234 : case RELKIND_RELATION:
6630 27234 : actual_target = ATT_TABLE;
6631 27234 : break;
6632 5286 : case RELKIND_PARTITIONED_TABLE:
6633 5286 : actual_target = ATT_PARTITIONED_TABLE;
6634 5286 : break;
6635 396 : case RELKIND_VIEW:
6636 396 : actual_target = ATT_VIEW;
6637 396 : break;
6638 46 : case RELKIND_MATVIEW:
6639 46 : actual_target = ATT_MATVIEW;
6640 46 : break;
6641 226 : case RELKIND_INDEX:
6642 226 : actual_target = ATT_INDEX;
6643 226 : break;
6644 434 : case RELKIND_PARTITIONED_INDEX:
6645 434 : actual_target = ATT_PARTITIONED_INDEX;
6646 434 : break;
6647 214 : case RELKIND_COMPOSITE_TYPE:
6648 214 : actual_target = ATT_COMPOSITE_TYPE;
6649 214 : break;
6650 908 : case RELKIND_FOREIGN_TABLE:
6651 908 : actual_target = ATT_FOREIGN_TABLE;
6652 908 : break;
6653 24 : case RELKIND_SEQUENCE:
6654 24 : actual_target = ATT_SEQUENCE;
6655 24 : break;
6656 0 : default:
6657 0 : actual_target = 0;
6658 0 : break;
6659 : }
6660 :
6661 : /* Wrong target type? */
6662 34768 : if ((actual_target & allowed_targets) == 0)
6663 : {
6664 78 : const char *action_str = alter_table_type_to_string(cmdtype);
6665 :
6666 78 : if (action_str)
6667 78 : ereport(ERROR,
6668 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6669 : /* translator: %s is a group of some SQL keywords */
6670 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6671 : action_str, RelationGetRelationName(rel)),
6672 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6673 : else
6674 : /* internal error? */
6675 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6676 : RelationGetRelationName(rel));
6677 : }
6678 :
6679 : /* Permissions checks */
6680 34690 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6681 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6682 12 : RelationGetRelationName(rel));
6683 :
6684 34678 : if (!allowSystemTableMods && IsSystemRelation(rel))
6685 0 : ereport(ERROR,
6686 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6687 : errmsg("permission denied: \"%s\" is a system catalog",
6688 : RelationGetRelationName(rel))));
6689 34678 : }
6690 :
6691 : /*
6692 : * ATSimpleRecursion
6693 : *
6694 : * Simple table recursion sufficient for most ALTER TABLE operations.
6695 : * All direct and indirect children are processed in an unspecified order.
6696 : * Note that if a child inherits from the original table via multiple
6697 : * inheritance paths, it will be visited just once.
6698 : */
6699 : static void
6700 1124 : ATSimpleRecursion(List **wqueue, Relation rel,
6701 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6702 : AlterTableUtilityContext *context)
6703 : {
6704 : /*
6705 : * Propagate to children, if desired and if there are (or might be) any
6706 : * children.
6707 : */
6708 1124 : if (recurse && rel->rd_rel->relhassubclass)
6709 : {
6710 66 : Oid relid = RelationGetRelid(rel);
6711 : ListCell *child;
6712 : List *children;
6713 :
6714 66 : children = find_all_inheritors(relid, lockmode, NULL);
6715 :
6716 : /*
6717 : * find_all_inheritors does the recursive search of the inheritance
6718 : * hierarchy, so all we have to do is process all of the relids in the
6719 : * list that it returns.
6720 : */
6721 294 : foreach(child, children)
6722 : {
6723 228 : Oid childrelid = lfirst_oid(child);
6724 : Relation childrel;
6725 :
6726 228 : if (childrelid == relid)
6727 66 : continue;
6728 : /* find_all_inheritors already got lock */
6729 162 : childrel = relation_open(childrelid, NoLock);
6730 162 : CheckAlterTableIsSafe(childrel);
6731 162 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6732 162 : relation_close(childrel, NoLock);
6733 : }
6734 : }
6735 1124 : }
6736 :
6737 : /*
6738 : * Obtain list of partitions of the given table, locking them all at the given
6739 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6740 : *
6741 : * This function is a no-op if the given relation is not a partitioned table;
6742 : * in particular, nothing is done if it's a legacy inheritance parent.
6743 : */
6744 : static void
6745 822 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6746 : {
6747 822 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6748 : {
6749 : List *inh;
6750 : ListCell *cell;
6751 :
6752 188 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6753 : /* first element is the parent rel; must ignore it */
6754 610 : for_each_from(cell, inh, 1)
6755 : {
6756 : Relation childrel;
6757 :
6758 : /* find_all_inheritors already got lock */
6759 428 : childrel = table_open(lfirst_oid(cell), NoLock);
6760 428 : CheckAlterTableIsSafe(childrel);
6761 422 : table_close(childrel, NoLock);
6762 : }
6763 182 : list_free(inh);
6764 : }
6765 816 : }
6766 :
6767 : /*
6768 : * ATTypedTableRecursion
6769 : *
6770 : * Propagate ALTER TYPE operations to the typed tables of that type.
6771 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6772 : * recursion to inheritance children of the typed tables.
6773 : */
6774 : static void
6775 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6776 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6777 : {
6778 : ListCell *child;
6779 : List *children;
6780 :
6781 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6782 :
6783 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6784 190 : RelationGetRelationName(rel),
6785 : cmd->behavior);
6786 :
6787 202 : foreach(child, children)
6788 : {
6789 30 : Oid childrelid = lfirst_oid(child);
6790 : Relation childrel;
6791 :
6792 30 : childrel = relation_open(childrelid, lockmode);
6793 30 : CheckAlterTableIsSafe(childrel);
6794 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6795 30 : relation_close(childrel, NoLock);
6796 : }
6797 172 : }
6798 :
6799 :
6800 : /*
6801 : * find_composite_type_dependencies
6802 : *
6803 : * Check to see if the type "typeOid" is being used as a column in some table
6804 : * (possibly nested several levels deep in composite types, arrays, etc!).
6805 : * Eventually, we'd like to propagate the check or rewrite operation
6806 : * into such tables, but for now, just error out if we find any.
6807 : *
6808 : * Caller should provide either the associated relation of a rowtype,
6809 : * or a type name (not both) for use in the error message, if any.
6810 : *
6811 : * Note that "typeOid" is not necessarily a composite type; it could also be
6812 : * another container type such as an array or range, or a domain over one of
6813 : * these things. The name of this function is therefore somewhat historical,
6814 : * but it's not worth changing.
6815 : *
6816 : * We assume that functions and views depending on the type are not reasons
6817 : * to reject the ALTER. (How safe is this really?)
6818 : */
6819 : void
6820 3960 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6821 : const char *origTypeName)
6822 : {
6823 : Relation depRel;
6824 : ScanKeyData key[2];
6825 : SysScanDesc depScan;
6826 : HeapTuple depTup;
6827 :
6828 : /* since this function recurses, it could be driven to stack overflow */
6829 3960 : check_stack_depth();
6830 :
6831 : /*
6832 : * We scan pg_depend to find those things that depend on the given type.
6833 : * (We assume we can ignore refobjsubid for a type.)
6834 : */
6835 3960 : depRel = table_open(DependRelationId, AccessShareLock);
6836 :
6837 3960 : ScanKeyInit(&key[0],
6838 : Anum_pg_depend_refclassid,
6839 : BTEqualStrategyNumber, F_OIDEQ,
6840 : ObjectIdGetDatum(TypeRelationId));
6841 3960 : ScanKeyInit(&key[1],
6842 : Anum_pg_depend_refobjid,
6843 : BTEqualStrategyNumber, F_OIDEQ,
6844 : ObjectIdGetDatum(typeOid));
6845 :
6846 3960 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6847 : NULL, 2, key);
6848 :
6849 6108 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6850 : {
6851 2244 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6852 : Relation rel;
6853 : TupleDesc tupleDesc;
6854 : Form_pg_attribute att;
6855 :
6856 : /* Check for directly dependent types */
6857 2244 : if (pg_depend->classid == TypeRelationId)
6858 : {
6859 : /*
6860 : * This must be an array, domain, or range containing the given
6861 : * type, so recursively check for uses of this type. Note that
6862 : * any error message will mention the original type not the
6863 : * container; this is intentional.
6864 : */
6865 1898 : find_composite_type_dependencies(pg_depend->objid,
6866 : origRelation, origTypeName);
6867 1874 : continue;
6868 : }
6869 :
6870 : /* Else, ignore dependees that aren't relations */
6871 346 : if (pg_depend->classid != RelationRelationId)
6872 122 : continue;
6873 :
6874 224 : rel = relation_open(pg_depend->objid, AccessShareLock);
6875 224 : tupleDesc = RelationGetDescr(rel);
6876 :
6877 : /*
6878 : * If objsubid identifies a specific column, refer to that in error
6879 : * messages. Otherwise, search to see if there's a user column of the
6880 : * type. (We assume system columns are never of interesting types.)
6881 : * The search is needed because an index containing an expression
6882 : * column of the target type will just be recorded as a whole-relation
6883 : * dependency. If we do not find a column of the type, the dependency
6884 : * must indicate that the type is transiently referenced in an index
6885 : * expression but not stored on disk, which we assume is OK, just as
6886 : * we do for references in views. (It could also be that the target
6887 : * type is embedded in some container type that is stored in an index
6888 : * column, but the previous recursion should catch such cases.)
6889 : */
6890 224 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6891 66 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6892 : else
6893 : {
6894 158 : att = NULL;
6895 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6896 : {
6897 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6898 254 : if (att->atttypid == typeOid && !att->attisdropped)
6899 6 : break;
6900 248 : att = NULL;
6901 : }
6902 158 : if (att == NULL)
6903 : {
6904 : /* No such column, so assume OK */
6905 152 : relation_close(rel, AccessShareLock);
6906 152 : continue;
6907 : }
6908 : }
6909 :
6910 : /*
6911 : * We definitely should reject if the relation has storage. If it's
6912 : * partitioned, then perhaps we don't have to reject: if there are
6913 : * partitions then we'll fail when we find one, else there is no
6914 : * stored data to worry about. However, it's possible that the type
6915 : * change would affect conclusions about whether the type is sortable
6916 : * or hashable and thus (if it's a partitioning column) break the
6917 : * partitioning rule. For now, reject for partitioned rels too.
6918 : */
6919 72 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6920 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6921 : {
6922 72 : if (origTypeName)
6923 30 : ereport(ERROR,
6924 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6925 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6926 : origTypeName,
6927 : RelationGetRelationName(rel),
6928 : NameStr(att->attname))));
6929 42 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6930 18 : ereport(ERROR,
6931 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6932 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6933 : RelationGetRelationName(origRelation),
6934 : RelationGetRelationName(rel),
6935 : NameStr(att->attname))));
6936 24 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6937 6 : ereport(ERROR,
6938 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6939 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6940 : RelationGetRelationName(origRelation),
6941 : RelationGetRelationName(rel),
6942 : NameStr(att->attname))));
6943 : else
6944 18 : ereport(ERROR,
6945 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6946 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6947 : RelationGetRelationName(origRelation),
6948 : RelationGetRelationName(rel),
6949 : NameStr(att->attname))));
6950 : }
6951 0 : else if (OidIsValid(rel->rd_rel->reltype))
6952 : {
6953 : /*
6954 : * A view or composite type itself isn't a problem, but we must
6955 : * recursively check for indirect dependencies via its rowtype.
6956 : */
6957 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6958 : origRelation, origTypeName);
6959 : }
6960 :
6961 0 : relation_close(rel, AccessShareLock);
6962 : }
6963 :
6964 3864 : systable_endscan(depScan);
6965 :
6966 3864 : relation_close(depRel, AccessShareLock);
6967 3864 : }
6968 :
6969 :
6970 : /*
6971 : * find_typed_table_dependencies
6972 : *
6973 : * Check to see if a composite type is being used as the type of a
6974 : * typed table. Abort if any are found and behavior is RESTRICT.
6975 : * Else return the list of tables.
6976 : */
6977 : static List *
6978 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6979 : {
6980 : Relation classRel;
6981 : ScanKeyData key[1];
6982 : TableScanDesc scan;
6983 : HeapTuple tuple;
6984 214 : List *result = NIL;
6985 :
6986 214 : classRel = table_open(RelationRelationId, AccessShareLock);
6987 :
6988 214 : ScanKeyInit(&key[0],
6989 : Anum_pg_class_reloftype,
6990 : BTEqualStrategyNumber, F_OIDEQ,
6991 : ObjectIdGetDatum(typeOid));
6992 :
6993 214 : scan = table_beginscan_catalog(classRel, 1, key);
6994 :
6995 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6996 : {
6997 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6998 :
6999 60 : if (behavior == DROP_RESTRICT)
7000 24 : ereport(ERROR,
7001 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7002 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7003 : typeName),
7004 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7005 : else
7006 36 : result = lappend_oid(result, classform->oid);
7007 : }
7008 :
7009 190 : table_endscan(scan);
7010 190 : table_close(classRel, AccessShareLock);
7011 :
7012 190 : return result;
7013 : }
7014 :
7015 :
7016 : /*
7017 : * check_of_type
7018 : *
7019 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7020 : * isn't suitable, throw an error. Currently, we require that the type
7021 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7022 : * would require handling a number of extra corner cases in the DDL commands.
7023 : * (Also, allowing domain-over-composite would open up a can of worms about
7024 : * whether and how the domain's constraints should apply to derived tables.)
7025 : */
7026 : void
7027 176 : check_of_type(HeapTuple typetuple)
7028 : {
7029 176 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7030 176 : bool typeOk = false;
7031 :
7032 176 : if (typ->typtype == TYPTYPE_COMPOSITE)
7033 : {
7034 : Relation typeRelation;
7035 :
7036 : Assert(OidIsValid(typ->typrelid));
7037 170 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7038 170 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7039 :
7040 : /*
7041 : * Close the parent rel, but keep our AccessShareLock on it until xact
7042 : * commit. That will prevent someone else from deleting or ALTERing
7043 : * the type before the typed table creation/conversion commits.
7044 : */
7045 170 : relation_close(typeRelation, NoLock);
7046 :
7047 170 : if (!typeOk)
7048 6 : ereport(ERROR,
7049 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7050 : errmsg("type %s is the row type of another table",
7051 : format_type_be(typ->oid)),
7052 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7053 : }
7054 : else
7055 6 : ereport(ERROR,
7056 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7057 : errmsg("type %s is not a composite type",
7058 : format_type_be(typ->oid))));
7059 164 : }
7060 :
7061 :
7062 : /*
7063 : * ALTER TABLE ADD COLUMN
7064 : *
7065 : * Adds an additional attribute to a relation making the assumption that
7066 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7067 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7068 : * AlterTableCmd's.
7069 : *
7070 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7071 : * have to decide at runtime whether to recurse or not depending on whether we
7072 : * actually add a column or merely merge with an existing column. (We can't
7073 : * check this in a static pre-pass because it won't handle multiple inheritance
7074 : * situations correctly.)
7075 : */
7076 : static void
7077 2014 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7078 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7079 : AlterTableUtilityContext *context)
7080 : {
7081 2014 : if (rel->rd_rel->reloftype && !recursing)
7082 6 : ereport(ERROR,
7083 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7084 : errmsg("cannot add column to typed table")));
7085 :
7086 2008 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7087 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7088 :
7089 2002 : if (recurse && !is_view)
7090 1902 : cmd->recurse = true;
7091 2002 : }
7092 :
7093 : /*
7094 : * Add a column to a table. The return value is the address of the
7095 : * new column in the parent relation.
7096 : *
7097 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7098 : * copy (but that happens only after we check for IF NOT EXISTS).
7099 : */
7100 : static ObjectAddress
7101 2644 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7102 : AlterTableCmd **cmd, bool recurse, bool recursing,
7103 : LOCKMODE lockmode, AlterTablePass cur_pass,
7104 : AlterTableUtilityContext *context)
7105 : {
7106 2644 : Oid myrelid = RelationGetRelid(rel);
7107 2644 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7108 2644 : bool if_not_exists = (*cmd)->missing_ok;
7109 : Relation pgclass,
7110 : attrdesc;
7111 : HeapTuple reltup;
7112 : Form_pg_class relform;
7113 : Form_pg_attribute attribute;
7114 : int newattnum;
7115 : char relkind;
7116 : Expr *defval;
7117 : List *children;
7118 : ListCell *child;
7119 : AlterTableCmd *childcmd;
7120 : ObjectAddress address;
7121 : TupleDesc tupdesc;
7122 :
7123 : /* since this function recurses, it could be driven to stack overflow */
7124 2644 : check_stack_depth();
7125 :
7126 : /* At top level, permission check was done in ATPrepCmd, else do it */
7127 2644 : if (recursing)
7128 648 : ATSimplePermissions((*cmd)->subtype, rel,
7129 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7130 :
7131 2644 : if (rel->rd_rel->relispartition && !recursing)
7132 12 : ereport(ERROR,
7133 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7134 : errmsg("cannot add column to a partition")));
7135 :
7136 2632 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7137 :
7138 : /*
7139 : * Are we adding the column to a recursion child? If so, check whether to
7140 : * merge with an existing definition for the column. If we do merge, we
7141 : * must not recurse. Children will already have the column, and recursing
7142 : * into them would mess up attinhcount.
7143 : */
7144 2632 : if (colDef->inhcount > 0)
7145 : {
7146 : HeapTuple tuple;
7147 :
7148 : /* Does child already have a column by this name? */
7149 648 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7150 648 : if (HeapTupleIsValid(tuple))
7151 : {
7152 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7153 : Oid ctypeId;
7154 : int32 ctypmod;
7155 : Oid ccollid;
7156 :
7157 : /* Child column must match on type, typmod, and collation */
7158 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7159 48 : if (ctypeId != childatt->atttypid ||
7160 48 : ctypmod != childatt->atttypmod)
7161 0 : ereport(ERROR,
7162 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7163 : errmsg("child table \"%s\" has different type for column \"%s\"",
7164 : RelationGetRelationName(rel), colDef->colname)));
7165 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7166 48 : if (ccollid != childatt->attcollation)
7167 0 : ereport(ERROR,
7168 : (errcode(ERRCODE_COLLATION_MISMATCH),
7169 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7170 : RelationGetRelationName(rel), colDef->colname),
7171 : errdetail("\"%s\" versus \"%s\"",
7172 : get_collation_name(ccollid),
7173 : get_collation_name(childatt->attcollation))));
7174 :
7175 : /* Bump the existing child att's inhcount */
7176 48 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7177 : &childatt->attinhcount))
7178 0 : ereport(ERROR,
7179 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7180 : errmsg("too many inheritance parents"));
7181 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7182 :
7183 48 : heap_freetuple(tuple);
7184 :
7185 : /* Inform the user about the merge */
7186 48 : ereport(NOTICE,
7187 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7188 : colDef->colname, RelationGetRelationName(rel))));
7189 :
7190 48 : table_close(attrdesc, RowExclusiveLock);
7191 :
7192 : /* Make the child column change visible */
7193 48 : CommandCounterIncrement();
7194 :
7195 48 : return InvalidObjectAddress;
7196 : }
7197 : }
7198 :
7199 : /* skip if the name already exists and if_not_exists is true */
7200 2584 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7201 : {
7202 54 : table_close(attrdesc, RowExclusiveLock);
7203 54 : return InvalidObjectAddress;
7204 : }
7205 :
7206 : /*
7207 : * Okay, we need to add the column, so go ahead and do parse
7208 : * transformation. This can result in queueing up, or even immediately
7209 : * executing, subsidiary operations (such as creation of unique indexes);
7210 : * so we mustn't do it until we have made the if_not_exists check.
7211 : *
7212 : * When recursing, the command was already transformed and we needn't do
7213 : * so again. Also, if context isn't given we can't transform. (That
7214 : * currently happens only for AT_AddColumnToView; we expect that view.c
7215 : * passed us a ColumnDef that doesn't need work.)
7216 : */
7217 2500 : if (context != NULL && !recursing)
7218 : {
7219 1876 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7220 : cur_pass, context);
7221 : Assert(*cmd != NULL);
7222 1870 : colDef = castNode(ColumnDef, (*cmd)->def);
7223 : }
7224 :
7225 : /*
7226 : * Regular inheritance children are independent enough not to inherit the
7227 : * identity column from parent hence cannot recursively add identity
7228 : * column if the table has inheritance children.
7229 : *
7230 : * Partitions, on the other hand, are integral part of a partitioned table
7231 : * and inherit identity column. Hence propagate identity column down the
7232 : * partition hierarchy.
7233 : */
7234 2494 : if (colDef->identity &&
7235 54 : recurse &&
7236 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7237 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7238 6 : ereport(ERROR,
7239 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7240 : errmsg("cannot recursively add identity column to table that has child tables")));
7241 :
7242 2488 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7243 :
7244 2488 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7245 2488 : if (!HeapTupleIsValid(reltup))
7246 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7247 2488 : relform = (Form_pg_class) GETSTRUCT(reltup);
7248 2488 : relkind = relform->relkind;
7249 :
7250 : /* Determine the new attribute's number */
7251 2488 : newattnum = relform->relnatts + 1;
7252 2488 : if (newattnum > MaxHeapAttributeNumber)
7253 0 : ereport(ERROR,
7254 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7255 : errmsg("tables can have at most %d columns",
7256 : MaxHeapAttributeNumber)));
7257 :
7258 : /*
7259 : * Construct new attribute's pg_attribute entry.
7260 : */
7261 2488 : tupdesc = BuildDescForRelation(list_make1(colDef));
7262 :
7263 2476 : attribute = TupleDescAttr(tupdesc, 0);
7264 :
7265 : /* Fix up attribute number */
7266 2476 : attribute->attnum = newattnum;
7267 :
7268 : /* make sure datatype is legal for a column */
7269 2476 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7270 2476 : list_make1_oid(rel->rd_rel->reltype),
7271 : 0);
7272 :
7273 2446 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7274 :
7275 2446 : table_close(attrdesc, RowExclusiveLock);
7276 :
7277 : /*
7278 : * Update pg_class tuple as appropriate
7279 : */
7280 2446 : relform->relnatts = newattnum;
7281 :
7282 2446 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7283 :
7284 2446 : heap_freetuple(reltup);
7285 :
7286 : /* Post creation hook for new attribute */
7287 2446 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7288 :
7289 2446 : table_close(pgclass, RowExclusiveLock);
7290 :
7291 : /* Make the attribute's catalog entry visible */
7292 2446 : CommandCounterIncrement();
7293 :
7294 : /*
7295 : * Store the DEFAULT, if any, in the catalogs
7296 : */
7297 2446 : if (colDef->raw_default)
7298 : {
7299 : RawColumnDefault *rawEnt;
7300 :
7301 708 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7302 708 : rawEnt->attnum = attribute->attnum;
7303 708 : rawEnt->raw_default = copyObject(colDef->raw_default);
7304 :
7305 : /*
7306 : * Attempt to skip a complete table rewrite by storing the specified
7307 : * DEFAULT value outside of the heap. This may be disabled inside
7308 : * AddRelationNewConstraints if the optimization cannot be applied.
7309 : */
7310 708 : rawEnt->missingMode = (!colDef->generated);
7311 :
7312 708 : rawEnt->generated = colDef->generated;
7313 :
7314 : /*
7315 : * This function is intended for CREATE TABLE, so it processes a
7316 : * _list_ of defaults, but we just do one.
7317 : */
7318 708 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7319 : false, true, false, NULL);
7320 :
7321 : /* Make the additional catalog changes visible */
7322 696 : CommandCounterIncrement();
7323 :
7324 : /*
7325 : * Did the request for a missing value work? If not we'll have to do a
7326 : * rewrite
7327 : */
7328 696 : if (!rawEnt->missingMode)
7329 108 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7330 : }
7331 :
7332 : /*
7333 : * Tell Phase 3 to fill in the default expression, if there is one.
7334 : *
7335 : * If there is no default, Phase 3 doesn't have to do anything, because
7336 : * that effectively means that the default is NULL. The heap tuple access
7337 : * routines always check for attnum > # of attributes in tuple, and return
7338 : * NULL if so, so without any modification of the tuple data we will get
7339 : * the effect of NULL values in the new column.
7340 : *
7341 : * An exception occurs when the new column is of a domain type: the domain
7342 : * might have a not-null constraint, or a check constraint that indirectly
7343 : * rejects nulls. If there are any domain constraints then we construct
7344 : * an explicit NULL default value that will be passed through
7345 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7346 : * rewriting the table which we really don't have to do, but the present
7347 : * design of domain processing doesn't offer any simple way of checking
7348 : * the constraints more directly.)
7349 : *
7350 : * Note: we use build_column_default, and not just the cooked default
7351 : * returned by AddRelationNewConstraints, so that the right thing happens
7352 : * when a datatype's default applies.
7353 : *
7354 : * Note: it might seem that this should happen at the end of Phase 2, so
7355 : * that the effects of subsequent subcommands can be taken into account.
7356 : * It's intentional that we do it now, though. The new column should be
7357 : * filled according to what is said in the ADD COLUMN subcommand, so that
7358 : * the effects are the same as if this subcommand had been run by itself
7359 : * and the later subcommands had been issued in new ALTER TABLE commands.
7360 : *
7361 : * We can skip this entirely for relations without storage, since Phase 3
7362 : * is certainly not going to touch them. System attributes don't have
7363 : * interesting defaults, either.
7364 : */
7365 2434 : if (RELKIND_HAS_STORAGE(relkind))
7366 : {
7367 : /*
7368 : * For an identity column, we can't use build_column_default(),
7369 : * because the sequence ownership isn't set yet. So do it manually.
7370 : */
7371 2082 : if (colDef->identity)
7372 : {
7373 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7374 :
7375 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7376 42 : nve->typeId = attribute->atttypid;
7377 :
7378 42 : defval = (Expr *) nve;
7379 :
7380 : /* must do a rewrite for identity columns */
7381 42 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7382 : }
7383 : else
7384 2040 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7385 :
7386 2082 : if (!defval && DomainHasConstraints(attribute->atttypid))
7387 : {
7388 : Oid baseTypeId;
7389 : int32 baseTypeMod;
7390 : Oid baseTypeColl;
7391 :
7392 6 : baseTypeMod = attribute->atttypmod;
7393 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7394 6 : baseTypeColl = get_typcollation(baseTypeId);
7395 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7396 6 : defval = (Expr *) coerce_to_target_type(NULL,
7397 : (Node *) defval,
7398 : baseTypeId,
7399 : attribute->atttypid,
7400 : attribute->atttypmod,
7401 : COERCION_ASSIGNMENT,
7402 : COERCE_IMPLICIT_CAST,
7403 : -1);
7404 6 : if (defval == NULL) /* should not happen */
7405 0 : elog(ERROR, "failed to coerce base type to domain");
7406 : }
7407 :
7408 2082 : if (defval)
7409 : {
7410 : NewColumnValue *newval;
7411 :
7412 610 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7413 610 : newval->attnum = attribute->attnum;
7414 610 : newval->expr = expression_planner(defval);
7415 610 : newval->is_generated = (colDef->generated != '\0');
7416 :
7417 610 : tab->newvals = lappend(tab->newvals, newval);
7418 : }
7419 :
7420 2082 : if (DomainHasConstraints(attribute->atttypid))
7421 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7422 :
7423 2082 : if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7424 : {
7425 : /*
7426 : * If the new column is NOT NULL, and there is no missing value,
7427 : * tell Phase 3 it needs to check for NULLs.
7428 : */
7429 1628 : tab->verify_new_notnull |= colDef->is_not_null;
7430 : }
7431 : }
7432 :
7433 : /*
7434 : * Add needed dependency entries for the new column.
7435 : */
7436 2434 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7437 2434 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7438 :
7439 : /*
7440 : * Propagate to children as appropriate. Unlike most other ALTER
7441 : * routines, we have to do this one level of recursion at a time; we can't
7442 : * use find_all_inheritors to do it in one pass.
7443 : */
7444 : children =
7445 2434 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7446 :
7447 : /*
7448 : * If we are told not to recurse, there had better not be any child
7449 : * tables; else the addition would put them out of step.
7450 : */
7451 2434 : if (children && !recurse)
7452 12 : ereport(ERROR,
7453 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7454 : errmsg("column must be added to child tables too")));
7455 :
7456 : /* Children should see column as singly inherited */
7457 2422 : if (!recursing)
7458 : {
7459 1822 : childcmd = copyObject(*cmd);
7460 1822 : colDef = castNode(ColumnDef, childcmd->def);
7461 1822 : colDef->inhcount = 1;
7462 1822 : colDef->is_local = false;
7463 : }
7464 : else
7465 600 : childcmd = *cmd; /* no need to copy again */
7466 :
7467 3070 : foreach(child, children)
7468 : {
7469 648 : Oid childrelid = lfirst_oid(child);
7470 : Relation childrel;
7471 : AlteredTableInfo *childtab;
7472 :
7473 : /* find_inheritance_children already got lock */
7474 648 : childrel = table_open(childrelid, NoLock);
7475 648 : CheckAlterTableIsSafe(childrel);
7476 :
7477 : /* Find or create work queue entry for this table */
7478 648 : childtab = ATGetQueueEntry(wqueue, childrel);
7479 :
7480 : /* Recurse to child; return value is ignored */
7481 648 : ATExecAddColumn(wqueue, childtab, childrel,
7482 : &childcmd, recurse, true,
7483 : lockmode, cur_pass, context);
7484 :
7485 648 : table_close(childrel, NoLock);
7486 : }
7487 :
7488 2422 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7489 2422 : return address;
7490 : }
7491 :
7492 : /*
7493 : * If a new or renamed column will collide with the name of an existing
7494 : * column and if_not_exists is false then error out, else do nothing.
7495 : */
7496 : static bool
7497 3034 : check_for_column_name_collision(Relation rel, const char *colname,
7498 : bool if_not_exists)
7499 : {
7500 : HeapTuple attTuple;
7501 : int attnum;
7502 :
7503 : /*
7504 : * this test is deliberately not attisdropped-aware, since if one tries to
7505 : * add a column matching a dropped column name, it's gonna fail anyway.
7506 : */
7507 3034 : attTuple = SearchSysCache2(ATTNAME,
7508 : ObjectIdGetDatum(RelationGetRelid(rel)),
7509 : PointerGetDatum(colname));
7510 3034 : if (!HeapTupleIsValid(attTuple))
7511 2938 : return true;
7512 :
7513 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7514 96 : ReleaseSysCache(attTuple);
7515 :
7516 : /*
7517 : * We throw a different error message for conflicts with system column
7518 : * names, since they are normally not shown and the user might otherwise
7519 : * be confused about the reason for the conflict.
7520 : */
7521 96 : if (attnum <= 0)
7522 12 : ereport(ERROR,
7523 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7524 : errmsg("column name \"%s\" conflicts with a system column name",
7525 : colname)));
7526 : else
7527 : {
7528 84 : if (if_not_exists)
7529 : {
7530 54 : ereport(NOTICE,
7531 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7532 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7533 : colname, RelationGetRelationName(rel))));
7534 54 : return false;
7535 : }
7536 :
7537 30 : ereport(ERROR,
7538 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7539 : errmsg("column \"%s\" of relation \"%s\" already exists",
7540 : colname, RelationGetRelationName(rel))));
7541 : }
7542 :
7543 : return true;
7544 : }
7545 :
7546 : /*
7547 : * Install a column's dependency on its datatype.
7548 : */
7549 : static void
7550 3374 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7551 : {
7552 : ObjectAddress myself,
7553 : referenced;
7554 :
7555 3374 : myself.classId = RelationRelationId;
7556 3374 : myself.objectId = relid;
7557 3374 : myself.objectSubId = attnum;
7558 3374 : referenced.classId = TypeRelationId;
7559 3374 : referenced.objectId = typid;
7560 3374 : referenced.objectSubId = 0;
7561 3374 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7562 3374 : }
7563 :
7564 : /*
7565 : * Install a column's dependency on its collation.
7566 : */
7567 : static void
7568 3374 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7569 : {
7570 : ObjectAddress myself,
7571 : referenced;
7572 :
7573 : /* We know the default collation is pinned, so don't bother recording it */
7574 3374 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7575 : {
7576 18 : myself.classId = RelationRelationId;
7577 18 : myself.objectId = relid;
7578 18 : myself.objectSubId = attnum;
7579 18 : referenced.classId = CollationRelationId;
7580 18 : referenced.objectId = collid;
7581 18 : referenced.objectSubId = 0;
7582 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7583 : }
7584 3374 : }
7585 :
7586 : /*
7587 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7588 : *
7589 : * Return the address of the modified column. If the column was already
7590 : * nullable, InvalidObjectAddress is returned.
7591 : */
7592 : static ObjectAddress
7593 256 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7594 : LOCKMODE lockmode)
7595 : {
7596 : HeapTuple tuple;
7597 : HeapTuple conTup;
7598 : Form_pg_attribute attTup;
7599 : AttrNumber attnum;
7600 : Relation attr_rel;
7601 : ObjectAddress address;
7602 :
7603 : /*
7604 : * lookup the attribute
7605 : */
7606 256 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7607 :
7608 256 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7609 256 : if (!HeapTupleIsValid(tuple))
7610 18 : ereport(ERROR,
7611 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7612 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7613 : colName, RelationGetRelationName(rel))));
7614 238 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7615 238 : attnum = attTup->attnum;
7616 238 : ObjectAddressSubSet(address, RelationRelationId,
7617 : RelationGetRelid(rel), attnum);
7618 :
7619 : /* If the column is already nullable there's nothing to do. */
7620 238 : if (!attTup->attnotnull)
7621 : {
7622 0 : table_close(attr_rel, RowExclusiveLock);
7623 0 : return InvalidObjectAddress;
7624 : }
7625 :
7626 : /* Prevent them from altering a system attribute */
7627 238 : if (attnum <= 0)
7628 0 : ereport(ERROR,
7629 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7630 : errmsg("cannot alter system column \"%s\"",
7631 : colName)));
7632 :
7633 238 : if (attTup->attidentity)
7634 18 : ereport(ERROR,
7635 : (errcode(ERRCODE_SYNTAX_ERROR),
7636 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7637 : colName, RelationGetRelationName(rel))));
7638 :
7639 : /*
7640 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7641 : */
7642 220 : if (rel->rd_rel->relispartition)
7643 : {
7644 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7645 12 : Relation parent = table_open(parentId, AccessShareLock);
7646 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7647 : AttrNumber parent_attnum;
7648 :
7649 12 : parent_attnum = get_attnum(parentId, colName);
7650 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7651 12 : ereport(ERROR,
7652 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7653 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7654 : colName)));
7655 0 : table_close(parent, AccessShareLock);
7656 : }
7657 :
7658 : /*
7659 : * Find the constraint that makes this column NOT NULL, and drop it.
7660 : * dropconstraint_internal() resets attnotnull.
7661 : */
7662 208 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7663 208 : if (conTup == NULL)
7664 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7665 : colName, RelationGetRelationName(rel));
7666 :
7667 : /* The normal case: we have a pg_constraint row, remove it */
7668 208 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7669 : false, lockmode);
7670 154 : heap_freetuple(conTup);
7671 :
7672 154 : InvokeObjectPostAlterHook(RelationRelationId,
7673 : RelationGetRelid(rel), attnum);
7674 :
7675 154 : table_close(attr_rel, RowExclusiveLock);
7676 :
7677 154 : return address;
7678 : }
7679 :
7680 : /*
7681 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7682 : * to verify it.
7683 : *
7684 : * When called to alter an existing table, 'wqueue' must be given so that we
7685 : * can queue a check that existing tuples pass the constraint. When called
7686 : * from table creation, 'wqueue' should be passed as NULL.
7687 : */
7688 : static void
7689 22884 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7690 : LOCKMODE lockmode)
7691 : {
7692 : Form_pg_attribute attr;
7693 :
7694 22884 : CheckAlterTableIsSafe(rel);
7695 :
7696 : /*
7697 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7698 : * attribute.
7699 : */
7700 22884 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7701 22884 : if (attr->attisdropped)
7702 0 : return;
7703 :
7704 22884 : if (!attr->attnotnull)
7705 : {
7706 : Relation attr_rel;
7707 : HeapTuple tuple;
7708 :
7709 1196 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7710 :
7711 1196 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7712 1196 : if (!HeapTupleIsValid(tuple))
7713 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7714 : attnum, RelationGetRelid(rel));
7715 :
7716 1196 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7717 : Assert(!attr->attnotnull);
7718 1196 : attr->attnotnull = true;
7719 1196 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7720 :
7721 : /*
7722 : * If the nullness isn't already proven by validated constraints, have
7723 : * ALTER TABLE phase 3 test for it.
7724 : */
7725 1196 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7726 : {
7727 : AlteredTableInfo *tab;
7728 :
7729 1098 : tab = ATGetQueueEntry(wqueue, rel);
7730 1098 : tab->verify_new_notnull = true;
7731 : }
7732 :
7733 1196 : CommandCounterIncrement();
7734 :
7735 1196 : table_close(attr_rel, RowExclusiveLock);
7736 1196 : heap_freetuple(tuple);
7737 : }
7738 : }
7739 :
7740 : /*
7741 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7742 : *
7743 : * Add a not-null constraint to a single table and its children. Returns
7744 : * the address of the constraint added to the parent relation, if one gets
7745 : * added, or InvalidObjectAddress otherwise.
7746 : *
7747 : * We must recurse to child tables during execution, rather than using
7748 : * ALTER TABLE's normal prep-time recursion.
7749 : */
7750 : static ObjectAddress
7751 580 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7752 : bool recurse, bool recursing, LOCKMODE lockmode)
7753 : {
7754 : HeapTuple tuple;
7755 : AttrNumber attnum;
7756 : ObjectAddress address;
7757 : Constraint *constraint;
7758 : CookedConstraint *ccon;
7759 : List *cooked;
7760 580 : bool is_no_inherit = false;
7761 :
7762 : /* Guard against stack overflow due to overly deep inheritance tree. */
7763 580 : check_stack_depth();
7764 :
7765 : /* At top level, permission check was done in ATPrepCmd, else do it */
7766 580 : if (recursing)
7767 : {
7768 202 : ATSimplePermissions(AT_AddConstraint, rel,
7769 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7770 : Assert(conName != NULL);
7771 : }
7772 :
7773 580 : attnum = get_attnum(RelationGetRelid(rel), colName);
7774 580 : if (attnum == InvalidAttrNumber)
7775 18 : ereport(ERROR,
7776 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7777 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7778 : colName, RelationGetRelationName(rel))));
7779 :
7780 : /* Prevent them from altering a system attribute */
7781 562 : if (attnum <= 0)
7782 0 : ereport(ERROR,
7783 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7784 : errmsg("cannot alter system column \"%s\"",
7785 : colName)));
7786 :
7787 : /* See if there's already a constraint */
7788 562 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7789 562 : if (HeapTupleIsValid(tuple))
7790 : {
7791 92 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7792 92 : bool changed = false;
7793 :
7794 : /*
7795 : * Don't let a NO INHERIT constraint be changed into inherit.
7796 : */
7797 92 : if (conForm->connoinherit && recurse)
7798 6 : ereport(ERROR,
7799 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7800 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7801 : NameStr(conForm->conname),
7802 : RelationGetRelationName(rel)));
7803 :
7804 : /*
7805 : * If we find an appropriate constraint, we're almost done, but just
7806 : * need to change some properties on it: if we're recursing, increment
7807 : * coninhcount; if not, set conislocal if not already set.
7808 : */
7809 86 : if (recursing)
7810 : {
7811 66 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7812 : &conForm->coninhcount))
7813 0 : ereport(ERROR,
7814 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7815 : errmsg("too many inheritance parents"));
7816 66 : changed = true;
7817 : }
7818 20 : else if (!conForm->conislocal)
7819 : {
7820 0 : conForm->conislocal = true;
7821 0 : changed = true;
7822 : }
7823 :
7824 86 : if (changed)
7825 : {
7826 : Relation constr_rel;
7827 :
7828 66 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7829 :
7830 66 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7831 66 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7832 66 : table_close(constr_rel, RowExclusiveLock);
7833 : }
7834 :
7835 86 : if (changed)
7836 66 : return address;
7837 : else
7838 20 : return InvalidObjectAddress;
7839 : }
7840 :
7841 : /*
7842 : * If we're asked not to recurse, and children exist, raise an error for
7843 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7844 : * specified.
7845 : */
7846 494 : if (!recurse &&
7847 24 : find_inheritance_children(RelationGetRelid(rel),
7848 : NoLock) != NIL)
7849 : {
7850 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7851 6 : ereport(ERROR,
7852 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7853 : errmsg("constraint must be added to child tables too"),
7854 : errhint("Do not specify the ONLY keyword."));
7855 : else
7856 12 : is_no_inherit = true;
7857 : }
7858 :
7859 : /*
7860 : * No constraint exists; we must add one. First determine a name to use,
7861 : * if we haven't already.
7862 : */
7863 464 : if (!recursing)
7864 : {
7865 : Assert(conName == NULL);
7866 328 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7867 : colName, "not_null",
7868 328 : RelationGetNamespace(rel),
7869 : NIL);
7870 : }
7871 :
7872 464 : constraint = makeNotNullConstraint(makeString(colName));
7873 464 : constraint->is_no_inherit = is_no_inherit;
7874 464 : constraint->conname = conName;
7875 :
7876 : /* and do it */
7877 464 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7878 464 : false, !recursing, false, NULL);
7879 464 : ccon = linitial(cooked);
7880 464 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7881 :
7882 464 : InvokeObjectPostAlterHook(RelationRelationId,
7883 : RelationGetRelid(rel), attnum);
7884 :
7885 : /* Mark pg_attribute.attnotnull for the column */
7886 464 : set_attnotnull(wqueue, rel, attnum, lockmode);
7887 :
7888 : /*
7889 : * Recurse to propagate the constraint to children that don't have one.
7890 : */
7891 464 : if (recurse)
7892 : {
7893 : List *children;
7894 :
7895 446 : children = find_inheritance_children(RelationGetRelid(rel),
7896 : lockmode);
7897 :
7898 1094 : foreach_oid(childoid, children)
7899 : {
7900 202 : Relation childrel = table_open(childoid, NoLock);
7901 :
7902 202 : CommandCounterIncrement();
7903 :
7904 202 : ATExecSetNotNull(wqueue, childrel, conName, colName,
7905 : recurse, true, lockmode);
7906 202 : table_close(childrel, NoLock);
7907 : }
7908 : }
7909 :
7910 464 : return address;
7911 : }
7912 :
7913 : /*
7914 : * NotNullImpliedByRelConstraints
7915 : * Does rel's existing constraints imply NOT NULL for the given attribute?
7916 : */
7917 : static bool
7918 1148 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7919 : {
7920 1148 : NullTest *nnulltest = makeNode(NullTest);
7921 :
7922 2296 : nnulltest->arg = (Expr *) makeVar(1,
7923 1148 : attr->attnum,
7924 : attr->atttypid,
7925 : attr->atttypmod,
7926 : attr->attcollation,
7927 : 0);
7928 1148 : nnulltest->nulltesttype = IS_NOT_NULL;
7929 :
7930 : /*
7931 : * argisrow = false is correct even for a composite column, because
7932 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7933 : * case, just IS DISTINCT FROM NULL.
7934 : */
7935 1148 : nnulltest->argisrow = false;
7936 1148 : nnulltest->location = -1;
7937 :
7938 1148 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7939 : {
7940 50 : ereport(DEBUG1,
7941 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7942 : RelationGetRelationName(rel), NameStr(attr->attname))));
7943 50 : return true;
7944 : }
7945 :
7946 1098 : return false;
7947 : }
7948 :
7949 : /*
7950 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7951 : *
7952 : * Return the address of the affected column.
7953 : */
7954 : static ObjectAddress
7955 562 : ATExecColumnDefault(Relation rel, const char *colName,
7956 : Node *newDefault, LOCKMODE lockmode)
7957 : {
7958 562 : TupleDesc tupdesc = RelationGetDescr(rel);
7959 : AttrNumber attnum;
7960 : ObjectAddress address;
7961 :
7962 : /*
7963 : * get the number of the attribute
7964 : */
7965 562 : attnum = get_attnum(RelationGetRelid(rel), colName);
7966 562 : if (attnum == InvalidAttrNumber)
7967 30 : ereport(ERROR,
7968 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7969 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7970 : colName, RelationGetRelationName(rel))));
7971 :
7972 : /* Prevent them from altering a system attribute */
7973 532 : if (attnum <= 0)
7974 0 : ereport(ERROR,
7975 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7976 : errmsg("cannot alter system column \"%s\"",
7977 : colName)));
7978 :
7979 532 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7980 18 : ereport(ERROR,
7981 : (errcode(ERRCODE_SYNTAX_ERROR),
7982 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7983 : colName, RelationGetRelationName(rel)),
7984 : /* translator: %s is an SQL ALTER command */
7985 : newDefault ? 0 : errhint("Use %s instead.",
7986 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7987 :
7988 514 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7989 6 : ereport(ERROR,
7990 : (errcode(ERRCODE_SYNTAX_ERROR),
7991 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
7992 : colName, RelationGetRelationName(rel)),
7993 : newDefault ?
7994 : /* translator: %s is an SQL ALTER command */
7995 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7996 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
7997 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7998 :
7999 : /*
8000 : * Remove any old default for the column. We use RESTRICT here for
8001 : * safety, but at present we do not expect anything to depend on the
8002 : * default.
8003 : *
8004 : * We treat removing the existing default as an internal operation when it
8005 : * is preparatory to adding a new default, but as a user-initiated
8006 : * operation when the user asked for a drop.
8007 : */
8008 508 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8009 : newDefault != NULL);
8010 :
8011 508 : if (newDefault)
8012 : {
8013 : /* SET DEFAULT */
8014 : RawColumnDefault *rawEnt;
8015 :
8016 334 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8017 334 : rawEnt->attnum = attnum;
8018 334 : rawEnt->raw_default = newDefault;
8019 334 : rawEnt->missingMode = false;
8020 334 : rawEnt->generated = '\0';
8021 :
8022 : /*
8023 : * This function is intended for CREATE TABLE, so it processes a
8024 : * _list_ of defaults, but we just do one.
8025 : */
8026 334 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8027 : false, true, false, NULL);
8028 : }
8029 :
8030 502 : ObjectAddressSubSet(address, RelationRelationId,
8031 : RelationGetRelid(rel), attnum);
8032 502 : return address;
8033 : }
8034 :
8035 : /*
8036 : * Add a pre-cooked default expression.
8037 : *
8038 : * Return the address of the affected column.
8039 : */
8040 : static ObjectAddress
8041 56 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8042 : Node *newDefault)
8043 : {
8044 : ObjectAddress address;
8045 :
8046 : /* We assume no checking is required */
8047 :
8048 : /*
8049 : * Remove any old default for the column. We use RESTRICT here for
8050 : * safety, but at present we do not expect anything to depend on the
8051 : * default. (In ordinary cases, there could not be a default in place
8052 : * anyway, but it's possible when combining LIKE with inheritance.)
8053 : */
8054 56 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8055 : true);
8056 :
8057 56 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8058 :
8059 56 : ObjectAddressSubSet(address, RelationRelationId,
8060 : RelationGetRelid(rel), attnum);
8061 56 : return address;
8062 : }
8063 :
8064 : /*
8065 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8066 : *
8067 : * Return the address of the affected column.
8068 : */
8069 : static ObjectAddress
8070 160 : ATExecAddIdentity(Relation rel, const char *colName,
8071 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8072 : {
8073 : Relation attrelation;
8074 : HeapTuple tuple;
8075 : Form_pg_attribute attTup;
8076 : AttrNumber attnum;
8077 : ObjectAddress address;
8078 160 : ColumnDef *cdef = castNode(ColumnDef, def);
8079 : bool ispartitioned;
8080 :
8081 160 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8082 160 : if (ispartitioned && !recurse)
8083 6 : ereport(ERROR,
8084 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8085 : errmsg("cannot add identity to a column of only the partitioned table"),
8086 : errhint("Do not specify the ONLY keyword.")));
8087 :
8088 154 : if (rel->rd_rel->relispartition && !recursing)
8089 12 : ereport(ERROR,
8090 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8091 : errmsg("cannot add identity to a column of a partition"));
8092 :
8093 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8094 :
8095 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8096 142 : if (!HeapTupleIsValid(tuple))
8097 0 : ereport(ERROR,
8098 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8099 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8100 : colName, RelationGetRelationName(rel))));
8101 142 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8102 142 : attnum = attTup->attnum;
8103 :
8104 : /* Can't alter a system attribute */
8105 142 : if (attnum <= 0)
8106 0 : ereport(ERROR,
8107 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8108 : errmsg("cannot alter system column \"%s\"",
8109 : colName)));
8110 :
8111 : /*
8112 : * Creating a column as identity implies NOT NULL, so adding the identity
8113 : * to an existing column that is not NOT NULL would create a state that
8114 : * cannot be reproduced without contortions.
8115 : */
8116 142 : if (!attTup->attnotnull)
8117 6 : ereport(ERROR,
8118 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8119 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8120 : colName, RelationGetRelationName(rel))));
8121 :
8122 136 : if (attTup->attidentity)
8123 18 : ereport(ERROR,
8124 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8125 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8126 : colName, RelationGetRelationName(rel))));
8127 :
8128 118 : if (attTup->atthasdef)
8129 6 : ereport(ERROR,
8130 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8131 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8132 : colName, RelationGetRelationName(rel))));
8133 :
8134 112 : attTup->attidentity = cdef->identity;
8135 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8136 :
8137 112 : InvokeObjectPostAlterHook(RelationRelationId,
8138 : RelationGetRelid(rel),
8139 : attTup->attnum);
8140 112 : ObjectAddressSubSet(address, RelationRelationId,
8141 : RelationGetRelid(rel), attnum);
8142 112 : heap_freetuple(tuple);
8143 :
8144 112 : table_close(attrelation, RowExclusiveLock);
8145 :
8146 : /*
8147 : * Recurse to propagate the identity column to partitions. Identity is
8148 : * not inherited in regular inheritance children.
8149 : */
8150 112 : if (recurse && ispartitioned)
8151 : {
8152 : List *children;
8153 : ListCell *lc;
8154 :
8155 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8156 :
8157 16 : foreach(lc, children)
8158 : {
8159 : Relation childrel;
8160 :
8161 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8162 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8163 6 : table_close(childrel, NoLock);
8164 : }
8165 : }
8166 :
8167 112 : return address;
8168 : }
8169 :
8170 : /*
8171 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8172 : *
8173 : * Return the address of the affected column.
8174 : */
8175 : static ObjectAddress
8176 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8177 : LOCKMODE lockmode, bool recurse, bool recursing)
8178 : {
8179 : ListCell *option;
8180 74 : DefElem *generatedEl = NULL;
8181 : HeapTuple tuple;
8182 : Form_pg_attribute attTup;
8183 : AttrNumber attnum;
8184 : Relation attrelation;
8185 : ObjectAddress address;
8186 : bool ispartitioned;
8187 :
8188 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8189 74 : if (ispartitioned && !recurse)
8190 6 : ereport(ERROR,
8191 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8192 : errmsg("cannot change identity column of only the partitioned table"),
8193 : errhint("Do not specify the ONLY keyword.")));
8194 :
8195 68 : if (rel->rd_rel->relispartition && !recursing)
8196 12 : ereport(ERROR,
8197 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8198 : errmsg("cannot change identity column of a partition"));
8199 :
8200 100 : foreach(option, castNode(List, def))
8201 : {
8202 44 : DefElem *defel = lfirst_node(DefElem, option);
8203 :
8204 44 : if (strcmp(defel->defname, "generated") == 0)
8205 : {
8206 44 : if (generatedEl)
8207 0 : ereport(ERROR,
8208 : (errcode(ERRCODE_SYNTAX_ERROR),
8209 : errmsg("conflicting or redundant options")));
8210 44 : generatedEl = defel;
8211 : }
8212 : else
8213 0 : elog(ERROR, "option \"%s\" not recognized",
8214 : defel->defname);
8215 : }
8216 :
8217 : /*
8218 : * Even if there is nothing to change here, we run all the checks. There
8219 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8220 : * there.
8221 : */
8222 :
8223 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8224 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8225 56 : if (!HeapTupleIsValid(tuple))
8226 0 : ereport(ERROR,
8227 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8228 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8229 : colName, RelationGetRelationName(rel))));
8230 :
8231 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8232 56 : attnum = attTup->attnum;
8233 :
8234 56 : if (attnum <= 0)
8235 0 : ereport(ERROR,
8236 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8237 : errmsg("cannot alter system column \"%s\"",
8238 : colName)));
8239 :
8240 56 : if (!attTup->attidentity)
8241 6 : ereport(ERROR,
8242 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8243 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8244 : colName, RelationGetRelationName(rel))));
8245 :
8246 50 : if (generatedEl)
8247 : {
8248 44 : attTup->attidentity = defGetInt32(generatedEl);
8249 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8250 :
8251 44 : InvokeObjectPostAlterHook(RelationRelationId,
8252 : RelationGetRelid(rel),
8253 : attTup->attnum);
8254 44 : ObjectAddressSubSet(address, RelationRelationId,
8255 : RelationGetRelid(rel), attnum);
8256 : }
8257 : else
8258 6 : address = InvalidObjectAddress;
8259 :
8260 50 : heap_freetuple(tuple);
8261 50 : table_close(attrelation, RowExclusiveLock);
8262 :
8263 : /*
8264 : * Recurse to propagate the identity change to partitions. Identity is not
8265 : * inherited in regular inheritance children.
8266 : */
8267 50 : if (generatedEl && recurse && ispartitioned)
8268 : {
8269 : List *children;
8270 : ListCell *lc;
8271 :
8272 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8273 :
8274 18 : foreach(lc, children)
8275 : {
8276 : Relation childrel;
8277 :
8278 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8279 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8280 12 : table_close(childrel, NoLock);
8281 : }
8282 : }
8283 :
8284 50 : return address;
8285 : }
8286 :
8287 : /*
8288 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8289 : *
8290 : * Return the address of the affected column.
8291 : */
8292 : static ObjectAddress
8293 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8294 : bool recurse, bool recursing)
8295 : {
8296 : HeapTuple tuple;
8297 : Form_pg_attribute attTup;
8298 : AttrNumber attnum;
8299 : Relation attrelation;
8300 : ObjectAddress address;
8301 : Oid seqid;
8302 : ObjectAddress seqaddress;
8303 : bool ispartitioned;
8304 :
8305 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8306 68 : if (ispartitioned && !recurse)
8307 6 : ereport(ERROR,
8308 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8309 : errmsg("cannot drop identity from a column of only the partitioned table"),
8310 : errhint("Do not specify the ONLY keyword.")));
8311 :
8312 62 : if (rel->rd_rel->relispartition && !recursing)
8313 6 : ereport(ERROR,
8314 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8315 : errmsg("cannot drop identity from a column of a partition"));
8316 :
8317 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8318 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8319 56 : if (!HeapTupleIsValid(tuple))
8320 0 : ereport(ERROR,
8321 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8322 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8323 : colName, RelationGetRelationName(rel))));
8324 :
8325 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8326 56 : attnum = attTup->attnum;
8327 :
8328 56 : if (attnum <= 0)
8329 0 : ereport(ERROR,
8330 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8331 : errmsg("cannot alter system column \"%s\"",
8332 : colName)));
8333 :
8334 56 : if (!attTup->attidentity)
8335 : {
8336 12 : if (!missing_ok)
8337 6 : ereport(ERROR,
8338 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8339 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8340 : colName, RelationGetRelationName(rel))));
8341 : else
8342 : {
8343 6 : ereport(NOTICE,
8344 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8345 : colName, RelationGetRelationName(rel))));
8346 6 : heap_freetuple(tuple);
8347 6 : table_close(attrelation, RowExclusiveLock);
8348 6 : return InvalidObjectAddress;
8349 : }
8350 : }
8351 :
8352 44 : attTup->attidentity = '\0';
8353 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8354 :
8355 44 : InvokeObjectPostAlterHook(RelationRelationId,
8356 : RelationGetRelid(rel),
8357 : attTup->attnum);
8358 44 : ObjectAddressSubSet(address, RelationRelationId,
8359 : RelationGetRelid(rel), attnum);
8360 44 : heap_freetuple(tuple);
8361 :
8362 44 : table_close(attrelation, RowExclusiveLock);
8363 :
8364 : /*
8365 : * Recurse to drop the identity from column in partitions. Identity is
8366 : * not inherited in regular inheritance children so ignore them.
8367 : */
8368 44 : if (recurse && ispartitioned)
8369 : {
8370 : List *children;
8371 : ListCell *lc;
8372 :
8373 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8374 :
8375 12 : foreach(lc, children)
8376 : {
8377 : Relation childrel;
8378 :
8379 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8380 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8381 6 : table_close(childrel, NoLock);
8382 : }
8383 : }
8384 :
8385 44 : if (!recursing)
8386 : {
8387 : /* drop the internal sequence */
8388 32 : seqid = getIdentitySequence(rel, attnum, false);
8389 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8390 : RelationRelationId, DEPENDENCY_INTERNAL);
8391 32 : CommandCounterIncrement();
8392 32 : seqaddress.classId = RelationRelationId;
8393 32 : seqaddress.objectId = seqid;
8394 32 : seqaddress.objectSubId = 0;
8395 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8396 : }
8397 :
8398 44 : return address;
8399 : }
8400 :
8401 : /*
8402 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8403 : *
8404 : * Return the address of the affected column.
8405 : */
8406 : static ObjectAddress
8407 84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8408 : Node *newExpr, LOCKMODE lockmode)
8409 : {
8410 : HeapTuple tuple;
8411 : Form_pg_attribute attTup;
8412 : AttrNumber attnum;
8413 : Oid attrdefoid;
8414 : ObjectAddress address;
8415 : Expr *defval;
8416 : NewColumnValue *newval;
8417 : RawColumnDefault *rawEnt;
8418 :
8419 84 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8420 84 : if (!HeapTupleIsValid(tuple))
8421 0 : ereport(ERROR,
8422 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8423 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8424 : colName, RelationGetRelationName(rel))));
8425 :
8426 84 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8427 84 : attnum = attTup->attnum;
8428 :
8429 84 : if (attnum <= 0)
8430 0 : ereport(ERROR,
8431 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8432 : errmsg("cannot alter system column \"%s\"",
8433 : colName)));
8434 :
8435 84 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8436 6 : ereport(ERROR,
8437 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8438 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8439 : colName, RelationGetRelationName(rel))));
8440 78 : ReleaseSysCache(tuple);
8441 :
8442 : /*
8443 : * Clear all the missing values if we're rewriting the table, since this
8444 : * renders them pointless.
8445 : */
8446 78 : RelationClearMissing(rel);
8447 :
8448 : /* make sure we don't conflict with later attribute modifications */
8449 78 : CommandCounterIncrement();
8450 :
8451 : /*
8452 : * Find everything that depends on the column (constraints, indexes, etc),
8453 : * and record enough information to let us recreate the objects after
8454 : * rewrite.
8455 : */
8456 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8457 :
8458 : /*
8459 : * Drop the dependency records of the GENERATED expression, in particular
8460 : * its INTERNAL dependency on the column, which would otherwise cause
8461 : * dependency.c to refuse to perform the deletion.
8462 : */
8463 78 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8464 78 : if (!OidIsValid(attrdefoid))
8465 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8466 : RelationGetRelid(rel), attnum);
8467 78 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8468 :
8469 : /* Make above changes visible */
8470 78 : CommandCounterIncrement();
8471 :
8472 : /*
8473 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8474 : * safety, but at present we do not expect anything to depend on the
8475 : * expression.
8476 : */
8477 78 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8478 : false, false);
8479 :
8480 : /* Prepare to store the new expression, in the catalogs */
8481 78 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8482 78 : rawEnt->attnum = attnum;
8483 78 : rawEnt->raw_default = newExpr;
8484 78 : rawEnt->missingMode = false;
8485 78 : rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8486 :
8487 : /* Store the generated expression */
8488 78 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8489 : false, true, false, NULL);
8490 :
8491 : /* Make above new expression visible */
8492 78 : CommandCounterIncrement();
8493 :
8494 : /* Prepare for table rewrite */
8495 78 : defval = (Expr *) build_column_default(rel, attnum);
8496 :
8497 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8498 78 : newval->attnum = attnum;
8499 78 : newval->expr = expression_planner(defval);
8500 78 : newval->is_generated = true;
8501 :
8502 78 : tab->newvals = lappend(tab->newvals, newval);
8503 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8504 :
8505 : /* Drop any pg_statistic entry for the column */
8506 78 : RemoveStatistics(RelationGetRelid(rel), attnum);
8507 :
8508 78 : InvokeObjectPostAlterHook(RelationRelationId,
8509 : RelationGetRelid(rel), attnum);
8510 :
8511 78 : ObjectAddressSubSet(address, RelationRelationId,
8512 : RelationGetRelid(rel), attnum);
8513 78 : return address;
8514 : }
8515 :
8516 : /*
8517 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8518 : */
8519 : static void
8520 44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8521 : {
8522 : /*
8523 : * Reject ONLY if there are child tables. We could implement this, but it
8524 : * is a bit complicated. GENERATED clauses must be attached to the column
8525 : * definition and cannot be added later like DEFAULT, so if a child table
8526 : * has a generation expression that the parent does not have, the child
8527 : * column will necessarily be an attislocal column. So to implement ONLY
8528 : * here, we'd need extra code to update attislocal of the direct child
8529 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8530 : * resulting state can be properly dumped and restored.
8531 : */
8532 56 : if (!recurse &&
8533 12 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8534 6 : ereport(ERROR,
8535 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8536 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8537 :
8538 : /*
8539 : * Cannot drop generation expression from inherited columns.
8540 : */
8541 38 : if (!recursing)
8542 : {
8543 : HeapTuple tuple;
8544 : Form_pg_attribute attTup;
8545 :
8546 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8547 32 : if (!HeapTupleIsValid(tuple))
8548 0 : ereport(ERROR,
8549 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8550 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8551 : cmd->name, RelationGetRelationName(rel))));
8552 :
8553 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8554 :
8555 32 : if (attTup->attinhcount > 0)
8556 6 : ereport(ERROR,
8557 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8558 : errmsg("cannot drop generation expression from inherited column")));
8559 : }
8560 32 : }
8561 :
8562 : /*
8563 : * Return the address of the affected column.
8564 : */
8565 : static ObjectAddress
8566 32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8567 : {
8568 : HeapTuple tuple;
8569 : Form_pg_attribute attTup;
8570 : AttrNumber attnum;
8571 : Relation attrelation;
8572 : Oid attrdefoid;
8573 : ObjectAddress address;
8574 :
8575 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8576 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8577 32 : if (!HeapTupleIsValid(tuple))
8578 0 : ereport(ERROR,
8579 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8580 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8581 : colName, RelationGetRelationName(rel))));
8582 :
8583 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8584 32 : attnum = attTup->attnum;
8585 :
8586 32 : if (attnum <= 0)
8587 0 : ereport(ERROR,
8588 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8589 : errmsg("cannot alter system column \"%s\"",
8590 : colName)));
8591 :
8592 32 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8593 : {
8594 12 : if (!missing_ok)
8595 6 : ereport(ERROR,
8596 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8597 : errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8598 : colName, RelationGetRelationName(rel))));
8599 : else
8600 : {
8601 6 : ereport(NOTICE,
8602 : (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8603 : colName, RelationGetRelationName(rel))));
8604 6 : heap_freetuple(tuple);
8605 6 : table_close(attrelation, RowExclusiveLock);
8606 6 : return InvalidObjectAddress;
8607 : }
8608 : }
8609 :
8610 : /*
8611 : * Mark the column as no longer generated. (The atthasdef flag needs to
8612 : * get cleared too, but RemoveAttrDefault will handle that.)
8613 : */
8614 20 : attTup->attgenerated = '\0';
8615 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8616 :
8617 20 : InvokeObjectPostAlterHook(RelationRelationId,
8618 : RelationGetRelid(rel),
8619 : attnum);
8620 20 : heap_freetuple(tuple);
8621 :
8622 20 : table_close(attrelation, RowExclusiveLock);
8623 :
8624 : /*
8625 : * Drop the dependency records of the GENERATED expression, in particular
8626 : * its INTERNAL dependency on the column, which would otherwise cause
8627 : * dependency.c to refuse to perform the deletion.
8628 : */
8629 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8630 20 : if (!OidIsValid(attrdefoid))
8631 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8632 : RelationGetRelid(rel), attnum);
8633 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8634 :
8635 : /* Make above changes visible */
8636 20 : CommandCounterIncrement();
8637 :
8638 : /*
8639 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8640 : * safety, but at present we do not expect anything to depend on the
8641 : * default.
8642 : */
8643 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8644 : false, false);
8645 :
8646 20 : ObjectAddressSubSet(address, RelationRelationId,
8647 : RelationGetRelid(rel), attnum);
8648 20 : return address;
8649 : }
8650 :
8651 : /*
8652 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8653 : *
8654 : * Return value is the address of the modified column
8655 : */
8656 : static ObjectAddress
8657 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8658 : {
8659 164 : int newtarget = 0;
8660 : bool newtarget_default;
8661 : Relation attrelation;
8662 : HeapTuple tuple,
8663 : newtuple;
8664 : Form_pg_attribute attrtuple;
8665 : AttrNumber attnum;
8666 : ObjectAddress address;
8667 : Datum repl_val[Natts_pg_attribute];
8668 : bool repl_null[Natts_pg_attribute];
8669 : bool repl_repl[Natts_pg_attribute];
8670 :
8671 : /*
8672 : * We allow referencing columns by numbers only for indexes, since table
8673 : * column numbers could contain gaps if columns are later dropped.
8674 : */
8675 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8676 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8677 : !colName)
8678 0 : ereport(ERROR,
8679 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8680 : errmsg("cannot refer to non-index column by number")));
8681 :
8682 : /* -1 was used in previous versions for the default setting */
8683 164 : if (newValue && intVal(newValue) != -1)
8684 : {
8685 120 : newtarget = intVal(newValue);
8686 120 : newtarget_default = false;
8687 : }
8688 : else
8689 44 : newtarget_default = true;
8690 :
8691 164 : if (!newtarget_default)
8692 : {
8693 : /*
8694 : * Limit target to a sane range
8695 : */
8696 120 : if (newtarget < 0)
8697 : {
8698 0 : ereport(ERROR,
8699 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8700 : errmsg("statistics target %d is too low",
8701 : newtarget)));
8702 : }
8703 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8704 : {
8705 0 : newtarget = MAX_STATISTICS_TARGET;
8706 0 : ereport(WARNING,
8707 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8708 : errmsg("lowering statistics target to %d",
8709 : newtarget)));
8710 : }
8711 : }
8712 :
8713 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8714 :
8715 164 : if (colName)
8716 : {
8717 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8718 :
8719 100 : if (!HeapTupleIsValid(tuple))
8720 12 : ereport(ERROR,
8721 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8722 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8723 : colName, RelationGetRelationName(rel))));
8724 : }
8725 : else
8726 : {
8727 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8728 :
8729 64 : if (!HeapTupleIsValid(tuple))
8730 12 : ereport(ERROR,
8731 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8732 : errmsg("column number %d of relation \"%s\" does not exist",
8733 : colNum, RelationGetRelationName(rel))));
8734 : }
8735 :
8736 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8737 :
8738 140 : attnum = attrtuple->attnum;
8739 140 : if (attnum <= 0)
8740 0 : ereport(ERROR,
8741 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8742 : errmsg("cannot alter system column \"%s\"",
8743 : colName)));
8744 :
8745 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8746 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8747 : {
8748 52 : if (attnum > rel->rd_index->indnkeyatts)
8749 6 : ereport(ERROR,
8750 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8751 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8752 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8753 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8754 18 : ereport(ERROR,
8755 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8756 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8757 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8758 : errhint("Alter statistics on table column instead.")));
8759 : }
8760 :
8761 : /* Build new tuple. */
8762 116 : memset(repl_null, false, sizeof(repl_null));
8763 116 : memset(repl_repl, false, sizeof(repl_repl));
8764 116 : if (!newtarget_default)
8765 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8766 : else
8767 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8768 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8769 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8770 : repl_val, repl_null, repl_repl);
8771 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8772 :
8773 116 : InvokeObjectPostAlterHook(RelationRelationId,
8774 : RelationGetRelid(rel),
8775 : attrtuple->attnum);
8776 116 : ObjectAddressSubSet(address, RelationRelationId,
8777 : RelationGetRelid(rel), attnum);
8778 :
8779 116 : heap_freetuple(newtuple);
8780 :
8781 116 : ReleaseSysCache(tuple);
8782 :
8783 116 : table_close(attrelation, RowExclusiveLock);
8784 :
8785 116 : return address;
8786 : }
8787 :
8788 : /*
8789 : * Return value is the address of the modified column
8790 : */
8791 : static ObjectAddress
8792 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8793 : bool isReset, LOCKMODE lockmode)
8794 : {
8795 : Relation attrelation;
8796 : HeapTuple tuple,
8797 : newtuple;
8798 : Form_pg_attribute attrtuple;
8799 : AttrNumber attnum;
8800 : Datum datum,
8801 : newOptions;
8802 : bool isnull;
8803 : ObjectAddress address;
8804 : Datum repl_val[Natts_pg_attribute];
8805 : bool repl_null[Natts_pg_attribute];
8806 : bool repl_repl[Natts_pg_attribute];
8807 :
8808 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8809 :
8810 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8811 :
8812 32 : if (!HeapTupleIsValid(tuple))
8813 0 : ereport(ERROR,
8814 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8815 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8816 : colName, RelationGetRelationName(rel))));
8817 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8818 :
8819 32 : attnum = attrtuple->attnum;
8820 32 : if (attnum <= 0)
8821 0 : ereport(ERROR,
8822 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8823 : errmsg("cannot alter system column \"%s\"",
8824 : colName)));
8825 :
8826 : /* Generate new proposed attoptions (text array) */
8827 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8828 : &isnull);
8829 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8830 : castNode(List, options), NULL, NULL,
8831 : false, isReset);
8832 : /* Validate new options */
8833 32 : (void) attribute_reloptions(newOptions, true);
8834 :
8835 : /* Build new tuple. */
8836 32 : memset(repl_null, false, sizeof(repl_null));
8837 32 : memset(repl_repl, false, sizeof(repl_repl));
8838 32 : if (newOptions != (Datum) 0)
8839 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8840 : else
8841 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8842 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8843 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8844 : repl_val, repl_null, repl_repl);
8845 :
8846 : /* Update system catalog. */
8847 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8848 :
8849 32 : InvokeObjectPostAlterHook(RelationRelationId,
8850 : RelationGetRelid(rel),
8851 : attrtuple->attnum);
8852 32 : ObjectAddressSubSet(address, RelationRelationId,
8853 : RelationGetRelid(rel), attnum);
8854 :
8855 32 : heap_freetuple(newtuple);
8856 :
8857 32 : ReleaseSysCache(tuple);
8858 :
8859 32 : table_close(attrelation, RowExclusiveLock);
8860 :
8861 32 : return address;
8862 : }
8863 :
8864 : /*
8865 : * Helper function for ATExecSetStorage and ATExecSetCompression
8866 : *
8867 : * Set the attstorage and/or attcompression fields for index columns
8868 : * associated with the specified table column.
8869 : */
8870 : static void
8871 284 : SetIndexStorageProperties(Relation rel, Relation attrelation,
8872 : AttrNumber attnum,
8873 : bool setstorage, char newstorage,
8874 : bool setcompression, char newcompression,
8875 : LOCKMODE lockmode)
8876 : {
8877 : ListCell *lc;
8878 :
8879 356 : foreach(lc, RelationGetIndexList(rel))
8880 : {
8881 72 : Oid indexoid = lfirst_oid(lc);
8882 : Relation indrel;
8883 72 : AttrNumber indattnum = 0;
8884 : HeapTuple tuple;
8885 :
8886 72 : indrel = index_open(indexoid, lockmode);
8887 :
8888 120 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
8889 : {
8890 78 : if (indrel->rd_index->indkey.values[i] == attnum)
8891 : {
8892 30 : indattnum = i + 1;
8893 30 : break;
8894 : }
8895 : }
8896 :
8897 72 : if (indattnum == 0)
8898 : {
8899 42 : index_close(indrel, lockmode);
8900 42 : continue;
8901 : }
8902 :
8903 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8904 :
8905 30 : if (HeapTupleIsValid(tuple))
8906 : {
8907 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8908 :
8909 30 : if (setstorage)
8910 24 : attrtuple->attstorage = newstorage;
8911 :
8912 30 : if (setcompression)
8913 6 : attrtuple->attcompression = newcompression;
8914 :
8915 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8916 :
8917 30 : InvokeObjectPostAlterHook(RelationRelationId,
8918 : RelationGetRelid(rel),
8919 : attrtuple->attnum);
8920 :
8921 30 : heap_freetuple(tuple);
8922 : }
8923 :
8924 30 : index_close(indrel, lockmode);
8925 : }
8926 284 : }
8927 :
8928 : /*
8929 : * ALTER TABLE ALTER COLUMN SET STORAGE
8930 : *
8931 : * Return value is the address of the modified column
8932 : */
8933 : static ObjectAddress
8934 234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8935 : {
8936 : Relation attrelation;
8937 : HeapTuple tuple;
8938 : Form_pg_attribute attrtuple;
8939 : AttrNumber attnum;
8940 : ObjectAddress address;
8941 :
8942 234 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8943 :
8944 234 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8945 :
8946 234 : if (!HeapTupleIsValid(tuple))
8947 12 : ereport(ERROR,
8948 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8949 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8950 : colName, RelationGetRelationName(rel))));
8951 222 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8952 :
8953 222 : attnum = attrtuple->attnum;
8954 222 : if (attnum <= 0)
8955 0 : ereport(ERROR,
8956 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8957 : errmsg("cannot alter system column \"%s\"",
8958 : colName)));
8959 :
8960 222 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8961 :
8962 222 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8963 :
8964 222 : InvokeObjectPostAlterHook(RelationRelationId,
8965 : RelationGetRelid(rel),
8966 : attrtuple->attnum);
8967 :
8968 : /*
8969 : * Apply the change to indexes as well (only for simple index columns,
8970 : * matching behavior of index.c ConstructTupleDescriptor()).
8971 : */
8972 222 : SetIndexStorageProperties(rel, attrelation, attnum,
8973 222 : true, attrtuple->attstorage,
8974 : false, 0,
8975 : lockmode);
8976 :
8977 222 : heap_freetuple(tuple);
8978 :
8979 222 : table_close(attrelation, RowExclusiveLock);
8980 :
8981 222 : ObjectAddressSubSet(address, RelationRelationId,
8982 : RelationGetRelid(rel), attnum);
8983 222 : return address;
8984 : }
8985 :
8986 :
8987 : /*
8988 : * ALTER TABLE DROP COLUMN
8989 : *
8990 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8991 : * because we have to decide at runtime whether to recurse or not depending
8992 : * on whether attinhcount goes to zero or not. (We can't check this in a
8993 : * static pre-pass because it won't handle multiple inheritance situations
8994 : * correctly.)
8995 : */
8996 : static void
8997 1604 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8998 : AlterTableCmd *cmd, LOCKMODE lockmode,
8999 : AlterTableUtilityContext *context)
9000 : {
9001 1604 : if (rel->rd_rel->reloftype && !recursing)
9002 6 : ereport(ERROR,
9003 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9004 : errmsg("cannot drop column from typed table")));
9005 :
9006 1598 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9007 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9008 :
9009 1592 : if (recurse)
9010 1324 : cmd->recurse = true;
9011 1592 : }
9012 :
9013 : /*
9014 : * Drops column 'colName' from relation 'rel' and returns the address of the
9015 : * dropped column. The column is also dropped (or marked as no longer
9016 : * inherited from relation) from the relation's inheritance children, if any.
9017 : *
9018 : * In the recursive invocations for inheritance child relations, instead of
9019 : * dropping the column directly (if to be dropped at all), its object address
9020 : * is added to 'addrs', which must be non-NULL in such invocations. All
9021 : * columns are dropped at the same time after all the children have been
9022 : * checked recursively.
9023 : */
9024 : static ObjectAddress
9025 2148 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9026 : DropBehavior behavior,
9027 : bool recurse, bool recursing,
9028 : bool missing_ok, LOCKMODE lockmode,
9029 : ObjectAddresses *addrs)
9030 : {
9031 : HeapTuple tuple;
9032 : Form_pg_attribute targetatt;
9033 : AttrNumber attnum;
9034 : List *children;
9035 : ObjectAddress object;
9036 : bool is_expr;
9037 :
9038 : /* At top level, permission check was done in ATPrepCmd, else do it */
9039 2148 : if (recursing)
9040 556 : ATSimplePermissions(AT_DropColumn, rel,
9041 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9042 :
9043 : /* Initialize addrs on the first invocation */
9044 : Assert(!recursing || addrs != NULL);
9045 :
9046 : /* since this function recurses, it could be driven to stack overflow */
9047 2148 : check_stack_depth();
9048 :
9049 2148 : if (!recursing)
9050 1592 : addrs = new_object_addresses();
9051 :
9052 : /*
9053 : * get the number of the attribute
9054 : */
9055 2148 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9056 2148 : if (!HeapTupleIsValid(tuple))
9057 : {
9058 54 : if (!missing_ok)
9059 : {
9060 36 : ereport(ERROR,
9061 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9062 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9063 : colName, RelationGetRelationName(rel))));
9064 : }
9065 : else
9066 : {
9067 18 : ereport(NOTICE,
9068 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9069 : colName, RelationGetRelationName(rel))));
9070 18 : return InvalidObjectAddress;
9071 : }
9072 : }
9073 2094 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9074 :
9075 2094 : attnum = targetatt->attnum;
9076 :
9077 : /* Can't drop a system attribute */
9078 2094 : if (attnum <= 0)
9079 6 : ereport(ERROR,
9080 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9081 : errmsg("cannot drop system column \"%s\"",
9082 : colName)));
9083 :
9084 : /*
9085 : * Don't drop inherited columns, unless recursing (presumably from a drop
9086 : * of the parent column)
9087 : */
9088 2088 : if (targetatt->attinhcount > 0 && !recursing)
9089 48 : ereport(ERROR,
9090 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9091 : errmsg("cannot drop inherited column \"%s\"",
9092 : colName)));
9093 :
9094 : /*
9095 : * Don't drop columns used in the partition key, either. (If we let this
9096 : * go through, the key column's dependencies would cause a cascaded drop
9097 : * of the whole table, which is surely not what the user expected.)
9098 : */
9099 2040 : if (has_partition_attrs(rel,
9100 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9101 : &is_expr))
9102 30 : ereport(ERROR,
9103 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9104 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9105 : colName, RelationGetRelationName(rel))));
9106 :
9107 2010 : ReleaseSysCache(tuple);
9108 :
9109 : /*
9110 : * Propagate to children as appropriate. Unlike most other ALTER
9111 : * routines, we have to do this one level of recursion at a time; we can't
9112 : * use find_all_inheritors to do it in one pass.
9113 : */
9114 : children =
9115 2010 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9116 :
9117 2010 : if (children)
9118 : {
9119 : Relation attr_rel;
9120 : ListCell *child;
9121 :
9122 : /*
9123 : * In case of a partitioned table, the column must be dropped from the
9124 : * partitions as well.
9125 : */
9126 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9127 6 : ereport(ERROR,
9128 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9129 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9130 : errhint("Do not specify the ONLY keyword.")));
9131 :
9132 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9133 882 : foreach(child, children)
9134 : {
9135 592 : Oid childrelid = lfirst_oid(child);
9136 : Relation childrel;
9137 : Form_pg_attribute childatt;
9138 :
9139 : /* find_inheritance_children already got lock */
9140 592 : childrel = table_open(childrelid, NoLock);
9141 592 : CheckAlterTableIsSafe(childrel);
9142 :
9143 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9144 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9145 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9146 : colName, childrelid);
9147 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9148 :
9149 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9150 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9151 : childrelid, colName);
9152 :
9153 592 : if (recurse)
9154 : {
9155 : /*
9156 : * If the child column has other definition sources, just
9157 : * decrement its inheritance count; if not, recurse to delete
9158 : * it.
9159 : */
9160 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9161 : {
9162 : /* Time to delete this child column, too */
9163 556 : ATExecDropColumn(wqueue, childrel, colName,
9164 : behavior, true, true,
9165 : false, lockmode, addrs);
9166 : }
9167 : else
9168 : {
9169 : /* Child column must survive my deletion */
9170 12 : childatt->attinhcount--;
9171 :
9172 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9173 :
9174 : /* Make update visible */
9175 12 : CommandCounterIncrement();
9176 : }
9177 : }
9178 : else
9179 : {
9180 : /*
9181 : * If we were told to drop ONLY in this table (no recursion),
9182 : * we need to mark the inheritors' attributes as locally
9183 : * defined rather than inherited.
9184 : */
9185 24 : childatt->attinhcount--;
9186 24 : childatt->attislocal = true;
9187 :
9188 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9189 :
9190 : /* Make update visible */
9191 24 : CommandCounterIncrement();
9192 : }
9193 :
9194 586 : heap_freetuple(tuple);
9195 :
9196 586 : table_close(childrel, NoLock);
9197 : }
9198 290 : table_close(attr_rel, RowExclusiveLock);
9199 : }
9200 :
9201 : /* Add object to delete */
9202 1998 : object.classId = RelationRelationId;
9203 1998 : object.objectId = RelationGetRelid(rel);
9204 1998 : object.objectSubId = attnum;
9205 1998 : add_exact_object_address(&object, addrs);
9206 :
9207 1998 : if (!recursing)
9208 : {
9209 : /* Recursion has ended, drop everything that was collected */
9210 1448 : performMultipleDeletions(addrs, behavior, 0);
9211 1400 : free_object_addresses(addrs);
9212 : }
9213 :
9214 1950 : return object;
9215 : }
9216 :
9217 : /*
9218 : * Prepare to add a primary key on table, by adding not-null constraints
9219 : * on all columns.
9220 : */
9221 : static void
9222 14938 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9223 : bool recurse, LOCKMODE lockmode,
9224 : AlterTableUtilityContext *context)
9225 : {
9226 : ListCell *lc;
9227 : Constraint *pkconstr;
9228 :
9229 14938 : pkconstr = castNode(Constraint, cmd->def);
9230 14938 : if (pkconstr->contype != CONSTR_PRIMARY)
9231 8776 : return;
9232 :
9233 : /*
9234 : * If not recursing, we must ensure that all children have a NOT NULL
9235 : * constraint on the columns, and error out if not.
9236 : */
9237 6162 : if (!recurse)
9238 : {
9239 : List *children;
9240 :
9241 254 : children = find_inheritance_children(RelationGetRelid(rel),
9242 : lockmode);
9243 628 : foreach_oid(childrelid, children)
9244 : {
9245 260 : foreach(lc, pkconstr->keys)
9246 : {
9247 : HeapTuple tup;
9248 : Form_pg_attribute attrForm;
9249 134 : char *attname = strVal(lfirst(lc));
9250 :
9251 134 : tup = SearchSysCacheAttName(childrelid, attname);
9252 134 : if (!tup)
9253 0 : elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9254 : attname, childrelid);
9255 134 : attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9256 134 : if (!attrForm->attnotnull)
9257 6 : ereport(ERROR,
9258 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9259 : attname, get_rel_name(childrelid)));
9260 128 : ReleaseSysCache(tup);
9261 : }
9262 : }
9263 : }
9264 :
9265 : /* Insert not-null constraints in the queue for the PK columns */
9266 7106 : foreach(lc, pkconstr->keys)
9267 : {
9268 : AlterTableCmd *newcmd;
9269 : Constraint *nnconstr;
9270 :
9271 950 : nnconstr = makeNotNullConstraint(lfirst(lc));
9272 :
9273 950 : newcmd = makeNode(AlterTableCmd);
9274 950 : newcmd->subtype = AT_AddConstraint;
9275 950 : newcmd->recurse = true;
9276 950 : newcmd->def = (Node *) nnconstr;
9277 :
9278 950 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9279 : }
9280 : }
9281 :
9282 : /*
9283 : * ALTER TABLE ADD INDEX
9284 : *
9285 : * There is no such command in the grammar, but parse_utilcmd.c converts
9286 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9287 : * us schedule creation of the index at the appropriate time during ALTER.
9288 : *
9289 : * Return value is the address of the new index.
9290 : */
9291 : static ObjectAddress
9292 1588 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9293 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9294 : {
9295 : bool check_rights;
9296 : bool skip_build;
9297 : bool quiet;
9298 : ObjectAddress address;
9299 :
9300 : Assert(IsA(stmt, IndexStmt));
9301 : Assert(!stmt->concurrent);
9302 :
9303 : /* The IndexStmt has already been through transformIndexStmt */
9304 : Assert(stmt->transformed);
9305 :
9306 : /* suppress schema rights check when rebuilding existing index */
9307 1588 : check_rights = !is_rebuild;
9308 : /* skip index build if phase 3 will do it or we're reusing an old one */
9309 1588 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9310 : /* suppress notices when rebuilding existing index */
9311 1588 : quiet = is_rebuild;
9312 :
9313 1588 : address = DefineIndex(RelationGetRelid(rel),
9314 : stmt,
9315 : InvalidOid, /* no predefined OID */
9316 : InvalidOid, /* no parent index */
9317 : InvalidOid, /* no parent constraint */
9318 : -1, /* total_parts unknown */
9319 : true, /* is_alter_table */
9320 : check_rights,
9321 : false, /* check_not_in_use - we did it already */
9322 : skip_build,
9323 : quiet);
9324 :
9325 : /*
9326 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9327 : * new index instead of building from scratch. Restore associated fields.
9328 : * This may store InvalidSubTransactionId in both fields, in which case
9329 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9330 : * this after the CCI that made catalog rows visible to any rebuild. The
9331 : * DROP of the old edition of this index will have scheduled the storage
9332 : * for deletion at commit, so cancel that pending deletion.
9333 : */
9334 1418 : if (RelFileNumberIsValid(stmt->oldNumber))
9335 : {
9336 74 : Relation irel = index_open(address.objectId, NoLock);
9337 :
9338 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9339 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9340 74 : RelationPreserveStorage(irel->rd_locator, true);
9341 74 : index_close(irel, NoLock);
9342 : }
9343 :
9344 1418 : return address;
9345 : }
9346 :
9347 : /*
9348 : * ALTER TABLE ADD STATISTICS
9349 : *
9350 : * This is no such command in the grammar, but we use this internally to add
9351 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9352 : * column type change.
9353 : */
9354 : static ObjectAddress
9355 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9356 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9357 : {
9358 : ObjectAddress address;
9359 :
9360 : Assert(IsA(stmt, CreateStatsStmt));
9361 :
9362 : /* The CreateStatsStmt has already been through transformStatsStmt */
9363 : Assert(stmt->transformed);
9364 :
9365 14 : address = CreateStatistics(stmt);
9366 :
9367 14 : return address;
9368 : }
9369 :
9370 : /*
9371 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9372 : *
9373 : * Returns the address of the new constraint.
9374 : */
9375 : static ObjectAddress
9376 9540 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9377 : IndexStmt *stmt, LOCKMODE lockmode)
9378 : {
9379 9540 : Oid index_oid = stmt->indexOid;
9380 : Relation indexRel;
9381 : char *indexName;
9382 : IndexInfo *indexInfo;
9383 : char *constraintName;
9384 : char constraintType;
9385 : ObjectAddress address;
9386 : bits16 flags;
9387 :
9388 : Assert(IsA(stmt, IndexStmt));
9389 : Assert(OidIsValid(index_oid));
9390 : Assert(stmt->isconstraint);
9391 :
9392 : /*
9393 : * Doing this on partitioned tables is not a simple feature to implement,
9394 : * so let's punt for now.
9395 : */
9396 9540 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9397 6 : ereport(ERROR,
9398 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9399 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9400 :
9401 9534 : indexRel = index_open(index_oid, AccessShareLock);
9402 :
9403 9534 : indexName = pstrdup(RelationGetRelationName(indexRel));
9404 :
9405 9534 : indexInfo = BuildIndexInfo(indexRel);
9406 :
9407 : /* this should have been checked at parse time */
9408 9534 : if (!indexInfo->ii_Unique)
9409 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9410 :
9411 : /*
9412 : * Determine name to assign to constraint. We require a constraint to
9413 : * have the same name as the underlying index; therefore, use the index's
9414 : * existing name as the default constraint name, and if the user
9415 : * explicitly gives some other name for the constraint, rename the index
9416 : * to match.
9417 : */
9418 9534 : constraintName = stmt->idxname;
9419 9534 : if (constraintName == NULL)
9420 9508 : constraintName = indexName;
9421 26 : else if (strcmp(constraintName, indexName) != 0)
9422 : {
9423 20 : ereport(NOTICE,
9424 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9425 : indexName, constraintName)));
9426 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9427 : }
9428 :
9429 : /* Extra checks needed if making primary key */
9430 9534 : if (stmt->primary)
9431 5386 : index_check_primary_key(rel, indexInfo, true, stmt);
9432 :
9433 : /* Note we currently don't support EXCLUSION constraints here */
9434 9528 : if (stmt->primary)
9435 5380 : constraintType = CONSTRAINT_PRIMARY;
9436 : else
9437 4148 : constraintType = CONSTRAINT_UNIQUE;
9438 :
9439 : /* Create the catalog entries for the constraint */
9440 9528 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9441 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9442 19056 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9443 9528 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9444 9528 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9445 :
9446 9528 : address = index_constraint_create(rel,
9447 : index_oid,
9448 : InvalidOid,
9449 : indexInfo,
9450 : constraintName,
9451 : constraintType,
9452 : flags,
9453 : allowSystemTableMods,
9454 : false); /* is_internal */
9455 :
9456 9528 : index_close(indexRel, NoLock);
9457 :
9458 9528 : return address;
9459 : }
9460 :
9461 : /*
9462 : * ALTER TABLE ADD CONSTRAINT
9463 : *
9464 : * Return value is the address of the new constraint; if no constraint was
9465 : * added, InvalidObjectAddress is returned.
9466 : */
9467 : static ObjectAddress
9468 11986 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9469 : Constraint *newConstraint, bool recurse, bool is_readd,
9470 : LOCKMODE lockmode)
9471 : {
9472 11986 : ObjectAddress address = InvalidObjectAddress;
9473 :
9474 : Assert(IsA(newConstraint, Constraint));
9475 :
9476 : /*
9477 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9478 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9479 : * parse_utilcmd.c).
9480 : */
9481 11986 : switch (newConstraint->contype)
9482 : {
9483 9468 : case CONSTR_CHECK:
9484 : case CONSTR_NOTNULL:
9485 : address =
9486 9468 : ATAddCheckNNConstraint(wqueue, tab, rel,
9487 : newConstraint, recurse, false, is_readd,
9488 : lockmode);
9489 9342 : break;
9490 :
9491 2518 : case CONSTR_FOREIGN:
9492 :
9493 : /*
9494 : * Assign or validate constraint name
9495 : */
9496 2518 : if (newConstraint->conname)
9497 : {
9498 1172 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9499 : RelationGetRelid(rel),
9500 1172 : newConstraint->conname))
9501 0 : ereport(ERROR,
9502 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9503 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9504 : newConstraint->conname,
9505 : RelationGetRelationName(rel))));
9506 : }
9507 : else
9508 1346 : newConstraint->conname =
9509 1346 : ChooseConstraintName(RelationGetRelationName(rel),
9510 1346 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9511 : "fkey",
9512 1346 : RelationGetNamespace(rel),
9513 : NIL);
9514 :
9515 2518 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9516 : newConstraint,
9517 : recurse, false,
9518 : lockmode);
9519 2006 : break;
9520 :
9521 0 : default:
9522 0 : elog(ERROR, "unrecognized constraint type: %d",
9523 : (int) newConstraint->contype);
9524 : }
9525 :
9526 11348 : return address;
9527 : }
9528 :
9529 : /*
9530 : * Generate the column-name portion of the constraint name for a new foreign
9531 : * key given the list of column names that reference the referenced
9532 : * table. This will be passed to ChooseConstraintName along with the parent
9533 : * table name and the "fkey" suffix.
9534 : *
9535 : * We know that less than NAMEDATALEN characters will actually be used, so we
9536 : * can truncate the result once we've generated that many.
9537 : *
9538 : * XXX see also ChooseExtendedStatisticNameAddition and
9539 : * ChooseIndexNameAddition.
9540 : */
9541 : static char *
9542 2222 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9543 : {
9544 : char buf[NAMEDATALEN * 2];
9545 2222 : int buflen = 0;
9546 : ListCell *lc;
9547 :
9548 2222 : buf[0] = '\0';
9549 5004 : foreach(lc, colnames)
9550 : {
9551 2782 : const char *name = strVal(lfirst(lc));
9552 :
9553 2782 : if (buflen > 0)
9554 560 : buf[buflen++] = '_'; /* insert _ between names */
9555 :
9556 : /*
9557 : * At this point we have buflen <= NAMEDATALEN. name should be less
9558 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9559 : */
9560 2782 : strlcpy(buf + buflen, name, NAMEDATALEN);
9561 2782 : buflen += strlen(buf + buflen);
9562 2782 : if (buflen >= NAMEDATALEN)
9563 0 : break;
9564 : }
9565 2222 : return pstrdup(buf);
9566 : }
9567 :
9568 : /*
9569 : * Add a check or not-null constraint to a single table and its children.
9570 : * Returns the address of the constraint added to the parent relation,
9571 : * if one gets added, or InvalidObjectAddress otherwise.
9572 : *
9573 : * Subroutine for ATExecAddConstraint.
9574 : *
9575 : * We must recurse to child tables during execution, rather than using
9576 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9577 : * constraints *must* be given the same name, else they won't be seen as
9578 : * related later. If the user didn't explicitly specify a name, then
9579 : * AddRelationNewConstraints would normally assign different names to the
9580 : * child constraints. To fix that, we must capture the name assigned at
9581 : * the parent table and pass that down.
9582 : */
9583 : static ObjectAddress
9584 10280 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9585 : Constraint *constr, bool recurse, bool recursing,
9586 : bool is_readd, LOCKMODE lockmode)
9587 : {
9588 : List *newcons;
9589 : ListCell *lcon;
9590 : List *children;
9591 : ListCell *child;
9592 10280 : ObjectAddress address = InvalidObjectAddress;
9593 :
9594 : /* Guard against stack overflow due to overly deep inheritance tree. */
9595 10280 : check_stack_depth();
9596 :
9597 : /* At top level, permission check was done in ATPrepCmd, else do it */
9598 10280 : if (recursing)
9599 678 : ATSimplePermissions(AT_AddConstraint, rel,
9600 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9601 :
9602 : /*
9603 : * Call AddRelationNewConstraints to do the work, making sure it works on
9604 : * a copy of the Constraint so transformExpr can't modify the original. It
9605 : * returns a list of cooked constraints.
9606 : *
9607 : * If the constraint ends up getting merged with a pre-existing one, it's
9608 : * omitted from the returned list, which is what we want: we do not need
9609 : * to do any validation work. That can only happen at child tables,
9610 : * though, since we disallow merging at the top level.
9611 : */
9612 10280 : newcons = AddRelationNewConstraints(rel, NIL,
9613 10280 : list_make1(copyObject(constr)),
9614 10280 : recursing || is_readd, /* allow_merge */
9615 10280 : !recursing, /* is_local */
9616 : is_readd, /* is_internal */
9617 10280 : NULL); /* queryString not available
9618 : * here */
9619 :
9620 : /* we don't expect more than one constraint here */
9621 : Assert(list_length(newcons) <= 1);
9622 :
9623 : /* Add each to-be-validated constraint to Phase 3's queue */
9624 19708 : foreach(lcon, newcons)
9625 : {
9626 9548 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9627 :
9628 9548 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9629 : {
9630 : NewConstraint *newcon;
9631 :
9632 832 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9633 832 : newcon->name = ccon->name;
9634 832 : newcon->contype = ccon->contype;
9635 832 : newcon->qual = ccon->expr;
9636 :
9637 832 : tab->constraints = lappend(tab->constraints, newcon);
9638 : }
9639 :
9640 : /* Save the actually assigned name if it was defaulted */
9641 9548 : if (constr->conname == NULL)
9642 8128 : constr->conname = ccon->name;
9643 :
9644 : /*
9645 : * If adding a not-null constraint, set the pg_attribute flag and tell
9646 : * phase 3 to verify existing rows, if needed.
9647 : */
9648 9548 : if (constr->contype == CONSTR_NOTNULL)
9649 8240 : set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9650 :
9651 9548 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9652 : }
9653 :
9654 : /* At this point we must have a locked-down name to use */
9655 : Assert(newcons == NIL || constr->conname != NULL);
9656 :
9657 : /* Advance command counter in case same table is visited multiple times */
9658 10160 : CommandCounterIncrement();
9659 :
9660 : /*
9661 : * If the constraint got merged with an existing constraint, we're done.
9662 : * We mustn't recurse to child tables in this case, because they've
9663 : * already got the constraint, and visiting them again would lead to an
9664 : * incorrect value for coninhcount.
9665 : */
9666 10160 : if (newcons == NIL)
9667 612 : return address;
9668 :
9669 : /*
9670 : * If adding a NO INHERIT constraint, no need to find our children.
9671 : */
9672 9548 : if (constr->is_no_inherit)
9673 72 : return address;
9674 :
9675 : /*
9676 : * Propagate to children as appropriate. Unlike most other ALTER
9677 : * routines, we have to do this one level of recursion at a time; we can't
9678 : * use find_all_inheritors to do it in one pass.
9679 : */
9680 : children =
9681 9476 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9682 :
9683 : /*
9684 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9685 : * constraint creation only if there are no children currently. Error out
9686 : * otherwise.
9687 : */
9688 9476 : if (!recurse && children != NIL)
9689 6 : ereport(ERROR,
9690 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9691 : errmsg("constraint must be added to child tables too")));
9692 :
9693 : /*
9694 : * Recurse to create the constraint on each child.
9695 : */
9696 10118 : foreach(child, children)
9697 : {
9698 678 : Oid childrelid = lfirst_oid(child);
9699 : Relation childrel;
9700 : AlteredTableInfo *childtab;
9701 :
9702 : /* find_inheritance_children already got lock */
9703 678 : childrel = table_open(childrelid, NoLock);
9704 678 : CheckAlterTableIsSafe(childrel);
9705 :
9706 : /* Find or create work queue entry for this table */
9707 678 : childtab = ATGetQueueEntry(wqueue, childrel);
9708 :
9709 : /* Recurse to this child */
9710 678 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9711 : constr, recurse, true, is_readd, lockmode);
9712 :
9713 648 : table_close(childrel, NoLock);
9714 : }
9715 :
9716 9440 : return address;
9717 : }
9718 :
9719 : /*
9720 : * Add a foreign-key constraint to a single table; return the new constraint's
9721 : * address.
9722 : *
9723 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9724 : * lock on the rel, and have done appropriate validity checks for it.
9725 : * We do permissions checks here, however.
9726 : *
9727 : * When the referenced or referencing tables (or both) are partitioned,
9728 : * multiple pg_constraint rows are required -- one for each partitioned table
9729 : * and each partition on each side (fortunately, not one for every combination
9730 : * thereof). We also need action triggers on each leaf partition on the
9731 : * referenced side, and check triggers on each leaf partition on the
9732 : * referencing side.
9733 : */
9734 : static ObjectAddress
9735 2518 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9736 : Constraint *fkconstraint,
9737 : bool recurse, bool recursing, LOCKMODE lockmode)
9738 : {
9739 : Relation pkrel;
9740 2518 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9741 2518 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9742 2518 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9743 2518 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9744 2518 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9745 2518 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9746 2518 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9747 2518 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9748 2518 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9749 2518 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9750 2518 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9751 : bool with_period;
9752 : bool pk_has_without_overlaps;
9753 : int i;
9754 : int numfks,
9755 : numpks,
9756 : numfkdelsetcols;
9757 : Oid indexOid;
9758 : bool old_check_ok;
9759 : ObjectAddress address;
9760 2518 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9761 :
9762 : /*
9763 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9764 : * delete rows out from under us.
9765 : */
9766 2518 : if (OidIsValid(fkconstraint->old_pktable_oid))
9767 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9768 : else
9769 2446 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9770 :
9771 : /*
9772 : * Validity checks (permission checks wait till we have the column
9773 : * numbers)
9774 : */
9775 2518 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9776 : {
9777 372 : if (!recurse)
9778 6 : ereport(ERROR,
9779 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9780 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9781 : RelationGetRelationName(rel),
9782 : RelationGetRelationName(pkrel))));
9783 366 : if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9784 6 : ereport(ERROR,
9785 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9786 : errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9787 : RelationGetRelationName(rel),
9788 : RelationGetRelationName(pkrel)),
9789 : errdetail("This feature is not yet supported on partitioned tables.")));
9790 : }
9791 :
9792 2506 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9793 326 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9794 0 : ereport(ERROR,
9795 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9796 : errmsg("referenced relation \"%s\" is not a table",
9797 : RelationGetRelationName(pkrel))));
9798 :
9799 2506 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9800 2 : ereport(ERROR,
9801 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9802 : errmsg("permission denied: \"%s\" is a system catalog",
9803 : RelationGetRelationName(pkrel))));
9804 :
9805 : /*
9806 : * References from permanent or unlogged tables to temp tables, and from
9807 : * permanent tables to unlogged tables, are disallowed because the
9808 : * referenced data can vanish out from under us. References from temp
9809 : * tables to any other table type are also disallowed, because other
9810 : * backends might need to run the RI triggers on the perm table, but they
9811 : * can't reliably see tuples in the local buffers of other backends.
9812 : */
9813 2504 : switch (rel->rd_rel->relpersistence)
9814 : {
9815 2214 : case RELPERSISTENCE_PERMANENT:
9816 2214 : if (!RelationIsPermanent(pkrel))
9817 0 : ereport(ERROR,
9818 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9819 : errmsg("constraints on permanent tables may reference only permanent tables")));
9820 2214 : break;
9821 12 : case RELPERSISTENCE_UNLOGGED:
9822 12 : if (!RelationIsPermanent(pkrel)
9823 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9824 0 : ereport(ERROR,
9825 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9826 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9827 12 : break;
9828 278 : case RELPERSISTENCE_TEMP:
9829 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9830 0 : ereport(ERROR,
9831 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9832 : errmsg("constraints on temporary tables may reference only temporary tables")));
9833 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9834 0 : ereport(ERROR,
9835 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9836 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
9837 278 : break;
9838 : }
9839 :
9840 : /*
9841 : * Look up the referencing attributes to make sure they exist, and record
9842 : * their attnums and type and collation OIDs.
9843 : */
9844 2504 : numfks = transformColumnNameList(RelationGetRelid(rel),
9845 : fkconstraint->fk_attrs,
9846 : fkattnum, fktypoid, fkcolloid);
9847 2474 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9848 2474 : if (with_period && !fkconstraint->fk_with_period)
9849 24 : ereport(ERROR,
9850 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9851 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9852 :
9853 2450 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9854 : fkconstraint->fk_del_set_cols,
9855 : fkdelsetcols, NULL, NULL);
9856 2444 : validateFkOnDeleteSetColumns(numfks, fkattnum,
9857 : numfkdelsetcols, fkdelsetcols,
9858 : fkconstraint->fk_del_set_cols);
9859 :
9860 : /*
9861 : * If the attribute list for the referenced table was omitted, lookup the
9862 : * definition of the primary key and use it. Otherwise, validate the
9863 : * supplied attribute list. In either case, discover the index OID and
9864 : * index opclasses, and the attnums and type and collation OIDs of the
9865 : * attributes.
9866 : */
9867 2438 : if (fkconstraint->pk_attrs == NIL)
9868 : {
9869 1112 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9870 : &fkconstraint->pk_attrs,
9871 : pkattnum, pktypoid, pkcolloid,
9872 : opclasses, &pk_has_without_overlaps);
9873 :
9874 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9875 1112 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9876 24 : ereport(ERROR,
9877 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9878 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9879 : }
9880 : else
9881 : {
9882 1326 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
9883 : fkconstraint->pk_attrs,
9884 : pkattnum, pktypoid, pkcolloid);
9885 :
9886 : /* Since we got pk_attrs, one should be a period. */
9887 1296 : if (with_period && !fkconstraint->pk_with_period)
9888 24 : ereport(ERROR,
9889 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9890 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9891 :
9892 : /* Look for an index matching the column list */
9893 1272 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9894 : with_period, opclasses, &pk_has_without_overlaps);
9895 : }
9896 :
9897 : /*
9898 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9899 : * must use PERIOD.
9900 : */
9901 2324 : if (pk_has_without_overlaps && !with_period)
9902 12 : ereport(ERROR,
9903 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9904 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9905 :
9906 : /*
9907 : * Now we can check permissions.
9908 : */
9909 2312 : checkFkeyPermissions(pkrel, pkattnum, numpks);
9910 :
9911 : /*
9912 : * Check some things for generated columns.
9913 : */
9914 5424 : for (i = 0; i < numfks; i++)
9915 : {
9916 3124 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9917 :
9918 3124 : if (attgenerated)
9919 : {
9920 : /*
9921 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
9922 : */
9923 30 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9924 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9925 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9926 6 : ereport(ERROR,
9927 : (errcode(ERRCODE_SYNTAX_ERROR),
9928 : errmsg("invalid %s action for foreign key constraint containing generated column",
9929 : "ON UPDATE")));
9930 24 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9931 18 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9932 6 : ereport(ERROR,
9933 : (errcode(ERRCODE_SYNTAX_ERROR),
9934 : errmsg("invalid %s action for foreign key constraint containing generated column",
9935 : "ON DELETE")));
9936 : }
9937 : }
9938 :
9939 : /*
9940 : * Some actions are currently unsupported for foreign keys using PERIOD.
9941 : */
9942 2300 : if (fkconstraint->fk_with_period)
9943 : {
9944 262 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
9945 244 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9946 226 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
9947 54 : ereport(ERROR,
9948 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9949 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
9950 : "ON UPDATE"));
9951 :
9952 208 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
9953 208 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9954 208 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9955 0 : ereport(ERROR,
9956 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9957 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
9958 : "ON DELETE"));
9959 : }
9960 :
9961 : /*
9962 : * Look up the equality operators to use in the constraint.
9963 : *
9964 : * Note that we have to be careful about the difference between the actual
9965 : * PK column type and the opclass' declared input type, which might be
9966 : * only binary-compatible with it. The declared opcintype is the right
9967 : * thing to probe pg_amop with.
9968 : */
9969 2246 : if (numfks != numpks)
9970 0 : ereport(ERROR,
9971 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9972 : errmsg("number of referencing and referenced columns for foreign key disagree")));
9973 :
9974 : /*
9975 : * On the strength of a previous constraint, we might avoid scanning
9976 : * tables to validate this one. See below.
9977 : */
9978 2246 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9979 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9980 :
9981 4866 : for (i = 0; i < numpks; i++)
9982 : {
9983 2860 : Oid pktype = pktypoid[i];
9984 2860 : Oid fktype = fktypoid[i];
9985 : Oid fktyped;
9986 2860 : Oid pkcoll = pkcolloid[i];
9987 2860 : Oid fkcoll = fkcolloid[i];
9988 : HeapTuple cla_ht;
9989 : Form_pg_opclass cla_tup;
9990 : Oid amid;
9991 : Oid opfamily;
9992 : Oid opcintype;
9993 : Oid pfeqop;
9994 : Oid ppeqop;
9995 : Oid ffeqop;
9996 : int16 eqstrategy;
9997 : Oid pfeqop_right;
9998 :
9999 : /* We need several fields out of the pg_opclass entry */
10000 2860 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10001 2860 : if (!HeapTupleIsValid(cla_ht))
10002 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10003 2860 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10004 2860 : amid = cla_tup->opcmethod;
10005 2860 : opfamily = cla_tup->opcfamily;
10006 2860 : opcintype = cla_tup->opcintype;
10007 2860 : ReleaseSysCache(cla_ht);
10008 :
10009 2860 : if (with_period)
10010 : {
10011 : CompareType cmptype;
10012 444 : bool for_overlaps = with_period && i == numpks - 1;
10013 :
10014 : /*
10015 : * GiST indexes are required to support temporal foreign keys
10016 : * because they combine equals and overlaps.
10017 : */
10018 444 : if (amid != GIST_AM_OID)
10019 0 : elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10020 :
10021 444 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10022 :
10023 : /*
10024 : * An opclass can use whatever strategy numbers it wants, so we
10025 : * ask the opclass what number it actually uses instead of our RT*
10026 : * constants.
10027 : */
10028 444 : eqstrategy = GistTranslateStratnum(opclasses[i], cmptype);
10029 444 : if (eqstrategy == InvalidStrategy)
10030 : {
10031 : HeapTuple tuple;
10032 :
10033 0 : tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10034 0 : if (!HeapTupleIsValid(tuple))
10035 0 : elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10036 :
10037 0 : ereport(ERROR,
10038 : errcode(ERRCODE_UNDEFINED_OBJECT),
10039 : for_overlaps
10040 : ? errmsg("could not identify an overlaps operator for foreign key")
10041 : : errmsg("could not identify an equality operator for foreign key"),
10042 : errdetail("Could not translate compare type %d for operator class \"%s\" for access method \"%s\".",
10043 : cmptype, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10044 : }
10045 : }
10046 : else
10047 : {
10048 : /*
10049 : * Check it's a btree; currently this can never fail since no
10050 : * other index AMs support unique indexes. If we ever did have
10051 : * other types of unique indexes, we'd need a way to determine
10052 : * which operator strategy number is equality. (We could use
10053 : * something like GistTranslateStratnum.)
10054 : */
10055 2416 : if (amid != BTREE_AM_OID)
10056 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
10057 2416 : eqstrategy = BTEqualStrategyNumber;
10058 : }
10059 :
10060 : /*
10061 : * There had better be a primary equality operator for the index.
10062 : * We'll use it for PK = PK comparisons.
10063 : */
10064 2860 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10065 : eqstrategy);
10066 :
10067 2860 : if (!OidIsValid(ppeqop))
10068 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10069 : eqstrategy, opcintype, opcintype, opfamily);
10070 :
10071 : /*
10072 : * Are there equality operators that take exactly the FK type? Assume
10073 : * we should look through any domain here.
10074 : */
10075 2860 : fktyped = getBaseType(fktype);
10076 :
10077 2860 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10078 : eqstrategy);
10079 2860 : if (OidIsValid(pfeqop))
10080 : {
10081 2160 : pfeqop_right = fktyped;
10082 2160 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10083 : eqstrategy);
10084 : }
10085 : else
10086 : {
10087 : /* keep compiler quiet */
10088 700 : pfeqop_right = InvalidOid;
10089 700 : ffeqop = InvalidOid;
10090 : }
10091 :
10092 2860 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10093 : {
10094 : /*
10095 : * Otherwise, look for an implicit cast from the FK type to the
10096 : * opcintype, and if found, use the primary equality operator.
10097 : * This is a bit tricky because opcintype might be a polymorphic
10098 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10099 : * whether the two actual column types can be concurrently cast to
10100 : * that type. (Otherwise, we'd fail to reject combinations such
10101 : * as int[] and point[].)
10102 : */
10103 : Oid input_typeids[2];
10104 : Oid target_typeids[2];
10105 :
10106 700 : input_typeids[0] = pktype;
10107 700 : input_typeids[1] = fktype;
10108 700 : target_typeids[0] = opcintype;
10109 700 : target_typeids[1] = opcintype;
10110 700 : if (can_coerce_type(2, input_typeids, target_typeids,
10111 : COERCION_IMPLICIT))
10112 : {
10113 472 : pfeqop = ffeqop = ppeqop;
10114 472 : pfeqop_right = opcintype;
10115 : }
10116 : }
10117 :
10118 2860 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10119 228 : ereport(ERROR,
10120 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10121 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10122 : fkconstraint->conname),
10123 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10124 : "are of incompatible types: %s and %s.",
10125 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10126 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10127 : format_type_be(fktype),
10128 : format_type_be(pktype))));
10129 :
10130 : /*
10131 : * This shouldn't be possible, but better check to make sure we have a
10132 : * consistent state for the check below.
10133 : */
10134 2632 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10135 0 : elog(ERROR, "key columns are not both collatable");
10136 :
10137 2632 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10138 : {
10139 : bool pkcolldet;
10140 : bool fkcolldet;
10141 :
10142 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10143 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10144 :
10145 : /*
10146 : * SQL requires that both collations are the same. This is
10147 : * because we need a consistent notion of equality on both
10148 : * columns. We relax this by allowing different collations if
10149 : * they are both deterministic. (This is also for backward
10150 : * compatibility, because PostgreSQL has always allowed this.)
10151 : */
10152 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10153 12 : ereport(ERROR,
10154 : (errcode(ERRCODE_COLLATION_MISMATCH),
10155 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10156 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10157 : "have incompatible collations: \"%s\" and \"%s\". "
10158 : "If either collation is nondeterministic, then both collations have to be the same.",
10159 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10160 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10161 : get_collation_name(fkcoll),
10162 : get_collation_name(pkcoll))));
10163 : }
10164 :
10165 2620 : if (old_check_ok)
10166 : {
10167 : /*
10168 : * When a pfeqop changes, revalidate the constraint. We could
10169 : * permit intra-opfamily changes, but that adds subtle complexity
10170 : * without any concrete benefit for core types. We need not
10171 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10172 : */
10173 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10174 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10175 : old_pfeqop_item);
10176 : }
10177 2620 : if (old_check_ok)
10178 : {
10179 : Oid old_fktype;
10180 : Oid new_fktype;
10181 : CoercionPathType old_pathtype;
10182 : CoercionPathType new_pathtype;
10183 : Oid old_castfunc;
10184 : Oid new_castfunc;
10185 : Oid old_fkcoll;
10186 : Oid new_fkcoll;
10187 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10188 6 : fkattnum[i] - 1);
10189 :
10190 : /*
10191 : * Identify coercion pathways from each of the old and new FK-side
10192 : * column types to the right (foreign) operand type of the pfeqop.
10193 : * We may assume that pg_constraint.conkey is not changing.
10194 : */
10195 6 : old_fktype = attr->atttypid;
10196 6 : new_fktype = fktype;
10197 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10198 : &old_castfunc);
10199 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10200 : &new_castfunc);
10201 :
10202 6 : old_fkcoll = attr->attcollation;
10203 6 : new_fkcoll = fkcoll;
10204 :
10205 : /*
10206 : * Upon a change to the cast from the FK column to its pfeqop
10207 : * operand, revalidate the constraint. For this evaluation, a
10208 : * binary coercion cast is equivalent to no cast at all. While
10209 : * type implementors should design implicit casts with an eye
10210 : * toward consistency of operations like equality, we cannot
10211 : * assume here that they have done so.
10212 : *
10213 : * A function with a polymorphic argument could change behavior
10214 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10215 : * when the cast destination is polymorphic, we only avoid
10216 : * revalidation if the input type has not changed at all. Given
10217 : * just the core data types and operator classes, this requirement
10218 : * prevents no would-be optimizations.
10219 : *
10220 : * If the cast converts from a base type to a domain thereon, then
10221 : * that domain type must be the opcintype of the unique index.
10222 : * Necessarily, the primary key column must then be of the domain
10223 : * type. Since the constraint was previously valid, all values on
10224 : * the foreign side necessarily exist on the primary side and in
10225 : * turn conform to the domain. Consequently, we need not treat
10226 : * domains specially here.
10227 : *
10228 : * If the collation changes, revalidation is required, unless both
10229 : * collations are deterministic, because those share the same
10230 : * notion of equality (because texteq reduces to bitwise
10231 : * equality).
10232 : *
10233 : * We need not directly consider the PK type. It's necessarily
10234 : * binary coercible to the opcintype of the unique index column,
10235 : * and ri_triggers.c will only deal with PK datums in terms of
10236 : * that opcintype. Changing the opcintype also changes pfeqop.
10237 : */
10238 6 : old_check_ok = (new_pathtype == old_pathtype &&
10239 6 : new_castfunc == old_castfunc &&
10240 6 : (!IsPolymorphicType(pfeqop_right) ||
10241 12 : new_fktype == old_fktype) &&
10242 0 : (new_fkcoll == old_fkcoll ||
10243 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10244 : }
10245 :
10246 2620 : pfeqoperators[i] = pfeqop;
10247 2620 : ppeqoperators[i] = ppeqop;
10248 2620 : ffeqoperators[i] = ffeqop;
10249 : }
10250 :
10251 : /*
10252 : * For FKs with PERIOD we need additional operators to check whether the
10253 : * referencing row's range is contained by the aggregated ranges of the
10254 : * referenced row(s). For rangetypes and multirangetypes this is
10255 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10256 : * support for now. FKs will look these up at "runtime", but we should
10257 : * make sure the lookup works here, even if we don't use the values.
10258 : */
10259 2006 : if (with_period)
10260 : {
10261 : Oid periodoperoid;
10262 : Oid aggedperiodoperoid;
10263 :
10264 190 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10265 : }
10266 :
10267 : /* First, create the constraint catalog entry itself. */
10268 2006 : address = addFkConstraint(addFkBothSides,
10269 : fkconstraint->conname, fkconstraint, rel, pkrel,
10270 : indexOid,
10271 : InvalidOid, /* no parent constraint */
10272 : numfks,
10273 : pkattnum,
10274 : fkattnum,
10275 : pfeqoperators,
10276 : ppeqoperators,
10277 : ffeqoperators,
10278 : numfkdelsetcols,
10279 : fkdelsetcols,
10280 : false,
10281 : with_period);
10282 :
10283 : /* Next process the action triggers at the referenced side and recurse */
10284 2006 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10285 : indexOid,
10286 : address.objectId,
10287 : numfks,
10288 : pkattnum,
10289 : fkattnum,
10290 : pfeqoperators,
10291 : ppeqoperators,
10292 : ffeqoperators,
10293 : numfkdelsetcols,
10294 : fkdelsetcols,
10295 : old_check_ok,
10296 : InvalidOid, InvalidOid,
10297 : with_period);
10298 :
10299 : /* Lastly create the check triggers at the referencing side and recurse */
10300 2006 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10301 : indexOid,
10302 : address.objectId,
10303 : numfks,
10304 : pkattnum,
10305 : fkattnum,
10306 : pfeqoperators,
10307 : ppeqoperators,
10308 : ffeqoperators,
10309 : numfkdelsetcols,
10310 : fkdelsetcols,
10311 : old_check_ok,
10312 : lockmode,
10313 : InvalidOid, InvalidOid,
10314 : with_period);
10315 :
10316 : /*
10317 : * Done. Close pk table, but keep lock until we've committed.
10318 : */
10319 2006 : table_close(pkrel, NoLock);
10320 :
10321 2006 : return address;
10322 : }
10323 :
10324 : /*
10325 : * validateFkOnDeleteSetColumns
10326 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10327 : * column lists are valid.
10328 : */
10329 : void
10330 2444 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10331 : int numfksetcols, const int16 *fksetcolsattnums,
10332 : List *fksetcols)
10333 : {
10334 2468 : for (int i = 0; i < numfksetcols; i++)
10335 : {
10336 30 : int16 setcol_attnum = fksetcolsattnums[i];
10337 30 : bool seen = false;
10338 :
10339 54 : for (int j = 0; j < numfks; j++)
10340 : {
10341 48 : if (fkattnums[j] == setcol_attnum)
10342 : {
10343 24 : seen = true;
10344 24 : break;
10345 : }
10346 : }
10347 :
10348 30 : if (!seen)
10349 : {
10350 6 : char *col = strVal(list_nth(fksetcols, i));
10351 :
10352 6 : ereport(ERROR,
10353 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10354 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10355 : }
10356 : }
10357 2438 : }
10358 :
10359 : /*
10360 : * addFkConstraint
10361 : * Install pg_constraint entries to implement a foreign key constraint.
10362 : * Caller must separately invoke addFkRecurseReferenced and
10363 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10364 : * and (for partitioned tables) recurse to partitions.
10365 : *
10366 : * fkside: the side of the FK (or both) to create. Caller should
10367 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10368 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10369 : * addFkBothSides.
10370 : * constraintname: the base name for the constraint being added,
10371 : * copied to fkconstraint->conname if the latter is not set
10372 : * fkconstraint: the constraint being added
10373 : * rel: the root referencing relation
10374 : * pkrel: the referenced relation; might be a partition, if recursing
10375 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10376 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10377 : * top-level constraint
10378 : * numfks: the number of columns in the foreign key
10379 : * pkattnum: the attnum array of referenced attributes
10380 : * fkattnum: the attnum array of referencing attributes
10381 : * pf/pp/ffeqoperators: OID array of operators between columns
10382 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10383 : * (...) clause
10384 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10385 : * NULL/DEFAULT clause
10386 : * with_period: true if this is a temporal FK
10387 : */
10388 : static ObjectAddress
10389 3658 : addFkConstraint(addFkConstraintSides fkside,
10390 : char *constraintname, Constraint *fkconstraint,
10391 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10392 : int numfks, int16 *pkattnum,
10393 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10394 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10395 : bool is_internal, bool with_period)
10396 : {
10397 : ObjectAddress address;
10398 : Oid constrOid;
10399 : char *conname;
10400 : bool conislocal;
10401 : int16 coninhcount;
10402 : bool connoinherit;
10403 :
10404 : /*
10405 : * Verify relkind for each referenced partition. At the top level, this
10406 : * is redundant with a previous check, but we need it when recursing.
10407 : */
10408 3658 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10409 784 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10410 0 : ereport(ERROR,
10411 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10412 : errmsg("referenced relation \"%s\" is not a table",
10413 : RelationGetRelationName(pkrel))));
10414 :
10415 : /*
10416 : * Caller supplies us with a constraint name; however, it may be used in
10417 : * this partition, so come up with a different one in that case.
10418 : */
10419 3658 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10420 : RelationGetRelid(rel),
10421 : constraintname))
10422 876 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10423 876 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10424 : "fkey",
10425 876 : RelationGetNamespace(rel), NIL);
10426 : else
10427 2782 : conname = constraintname;
10428 :
10429 3658 : if (fkconstraint->conname == NULL)
10430 400 : fkconstraint->conname = pstrdup(conname);
10431 :
10432 3658 : if (OidIsValid(parentConstr))
10433 : {
10434 1652 : conislocal = false;
10435 1652 : coninhcount = 1;
10436 1652 : connoinherit = false;
10437 : }
10438 : else
10439 : {
10440 2006 : conislocal = true;
10441 2006 : coninhcount = 0;
10442 :
10443 : /*
10444 : * always inherit for partitioned tables, never for legacy inheritance
10445 : */
10446 2006 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10447 : }
10448 :
10449 : /*
10450 : * Record the FK constraint in pg_constraint.
10451 : */
10452 3658 : constrOid = CreateConstraintEntry(conname,
10453 3658 : RelationGetNamespace(rel),
10454 : CONSTRAINT_FOREIGN,
10455 3658 : fkconstraint->deferrable,
10456 3658 : fkconstraint->initdeferred,
10457 : true, /* Is Enforced */
10458 3658 : fkconstraint->initially_valid,
10459 : parentConstr,
10460 : RelationGetRelid(rel),
10461 : fkattnum,
10462 : numfks,
10463 : numfks,
10464 : InvalidOid, /* not a domain constraint */
10465 : indexOid,
10466 : RelationGetRelid(pkrel),
10467 : pkattnum,
10468 : pfeqoperators,
10469 : ppeqoperators,
10470 : ffeqoperators,
10471 : numfks,
10472 3658 : fkconstraint->fk_upd_action,
10473 3658 : fkconstraint->fk_del_action,
10474 : fkdelsetcols,
10475 : numfkdelsetcols,
10476 3658 : fkconstraint->fk_matchtype,
10477 : NULL, /* no exclusion constraint */
10478 : NULL, /* no check constraint */
10479 : NULL,
10480 : conislocal, /* islocal */
10481 : coninhcount, /* inhcount */
10482 : connoinherit, /* conNoInherit */
10483 : with_period, /* conPeriod */
10484 : is_internal); /* is_internal */
10485 :
10486 3658 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10487 :
10488 : /*
10489 : * In partitioning cases, create the dependency entries for this
10490 : * constraint. (For non-partitioned cases, relevant entries were created
10491 : * by CreateConstraintEntry.)
10492 : *
10493 : * On the referenced side, we need the constraint to have an internal
10494 : * dependency on its parent constraint; this means that this constraint
10495 : * cannot be dropped on its own -- only through the parent constraint. It
10496 : * also means the containing partition cannot be dropped on its own, but
10497 : * it can be detached, at which point this dependency is removed (after
10498 : * verifying that no rows are referenced via this FK.)
10499 : *
10500 : * When processing the referencing side, we link the constraint via the
10501 : * special partitioning dependencies: the parent constraint is the primary
10502 : * dependent, and the partition on which the foreign key exists is the
10503 : * secondary dependency. That way, this constraint is dropped if either
10504 : * of these objects is.
10505 : *
10506 : * Note that this is only necessary for the subsidiary pg_constraint rows
10507 : * in partitions; the topmost row doesn't need any of this.
10508 : */
10509 3658 : if (OidIsValid(parentConstr))
10510 : {
10511 : ObjectAddress referenced;
10512 :
10513 1652 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10514 :
10515 : Assert(fkside != addFkBothSides);
10516 1652 : if (fkside == addFkReferencedSide)
10517 870 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10518 : else
10519 : {
10520 782 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10521 782 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10522 782 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10523 : }
10524 : }
10525 :
10526 : /* make new constraint visible, in case we add more */
10527 3658 : CommandCounterIncrement();
10528 :
10529 3658 : return address;
10530 : }
10531 :
10532 : /*
10533 : * addFkRecurseReferenced
10534 : * Recursive helper for the referenced side of foreign key creation,
10535 : * which creates the action triggers and recurses
10536 : *
10537 : * If the referenced relation is a plain relation, create the necessary action
10538 : * triggers that implement the constraint. If the referenced relation is a
10539 : * partitioned table, then we create a pg_constraint row referencing the parent
10540 : * of the referencing side for it and recurse on this routine for each
10541 : * partition.
10542 : *
10543 : * fkconstraint: the constraint being added
10544 : * rel: the root referencing relation
10545 : * pkrel: the referenced relation; might be a partition, if recursing
10546 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10547 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10548 : * top-level constraint
10549 : * numfks: the number of columns in the foreign key
10550 : * pkattnum: the attnum array of referenced attributes
10551 : * fkattnum: the attnum array of referencing attributes
10552 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10553 : * NULL/DEFAULT (...) clause
10554 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10555 : * NULL/DEFAULT clause
10556 : * pf/pp/ffeqoperators: OID array of operators between columns
10557 : * old_check_ok: true if this constraint replaces an existing one that
10558 : * was already validated (thus this one doesn't need validation)
10559 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10560 : * partition, the OIDs of the parent action triggers for DELETE and
10561 : * UPDATE respectively.
10562 : * with_period: true if this is a temporal FK
10563 : */
10564 : static void
10565 2948 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10566 : Relation pkrel, Oid indexOid, Oid parentConstr,
10567 : int numfks,
10568 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10569 : Oid *ppeqoperators, Oid *ffeqoperators,
10570 : int numfkdelsetcols, int16 *fkdelsetcols,
10571 : bool old_check_ok,
10572 : Oid parentDelTrigger, Oid parentUpdTrigger,
10573 : bool with_period)
10574 : {
10575 : Oid deleteTriggerOid,
10576 : updateTriggerOid;
10577 :
10578 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10579 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10580 :
10581 : /*
10582 : * Create the action triggers that enforce the constraint.
10583 : */
10584 2948 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10585 : fkconstraint,
10586 : parentConstr, indexOid,
10587 : parentDelTrigger, parentUpdTrigger,
10588 : &deleteTriggerOid, &updateTriggerOid);
10589 :
10590 : /*
10591 : * If the referenced table is partitioned, recurse on ourselves to handle
10592 : * each partition. We need one pg_constraint row created for each
10593 : * partition in addition to the pg_constraint row for the parent table.
10594 : */
10595 2948 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10596 : {
10597 462 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10598 :
10599 1206 : for (int i = 0; i < pd->nparts; i++)
10600 : {
10601 : Relation partRel;
10602 : AttrMap *map;
10603 : AttrNumber *mapped_pkattnum;
10604 : Oid partIndexId;
10605 : ObjectAddress address;
10606 :
10607 : /* XXX would it be better to acquire these locks beforehand? */
10608 744 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10609 :
10610 : /*
10611 : * Map the attribute numbers in the referenced side of the FK
10612 : * definition to match the partition's column layout.
10613 : */
10614 744 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10615 : RelationGetDescr(pkrel),
10616 : false);
10617 744 : if (map)
10618 : {
10619 58 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10620 128 : for (int j = 0; j < numfks; j++)
10621 70 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10622 : }
10623 : else
10624 686 : mapped_pkattnum = pkattnum;
10625 :
10626 : /* Determine the index to use at this level */
10627 744 : partIndexId = index_get_partition(partRel, indexOid);
10628 744 : if (!OidIsValid(partIndexId))
10629 0 : elog(ERROR, "index for %u not found in partition %s",
10630 : indexOid, RelationGetRelationName(partRel));
10631 :
10632 : /* Create entry at this level ... */
10633 744 : address = addFkConstraint(addFkReferencedSide,
10634 : fkconstraint->conname, fkconstraint, rel,
10635 : partRel, partIndexId, parentConstr,
10636 : numfks, mapped_pkattnum,
10637 : fkattnum, pfeqoperators, ppeqoperators,
10638 : ffeqoperators, numfkdelsetcols,
10639 : fkdelsetcols, true, with_period);
10640 : /* ... and recurse to our children */
10641 744 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10642 : partIndexId, address.objectId, numfks,
10643 : mapped_pkattnum, fkattnum,
10644 : pfeqoperators, ppeqoperators, ffeqoperators,
10645 : numfkdelsetcols, fkdelsetcols,
10646 : old_check_ok,
10647 : deleteTriggerOid, updateTriggerOid,
10648 : with_period);
10649 :
10650 : /* Done -- clean up (but keep the lock) */
10651 744 : table_close(partRel, NoLock);
10652 744 : if (map)
10653 : {
10654 58 : pfree(mapped_pkattnum);
10655 58 : free_attrmap(map);
10656 : }
10657 : }
10658 : }
10659 2948 : }
10660 :
10661 : /*
10662 : * addFkRecurseReferencing
10663 : * Recursive helper for the referencing side of foreign key creation,
10664 : * which creates the check triggers and recurses
10665 : *
10666 : * If the referencing relation is a plain relation, create the necessary check
10667 : * triggers that implement the constraint, and set up for Phase 3 constraint
10668 : * verification. If the referencing relation is a partitioned table, then
10669 : * we create a pg_constraint row for it and recurse on this routine for each
10670 : * partition.
10671 : *
10672 : * We assume that the referenced relation is locked against concurrent
10673 : * deletions. If it's a partitioned relation, every partition must be so
10674 : * locked.
10675 : *
10676 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10677 : * of an ALTER TABLE sequence.
10678 : * fkconstraint: the constraint being added
10679 : * rel: the referencing relation; might be a partition, if recursing
10680 : * pkrel: the root referenced relation
10681 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10682 : * parentConstr: the OID of the parent constraint (there is always one)
10683 : * numfks: the number of columns in the foreign key
10684 : * pkattnum: the attnum array of referenced attributes
10685 : * fkattnum: the attnum array of referencing attributes
10686 : * pf/pp/ffeqoperators: OID array of operators between columns
10687 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10688 : * (...) clause
10689 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10690 : * NULL/DEFAULT clause
10691 : * old_check_ok: true if this constraint replaces an existing one that
10692 : * was already validated (thus this one doesn't need validation)
10693 : * lockmode: the lockmode to acquire on partitions when recursing
10694 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10695 : * a partition, the OIDs of the parent check triggers for INSERT and
10696 : * UPDATE respectively.
10697 : * with_period: true if this is a temporal FK
10698 : */
10699 : static void
10700 2788 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10701 : Relation pkrel, Oid indexOid, Oid parentConstr,
10702 : int numfks, int16 *pkattnum, int16 *fkattnum,
10703 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10704 : int numfkdelsetcols, int16 *fkdelsetcols,
10705 : bool old_check_ok, LOCKMODE lockmode,
10706 : Oid parentInsTrigger, Oid parentUpdTrigger,
10707 : bool with_period)
10708 : {
10709 : Oid insertTriggerOid,
10710 : updateTriggerOid;
10711 :
10712 : Assert(OidIsValid(parentConstr));
10713 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10714 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10715 :
10716 2788 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10717 0 : ereport(ERROR,
10718 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10719 : errmsg("foreign key constraints are not supported on foreign tables")));
10720 :
10721 : /*
10722 : * Add the check triggers to it and, if necessary, schedule it to be
10723 : * checked in Phase 3.
10724 : *
10725 : * If the relation is partitioned, drill down to do it to its partitions.
10726 : */
10727 2788 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10728 : RelationGetRelid(pkrel),
10729 : fkconstraint,
10730 : parentConstr,
10731 : indexOid,
10732 : parentInsTrigger, parentUpdTrigger,
10733 : &insertTriggerOid, &updateTriggerOid);
10734 :
10735 2788 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10736 : {
10737 : /*
10738 : * Tell Phase 3 to check that the constraint is satisfied by existing
10739 : * rows. We can skip this during table creation, when requested
10740 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10741 : * and when we're recreating a constraint following a SET DATA TYPE
10742 : * operation that did not impugn its validity.
10743 : */
10744 2340 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10745 : {
10746 : NewConstraint *newcon;
10747 : AlteredTableInfo *tab;
10748 :
10749 816 : tab = ATGetQueueEntry(wqueue, rel);
10750 :
10751 816 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10752 816 : newcon->name = get_constraint_name(parentConstr);
10753 816 : newcon->contype = CONSTR_FOREIGN;
10754 816 : newcon->refrelid = RelationGetRelid(pkrel);
10755 816 : newcon->refindid = indexOid;
10756 816 : newcon->conid = parentConstr;
10757 816 : newcon->conwithperiod = fkconstraint->fk_with_period;
10758 816 : newcon->qual = (Node *) fkconstraint;
10759 :
10760 816 : tab->constraints = lappend(tab->constraints, newcon);
10761 : }
10762 : }
10763 448 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10764 : {
10765 448 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10766 : Relation trigrel;
10767 :
10768 : /*
10769 : * Triggers of the foreign keys will be manipulated a bunch of times
10770 : * in the loop below. To avoid repeatedly opening/closing the trigger
10771 : * catalog relation, we open it here and pass it to the subroutines
10772 : * called below.
10773 : */
10774 448 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10775 :
10776 : /*
10777 : * Recurse to take appropriate action on each partition; either we
10778 : * find an existing constraint to reparent to ours, or we create a new
10779 : * one.
10780 : */
10781 842 : for (int i = 0; i < pd->nparts; i++)
10782 : {
10783 400 : Oid partitionId = pd->oids[i];
10784 400 : Relation partition = table_open(partitionId, lockmode);
10785 : List *partFKs;
10786 : AttrMap *attmap;
10787 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10788 : bool attached;
10789 : ObjectAddress address;
10790 : ListCell *cell;
10791 :
10792 400 : CheckAlterTableIsSafe(partition);
10793 :
10794 394 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10795 : RelationGetDescr(rel),
10796 : false);
10797 1022 : for (int j = 0; j < numfks; j++)
10798 628 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10799 :
10800 : /* Check whether an existing constraint can be repurposed */
10801 394 : partFKs = copyObject(RelationGetFKeyList(partition));
10802 394 : attached = false;
10803 412 : foreach(cell, partFKs)
10804 : {
10805 : ForeignKeyCacheInfo *fk;
10806 :
10807 30 : fk = lfirst_node(ForeignKeyCacheInfo, cell);
10808 30 : if (tryAttachPartitionForeignKey(fk,
10809 : partitionId,
10810 : parentConstr,
10811 : numfks,
10812 : mapped_fkattnum,
10813 : pkattnum,
10814 : pfeqoperators,
10815 : insertTriggerOid,
10816 : updateTriggerOid,
10817 : trigrel))
10818 : {
10819 12 : attached = true;
10820 12 : break;
10821 : }
10822 : }
10823 394 : if (attached)
10824 : {
10825 12 : table_close(partition, NoLock);
10826 12 : continue;
10827 : }
10828 :
10829 : /*
10830 : * No luck finding a good constraint to reuse; create our own.
10831 : */
10832 382 : address = addFkConstraint(addFkReferencingSide,
10833 : fkconstraint->conname, fkconstraint,
10834 : partition, pkrel, indexOid, parentConstr,
10835 : numfks, pkattnum,
10836 : mapped_fkattnum, pfeqoperators,
10837 : ppeqoperators, ffeqoperators,
10838 : numfkdelsetcols, fkdelsetcols, true,
10839 : with_period);
10840 :
10841 : /* call ourselves to finalize the creation and we're done */
10842 382 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10843 : indexOid,
10844 : address.objectId,
10845 : numfks,
10846 : pkattnum,
10847 : mapped_fkattnum,
10848 : pfeqoperators,
10849 : ppeqoperators,
10850 : ffeqoperators,
10851 : numfkdelsetcols,
10852 : fkdelsetcols,
10853 : old_check_ok,
10854 : lockmode,
10855 : insertTriggerOid,
10856 : updateTriggerOid,
10857 : with_period);
10858 :
10859 382 : table_close(partition, NoLock);
10860 : }
10861 :
10862 442 : table_close(trigrel, RowExclusiveLock);
10863 : }
10864 2782 : }
10865 :
10866 : /*
10867 : * CloneForeignKeyConstraints
10868 : * Clone foreign keys from a partitioned table to a newly acquired
10869 : * partition.
10870 : *
10871 : * partitionRel is a partition of parentRel, so we can be certain that it has
10872 : * the same columns with the same datatypes. The columns may be in different
10873 : * order, though.
10874 : *
10875 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10876 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10877 : * PARTITION OF).
10878 : */
10879 : static void
10880 9286 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10881 : Relation partitionRel)
10882 : {
10883 : /* This only works for declarative partitioning */
10884 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10885 :
10886 : /*
10887 : * Clone constraints for which the parent is on the referenced side.
10888 : */
10889 9286 : CloneFkReferenced(parentRel, partitionRel);
10890 :
10891 : /*
10892 : * Now clone constraints where the parent is on the referencing side.
10893 : */
10894 9286 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10895 9274 : }
10896 :
10897 : /*
10898 : * CloneFkReferenced
10899 : * Subroutine for CloneForeignKeyConstraints
10900 : *
10901 : * Find all the FKs that have the parent relation on the referenced side;
10902 : * clone those constraints to the given partition. This is to be called
10903 : * when the partition is being created or attached.
10904 : *
10905 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10906 : *
10907 : * This recurses to partitions, if the relation being attached is partitioned.
10908 : * Recursion is done by calling addFkRecurseReferenced.
10909 : */
10910 : static void
10911 9286 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10912 : {
10913 : Relation pg_constraint;
10914 : AttrMap *attmap;
10915 : ListCell *cell;
10916 : SysScanDesc scan;
10917 : ScanKeyData key[2];
10918 : HeapTuple tuple;
10919 9286 : List *clone = NIL;
10920 : Relation trigrel;
10921 :
10922 : /*
10923 : * Search for any constraints where this partition's parent is in the
10924 : * referenced side. However, we must not clone any constraint whose
10925 : * parent constraint is also going to be cloned, to avoid duplicates. So
10926 : * do it in two steps: first construct the list of constraints to clone,
10927 : * then go over that list cloning those whose parents are not in the list.
10928 : * (We must not rely on the parent being seen first, since the catalog
10929 : * scan could return children first.)
10930 : */
10931 9286 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10932 9286 : ScanKeyInit(&key[0],
10933 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10934 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10935 9286 : ScanKeyInit(&key[1],
10936 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
10937 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10938 : /* This is a seqscan, as we don't have a usable index ... */
10939 9286 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
10940 : NULL, 2, key);
10941 9592 : while ((tuple = systable_getnext(scan)) != NULL)
10942 : {
10943 306 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10944 :
10945 306 : clone = lappend_oid(clone, constrForm->oid);
10946 : }
10947 9286 : systable_endscan(scan);
10948 9286 : table_close(pg_constraint, RowShareLock);
10949 :
10950 : /*
10951 : * Triggers of the foreign keys will be manipulated a bunch of times in
10952 : * the loop below. To avoid repeatedly opening/closing the trigger
10953 : * catalog relation, we open it here and pass it to the subroutines called
10954 : * below.
10955 : */
10956 9286 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10957 :
10958 9286 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10959 : RelationGetDescr(parentRel),
10960 : false);
10961 9592 : foreach(cell, clone)
10962 : {
10963 306 : Oid constrOid = lfirst_oid(cell);
10964 : Form_pg_constraint constrForm;
10965 : Relation fkRel;
10966 : Oid indexOid;
10967 : Oid partIndexId;
10968 : int numfks;
10969 : AttrNumber conkey[INDEX_MAX_KEYS];
10970 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10971 : AttrNumber confkey[INDEX_MAX_KEYS];
10972 : Oid conpfeqop[INDEX_MAX_KEYS];
10973 : Oid conppeqop[INDEX_MAX_KEYS];
10974 : Oid conffeqop[INDEX_MAX_KEYS];
10975 : int numfkdelsetcols;
10976 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10977 : Constraint *fkconstraint;
10978 : ObjectAddress address;
10979 : Oid deleteTriggerOid,
10980 : updateTriggerOid;
10981 :
10982 306 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10983 306 : if (!HeapTupleIsValid(tuple))
10984 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10985 306 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10986 :
10987 : /*
10988 : * As explained above: don't try to clone a constraint for which we're
10989 : * going to clone the parent.
10990 : */
10991 306 : if (list_member_oid(clone, constrForm->conparentid))
10992 : {
10993 126 : ReleaseSysCache(tuple);
10994 180 : continue;
10995 : }
10996 :
10997 : /*
10998 : * Don't clone self-referencing foreign keys, which can be in the
10999 : * partitioned table or in the partition-to-be.
11000 : */
11001 180 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11002 138 : constrForm->conrelid == RelationGetRelid(partitionRel))
11003 : {
11004 54 : ReleaseSysCache(tuple);
11005 54 : continue;
11006 : }
11007 :
11008 : /* We need the same lock level that CreateTrigger will acquire */
11009 126 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11010 :
11011 126 : indexOid = constrForm->conindid;
11012 126 : DeconstructFkConstraintRow(tuple,
11013 : &numfks,
11014 : conkey,
11015 : confkey,
11016 : conpfeqop,
11017 : conppeqop,
11018 : conffeqop,
11019 : &numfkdelsetcols,
11020 : confdelsetcols);
11021 :
11022 258 : for (int i = 0; i < numfks; i++)
11023 132 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11024 :
11025 126 : fkconstraint = makeNode(Constraint);
11026 126 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11027 126 : fkconstraint->conname = NameStr(constrForm->conname);
11028 126 : fkconstraint->deferrable = constrForm->condeferrable;
11029 126 : fkconstraint->initdeferred = constrForm->condeferred;
11030 126 : fkconstraint->location = -1;
11031 126 : fkconstraint->pktable = NULL;
11032 : /* ->fk_attrs determined below */
11033 126 : fkconstraint->pk_attrs = NIL;
11034 126 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11035 126 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11036 126 : fkconstraint->fk_del_action = constrForm->confdeltype;
11037 126 : fkconstraint->fk_del_set_cols = NIL;
11038 126 : fkconstraint->old_conpfeqop = NIL;
11039 126 : fkconstraint->old_pktable_oid = InvalidOid;
11040 126 : fkconstraint->skip_validation = false;
11041 126 : fkconstraint->initially_valid = true;
11042 :
11043 : /* set up colnames that are used to generate the constraint name */
11044 258 : for (int i = 0; i < numfks; i++)
11045 : {
11046 : Form_pg_attribute att;
11047 :
11048 132 : att = TupleDescAttr(RelationGetDescr(fkRel),
11049 132 : conkey[i] - 1);
11050 132 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11051 132 : makeString(NameStr(att->attname)));
11052 : }
11053 :
11054 : /*
11055 : * Add the new foreign key constraint pointing to the new partition.
11056 : * Because this new partition appears in the referenced side of the
11057 : * constraint, we don't need to set up for Phase 3 check.
11058 : */
11059 126 : partIndexId = index_get_partition(partitionRel, indexOid);
11060 126 : if (!OidIsValid(partIndexId))
11061 0 : elog(ERROR, "index for %u not found in partition %s",
11062 : indexOid, RelationGetRelationName(partitionRel));
11063 :
11064 : /*
11065 : * Get the "action" triggers belonging to the constraint to pass as
11066 : * parent OIDs for similar triggers that will be created on the
11067 : * partition in addFkRecurseReferenced().
11068 : */
11069 126 : GetForeignKeyActionTriggers(trigrel, constrOid,
11070 : constrForm->confrelid, constrForm->conrelid,
11071 : &deleteTriggerOid, &updateTriggerOid);
11072 :
11073 : /* Add this constraint ... */
11074 126 : address = addFkConstraint(addFkReferencedSide,
11075 : fkconstraint->conname, fkconstraint, fkRel,
11076 : partitionRel, partIndexId, constrOid,
11077 : numfks, mapped_confkey,
11078 : conkey, conpfeqop, conppeqop, conffeqop,
11079 : numfkdelsetcols, confdelsetcols, false,
11080 126 : constrForm->conperiod);
11081 : /* ... and recurse */
11082 126 : addFkRecurseReferenced(fkconstraint,
11083 : fkRel,
11084 : partitionRel,
11085 : partIndexId,
11086 : address.objectId,
11087 : numfks,
11088 : mapped_confkey,
11089 : conkey,
11090 : conpfeqop,
11091 : conppeqop,
11092 : conffeqop,
11093 : numfkdelsetcols,
11094 : confdelsetcols,
11095 : true,
11096 : deleteTriggerOid,
11097 : updateTriggerOid,
11098 126 : constrForm->conperiod);
11099 :
11100 126 : table_close(fkRel, NoLock);
11101 126 : ReleaseSysCache(tuple);
11102 : }
11103 :
11104 9286 : table_close(trigrel, RowExclusiveLock);
11105 9286 : }
11106 :
11107 : /*
11108 : * CloneFkReferencing
11109 : * Subroutine for CloneForeignKeyConstraints
11110 : *
11111 : * For each FK constraint of the parent relation in the given list, find an
11112 : * equivalent constraint in its partition relation that can be reparented;
11113 : * if one cannot be found, create a new constraint in the partition as its
11114 : * child.
11115 : *
11116 : * If wqueue is given, it is used to set up phase-3 verification for each
11117 : * cloned constraint; omit it if such verification is not needed
11118 : * (example: the partition is being created anew).
11119 : */
11120 : static void
11121 9286 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11122 : {
11123 : AttrMap *attmap;
11124 : List *partFKs;
11125 9286 : List *clone = NIL;
11126 : ListCell *cell;
11127 : Relation trigrel;
11128 :
11129 : /* obtain a list of constraints that we need to clone */
11130 10356 : foreach(cell, RelationGetFKeyList(parentRel))
11131 : {
11132 1076 : ForeignKeyCacheInfo *fk = lfirst(cell);
11133 :
11134 : /*
11135 : * Refuse to attach a table as partition that this partitioned table
11136 : * already has a foreign key to. This isn't useful schema, which is
11137 : * proven by the fact that there have been no user complaints that
11138 : * it's already impossible to achieve this in the opposite direction,
11139 : * i.e., creating a foreign key that references a partition. This
11140 : * restriction allows us to dodge some complexities around
11141 : * pg_constraint and pg_trigger row creations that would be needed
11142 : * during ATTACH/DETACH for this kind of relationship.
11143 : */
11144 1076 : if (fk->confrelid == RelationGetRelid(partRel))
11145 6 : ereport(ERROR,
11146 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11147 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11148 : RelationGetRelationName(partRel),
11149 : get_constraint_name(fk->conoid))));
11150 :
11151 1070 : clone = lappend_oid(clone, fk->conoid);
11152 : }
11153 :
11154 : /*
11155 : * Silently do nothing if there's nothing to do. In particular, this
11156 : * avoids throwing a spurious error for foreign tables.
11157 : */
11158 9280 : if (clone == NIL)
11159 8826 : return;
11160 :
11161 454 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11162 0 : ereport(ERROR,
11163 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11164 : errmsg("foreign key constraints are not supported on foreign tables")));
11165 :
11166 : /*
11167 : * Triggers of the foreign keys will be manipulated a bunch of times in
11168 : * the loop below. To avoid repeatedly opening/closing the trigger
11169 : * catalog relation, we open it here and pass it to the subroutines called
11170 : * below.
11171 : */
11172 454 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11173 :
11174 : /*
11175 : * The constraint key may differ, if the columns in the partition are
11176 : * different. This map is used to convert them.
11177 : */
11178 454 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11179 : RelationGetDescr(parentRel),
11180 : false);
11181 :
11182 454 : partFKs = copyObject(RelationGetFKeyList(partRel));
11183 :
11184 1518 : foreach(cell, clone)
11185 : {
11186 1070 : Oid parentConstrOid = lfirst_oid(cell);
11187 : Form_pg_constraint constrForm;
11188 : Relation pkrel;
11189 : HeapTuple tuple;
11190 : int numfks;
11191 : AttrNumber conkey[INDEX_MAX_KEYS];
11192 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11193 : AttrNumber confkey[INDEX_MAX_KEYS];
11194 : Oid conpfeqop[INDEX_MAX_KEYS];
11195 : Oid conppeqop[INDEX_MAX_KEYS];
11196 : Oid conffeqop[INDEX_MAX_KEYS];
11197 : int numfkdelsetcols;
11198 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11199 : Constraint *fkconstraint;
11200 : bool attached;
11201 : Oid indexOid;
11202 : ObjectAddress address;
11203 : ListCell *lc;
11204 : Oid insertTriggerOid,
11205 : updateTriggerOid;
11206 : bool with_period;
11207 :
11208 1070 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11209 1070 : if (!HeapTupleIsValid(tuple))
11210 0 : elog(ERROR, "cache lookup failed for constraint %u",
11211 : parentConstrOid);
11212 1070 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11213 :
11214 : /* Don't clone constraints whose parents are being cloned */
11215 1070 : if (list_member_oid(clone, constrForm->conparentid))
11216 : {
11217 592 : ReleaseSysCache(tuple);
11218 670 : continue;
11219 : }
11220 :
11221 : /*
11222 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11223 : * relation, that means to lock all partitions.
11224 : */
11225 478 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11226 478 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11227 220 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11228 : ShareRowExclusiveLock, NULL);
11229 :
11230 478 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11231 : conpfeqop, conppeqop, conffeqop,
11232 : &numfkdelsetcols, confdelsetcols);
11233 1130 : for (int i = 0; i < numfks; i++)
11234 652 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11235 :
11236 : /*
11237 : * Get the "check" triggers belonging to the constraint to pass as
11238 : * parent OIDs for similar triggers that will be created on the
11239 : * partition in addFkRecurseReferencing(). They are also passed to
11240 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11241 : * the partition's existing "check" triggers, that is, if the
11242 : * corresponding constraints is deemed attachable to the parent
11243 : * constraint.
11244 : */
11245 478 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11246 : constrForm->confrelid, constrForm->conrelid,
11247 : &insertTriggerOid, &updateTriggerOid);
11248 :
11249 : /*
11250 : * Before creating a new constraint, see whether any existing FKs are
11251 : * fit for the purpose. If one is, attach the parent constraint to
11252 : * it, and don't clone anything. This way we avoid the expensive
11253 : * verification step and don't end up with a duplicate FK, and we
11254 : * don't need to recurse to partitions for this constraint.
11255 : */
11256 478 : attached = false;
11257 682 : foreach(lc, partFKs)
11258 : {
11259 282 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11260 :
11261 282 : if (tryAttachPartitionForeignKey(fk,
11262 : RelationGetRelid(partRel),
11263 : parentConstrOid,
11264 : numfks,
11265 : mapped_conkey,
11266 : confkey,
11267 : conpfeqop,
11268 : insertTriggerOid,
11269 : updateTriggerOid,
11270 : trigrel))
11271 : {
11272 78 : attached = true;
11273 78 : table_close(pkrel, NoLock);
11274 78 : break;
11275 : }
11276 : }
11277 478 : if (attached)
11278 : {
11279 78 : ReleaseSysCache(tuple);
11280 78 : continue;
11281 : }
11282 :
11283 : /* No dice. Set up to create our own constraint */
11284 400 : fkconstraint = makeNode(Constraint);
11285 400 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11286 : /* ->conname determined below */
11287 400 : fkconstraint->deferrable = constrForm->condeferrable;
11288 400 : fkconstraint->initdeferred = constrForm->condeferred;
11289 400 : fkconstraint->location = -1;
11290 400 : fkconstraint->pktable = NULL;
11291 : /* ->fk_attrs determined below */
11292 400 : fkconstraint->pk_attrs = NIL;
11293 400 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11294 400 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11295 400 : fkconstraint->fk_del_action = constrForm->confdeltype;
11296 400 : fkconstraint->fk_del_set_cols = NIL;
11297 400 : fkconstraint->old_conpfeqop = NIL;
11298 400 : fkconstraint->old_pktable_oid = InvalidOid;
11299 400 : fkconstraint->skip_validation = false;
11300 400 : fkconstraint->initially_valid = constrForm->convalidated;
11301 914 : for (int i = 0; i < numfks; i++)
11302 : {
11303 : Form_pg_attribute att;
11304 :
11305 514 : att = TupleDescAttr(RelationGetDescr(partRel),
11306 514 : mapped_conkey[i] - 1);
11307 514 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11308 514 : makeString(NameStr(att->attname)));
11309 : }
11310 :
11311 400 : indexOid = constrForm->conindid;
11312 400 : with_period = constrForm->conperiod;
11313 :
11314 : /* Create the pg_constraint entry at this level */
11315 400 : address = addFkConstraint(addFkReferencingSide,
11316 400 : NameStr(constrForm->conname), fkconstraint,
11317 : partRel, pkrel, indexOid, parentConstrOid,
11318 : numfks, confkey,
11319 : mapped_conkey, conpfeqop,
11320 : conppeqop, conffeqop,
11321 : numfkdelsetcols, confdelsetcols,
11322 : false, with_period);
11323 :
11324 : /* Done with the cloned constraint's tuple */
11325 400 : ReleaseSysCache(tuple);
11326 :
11327 : /* Create the check triggers, and recurse to partitions, if any */
11328 400 : addFkRecurseReferencing(wqueue,
11329 : fkconstraint,
11330 : partRel,
11331 : pkrel,
11332 : indexOid,
11333 : address.objectId,
11334 : numfks,
11335 : confkey,
11336 : mapped_conkey,
11337 : conpfeqop,
11338 : conppeqop,
11339 : conffeqop,
11340 : numfkdelsetcols,
11341 : confdelsetcols,
11342 : false, /* no old check exists */
11343 : AccessExclusiveLock,
11344 : insertTriggerOid,
11345 : updateTriggerOid,
11346 : with_period);
11347 394 : table_close(pkrel, NoLock);
11348 : }
11349 :
11350 448 : table_close(trigrel, RowExclusiveLock);
11351 : }
11352 :
11353 : /*
11354 : * When the parent of a partition receives [the referencing side of] a foreign
11355 : * key, we must propagate that foreign key to the partition. However, the
11356 : * partition might already have an equivalent foreign key; this routine
11357 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11358 : * by the other parameters. If they are equivalent, create the link between
11359 : * the two constraints and return true.
11360 : *
11361 : * If the given FK does not match the one defined by rest of the params,
11362 : * return false.
11363 : */
11364 : static bool
11365 312 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11366 : Oid partRelid,
11367 : Oid parentConstrOid,
11368 : int numfks,
11369 : AttrNumber *mapped_conkey,
11370 : AttrNumber *confkey,
11371 : Oid *conpfeqop,
11372 : Oid parentInsTrigger,
11373 : Oid parentUpdTrigger,
11374 : Relation trigrel)
11375 : {
11376 : HeapTuple parentConstrTup;
11377 : Form_pg_constraint parentConstr;
11378 : HeapTuple partcontup;
11379 : Form_pg_constraint partConstr;
11380 : ScanKeyData key;
11381 : SysScanDesc scan;
11382 : HeapTuple trigtup;
11383 : Oid insertTriggerOid,
11384 : updateTriggerOid;
11385 :
11386 312 : parentConstrTup = SearchSysCache1(CONSTROID,
11387 : ObjectIdGetDatum(parentConstrOid));
11388 312 : if (!HeapTupleIsValid(parentConstrTup))
11389 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11390 312 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11391 :
11392 : /*
11393 : * Do some quick & easy initial checks. If any of these fail, we cannot
11394 : * use this constraint.
11395 : */
11396 312 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11397 : {
11398 120 : ReleaseSysCache(parentConstrTup);
11399 120 : return false;
11400 : }
11401 546 : for (int i = 0; i < numfks; i++)
11402 : {
11403 354 : if (fk->conkey[i] != mapped_conkey[i] ||
11404 354 : fk->confkey[i] != confkey[i] ||
11405 354 : fk->conpfeqop[i] != conpfeqop[i])
11406 : {
11407 0 : ReleaseSysCache(parentConstrTup);
11408 0 : return false;
11409 : }
11410 : }
11411 :
11412 : /*
11413 : * Looks good so far; do some more extensive checks. Presumably the check
11414 : * for 'convalidated' could be dropped, since we don't really care about
11415 : * that, but let's be careful for now.
11416 : */
11417 192 : partcontup = SearchSysCache1(CONSTROID,
11418 : ObjectIdGetDatum(fk->conoid));
11419 192 : if (!HeapTupleIsValid(partcontup))
11420 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11421 192 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11422 192 : if (OidIsValid(partConstr->conparentid) ||
11423 168 : !partConstr->convalidated ||
11424 168 : partConstr->condeferrable != parentConstr->condeferrable ||
11425 140 : partConstr->condeferred != parentConstr->condeferred ||
11426 140 : partConstr->confupdtype != parentConstr->confupdtype ||
11427 104 : partConstr->confdeltype != parentConstr->confdeltype ||
11428 104 : partConstr->confmatchtype != parentConstr->confmatchtype)
11429 : {
11430 102 : ReleaseSysCache(parentConstrTup);
11431 102 : ReleaseSysCache(partcontup);
11432 102 : return false;
11433 : }
11434 :
11435 90 : ReleaseSysCache(partcontup);
11436 90 : ReleaseSysCache(parentConstrTup);
11437 :
11438 : /*
11439 : * Looks good! Attach this constraint. The action triggers in the new
11440 : * partition become redundant -- the parent table already has equivalent
11441 : * ones, and those will be able to reach the partition. Remove the ones
11442 : * in the partition. We identify them because they have our constraint
11443 : * OID, as well as being on the referenced rel.
11444 : */
11445 90 : ScanKeyInit(&key,
11446 : Anum_pg_trigger_tgconstraint,
11447 : BTEqualStrategyNumber, F_OIDEQ,
11448 : ObjectIdGetDatum(fk->conoid));
11449 90 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11450 : NULL, 1, &key);
11451 450 : while ((trigtup = systable_getnext(scan)) != NULL)
11452 : {
11453 360 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11454 : ObjectAddress trigger;
11455 :
11456 360 : if (trgform->tgconstrrelid != fk->conrelid)
11457 180 : continue;
11458 180 : if (trgform->tgrelid != fk->confrelid)
11459 0 : continue;
11460 :
11461 : /*
11462 : * The constraint is originally set up to contain this trigger as an
11463 : * implementation object, so there's a dependency record that links
11464 : * the two; however, since the trigger is no longer needed, we remove
11465 : * the dependency link in order to be able to drop the trigger while
11466 : * keeping the constraint intact.
11467 : */
11468 180 : deleteDependencyRecordsFor(TriggerRelationId,
11469 : trgform->oid,
11470 : false);
11471 : /* make dependency deletion visible to performDeletion */
11472 180 : CommandCounterIncrement();
11473 180 : ObjectAddressSet(trigger, TriggerRelationId,
11474 : trgform->oid);
11475 180 : performDeletion(&trigger, DROP_RESTRICT, 0);
11476 : /* make trigger drop visible, in case the loop iterates */
11477 180 : CommandCounterIncrement();
11478 : }
11479 :
11480 90 : systable_endscan(scan);
11481 :
11482 90 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11483 :
11484 : /*
11485 : * Like the constraint, attach partition's "check" triggers to the
11486 : * corresponding parent triggers.
11487 : */
11488 90 : GetForeignKeyCheckTriggers(trigrel,
11489 : fk->conoid, fk->confrelid, fk->conrelid,
11490 : &insertTriggerOid, &updateTriggerOid);
11491 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11492 90 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11493 : partRelid);
11494 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11495 90 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11496 : partRelid);
11497 :
11498 : /*
11499 : * If the referenced table is partitioned, then the partition we're
11500 : * attaching now has extra pg_constraint rows and action triggers that are
11501 : * no longer needed. Remove those.
11502 : */
11503 90 : if (get_rel_relkind(fk->confrelid) == RELKIND_PARTITIONED_TABLE)
11504 : {
11505 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11506 : ObjectAddresses *objs;
11507 : HeapTuple consttup;
11508 :
11509 24 : ScanKeyInit(&key,
11510 : Anum_pg_constraint_conrelid,
11511 : BTEqualStrategyNumber, F_OIDEQ,
11512 : ObjectIdGetDatum(fk->conrelid));
11513 :
11514 24 : scan = systable_beginscan(pg_constraint,
11515 : ConstraintRelidTypidNameIndexId,
11516 : true, NULL, 1, &key);
11517 24 : objs = new_object_addresses();
11518 240 : while ((consttup = systable_getnext(scan)) != NULL)
11519 : {
11520 216 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11521 :
11522 216 : if (conform->conparentid != fk->conoid)
11523 168 : continue;
11524 : else
11525 : {
11526 : ObjectAddress addr;
11527 : SysScanDesc scan2;
11528 : ScanKeyData key2;
11529 : int n PG_USED_FOR_ASSERTS_ONLY;
11530 :
11531 48 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11532 48 : add_exact_object_address(&addr, objs);
11533 :
11534 : /*
11535 : * First we must delete the dependency record that binds the
11536 : * constraint records together.
11537 : */
11538 48 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11539 : conform->oid,
11540 : DEPENDENCY_INTERNAL,
11541 : ConstraintRelationId,
11542 : fk->conoid);
11543 : Assert(n == 1); /* actually only one is expected */
11544 :
11545 : /*
11546 : * Now search for the triggers for this constraint and set
11547 : * them up for deletion too
11548 : */
11549 48 : ScanKeyInit(&key2,
11550 : Anum_pg_trigger_tgconstraint,
11551 : BTEqualStrategyNumber, F_OIDEQ,
11552 : ObjectIdGetDatum(conform->oid));
11553 48 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11554 : true, NULL, 1, &key2);
11555 144 : while ((trigtup = systable_getnext(scan2)) != NULL)
11556 : {
11557 96 : ObjectAddressSet(addr, TriggerRelationId,
11558 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11559 96 : add_exact_object_address(&addr, objs);
11560 : }
11561 48 : systable_endscan(scan2);
11562 : }
11563 : }
11564 : /* make the dependency deletions visible */
11565 24 : CommandCounterIncrement();
11566 24 : performMultipleDeletions(objs, DROP_RESTRICT,
11567 : PERFORM_DELETION_INTERNAL);
11568 24 : systable_endscan(scan);
11569 :
11570 24 : table_close(pg_constraint, RowShareLock);
11571 : }
11572 :
11573 90 : CommandCounterIncrement();
11574 90 : return true;
11575 : }
11576 :
11577 : /*
11578 : * GetForeignKeyActionTriggers
11579 : * Returns delete and update "action" triggers of the given relation
11580 : * belonging to the given constraint
11581 : */
11582 : static void
11583 126 : GetForeignKeyActionTriggers(Relation trigrel,
11584 : Oid conoid, Oid confrelid, Oid conrelid,
11585 : Oid *deleteTriggerOid,
11586 : Oid *updateTriggerOid)
11587 : {
11588 : ScanKeyData key;
11589 : SysScanDesc scan;
11590 : HeapTuple trigtup;
11591 :
11592 126 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11593 126 : ScanKeyInit(&key,
11594 : Anum_pg_trigger_tgconstraint,
11595 : BTEqualStrategyNumber, F_OIDEQ,
11596 : ObjectIdGetDatum(conoid));
11597 :
11598 126 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11599 : NULL, 1, &key);
11600 264 : while ((trigtup = systable_getnext(scan)) != NULL)
11601 : {
11602 264 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11603 :
11604 264 : if (trgform->tgconstrrelid != conrelid)
11605 12 : continue;
11606 252 : if (trgform->tgrelid != confrelid)
11607 0 : continue;
11608 : /* Only ever look at "action" triggers on the PK side. */
11609 252 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11610 0 : continue;
11611 252 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11612 : {
11613 : Assert(*deleteTriggerOid == InvalidOid);
11614 126 : *deleteTriggerOid = trgform->oid;
11615 : }
11616 126 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11617 : {
11618 : Assert(*updateTriggerOid == InvalidOid);
11619 126 : *updateTriggerOid = trgform->oid;
11620 : }
11621 : #ifndef USE_ASSERT_CHECKING
11622 : /* In an assert-enabled build, continue looking to find duplicates */
11623 252 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11624 126 : break;
11625 : #endif
11626 : }
11627 :
11628 126 : if (!OidIsValid(*deleteTriggerOid))
11629 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11630 : conoid);
11631 126 : if (!OidIsValid(*updateTriggerOid))
11632 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11633 : conoid);
11634 :
11635 126 : systable_endscan(scan);
11636 126 : }
11637 :
11638 : /*
11639 : * GetForeignKeyCheckTriggers
11640 : * Returns insert and update "check" triggers of the given relation
11641 : * belonging to the given constraint
11642 : */
11643 : static void
11644 640 : GetForeignKeyCheckTriggers(Relation trigrel,
11645 : Oid conoid, Oid confrelid, Oid conrelid,
11646 : Oid *insertTriggerOid,
11647 : Oid *updateTriggerOid)
11648 : {
11649 : ScanKeyData key;
11650 : SysScanDesc scan;
11651 : HeapTuple trigtup;
11652 :
11653 640 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11654 640 : ScanKeyInit(&key,
11655 : Anum_pg_trigger_tgconstraint,
11656 : BTEqualStrategyNumber, F_OIDEQ,
11657 : ObjectIdGetDatum(conoid));
11658 :
11659 640 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11660 : NULL, 1, &key);
11661 2100 : while ((trigtup = systable_getnext(scan)) != NULL)
11662 : {
11663 2100 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11664 :
11665 2100 : if (trgform->tgconstrrelid != confrelid)
11666 736 : continue;
11667 1364 : if (trgform->tgrelid != conrelid)
11668 0 : continue;
11669 : /* Only ever look at "check" triggers on the FK side. */
11670 1364 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11671 84 : continue;
11672 1280 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11673 : {
11674 : Assert(*insertTriggerOid == InvalidOid);
11675 640 : *insertTriggerOid = trgform->oid;
11676 : }
11677 640 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11678 : {
11679 : Assert(*updateTriggerOid == InvalidOid);
11680 640 : *updateTriggerOid = trgform->oid;
11681 : }
11682 : #ifndef USE_ASSERT_CHECKING
11683 : /* In an assert-enabled build, continue looking to find duplicates. */
11684 1280 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11685 640 : break;
11686 : #endif
11687 : }
11688 :
11689 640 : if (!OidIsValid(*insertTriggerOid))
11690 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11691 : conoid);
11692 640 : if (!OidIsValid(*updateTriggerOid))
11693 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11694 : conoid);
11695 :
11696 640 : systable_endscan(scan);
11697 640 : }
11698 :
11699 : /*
11700 : * ALTER TABLE ALTER CONSTRAINT
11701 : *
11702 : * Update the attributes of a constraint.
11703 : *
11704 : * Currently only works for Foreign Key constraints.
11705 : *
11706 : * If the constraint is modified, returns its address; otherwise, return
11707 : * InvalidObjectAddress.
11708 : */
11709 : static ObjectAddress
11710 126 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11711 : bool recursing, LOCKMODE lockmode)
11712 : {
11713 : Constraint *cmdcon;
11714 : Relation conrel;
11715 : Relation tgrel;
11716 : SysScanDesc scan;
11717 : ScanKeyData skey[3];
11718 : HeapTuple contuple;
11719 : Form_pg_constraint currcon;
11720 : ObjectAddress address;
11721 126 : List *otherrelids = NIL;
11722 : ListCell *lc;
11723 :
11724 126 : cmdcon = castNode(Constraint, cmd->def);
11725 :
11726 126 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11727 126 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11728 :
11729 : /*
11730 : * Find and check the target constraint
11731 : */
11732 126 : ScanKeyInit(&skey[0],
11733 : Anum_pg_constraint_conrelid,
11734 : BTEqualStrategyNumber, F_OIDEQ,
11735 : ObjectIdGetDatum(RelationGetRelid(rel)));
11736 126 : ScanKeyInit(&skey[1],
11737 : Anum_pg_constraint_contypid,
11738 : BTEqualStrategyNumber, F_OIDEQ,
11739 : ObjectIdGetDatum(InvalidOid));
11740 126 : ScanKeyInit(&skey[2],
11741 : Anum_pg_constraint_conname,
11742 : BTEqualStrategyNumber, F_NAMEEQ,
11743 126 : CStringGetDatum(cmdcon->conname));
11744 126 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11745 : true, NULL, 3, skey);
11746 :
11747 : /* There can be at most one matching row */
11748 126 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11749 0 : ereport(ERROR,
11750 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11751 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11752 : cmdcon->conname, RelationGetRelationName(rel))));
11753 :
11754 126 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11755 126 : if (currcon->contype != CONSTRAINT_FOREIGN)
11756 0 : ereport(ERROR,
11757 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11758 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11759 : cmdcon->conname, RelationGetRelationName(rel))));
11760 :
11761 : /*
11762 : * If it's not the topmost constraint, raise an error.
11763 : *
11764 : * Altering a non-topmost constraint leaves some triggers untouched, since
11765 : * they are not directly connected to this constraint; also, pg_dump would
11766 : * ignore the deferrability status of the individual constraint, since it
11767 : * only dumps topmost constraints. Avoid these problems by refusing this
11768 : * operation and telling the user to alter the parent constraint instead.
11769 : */
11770 126 : if (OidIsValid(currcon->conparentid))
11771 : {
11772 : HeapTuple tp;
11773 12 : Oid parent = currcon->conparentid;
11774 12 : char *ancestorname = NULL;
11775 12 : char *ancestortable = NULL;
11776 :
11777 : /* Loop to find the topmost constraint */
11778 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11779 : {
11780 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11781 :
11782 : /* If no parent, this is the constraint we want */
11783 24 : if (!OidIsValid(contup->conparentid))
11784 : {
11785 12 : ancestorname = pstrdup(NameStr(contup->conname));
11786 12 : ancestortable = get_rel_name(contup->conrelid);
11787 12 : ReleaseSysCache(tp);
11788 12 : break;
11789 : }
11790 :
11791 12 : parent = contup->conparentid;
11792 12 : ReleaseSysCache(tp);
11793 : }
11794 :
11795 12 : ereport(ERROR,
11796 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11797 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11798 : cmdcon->conname, RelationGetRelationName(rel)),
11799 : ancestorname && ancestortable ?
11800 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11801 : cmdcon->conname, ancestorname, ancestortable) : 0,
11802 : errhint("You may alter the constraint it derives from instead.")));
11803 : }
11804 :
11805 : /*
11806 : * Do the actual catalog work. We can skip changing if already in the
11807 : * desired state, but not if a partitioned table: partitions need to be
11808 : * processed regardless, in case they had the constraint locally changed.
11809 : */
11810 114 : address = InvalidObjectAddress;
11811 114 : if (currcon->condeferrable != cmdcon->deferrable ||
11812 6 : currcon->condeferred != cmdcon->initdeferred ||
11813 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11814 : {
11815 114 : if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11816 : &otherrelids, lockmode))
11817 114 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11818 : }
11819 :
11820 : /*
11821 : * ATExecAlterConstrRecurse already invalidated relcache for the relations
11822 : * having the constraint itself; here we also invalidate for relations
11823 : * that have any triggers that are part of the constraint.
11824 : */
11825 258 : foreach(lc, otherrelids)
11826 144 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11827 :
11828 114 : systable_endscan(scan);
11829 :
11830 114 : table_close(tgrel, RowExclusiveLock);
11831 114 : table_close(conrel, RowExclusiveLock);
11832 :
11833 114 : return address;
11834 : }
11835 :
11836 : /*
11837 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11838 : * constraint is altered.
11839 : *
11840 : * *otherrelids is appended OIDs of relations containing affected triggers.
11841 : *
11842 : * Note that we must recurse even when the values are correct, in case
11843 : * indirect descendants have had their constraints altered locally.
11844 : * (This could be avoided if we forbade altering constraints in partitions
11845 : * but existing releases don't do that.)
11846 : */
11847 : static bool
11848 180 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11849 : Relation rel, HeapTuple contuple, List **otherrelids,
11850 : LOCKMODE lockmode)
11851 : {
11852 : Form_pg_constraint currcon;
11853 : Oid conoid;
11854 : Oid refrelid;
11855 180 : bool changed = false;
11856 :
11857 : /* since this function recurses, it could be driven to stack overflow */
11858 180 : check_stack_depth();
11859 :
11860 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11861 180 : conoid = currcon->oid;
11862 180 : refrelid = currcon->confrelid;
11863 :
11864 : /*
11865 : * Update pg_constraint with the flags from cmdcon.
11866 : *
11867 : * If called to modify a constraint that's already in the desired state,
11868 : * silently do nothing.
11869 : */
11870 180 : if (currcon->condeferrable != cmdcon->deferrable ||
11871 6 : currcon->condeferred != cmdcon->initdeferred)
11872 : {
11873 : HeapTuple copyTuple;
11874 : Form_pg_constraint copy_con;
11875 :
11876 180 : copyTuple = heap_copytuple(contuple);
11877 180 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11878 180 : copy_con->condeferrable = cmdcon->deferrable;
11879 180 : copy_con->condeferred = cmdcon->initdeferred;
11880 180 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11881 :
11882 180 : InvokeObjectPostAlterHook(ConstraintRelationId,
11883 : conoid, 0);
11884 :
11885 180 : heap_freetuple(copyTuple);
11886 180 : changed = true;
11887 :
11888 : /* Make new constraint flags visible to others */
11889 180 : CacheInvalidateRelcache(rel);
11890 :
11891 : /*
11892 : * Now we need to update the multiple entries in pg_trigger that
11893 : * implement the constraint.
11894 : */
11895 180 : AlterConstrTriggerDeferrability(conoid, tgrel, rel, cmdcon->deferrable,
11896 180 : cmdcon->initdeferred, otherrelids);
11897 : }
11898 :
11899 : /*
11900 : * If the table at either end of the constraint is partitioned, we need to
11901 : * recurse and handle every constraint that is a child of this one.
11902 : *
11903 : * (This assumes that the recurse flag is forcibly set for partitioned
11904 : * tables, and not set for legacy inheritance, though we don't check for
11905 : * that here.)
11906 : */
11907 336 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11908 156 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11909 42 : ATExecAlterChildConstr(cmdcon, conrel, tgrel, rel, contuple,
11910 : otherrelids, lockmode);
11911 :
11912 180 : return changed;
11913 : }
11914 :
11915 : /*
11916 : * A subroutine of ATExecAlterConstrRecurse that updated constraint trigger's
11917 : * deferrability.
11918 : *
11919 : * The arguments to this function have the same meaning as the arguments to
11920 : * ATExecAlterConstrRecurse.
11921 : */
11922 : static void
11923 180 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
11924 : bool deferrable, bool initdeferred,
11925 : List **otherrelids)
11926 : {
11927 : HeapTuple tgtuple;
11928 : ScanKeyData tgkey;
11929 : SysScanDesc tgscan;
11930 :
11931 180 : ScanKeyInit(&tgkey,
11932 : Anum_pg_trigger_tgconstraint,
11933 : BTEqualStrategyNumber, F_OIDEQ,
11934 : ObjectIdGetDatum(conoid));
11935 180 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11936 : NULL, 1, &tgkey);
11937 768 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11938 : {
11939 588 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11940 : Form_pg_trigger copy_tg;
11941 : HeapTuple tgCopyTuple;
11942 :
11943 : /*
11944 : * Remember OIDs of other relation(s) involved in FK constraint.
11945 : * (Note: it's likely that we could skip forcing a relcache inval for
11946 : * other rels that don't have a trigger whose properties change, but
11947 : * let's be conservative.)
11948 : */
11949 588 : if (tgform->tgrelid != RelationGetRelid(rel))
11950 288 : *otherrelids = list_append_unique_oid(*otherrelids,
11951 : tgform->tgrelid);
11952 :
11953 : /*
11954 : * Update enable status and deferrability of RI_FKey_noaction_del,
11955 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11956 : * triggers, but not others; see createForeignKeyActionTriggers and
11957 : * CreateFKCheckTrigger.
11958 : */
11959 588 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11960 474 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11961 342 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11962 192 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11963 42 : continue;
11964 :
11965 546 : tgCopyTuple = heap_copytuple(tgtuple);
11966 546 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11967 :
11968 546 : copy_tg->tgdeferrable = deferrable;
11969 546 : copy_tg->tginitdeferred = initdeferred;
11970 546 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11971 :
11972 546 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11973 :
11974 546 : heap_freetuple(tgCopyTuple);
11975 : }
11976 :
11977 180 : systable_endscan(tgscan);
11978 180 : }
11979 :
11980 : /*
11981 : * Invokes ATExecAlterConstrRecurse for each constraint that is a child of the
11982 : * specified constraint.
11983 : *
11984 : * The arguments to this function have the same meaning as the arguments to
11985 : * ATExecAlterConstrRecurse.
11986 : */
11987 : static void
11988 42 : ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
11989 : Relation rel, HeapTuple contuple, List **otherrelids,
11990 : LOCKMODE lockmode)
11991 : {
11992 : Form_pg_constraint currcon;
11993 : Oid conoid;
11994 : ScanKeyData pkey;
11995 : SysScanDesc pscan;
11996 : HeapTuple childtup;
11997 :
11998 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11999 42 : conoid = currcon->oid;
12000 :
12001 42 : ScanKeyInit(&pkey,
12002 : Anum_pg_constraint_conparentid,
12003 : BTEqualStrategyNumber, F_OIDEQ,
12004 : ObjectIdGetDatum(conoid));
12005 :
12006 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12007 : true, NULL, 1, &pkey);
12008 :
12009 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12010 : {
12011 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12012 : Relation childrel;
12013 :
12014 66 : childrel = table_open(childcon->conrelid, lockmode);
12015 66 : ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
12016 : otherrelids, lockmode);
12017 66 : table_close(childrel, NoLock);
12018 : }
12019 :
12020 42 : systable_endscan(pscan);
12021 42 : }
12022 :
12023 : /*
12024 : * ALTER TABLE VALIDATE CONSTRAINT
12025 : *
12026 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12027 : * there's no good way to skip recursing when handling foreign keys: there is
12028 : * no need to lock children in that case, yet we wouldn't be able to avoid
12029 : * doing so at that level.
12030 : *
12031 : * Return value is the address of the validated constraint. If the constraint
12032 : * was already validated, InvalidObjectAddress is returned.
12033 : */
12034 : static ObjectAddress
12035 442 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12036 : bool recurse, bool recursing, LOCKMODE lockmode)
12037 : {
12038 : Relation conrel;
12039 : SysScanDesc scan;
12040 : ScanKeyData skey[3];
12041 : HeapTuple tuple;
12042 : Form_pg_constraint con;
12043 : ObjectAddress address;
12044 :
12045 442 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12046 :
12047 : /*
12048 : * Find and check the target constraint
12049 : */
12050 442 : ScanKeyInit(&skey[0],
12051 : Anum_pg_constraint_conrelid,
12052 : BTEqualStrategyNumber, F_OIDEQ,
12053 : ObjectIdGetDatum(RelationGetRelid(rel)));
12054 442 : ScanKeyInit(&skey[1],
12055 : Anum_pg_constraint_contypid,
12056 : BTEqualStrategyNumber, F_OIDEQ,
12057 : ObjectIdGetDatum(InvalidOid));
12058 442 : ScanKeyInit(&skey[2],
12059 : Anum_pg_constraint_conname,
12060 : BTEqualStrategyNumber, F_NAMEEQ,
12061 : CStringGetDatum(constrName));
12062 442 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12063 : true, NULL, 3, skey);
12064 :
12065 : /* There can be at most one matching row */
12066 442 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12067 0 : ereport(ERROR,
12068 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12069 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12070 : constrName, RelationGetRelationName(rel))));
12071 :
12072 442 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12073 442 : if (con->contype != CONSTRAINT_FOREIGN &&
12074 138 : con->contype != CONSTRAINT_CHECK)
12075 0 : ereport(ERROR,
12076 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12077 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12078 : constrName, RelationGetRelationName(rel))));
12079 :
12080 442 : if (!con->conenforced)
12081 6 : ereport(ERROR,
12082 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12083 : errmsg("cannot validate NOT ENFORCED constraint")));
12084 :
12085 436 : if (!con->convalidated)
12086 : {
12087 418 : if (con->contype == CONSTRAINT_FOREIGN)
12088 : {
12089 298 : QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12090 : }
12091 120 : else if (con->contype == CONSTRAINT_CHECK)
12092 : {
12093 120 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12094 : tuple, recurse, recursing, lockmode);
12095 : }
12096 :
12097 418 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12098 : }
12099 : else
12100 18 : address = InvalidObjectAddress; /* already validated */
12101 :
12102 436 : systable_endscan(scan);
12103 :
12104 436 : table_close(conrel, RowExclusiveLock);
12105 :
12106 436 : return address;
12107 : }
12108 :
12109 : /*
12110 : * QueueFKConstraintValidation
12111 : *
12112 : * Add an entry to the wqueue to validate the given foreign key constraint in
12113 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12114 : * for the specified relation.
12115 : */
12116 : static void
12117 298 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12118 : HeapTuple contuple, LOCKMODE lockmode)
12119 : {
12120 : Form_pg_constraint con;
12121 : AlteredTableInfo *tab;
12122 : HeapTuple copyTuple;
12123 : Form_pg_constraint copy_con;
12124 :
12125 298 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12126 : Assert(con->contype == CONSTRAINT_FOREIGN);
12127 :
12128 298 : if (rel->rd_rel->relkind == RELKIND_RELATION)
12129 : {
12130 : NewConstraint *newcon;
12131 : Constraint *fkconstraint;
12132 :
12133 : /* Queue validation for phase 3 */
12134 298 : fkconstraint = makeNode(Constraint);
12135 : /* for now this is all we need */
12136 298 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12137 :
12138 298 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12139 298 : newcon->name = fkconstraint->conname;
12140 298 : newcon->contype = CONSTR_FOREIGN;
12141 298 : newcon->refrelid = con->confrelid;
12142 298 : newcon->refindid = con->conindid;
12143 298 : newcon->conid = con->oid;
12144 298 : newcon->qual = (Node *) fkconstraint;
12145 :
12146 : /* Find or create work queue entry for this table */
12147 298 : tab = ATGetQueueEntry(wqueue, rel);
12148 298 : tab->constraints = lappend(tab->constraints, newcon);
12149 : }
12150 :
12151 : /*
12152 : * We disallow creating invalid foreign keys to or from partitioned
12153 : * tables, so ignoring the recursion bit is okay.
12154 : */
12155 :
12156 : /*
12157 : * Now update the catalog, while we have the door open.
12158 : */
12159 298 : copyTuple = heap_copytuple(contuple);
12160 298 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12161 298 : copy_con->convalidated = true;
12162 298 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12163 :
12164 298 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12165 :
12166 298 : heap_freetuple(copyTuple);
12167 298 : }
12168 :
12169 : /*
12170 : * QueueCheckConstraintValidation
12171 : *
12172 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
12173 : * and update the convalidated field in the pg_constraint catalog for the
12174 : * specified relation and all its inheriting children.
12175 : */
12176 : static void
12177 120 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12178 : char *constrName, HeapTuple contuple,
12179 : bool recurse, bool recursing, LOCKMODE lockmode)
12180 : {
12181 : Form_pg_constraint con;
12182 : AlteredTableInfo *tab;
12183 : HeapTuple copyTuple;
12184 : Form_pg_constraint copy_con;
12185 :
12186 120 : List *children = NIL;
12187 : ListCell *child;
12188 : NewConstraint *newcon;
12189 : Datum val;
12190 : char *conbin;
12191 :
12192 120 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12193 : Assert(con->contype == CONSTRAINT_CHECK);
12194 :
12195 : /*
12196 : * If we're recursing, the parent has already done this, so skip it. Also,
12197 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
12198 : * for it in the children.
12199 : */
12200 120 : if (!recursing && !con->connoinherit)
12201 66 : children = find_all_inheritors(RelationGetRelid(rel),
12202 : lockmode, NULL);
12203 :
12204 : /*
12205 : * For CHECK constraints, we must ensure that we only mark the constraint
12206 : * as validated on the parent if it's already validated on the children.
12207 : *
12208 : * We recurse before validating on the parent, to reduce risk of
12209 : * deadlocks.
12210 : */
12211 234 : foreach(child, children)
12212 : {
12213 114 : Oid childoid = lfirst_oid(child);
12214 : Relation childrel;
12215 :
12216 114 : if (childoid == RelationGetRelid(rel))
12217 66 : continue;
12218 :
12219 : /*
12220 : * If we are told not to recurse, there had better not be any child
12221 : * tables, because we can't mark the constraint on the parent valid
12222 : * unless it is valid for all child tables.
12223 : */
12224 48 : if (!recurse)
12225 0 : ereport(ERROR,
12226 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12227 : errmsg("constraint must be validated on child tables too")));
12228 :
12229 : /* find_all_inheritors already got lock */
12230 48 : childrel = table_open(childoid, NoLock);
12231 :
12232 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12233 : true, lockmode);
12234 48 : table_close(childrel, NoLock);
12235 : }
12236 :
12237 : /* Queue validation for phase 3 */
12238 120 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12239 120 : newcon->name = constrName;
12240 120 : newcon->contype = CONSTR_CHECK;
12241 120 : newcon->refrelid = InvalidOid;
12242 120 : newcon->refindid = InvalidOid;
12243 120 : newcon->conid = con->oid;
12244 :
12245 120 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
12246 : Anum_pg_constraint_conbin);
12247 120 : conbin = TextDatumGetCString(val);
12248 120 : newcon->qual = (Node *) stringToNode(conbin);
12249 :
12250 : /* Find or create work queue entry for this table */
12251 120 : tab = ATGetQueueEntry(wqueue, rel);
12252 120 : tab->constraints = lappend(tab->constraints, newcon);
12253 :
12254 : /*
12255 : * Invalidate relcache so that others see the new validated constraint.
12256 : */
12257 120 : CacheInvalidateRelcache(rel);
12258 :
12259 : /*
12260 : * Now update the catalog, while we have the door open.
12261 : */
12262 120 : copyTuple = heap_copytuple(contuple);
12263 120 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12264 120 : copy_con->convalidated = true;
12265 120 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12266 :
12267 120 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12268 :
12269 120 : heap_freetuple(copyTuple);
12270 120 : }
12271 :
12272 : /*
12273 : * transformColumnNameList - transform list of column names
12274 : *
12275 : * Lookup each name and return its attnum and, optionally, type and collation
12276 : * OIDs
12277 : *
12278 : * Note: the name of this function suggests that it's general-purpose,
12279 : * but actually it's only used to look up names appearing in foreign-key
12280 : * clauses. The error messages would need work to use it in other cases,
12281 : * and perhaps the validity checks as well.
12282 : */
12283 : static int
12284 6280 : transformColumnNameList(Oid relId, List *colList,
12285 : int16 *attnums, Oid *atttypids, Oid *attcollids)
12286 : {
12287 : ListCell *l;
12288 : int attnum;
12289 :
12290 6280 : attnum = 0;
12291 11484 : foreach(l, colList)
12292 : {
12293 5270 : char *attname = strVal(lfirst(l));
12294 : HeapTuple atttuple;
12295 : Form_pg_attribute attform;
12296 :
12297 5270 : atttuple = SearchSysCacheAttName(relId, attname);
12298 5270 : if (!HeapTupleIsValid(atttuple))
12299 54 : ereport(ERROR,
12300 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12301 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12302 : attname)));
12303 5216 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12304 5216 : if (attform->attnum < 0)
12305 12 : ereport(ERROR,
12306 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12307 : errmsg("system columns cannot be used in foreign keys")));
12308 5204 : if (attnum >= INDEX_MAX_KEYS)
12309 0 : ereport(ERROR,
12310 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12311 : errmsg("cannot have more than %d keys in a foreign key",
12312 : INDEX_MAX_KEYS)));
12313 5204 : attnums[attnum] = attform->attnum;
12314 5204 : if (atttypids != NULL)
12315 5174 : atttypids[attnum] = attform->atttypid;
12316 5204 : if (attcollids != NULL)
12317 5174 : attcollids[attnum] = attform->attcollation;
12318 5204 : ReleaseSysCache(atttuple);
12319 5204 : attnum++;
12320 : }
12321 :
12322 6214 : return attnum;
12323 : }
12324 :
12325 : /*
12326 : * transformFkeyGetPrimaryKey -
12327 : *
12328 : * Look up the names, attnums, types, and collations of the primary key attributes
12329 : * for the pkrel. Also return the index OID and index opclasses of the
12330 : * index supporting the primary key. Also return whether the index has
12331 : * WITHOUT OVERLAPS.
12332 : *
12333 : * All parameters except pkrel are output parameters. Also, the function
12334 : * return value is the number of attributes in the primary key.
12335 : *
12336 : * Used when the column list in the REFERENCES specification is omitted.
12337 : */
12338 : static int
12339 1112 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12340 : List **attnamelist,
12341 : int16 *attnums, Oid *atttypids, Oid *attcollids,
12342 : Oid *opclasses, bool *pk_has_without_overlaps)
12343 : {
12344 : List *indexoidlist;
12345 : ListCell *indexoidscan;
12346 1112 : HeapTuple indexTuple = NULL;
12347 1112 : Form_pg_index indexStruct = NULL;
12348 : Datum indclassDatum;
12349 : oidvector *indclass;
12350 : int i;
12351 :
12352 : /*
12353 : * Get the list of index OIDs for the table from the relcache, and look up
12354 : * each one in the pg_index syscache until we find one marked primary key
12355 : * (hopefully there isn't more than one such). Insist it's valid, too.
12356 : */
12357 1112 : *indexOid = InvalidOid;
12358 :
12359 1112 : indexoidlist = RelationGetIndexList(pkrel);
12360 :
12361 1118 : foreach(indexoidscan, indexoidlist)
12362 : {
12363 1118 : Oid indexoid = lfirst_oid(indexoidscan);
12364 :
12365 1118 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12366 1118 : if (!HeapTupleIsValid(indexTuple))
12367 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12368 1118 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12369 1118 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12370 : {
12371 : /*
12372 : * Refuse to use a deferrable primary key. This is per SQL spec,
12373 : * and there would be a lot of interesting semantic problems if we
12374 : * tried to allow it.
12375 : */
12376 1112 : if (!indexStruct->indimmediate)
12377 0 : ereport(ERROR,
12378 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12379 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12380 : RelationGetRelationName(pkrel))));
12381 :
12382 1112 : *indexOid = indexoid;
12383 1112 : break;
12384 : }
12385 6 : ReleaseSysCache(indexTuple);
12386 : }
12387 :
12388 1112 : list_free(indexoidlist);
12389 :
12390 : /*
12391 : * Check that we found it
12392 : */
12393 1112 : if (!OidIsValid(*indexOid))
12394 0 : ereport(ERROR,
12395 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12396 : errmsg("there is no primary key for referenced table \"%s\"",
12397 : RelationGetRelationName(pkrel))));
12398 :
12399 : /* Must get indclass the hard way */
12400 1112 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12401 : Anum_pg_index_indclass);
12402 1112 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12403 :
12404 : /*
12405 : * Now build the list of PK attributes from the indkey definition (we
12406 : * assume a primary key cannot have expressional elements)
12407 : */
12408 1112 : *attnamelist = NIL;
12409 2612 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12410 : {
12411 1500 : int pkattno = indexStruct->indkey.values[i];
12412 :
12413 1500 : attnums[i] = pkattno;
12414 1500 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12415 1500 : attcollids[i] = attnumCollationId(pkrel, pkattno);
12416 1500 : opclasses[i] = indclass->values[i];
12417 1500 : *attnamelist = lappend(*attnamelist,
12418 1500 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12419 : }
12420 :
12421 1112 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12422 :
12423 1112 : ReleaseSysCache(indexTuple);
12424 :
12425 1112 : return i;
12426 : }
12427 :
12428 : /*
12429 : * transformFkeyCheckAttrs -
12430 : *
12431 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12432 : * reference as part of a foreign key constraint.
12433 : *
12434 : * Returns the OID of the unique index supporting the constraint and
12435 : * populates the caller-provided 'opclasses' array with the opclasses
12436 : * associated with the index columns. Also sets whether the index
12437 : * uses WITHOUT OVERLAPS.
12438 : *
12439 : * Raises an ERROR on validation failure.
12440 : */
12441 : static Oid
12442 1272 : transformFkeyCheckAttrs(Relation pkrel,
12443 : int numattrs, int16 *attnums,
12444 : bool with_period, Oid *opclasses,
12445 : bool *pk_has_without_overlaps)
12446 : {
12447 1272 : Oid indexoid = InvalidOid;
12448 1272 : bool found = false;
12449 1272 : bool found_deferrable = false;
12450 : List *indexoidlist;
12451 : ListCell *indexoidscan;
12452 : int i,
12453 : j;
12454 :
12455 : /*
12456 : * Reject duplicate appearances of columns in the referenced-columns list.
12457 : * Such a case is forbidden by the SQL standard, and even if we thought it
12458 : * useful to allow it, there would be ambiguity about how to match the
12459 : * list to unique indexes (in particular, it'd be unclear which index
12460 : * opclass goes with which FK column).
12461 : */
12462 2980 : for (i = 0; i < numattrs; i++)
12463 : {
12464 2262 : for (j = i + 1; j < numattrs; j++)
12465 : {
12466 554 : if (attnums[i] == attnums[j])
12467 24 : ereport(ERROR,
12468 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12469 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12470 : }
12471 : }
12472 :
12473 : /*
12474 : * Get the list of index OIDs for the table from the relcache, and look up
12475 : * each one in the pg_index syscache, and match unique indexes to the list
12476 : * of attnums we are given.
12477 : */
12478 1248 : indexoidlist = RelationGetIndexList(pkrel);
12479 :
12480 1428 : foreach(indexoidscan, indexoidlist)
12481 : {
12482 : HeapTuple indexTuple;
12483 : Form_pg_index indexStruct;
12484 :
12485 1416 : indexoid = lfirst_oid(indexoidscan);
12486 1416 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12487 1416 : if (!HeapTupleIsValid(indexTuple))
12488 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12489 1416 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12490 :
12491 : /*
12492 : * Must have the right number of columns; must be unique (or if
12493 : * temporal then exclusion instead) and not a partial index; forget it
12494 : * if there are any expressions, too. Invalid indexes are out as well.
12495 : */
12496 2724 : if (indexStruct->indnkeyatts == numattrs &&
12497 1308 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12498 2588 : indexStruct->indisvalid &&
12499 2588 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12500 1294 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12501 : {
12502 : Datum indclassDatum;
12503 : oidvector *indclass;
12504 :
12505 : /* Must get indclass the hard way */
12506 1294 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12507 : Anum_pg_index_indclass);
12508 1294 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12509 :
12510 : /*
12511 : * The given attnum list may match the index columns in any order.
12512 : * Check for a match, and extract the appropriate opclasses while
12513 : * we're at it.
12514 : *
12515 : * We know that attnums[] is duplicate-free per the test at the
12516 : * start of this function, and we checked above that the number of
12517 : * index columns agrees, so if we find a match for each attnums[]
12518 : * entry then we must have a one-to-one match in some order.
12519 : */
12520 2990 : for (i = 0; i < numattrs; i++)
12521 : {
12522 1754 : found = false;
12523 2342 : for (j = 0; j < numattrs; j++)
12524 : {
12525 2284 : if (attnums[i] == indexStruct->indkey.values[j])
12526 : {
12527 1696 : opclasses[i] = indclass->values[j];
12528 1696 : found = true;
12529 1696 : break;
12530 : }
12531 : }
12532 1754 : if (!found)
12533 58 : break;
12534 : }
12535 : /* The last attribute in the index must be the PERIOD FK part */
12536 1294 : if (found && with_period)
12537 : {
12538 130 : int16 periodattnum = attnums[numattrs - 1];
12539 :
12540 130 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12541 : }
12542 :
12543 : /*
12544 : * Refuse to use a deferrable unique/primary key. This is per SQL
12545 : * spec, and there would be a lot of interesting semantic problems
12546 : * if we tried to allow it.
12547 : */
12548 1294 : if (found && !indexStruct->indimmediate)
12549 : {
12550 : /*
12551 : * Remember that we found an otherwise matching index, so that
12552 : * we can generate a more appropriate error message.
12553 : */
12554 0 : found_deferrable = true;
12555 0 : found = false;
12556 : }
12557 :
12558 : /* We need to know whether the index has WITHOUT OVERLAPS */
12559 1294 : if (found)
12560 1236 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12561 : }
12562 1416 : ReleaseSysCache(indexTuple);
12563 1416 : if (found)
12564 1236 : break;
12565 : }
12566 :
12567 1248 : if (!found)
12568 : {
12569 12 : if (found_deferrable)
12570 0 : ereport(ERROR,
12571 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12572 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12573 : RelationGetRelationName(pkrel))));
12574 : else
12575 12 : ereport(ERROR,
12576 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12577 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12578 : RelationGetRelationName(pkrel))));
12579 : }
12580 :
12581 1236 : list_free(indexoidlist);
12582 :
12583 1236 : return indexoid;
12584 : }
12585 :
12586 : /*
12587 : * findFkeyCast -
12588 : *
12589 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12590 : * Caller has equal regard for binary coercibility and for an exact match.
12591 : */
12592 : static CoercionPathType
12593 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12594 : {
12595 : CoercionPathType ret;
12596 :
12597 12 : if (targetTypeId == sourceTypeId)
12598 : {
12599 12 : ret = COERCION_PATH_RELABELTYPE;
12600 12 : *funcid = InvalidOid;
12601 : }
12602 : else
12603 : {
12604 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12605 : COERCION_IMPLICIT, funcid);
12606 0 : if (ret == COERCION_PATH_NONE)
12607 : /* A previously-relied-upon cast is now gone. */
12608 0 : elog(ERROR, "could not find cast from %u to %u",
12609 : sourceTypeId, targetTypeId);
12610 : }
12611 :
12612 12 : return ret;
12613 : }
12614 :
12615 : /*
12616 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12617 : *
12618 : * Note: we have already checked that the user owns the referencing table,
12619 : * else we'd have failed much earlier; no additional checks are needed for it.
12620 : */
12621 : static void
12622 2312 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12623 : {
12624 2312 : Oid roleid = GetUserId();
12625 : AclResult aclresult;
12626 : int i;
12627 :
12628 : /* Okay if we have relation-level REFERENCES permission */
12629 2312 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12630 : ACL_REFERENCES);
12631 2312 : if (aclresult == ACLCHECK_OK)
12632 2312 : return;
12633 : /* Else we must have REFERENCES on each column */
12634 0 : for (i = 0; i < natts; i++)
12635 : {
12636 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12637 : roleid, ACL_REFERENCES);
12638 0 : if (aclresult != ACLCHECK_OK)
12639 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12640 0 : RelationGetRelationName(rel));
12641 : }
12642 : }
12643 :
12644 : /*
12645 : * Scan the existing rows in a table to verify they meet a proposed FK
12646 : * constraint.
12647 : *
12648 : * Caller must have opened and locked both relations appropriately.
12649 : */
12650 : static void
12651 1108 : validateForeignKeyConstraint(char *conname,
12652 : Relation rel,
12653 : Relation pkrel,
12654 : Oid pkindOid,
12655 : Oid constraintOid,
12656 : bool hasperiod)
12657 : {
12658 : TupleTableSlot *slot;
12659 : TableScanDesc scan;
12660 1108 : Trigger trig = {0};
12661 : Snapshot snapshot;
12662 : MemoryContext oldcxt;
12663 : MemoryContext perTupCxt;
12664 :
12665 1108 : ereport(DEBUG1,
12666 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12667 :
12668 : /*
12669 : * Build a trigger call structure; we'll need it either way.
12670 : */
12671 1108 : trig.tgoid = InvalidOid;
12672 1108 : trig.tgname = conname;
12673 1108 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12674 1108 : trig.tgisinternal = true;
12675 1108 : trig.tgconstrrelid = RelationGetRelid(pkrel);
12676 1108 : trig.tgconstrindid = pkindOid;
12677 1108 : trig.tgconstraint = constraintOid;
12678 1108 : trig.tgdeferrable = false;
12679 1108 : trig.tginitdeferred = false;
12680 : /* we needn't fill in remaining fields */
12681 :
12682 : /*
12683 : * See if we can do it with a single LEFT JOIN query. A false result
12684 : * indicates we must proceed with the fire-the-trigger method. We can't do
12685 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12686 : * left joins.
12687 : */
12688 1108 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12689 888 : return;
12690 :
12691 : /*
12692 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12693 : * if that tuple had just been inserted. If any of those fail, it should
12694 : * ereport(ERROR) and that's that.
12695 : */
12696 158 : snapshot = RegisterSnapshot(GetLatestSnapshot());
12697 158 : slot = table_slot_create(rel, NULL);
12698 158 : scan = table_beginscan(rel, snapshot, 0, NULL);
12699 :
12700 158 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12701 : "validateForeignKeyConstraint",
12702 : ALLOCSET_SMALL_SIZES);
12703 158 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12704 :
12705 242 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12706 : {
12707 102 : LOCAL_FCINFO(fcinfo, 0);
12708 102 : TriggerData trigdata = {0};
12709 :
12710 102 : CHECK_FOR_INTERRUPTS();
12711 :
12712 : /*
12713 : * Make a call to the trigger function
12714 : *
12715 : * No parameters are passed, but we do set a context
12716 : */
12717 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12718 :
12719 : /*
12720 : * We assume RI_FKey_check_ins won't look at flinfo...
12721 : */
12722 102 : trigdata.type = T_TriggerData;
12723 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12724 102 : trigdata.tg_relation = rel;
12725 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12726 102 : trigdata.tg_trigslot = slot;
12727 102 : trigdata.tg_trigger = &trig;
12728 :
12729 102 : fcinfo->context = (Node *) &trigdata;
12730 :
12731 102 : RI_FKey_check_ins(fcinfo);
12732 :
12733 84 : MemoryContextReset(perTupCxt);
12734 : }
12735 :
12736 140 : MemoryContextSwitchTo(oldcxt);
12737 140 : MemoryContextDelete(perTupCxt);
12738 140 : table_endscan(scan);
12739 140 : UnregisterSnapshot(snapshot);
12740 140 : ExecDropSingleTupleTableSlot(slot);
12741 : }
12742 :
12743 : /*
12744 : * CreateFKCheckTrigger
12745 : * Creates the insert (on_insert=true) or update "check" trigger that
12746 : * implements a given foreign key
12747 : *
12748 : * Returns the OID of the so created trigger.
12749 : */
12750 : static Oid
12751 5576 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12752 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12753 : bool on_insert)
12754 : {
12755 : ObjectAddress trigAddress;
12756 : CreateTrigStmt *fk_trigger;
12757 :
12758 : /*
12759 : * Note: for a self-referential FK (referencing and referenced tables are
12760 : * the same), it is important that the ON UPDATE action fires before the
12761 : * CHECK action, since both triggers will fire on the same row during an
12762 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12763 : * state of the row. Triggers fire in name order, so we ensure this by
12764 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12765 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12766 : */
12767 5576 : fk_trigger = makeNode(CreateTrigStmt);
12768 5576 : fk_trigger->replace = false;
12769 5576 : fk_trigger->isconstraint = true;
12770 5576 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
12771 5576 : fk_trigger->relation = NULL;
12772 :
12773 : /* Either ON INSERT or ON UPDATE */
12774 5576 : if (on_insert)
12775 : {
12776 2788 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12777 2788 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12778 : }
12779 : else
12780 : {
12781 2788 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12782 2788 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12783 : }
12784 :
12785 5576 : fk_trigger->args = NIL;
12786 5576 : fk_trigger->row = true;
12787 5576 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12788 5576 : fk_trigger->columns = NIL;
12789 5576 : fk_trigger->whenClause = NULL;
12790 5576 : fk_trigger->transitionRels = NIL;
12791 5576 : fk_trigger->deferrable = fkconstraint->deferrable;
12792 5576 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12793 5576 : fk_trigger->constrrel = NULL;
12794 :
12795 5576 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12796 : constraintOid, indexOid, InvalidOid,
12797 : parentTrigOid, NULL, true, false);
12798 :
12799 : /* Make changes-so-far visible */
12800 5576 : CommandCounterIncrement();
12801 :
12802 5576 : return trigAddress.objectId;
12803 : }
12804 :
12805 : /*
12806 : * createForeignKeyActionTriggers
12807 : * Create the referenced-side "action" triggers that implement a foreign
12808 : * key.
12809 : *
12810 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
12811 : * *updateTrigOid.
12812 : */
12813 : static void
12814 2948 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12815 : Oid constraintOid, Oid indexOid,
12816 : Oid parentDelTrigger, Oid parentUpdTrigger,
12817 : Oid *deleteTrigOid, Oid *updateTrigOid)
12818 : {
12819 : CreateTrigStmt *fk_trigger;
12820 : ObjectAddress trigAddress;
12821 :
12822 : /*
12823 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12824 : * DELETE action on the referenced table.
12825 : */
12826 2948 : fk_trigger = makeNode(CreateTrigStmt);
12827 2948 : fk_trigger->replace = false;
12828 2948 : fk_trigger->isconstraint = true;
12829 2948 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12830 2948 : fk_trigger->relation = NULL;
12831 2948 : fk_trigger->args = NIL;
12832 2948 : fk_trigger->row = true;
12833 2948 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12834 2948 : fk_trigger->events = TRIGGER_TYPE_DELETE;
12835 2948 : fk_trigger->columns = NIL;
12836 2948 : fk_trigger->whenClause = NULL;
12837 2948 : fk_trigger->transitionRels = NIL;
12838 2948 : fk_trigger->constrrel = NULL;
12839 :
12840 2948 : switch (fkconstraint->fk_del_action)
12841 : {
12842 2280 : case FKCONSTR_ACTION_NOACTION:
12843 2280 : fk_trigger->deferrable = fkconstraint->deferrable;
12844 2280 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12845 2280 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12846 2280 : break;
12847 82 : case FKCONSTR_ACTION_RESTRICT:
12848 82 : fk_trigger->deferrable = false;
12849 82 : fk_trigger->initdeferred = false;
12850 82 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12851 82 : break;
12852 428 : case FKCONSTR_ACTION_CASCADE:
12853 428 : fk_trigger->deferrable = false;
12854 428 : fk_trigger->initdeferred = false;
12855 428 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12856 428 : break;
12857 98 : case FKCONSTR_ACTION_SETNULL:
12858 98 : fk_trigger->deferrable = false;
12859 98 : fk_trigger->initdeferred = false;
12860 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12861 98 : break;
12862 60 : case FKCONSTR_ACTION_SETDEFAULT:
12863 60 : fk_trigger->deferrable = false;
12864 60 : fk_trigger->initdeferred = false;
12865 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12866 60 : break;
12867 0 : default:
12868 0 : elog(ERROR, "unrecognized FK action type: %d",
12869 : (int) fkconstraint->fk_del_action);
12870 : break;
12871 : }
12872 :
12873 2948 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12874 : RelationGetRelid(rel),
12875 : constraintOid, indexOid, InvalidOid,
12876 : parentDelTrigger, NULL, true, false);
12877 2948 : if (deleteTrigOid)
12878 2948 : *deleteTrigOid = trigAddress.objectId;
12879 :
12880 : /* Make changes-so-far visible */
12881 2948 : CommandCounterIncrement();
12882 :
12883 : /*
12884 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12885 : * UPDATE action on the referenced table.
12886 : */
12887 2948 : fk_trigger = makeNode(CreateTrigStmt);
12888 2948 : fk_trigger->replace = false;
12889 2948 : fk_trigger->isconstraint = true;
12890 2948 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12891 2948 : fk_trigger->relation = NULL;
12892 2948 : fk_trigger->args = NIL;
12893 2948 : fk_trigger->row = true;
12894 2948 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12895 2948 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12896 2948 : fk_trigger->columns = NIL;
12897 2948 : fk_trigger->whenClause = NULL;
12898 2948 : fk_trigger->transitionRels = NIL;
12899 2948 : fk_trigger->constrrel = NULL;
12900 :
12901 2948 : switch (fkconstraint->fk_upd_action)
12902 : {
12903 2514 : case FKCONSTR_ACTION_NOACTION:
12904 2514 : fk_trigger->deferrable = fkconstraint->deferrable;
12905 2514 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12906 2514 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12907 2514 : break;
12908 48 : case FKCONSTR_ACTION_RESTRICT:
12909 48 : fk_trigger->deferrable = false;
12910 48 : fk_trigger->initdeferred = false;
12911 48 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12912 48 : break;
12913 282 : case FKCONSTR_ACTION_CASCADE:
12914 282 : fk_trigger->deferrable = false;
12915 282 : fk_trigger->initdeferred = false;
12916 282 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12917 282 : break;
12918 62 : case FKCONSTR_ACTION_SETNULL:
12919 62 : fk_trigger->deferrable = false;
12920 62 : fk_trigger->initdeferred = false;
12921 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12922 62 : break;
12923 42 : case FKCONSTR_ACTION_SETDEFAULT:
12924 42 : fk_trigger->deferrable = false;
12925 42 : fk_trigger->initdeferred = false;
12926 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12927 42 : break;
12928 0 : default:
12929 0 : elog(ERROR, "unrecognized FK action type: %d",
12930 : (int) fkconstraint->fk_upd_action);
12931 : break;
12932 : }
12933 :
12934 2948 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12935 : RelationGetRelid(rel),
12936 : constraintOid, indexOid, InvalidOid,
12937 : parentUpdTrigger, NULL, true, false);
12938 2948 : if (updateTrigOid)
12939 2948 : *updateTrigOid = trigAddress.objectId;
12940 2948 : }
12941 :
12942 : /*
12943 : * createForeignKeyCheckTriggers
12944 : * Create the referencing-side "check" triggers that implement a foreign
12945 : * key.
12946 : *
12947 : * Returns the OIDs of the so created triggers in *insertTrigOid and
12948 : * *updateTrigOid.
12949 : */
12950 : static void
12951 2788 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12952 : Constraint *fkconstraint, Oid constraintOid,
12953 : Oid indexOid,
12954 : Oid parentInsTrigger, Oid parentUpdTrigger,
12955 : Oid *insertTrigOid, Oid *updateTrigOid)
12956 : {
12957 2788 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12958 : constraintOid, indexOid,
12959 : parentInsTrigger, true);
12960 2788 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12961 : constraintOid, indexOid,
12962 : parentUpdTrigger, false);
12963 2788 : }
12964 :
12965 : /*
12966 : * ALTER TABLE DROP CONSTRAINT
12967 : *
12968 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12969 : */
12970 : static void
12971 816 : ATExecDropConstraint(Relation rel, const char *constrName,
12972 : DropBehavior behavior, bool recurse,
12973 : bool missing_ok, LOCKMODE lockmode)
12974 : {
12975 : Relation conrel;
12976 : SysScanDesc scan;
12977 : ScanKeyData skey[3];
12978 : HeapTuple tuple;
12979 816 : bool found = false;
12980 :
12981 816 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12982 :
12983 : /*
12984 : * Find and drop the target constraint
12985 : */
12986 816 : ScanKeyInit(&skey[0],
12987 : Anum_pg_constraint_conrelid,
12988 : BTEqualStrategyNumber, F_OIDEQ,
12989 : ObjectIdGetDatum(RelationGetRelid(rel)));
12990 816 : ScanKeyInit(&skey[1],
12991 : Anum_pg_constraint_contypid,
12992 : BTEqualStrategyNumber, F_OIDEQ,
12993 : ObjectIdGetDatum(InvalidOid));
12994 816 : ScanKeyInit(&skey[2],
12995 : Anum_pg_constraint_conname,
12996 : BTEqualStrategyNumber, F_NAMEEQ,
12997 : CStringGetDatum(constrName));
12998 816 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12999 : true, NULL, 3, skey);
13000 :
13001 : /* There can be at most one matching row */
13002 816 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13003 : {
13004 780 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
13005 : missing_ok, lockmode);
13006 594 : found = true;
13007 : }
13008 :
13009 630 : systable_endscan(scan);
13010 :
13011 630 : if (!found)
13012 : {
13013 36 : if (!missing_ok)
13014 24 : ereport(ERROR,
13015 : errcode(ERRCODE_UNDEFINED_OBJECT),
13016 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13017 : constrName, RelationGetRelationName(rel)));
13018 : else
13019 12 : ereport(NOTICE,
13020 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
13021 : constrName, RelationGetRelationName(rel)));
13022 : }
13023 :
13024 606 : table_close(conrel, RowExclusiveLock);
13025 606 : }
13026 :
13027 : /*
13028 : * Remove a constraint, using its pg_constraint tuple
13029 : *
13030 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
13031 : * DROP NOT NULL.
13032 : *
13033 : * Returns the address of the constraint being removed.
13034 : */
13035 : static ObjectAddress
13036 1198 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
13037 : bool recurse, bool recursing, bool missing_ok,
13038 : LOCKMODE lockmode)
13039 : {
13040 : Relation conrel;
13041 : Form_pg_constraint con;
13042 : ObjectAddress conobj;
13043 : List *children;
13044 1198 : bool is_no_inherit_constraint = false;
13045 : char *constrName;
13046 1198 : char *colname = NULL;
13047 :
13048 : /* Guard against stack overflow due to overly deep inheritance tree. */
13049 1198 : check_stack_depth();
13050 :
13051 : /* At top level, permission check was done in ATPrepCmd, else do it */
13052 1198 : if (recursing)
13053 210 : ATSimplePermissions(AT_DropConstraint, rel,
13054 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
13055 :
13056 1192 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13057 :
13058 1192 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
13059 1192 : constrName = NameStr(con->conname);
13060 :
13061 : /* Don't allow drop of inherited constraints */
13062 1192 : if (con->coninhcount > 0 && !recursing)
13063 156 : ereport(ERROR,
13064 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13065 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13066 : constrName, RelationGetRelationName(rel))));
13067 :
13068 : /*
13069 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
13070 : *
13071 : * While doing that, we're in a good position to disallow dropping a not-
13072 : * null constraint underneath a primary key, a replica identity index, or
13073 : * a generated identity column.
13074 : */
13075 1036 : if (con->contype == CONSTRAINT_NOTNULL)
13076 : {
13077 278 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
13078 278 : AttrNumber attnum = extractNotNullColumn(constraintTup);
13079 : Bitmapset *pkattrs;
13080 : Bitmapset *irattrs;
13081 : HeapTuple atttup;
13082 : Form_pg_attribute attForm;
13083 :
13084 : /* save column name for recursion step */
13085 278 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13086 :
13087 : /*
13088 : * Disallow if it's in the primary key. For partitioned tables we
13089 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
13090 : * return NULL if the primary key is invalid; but we still need to
13091 : * protect not-null constraints under such a constraint, so check the
13092 : * slow way.
13093 : */
13094 278 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
13095 :
13096 278 : if (pkattrs == NULL &&
13097 242 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13098 : {
13099 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
13100 :
13101 18 : if (OidIsValid(pkindex))
13102 : {
13103 0 : Relation pk = relation_open(pkindex, AccessShareLock);
13104 :
13105 0 : pkattrs = NULL;
13106 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
13107 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
13108 :
13109 0 : relation_close(pk, AccessShareLock);
13110 : }
13111 : }
13112 :
13113 314 : if (pkattrs &&
13114 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
13115 24 : ereport(ERROR,
13116 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13117 : errmsg("column \"%s\" is in a primary key",
13118 : get_attname(RelationGetRelid(rel), attnum, false)));
13119 :
13120 : /* Disallow if it's in the replica identity */
13121 254 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13122 254 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
13123 12 : ereport(ERROR,
13124 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13125 : errmsg("column \"%s\" is in index used as replica identity",
13126 : get_attname(RelationGetRelid(rel), attnum, false)));
13127 :
13128 : /* Disallow if it's a GENERATED AS IDENTITY column */
13129 242 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
13130 242 : if (!HeapTupleIsValid(atttup))
13131 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13132 : attnum, RelationGetRelid(rel));
13133 242 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13134 242 : if (attForm->attidentity != '\0')
13135 0 : ereport(ERROR,
13136 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13137 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13138 : get_attname(RelationGetRelid(rel), attnum,
13139 : false),
13140 : RelationGetRelationName(rel)));
13141 :
13142 : /* All good -- reset attnotnull if needed */
13143 242 : if (attForm->attnotnull)
13144 : {
13145 242 : attForm->attnotnull = false;
13146 242 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13147 : }
13148 :
13149 242 : table_close(attrel, RowExclusiveLock);
13150 : }
13151 :
13152 1000 : is_no_inherit_constraint = con->connoinherit;
13153 :
13154 : /*
13155 : * If it's a foreign-key constraint, we'd better lock the referenced table
13156 : * and check that that's not in use, just as we've already done for the
13157 : * constrained table (else we might, eg, be dropping a trigger that has
13158 : * unfired events). But we can/must skip that in the self-referential
13159 : * case.
13160 : */
13161 1000 : if (con->contype == CONSTRAINT_FOREIGN &&
13162 216 : con->confrelid != RelationGetRelid(rel))
13163 : {
13164 : Relation frel;
13165 :
13166 : /* Must match lock taken by RemoveTriggerById: */
13167 216 : frel = table_open(con->confrelid, AccessExclusiveLock);
13168 216 : CheckAlterTableIsSafe(frel);
13169 210 : table_close(frel, NoLock);
13170 : }
13171 :
13172 : /*
13173 : * Perform the actual constraint deletion
13174 : */
13175 994 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13176 994 : performDeletion(&conobj, behavior, 0);
13177 :
13178 : /*
13179 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13180 : * are dropped via the dependency mechanism, so we're done here.
13181 : */
13182 958 : if (con->contype != CONSTRAINT_CHECK &&
13183 640 : con->contype != CONSTRAINT_NOTNULL &&
13184 398 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13185 : {
13186 90 : table_close(conrel, RowExclusiveLock);
13187 90 : return conobj;
13188 : }
13189 :
13190 : /*
13191 : * Propagate to children as appropriate. Unlike most other ALTER
13192 : * routines, we have to do this one level of recursion at a time; we can't
13193 : * use find_all_inheritors to do it in one pass.
13194 : */
13195 868 : if (!is_no_inherit_constraint)
13196 554 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13197 : else
13198 314 : children = NIL;
13199 :
13200 2102 : foreach_oid(childrelid, children)
13201 : {
13202 : Relation childrel;
13203 : HeapTuple tuple;
13204 : Form_pg_constraint childcon;
13205 :
13206 : /* find_inheritance_children already got lock */
13207 378 : childrel = table_open(childrelid, NoLock);
13208 378 : CheckAlterTableIsSafe(childrel);
13209 :
13210 : /*
13211 : * We search for not-null constraints by column name, and others by
13212 : * constraint name.
13213 : */
13214 378 : if (con->contype == CONSTRAINT_NOTNULL)
13215 : {
13216 142 : tuple = findNotNullConstraint(childrelid, colname);
13217 142 : if (!HeapTupleIsValid(tuple))
13218 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13219 : colname, RelationGetRelid(childrel));
13220 : }
13221 : else
13222 : {
13223 : SysScanDesc scan;
13224 : ScanKeyData skey[3];
13225 :
13226 236 : ScanKeyInit(&skey[0],
13227 : Anum_pg_constraint_conrelid,
13228 : BTEqualStrategyNumber, F_OIDEQ,
13229 : ObjectIdGetDatum(childrelid));
13230 236 : ScanKeyInit(&skey[1],
13231 : Anum_pg_constraint_contypid,
13232 : BTEqualStrategyNumber, F_OIDEQ,
13233 : ObjectIdGetDatum(InvalidOid));
13234 236 : ScanKeyInit(&skey[2],
13235 : Anum_pg_constraint_conname,
13236 : BTEqualStrategyNumber, F_NAMEEQ,
13237 : CStringGetDatum(constrName));
13238 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13239 : true, NULL, 3, skey);
13240 : /* There can only be one, so no need to loop */
13241 236 : tuple = systable_getnext(scan);
13242 236 : if (!HeapTupleIsValid(tuple))
13243 0 : ereport(ERROR,
13244 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13245 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13246 : constrName,
13247 : RelationGetRelationName(childrel))));
13248 236 : tuple = heap_copytuple(tuple);
13249 236 : systable_endscan(scan);
13250 : }
13251 :
13252 378 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13253 :
13254 : /* Right now only CHECK and not-null constraints can be inherited */
13255 378 : if (childcon->contype != CONSTRAINT_CHECK &&
13256 142 : childcon->contype != CONSTRAINT_NOTNULL)
13257 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13258 :
13259 378 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13260 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13261 : childrelid, NameStr(childcon->conname));
13262 :
13263 378 : if (recurse)
13264 : {
13265 : /*
13266 : * If the child constraint has other definition sources, just
13267 : * decrement its inheritance count; if not, recurse to delete it.
13268 : */
13269 276 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13270 : {
13271 : /* Time to delete this child constraint, too */
13272 210 : dropconstraint_internal(childrel, tuple, behavior,
13273 : recurse, true, missing_ok,
13274 : lockmode);
13275 : }
13276 : else
13277 : {
13278 : /* Child constraint must survive my deletion */
13279 66 : childcon->coninhcount--;
13280 66 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13281 :
13282 : /* Make update visible */
13283 66 : CommandCounterIncrement();
13284 : }
13285 : }
13286 : else
13287 : {
13288 : /*
13289 : * If we were told to drop ONLY in this table (no recursion) and
13290 : * there are no further parents for this constraint, we need to
13291 : * mark the inheritors' constraints as locally defined rather than
13292 : * inherited.
13293 : */
13294 102 : childcon->coninhcount--;
13295 102 : if (childcon->coninhcount == 0)
13296 102 : childcon->conislocal = true;
13297 :
13298 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13299 :
13300 : /* Make update visible */
13301 102 : CommandCounterIncrement();
13302 : }
13303 :
13304 372 : heap_freetuple(tuple);
13305 :
13306 372 : table_close(childrel, NoLock);
13307 : }
13308 :
13309 862 : table_close(conrel, RowExclusiveLock);
13310 :
13311 862 : return conobj;
13312 : }
13313 :
13314 : /*
13315 : * ALTER COLUMN TYPE
13316 : *
13317 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13318 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13319 : * transformed (and must be, because we rely on some transformed fields).
13320 : *
13321 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13322 : * table will be done "in parallel" during phase 3, so all the USING
13323 : * expressions should be parsed assuming the original column types. Also,
13324 : * this allows a USING expression to refer to a field that will be dropped.
13325 : *
13326 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13327 : * the first two execution steps in phase 2; they must not see the effects
13328 : * of any other subcommand types, since the USING expressions are parsed
13329 : * against the unmodified table's state.
13330 : */
13331 : static void
13332 1168 : ATPrepAlterColumnType(List **wqueue,
13333 : AlteredTableInfo *tab, Relation rel,
13334 : bool recurse, bool recursing,
13335 : AlterTableCmd *cmd, LOCKMODE lockmode,
13336 : AlterTableUtilityContext *context)
13337 : {
13338 1168 : char *colName = cmd->name;
13339 1168 : ColumnDef *def = (ColumnDef *) cmd->def;
13340 1168 : TypeName *typeName = def->typeName;
13341 1168 : Node *transform = def->cooked_default;
13342 : HeapTuple tuple;
13343 : Form_pg_attribute attTup;
13344 : AttrNumber attnum;
13345 : Oid targettype;
13346 : int32 targettypmod;
13347 : Oid targetcollid;
13348 : NewColumnValue *newval;
13349 1168 : ParseState *pstate = make_parsestate(NULL);
13350 : AclResult aclresult;
13351 : bool is_expr;
13352 :
13353 1168 : if (rel->rd_rel->reloftype && !recursing)
13354 6 : ereport(ERROR,
13355 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13356 : errmsg("cannot alter column type of typed table")));
13357 :
13358 : /* lookup the attribute so we can check inheritance status */
13359 1162 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13360 1162 : if (!HeapTupleIsValid(tuple))
13361 0 : ereport(ERROR,
13362 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13363 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13364 : colName, RelationGetRelationName(rel))));
13365 1162 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13366 1162 : attnum = attTup->attnum;
13367 :
13368 : /* Can't alter a system attribute */
13369 1162 : if (attnum <= 0)
13370 6 : ereport(ERROR,
13371 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13372 : errmsg("cannot alter system column \"%s\"",
13373 : colName)));
13374 :
13375 : /*
13376 : * Cannot specify USING when altering type of a generated column, because
13377 : * that would violate the generation expression.
13378 : */
13379 1156 : if (attTup->attgenerated && def->cooked_default)
13380 6 : ereport(ERROR,
13381 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13382 : errmsg("cannot specify USING when altering type of generated column"),
13383 : errdetail("Column \"%s\" is a generated column.", colName)));
13384 :
13385 : /*
13386 : * Don't alter inherited columns. At outer level, there had better not be
13387 : * any inherited definition; when recursing, we assume this was checked at
13388 : * the parent level (see below).
13389 : */
13390 1150 : if (attTup->attinhcount > 0 && !recursing)
13391 6 : ereport(ERROR,
13392 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13393 : errmsg("cannot alter inherited column \"%s\"",
13394 : colName)));
13395 :
13396 : /* Don't alter columns used in the partition key */
13397 1144 : if (has_partition_attrs(rel,
13398 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13399 : &is_expr))
13400 18 : ereport(ERROR,
13401 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13402 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13403 : colName, RelationGetRelationName(rel))));
13404 :
13405 : /* Look up the target type */
13406 1126 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13407 :
13408 1120 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13409 1120 : if (aclresult != ACLCHECK_OK)
13410 12 : aclcheck_error_type(aclresult, targettype);
13411 :
13412 : /* And the collation */
13413 1108 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13414 :
13415 : /* make sure datatype is legal for a column */
13416 1102 : CheckAttributeType(colName, targettype, targetcollid,
13417 1102 : list_make1_oid(rel->rd_rel->reltype),
13418 : 0);
13419 :
13420 1096 : if (tab->relkind == RELKIND_RELATION ||
13421 196 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13422 : {
13423 : /*
13424 : * Set up an expression to transform the old data value to the new
13425 : * type. If a USING option was given, use the expression as
13426 : * transformed by transformAlterTableStmt, else just take the old
13427 : * value and try to coerce it. We do this first so that type
13428 : * incompatibility can be detected before we waste effort, and because
13429 : * we need the expression to be parsed against the original table row
13430 : * type.
13431 : */
13432 960 : if (!transform)
13433 : {
13434 738 : transform = (Node *) makeVar(1, attnum,
13435 : attTup->atttypid, attTup->atttypmod,
13436 : attTup->attcollation,
13437 : 0);
13438 : }
13439 :
13440 960 : transform = coerce_to_target_type(pstate,
13441 : transform, exprType(transform),
13442 : targettype, targettypmod,
13443 : COERCION_ASSIGNMENT,
13444 : COERCE_IMPLICIT_CAST,
13445 : -1);
13446 960 : if (transform == NULL)
13447 : {
13448 : /* error text depends on whether USING was specified or not */
13449 24 : if (def->cooked_default != NULL)
13450 6 : ereport(ERROR,
13451 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13452 : errmsg("result of USING clause for column \"%s\""
13453 : " cannot be cast automatically to type %s",
13454 : colName, format_type_be(targettype)),
13455 : errhint("You might need to add an explicit cast.")));
13456 : else
13457 18 : ereport(ERROR,
13458 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13459 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13460 : colName, format_type_be(targettype)),
13461 : !attTup->attgenerated ?
13462 : /* translator: USING is SQL, don't translate it */
13463 : errhint("You might need to specify \"USING %s::%s\".",
13464 : quote_identifier(colName),
13465 : format_type_with_typemod(targettype,
13466 : targettypmod)) : 0));
13467 : }
13468 :
13469 : /* Fix collations after all else */
13470 936 : assign_expr_collations(pstate, transform);
13471 :
13472 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13473 936 : transform = (Node *) expression_planner((Expr *) transform);
13474 :
13475 : /*
13476 : * Add a work queue item to make ATRewriteTable update the column
13477 : * contents.
13478 : */
13479 936 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13480 936 : newval->attnum = attnum;
13481 936 : newval->expr = (Expr *) transform;
13482 936 : newval->is_generated = false;
13483 :
13484 936 : tab->newvals = lappend(tab->newvals, newval);
13485 936 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13486 752 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13487 : }
13488 136 : else if (transform)
13489 12 : ereport(ERROR,
13490 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13491 : errmsg("\"%s\" is not a table",
13492 : RelationGetRelationName(rel))));
13493 :
13494 1060 : if (!RELKIND_HAS_STORAGE(tab->relkind))
13495 : {
13496 : /*
13497 : * For relations without storage, do this check now. Regular tables
13498 : * will check it later when the table is being rewritten.
13499 : */
13500 184 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13501 : }
13502 :
13503 1030 : ReleaseSysCache(tuple);
13504 :
13505 : /*
13506 : * Recurse manually by queueing a new command for each child, if
13507 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13508 : * remap attribute numbers in the USING expression, if any.
13509 : *
13510 : * If we are told not to recurse, there had better not be any child
13511 : * tables; else the alter would put them out of step.
13512 : */
13513 1030 : if (recurse)
13514 : {
13515 772 : Oid relid = RelationGetRelid(rel);
13516 : List *child_oids,
13517 : *child_numparents;
13518 : ListCell *lo,
13519 : *li;
13520 :
13521 772 : child_oids = find_all_inheritors(relid, lockmode,
13522 : &child_numparents);
13523 :
13524 : /*
13525 : * find_all_inheritors does the recursive search of the inheritance
13526 : * hierarchy, so all we have to do is process all of the relids in the
13527 : * list that it returns.
13528 : */
13529 1752 : forboth(lo, child_oids, li, child_numparents)
13530 : {
13531 1004 : Oid childrelid = lfirst_oid(lo);
13532 1004 : int numparents = lfirst_int(li);
13533 : Relation childrel;
13534 : HeapTuple childtuple;
13535 : Form_pg_attribute childattTup;
13536 :
13537 1004 : if (childrelid == relid)
13538 772 : continue;
13539 :
13540 : /* find_all_inheritors already got lock */
13541 232 : childrel = relation_open(childrelid, NoLock);
13542 232 : CheckAlterTableIsSafe(childrel);
13543 :
13544 : /*
13545 : * Verify that the child doesn't have any inherited definitions of
13546 : * this column that came from outside this inheritance hierarchy.
13547 : * (renameatt makes a similar test, though in a different way
13548 : * because of its different recursion mechanism.)
13549 : */
13550 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13551 : colName);
13552 232 : if (!HeapTupleIsValid(childtuple))
13553 0 : ereport(ERROR,
13554 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13555 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13556 : colName, RelationGetRelationName(childrel))));
13557 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13558 :
13559 232 : if (childattTup->attinhcount > numparents)
13560 6 : ereport(ERROR,
13561 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13562 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13563 : colName, RelationGetRelationName(childrel))));
13564 :
13565 226 : ReleaseSysCache(childtuple);
13566 :
13567 : /*
13568 : * Remap the attribute numbers. If no USING expression was
13569 : * specified, there is no need for this step.
13570 : */
13571 226 : if (def->cooked_default)
13572 : {
13573 : AttrMap *attmap;
13574 : bool found_whole_row;
13575 :
13576 : /* create a copy to scribble on */
13577 78 : cmd = copyObject(cmd);
13578 :
13579 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13580 : RelationGetDescr(rel),
13581 : false);
13582 156 : ((ColumnDef *) cmd->def)->cooked_default =
13583 78 : map_variable_attnos(def->cooked_default,
13584 : 1, 0,
13585 : attmap,
13586 : InvalidOid, &found_whole_row);
13587 78 : if (found_whole_row)
13588 6 : ereport(ERROR,
13589 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13590 : errmsg("cannot convert whole-row table reference"),
13591 : errdetail("USING expression contains a whole-row table reference.")));
13592 72 : pfree(attmap);
13593 : }
13594 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13595 208 : relation_close(childrel, NoLock);
13596 : }
13597 : }
13598 308 : else if (!recursing &&
13599 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13600 0 : ereport(ERROR,
13601 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13602 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13603 : colName)));
13604 :
13605 1006 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13606 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13607 1000 : }
13608 :
13609 : /*
13610 : * When the data type of a column is changed, a rewrite might not be required
13611 : * if the new type is sufficiently identical to the old one, and the USING
13612 : * clause isn't trying to insert some other value. It's safe to skip the
13613 : * rewrite in these cases:
13614 : *
13615 : * - the old type is binary coercible to the new type
13616 : * - the new type is an unconstrained domain over the old type
13617 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13618 : *
13619 : * In the case of a constrained domain, we could get by with scanning the
13620 : * table and checking the constraint rather than actually rewriting it, but we
13621 : * don't currently try to do that.
13622 : */
13623 : static bool
13624 1042 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13625 : {
13626 : Assert(expr != NULL);
13627 :
13628 : for (;;)
13629 : {
13630 : /* only one varno, so no need to check that */
13631 1042 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13632 184 : return false;
13633 858 : else if (IsA(expr, RelabelType))
13634 94 : expr = (Node *) ((RelabelType *) expr)->arg;
13635 764 : else if (IsA(expr, CoerceToDomain))
13636 : {
13637 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
13638 :
13639 0 : if (DomainHasConstraints(d->resulttype))
13640 0 : return true;
13641 0 : expr = (Node *) d->arg;
13642 : }
13643 764 : else if (IsA(expr, FuncExpr))
13644 : {
13645 564 : FuncExpr *f = (FuncExpr *) expr;
13646 :
13647 564 : switch (f->funcid)
13648 : {
13649 18 : case F_TIMESTAMPTZ_TIMESTAMP:
13650 : case F_TIMESTAMP_TIMESTAMPTZ:
13651 18 : if (TimestampTimestampTzRequiresRewrite())
13652 6 : return true;
13653 : else
13654 12 : expr = linitial(f->args);
13655 12 : break;
13656 546 : default:
13657 546 : return true;
13658 : }
13659 : }
13660 : else
13661 200 : return true;
13662 : }
13663 : }
13664 :
13665 : /*
13666 : * ALTER COLUMN .. SET DATA TYPE
13667 : *
13668 : * Return the address of the modified column.
13669 : */
13670 : static ObjectAddress
13671 970 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13672 : AlterTableCmd *cmd, LOCKMODE lockmode)
13673 : {
13674 970 : char *colName = cmd->name;
13675 970 : ColumnDef *def = (ColumnDef *) cmd->def;
13676 970 : TypeName *typeName = def->typeName;
13677 : HeapTuple heapTup;
13678 : Form_pg_attribute attTup,
13679 : attOldTup;
13680 : AttrNumber attnum;
13681 : HeapTuple typeTuple;
13682 : Form_pg_type tform;
13683 : Oid targettype;
13684 : int32 targettypmod;
13685 : Oid targetcollid;
13686 : Node *defaultexpr;
13687 : Relation attrelation;
13688 : Relation depRel;
13689 : ScanKeyData key[3];
13690 : SysScanDesc scan;
13691 : HeapTuple depTup;
13692 : ObjectAddress address;
13693 :
13694 : /*
13695 : * Clear all the missing values if we're rewriting the table, since this
13696 : * renders them pointless.
13697 : */
13698 970 : if (tab->rewrite)
13699 : {
13700 : Relation newrel;
13701 :
13702 698 : newrel = table_open(RelationGetRelid(rel), NoLock);
13703 698 : RelationClearMissing(newrel);
13704 698 : relation_close(newrel, NoLock);
13705 : /* make sure we don't conflict with later attribute modifications */
13706 698 : CommandCounterIncrement();
13707 : }
13708 :
13709 970 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13710 :
13711 : /* Look up the target column */
13712 970 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13713 970 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13714 0 : ereport(ERROR,
13715 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13716 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13717 : colName, RelationGetRelationName(rel))));
13718 970 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13719 970 : attnum = attTup->attnum;
13720 970 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13721 :
13722 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13723 970 : if (attTup->atttypid != attOldTup->atttypid ||
13724 970 : attTup->atttypmod != attOldTup->atttypmod)
13725 0 : ereport(ERROR,
13726 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13727 : errmsg("cannot alter type of column \"%s\" twice",
13728 : colName)));
13729 :
13730 : /* Look up the target type (should not fail, since prep found it) */
13731 970 : typeTuple = typenameType(NULL, typeName, &targettypmod);
13732 970 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
13733 970 : targettype = tform->oid;
13734 : /* And the collation */
13735 970 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13736 :
13737 : /*
13738 : * If there is a default expression for the column, get it and ensure we
13739 : * can coerce it to the new datatype. (We must do this before changing
13740 : * the column type, because build_column_default itself will try to
13741 : * coerce, and will not issue the error message we want if it fails.)
13742 : *
13743 : * We remove any implicit coercion steps at the top level of the old
13744 : * default expression; this has been agreed to satisfy the principle of
13745 : * least surprise. (The conversion to the new column type should act like
13746 : * it started from what the user sees as the stored expression, and the
13747 : * implicit coercions aren't going to be shown.)
13748 : */
13749 970 : if (attTup->atthasdef)
13750 : {
13751 56 : defaultexpr = build_column_default(rel, attnum);
13752 : Assert(defaultexpr);
13753 56 : defaultexpr = strip_implicit_coercions(defaultexpr);
13754 56 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13755 : defaultexpr, exprType(defaultexpr),
13756 : targettype, targettypmod,
13757 : COERCION_ASSIGNMENT,
13758 : COERCE_IMPLICIT_CAST,
13759 : -1);
13760 56 : if (defaultexpr == NULL)
13761 : {
13762 6 : if (attTup->attgenerated)
13763 0 : ereport(ERROR,
13764 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13765 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13766 : colName, format_type_be(targettype))));
13767 : else
13768 6 : ereport(ERROR,
13769 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13770 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13771 : colName, format_type_be(targettype))));
13772 : }
13773 : }
13774 : else
13775 914 : defaultexpr = NULL;
13776 :
13777 : /*
13778 : * Find everything that depends on the column (constraints, indexes, etc),
13779 : * and record enough information to let us recreate the objects.
13780 : *
13781 : * The actual recreation does not happen here, but only after we have
13782 : * performed all the individual ALTER TYPE operations. We have to save
13783 : * the info before executing ALTER TYPE, though, else the deparser will
13784 : * get confused.
13785 : */
13786 964 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13787 :
13788 : /*
13789 : * Now scan for dependencies of this column on other things. The only
13790 : * things we should find are the dependency on the column datatype and
13791 : * possibly a collation dependency. Those can be removed.
13792 : */
13793 940 : depRel = table_open(DependRelationId, RowExclusiveLock);
13794 :
13795 940 : ScanKeyInit(&key[0],
13796 : Anum_pg_depend_classid,
13797 : BTEqualStrategyNumber, F_OIDEQ,
13798 : ObjectIdGetDatum(RelationRelationId));
13799 940 : ScanKeyInit(&key[1],
13800 : Anum_pg_depend_objid,
13801 : BTEqualStrategyNumber, F_OIDEQ,
13802 : ObjectIdGetDatum(RelationGetRelid(rel)));
13803 940 : ScanKeyInit(&key[2],
13804 : Anum_pg_depend_objsubid,
13805 : BTEqualStrategyNumber, F_INT4EQ,
13806 : Int32GetDatum((int32) attnum));
13807 :
13808 940 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
13809 : NULL, 3, key);
13810 :
13811 944 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13812 : {
13813 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13814 : ObjectAddress foundObject;
13815 :
13816 4 : foundObject.classId = foundDep->refclassid;
13817 4 : foundObject.objectId = foundDep->refobjid;
13818 4 : foundObject.objectSubId = foundDep->refobjsubid;
13819 :
13820 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
13821 0 : elog(ERROR, "found unexpected dependency type '%c'",
13822 : foundDep->deptype);
13823 4 : if (!(foundDep->refclassid == TypeRelationId &&
13824 4 : foundDep->refobjid == attTup->atttypid) &&
13825 0 : !(foundDep->refclassid == CollationRelationId &&
13826 0 : foundDep->refobjid == attTup->attcollation))
13827 0 : elog(ERROR, "found unexpected dependency for column: %s",
13828 : getObjectDescription(&foundObject, false));
13829 :
13830 4 : CatalogTupleDelete(depRel, &depTup->t_self);
13831 : }
13832 :
13833 940 : systable_endscan(scan);
13834 :
13835 940 : table_close(depRel, RowExclusiveLock);
13836 :
13837 : /*
13838 : * Here we go --- change the recorded column type and collation. (Note
13839 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13840 : * fix up the missing value if any.
13841 : */
13842 940 : if (attTup->atthasmissing)
13843 : {
13844 : Datum missingval;
13845 : bool missingNull;
13846 :
13847 : /* if rewrite is true the missing value should already be cleared */
13848 : Assert(tab->rewrite == 0);
13849 :
13850 : /* Get the missing value datum */
13851 6 : missingval = heap_getattr(heapTup,
13852 : Anum_pg_attribute_attmissingval,
13853 : attrelation->rd_att,
13854 : &missingNull);
13855 :
13856 : /* if it's a null array there is nothing to do */
13857 :
13858 6 : if (!missingNull)
13859 : {
13860 : /*
13861 : * Get the datum out of the array and repack it in a new array
13862 : * built with the new type data. We assume that since the table
13863 : * doesn't need rewriting, the actual Datum doesn't need to be
13864 : * changed, only the array metadata.
13865 : */
13866 :
13867 6 : int one = 1;
13868 : bool isNull;
13869 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
13870 6 : bool nullsAtt[Natts_pg_attribute] = {0};
13871 6 : bool replacesAtt[Natts_pg_attribute] = {0};
13872 : HeapTuple newTup;
13873 :
13874 12 : missingval = array_get_element(missingval,
13875 : 1,
13876 : &one,
13877 : 0,
13878 6 : attTup->attlen,
13879 6 : attTup->attbyval,
13880 6 : attTup->attalign,
13881 : &isNull);
13882 6 : missingval = PointerGetDatum(construct_array(&missingval,
13883 : 1,
13884 : targettype,
13885 6 : tform->typlen,
13886 6 : tform->typbyval,
13887 6 : tform->typalign));
13888 :
13889 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13890 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13891 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13892 :
13893 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13894 : valuesAtt, nullsAtt, replacesAtt);
13895 6 : heap_freetuple(heapTup);
13896 6 : heapTup = newTup;
13897 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13898 : }
13899 : }
13900 :
13901 940 : attTup->atttypid = targettype;
13902 940 : attTup->atttypmod = targettypmod;
13903 940 : attTup->attcollation = targetcollid;
13904 940 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13905 0 : ereport(ERROR,
13906 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13907 : errmsg("too many array dimensions"));
13908 940 : attTup->attndims = list_length(typeName->arrayBounds);
13909 940 : attTup->attlen = tform->typlen;
13910 940 : attTup->attbyval = tform->typbyval;
13911 940 : attTup->attalign = tform->typalign;
13912 940 : attTup->attstorage = tform->typstorage;
13913 940 : attTup->attcompression = InvalidCompressionMethod;
13914 :
13915 940 : ReleaseSysCache(typeTuple);
13916 :
13917 940 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13918 :
13919 940 : table_close(attrelation, RowExclusiveLock);
13920 :
13921 : /* Install dependencies on new datatype and collation */
13922 940 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13923 940 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13924 :
13925 : /*
13926 : * Drop any pg_statistic entry for the column, since it's now wrong type
13927 : */
13928 940 : RemoveStatistics(RelationGetRelid(rel), attnum);
13929 :
13930 940 : InvokeObjectPostAlterHook(RelationRelationId,
13931 : RelationGetRelid(rel), attnum);
13932 :
13933 : /*
13934 : * Update the default, if present, by brute force --- remove and re-add
13935 : * the default. Probably unsafe to take shortcuts, since the new version
13936 : * may well have additional dependencies. (It's okay to do this now,
13937 : * rather than after other ALTER TYPE commands, since the default won't
13938 : * depend on other column types.)
13939 : */
13940 940 : if (defaultexpr)
13941 : {
13942 : /*
13943 : * If it's a GENERATED default, drop its dependency records, in
13944 : * particular its INTERNAL dependency on the column, which would
13945 : * otherwise cause dependency.c to refuse to perform the deletion.
13946 : */
13947 50 : if (attTup->attgenerated)
13948 : {
13949 6 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13950 :
13951 6 : if (!OidIsValid(attrdefoid))
13952 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13953 : RelationGetRelid(rel), attnum);
13954 6 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13955 : }
13956 :
13957 : /*
13958 : * Make updates-so-far visible, particularly the new pg_attribute row
13959 : * which will be updated again.
13960 : */
13961 50 : CommandCounterIncrement();
13962 :
13963 : /*
13964 : * We use RESTRICT here for safety, but at present we do not expect
13965 : * anything to depend on the default.
13966 : */
13967 50 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13968 : true);
13969 :
13970 50 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13971 : }
13972 :
13973 940 : ObjectAddressSubSet(address, RelationRelationId,
13974 : RelationGetRelid(rel), attnum);
13975 :
13976 : /* Cleanup */
13977 940 : heap_freetuple(heapTup);
13978 :
13979 940 : return address;
13980 : }
13981 :
13982 : /*
13983 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13984 : * that depends on the column (constraints, indexes, etc), and record enough
13985 : * information to let us recreate the objects.
13986 : */
13987 : static void
13988 1042 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13989 : Relation rel, AttrNumber attnum, const char *colName)
13990 : {
13991 : Relation depRel;
13992 : ScanKeyData key[3];
13993 : SysScanDesc scan;
13994 : HeapTuple depTup;
13995 :
13996 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13997 :
13998 1042 : depRel = table_open(DependRelationId, RowExclusiveLock);
13999 :
14000 1042 : ScanKeyInit(&key[0],
14001 : Anum_pg_depend_refclassid,
14002 : BTEqualStrategyNumber, F_OIDEQ,
14003 : ObjectIdGetDatum(RelationRelationId));
14004 1042 : ScanKeyInit(&key[1],
14005 : Anum_pg_depend_refobjid,
14006 : BTEqualStrategyNumber, F_OIDEQ,
14007 : ObjectIdGetDatum(RelationGetRelid(rel)));
14008 1042 : ScanKeyInit(&key[2],
14009 : Anum_pg_depend_refobjsubid,
14010 : BTEqualStrategyNumber, F_INT4EQ,
14011 : Int32GetDatum((int32) attnum));
14012 :
14013 1042 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14014 : NULL, 3, key);
14015 :
14016 2132 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14017 : {
14018 1114 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14019 : ObjectAddress foundObject;
14020 :
14021 1114 : foundObject.classId = foundDep->classid;
14022 1114 : foundObject.objectId = foundDep->objid;
14023 1114 : foundObject.objectSubId = foundDep->objsubid;
14024 :
14025 1114 : switch (foundObject.classId)
14026 : {
14027 274 : case RelationRelationId:
14028 : {
14029 274 : char relKind = get_rel_relkind(foundObject.objectId);
14030 :
14031 274 : if (relKind == RELKIND_INDEX ||
14032 : relKind == RELKIND_PARTITIONED_INDEX)
14033 : {
14034 : Assert(foundObject.objectSubId == 0);
14035 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
14036 : }
14037 38 : else if (relKind == RELKIND_SEQUENCE)
14038 : {
14039 : /*
14040 : * This must be a SERIAL column's sequence. We need
14041 : * not do anything to it.
14042 : */
14043 : Assert(foundObject.objectSubId == 0);
14044 : }
14045 : else
14046 : {
14047 : /* Not expecting any other direct dependencies... */
14048 0 : elog(ERROR, "unexpected object depending on column: %s",
14049 : getObjectDescription(&foundObject, false));
14050 : }
14051 274 : break;
14052 : }
14053 :
14054 674 : case ConstraintRelationId:
14055 : Assert(foundObject.objectSubId == 0);
14056 674 : RememberConstraintForRebuilding(foundObject.objectId, tab);
14057 674 : break;
14058 :
14059 0 : case ProcedureRelationId:
14060 :
14061 : /*
14062 : * A new-style SQL function can depend on a column, if that
14063 : * column is referenced in the parsed function body. Ideally
14064 : * we'd automatically update the function by deparsing and
14065 : * reparsing it, but that's risky and might well fail anyhow.
14066 : * FIXME someday.
14067 : *
14068 : * This is only a problem for AT_AlterColumnType, not
14069 : * AT_SetExpression.
14070 : */
14071 0 : if (subtype == AT_AlterColumnType)
14072 0 : ereport(ERROR,
14073 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14074 : errmsg("cannot alter type of a column used by a function or procedure"),
14075 : errdetail("%s depends on column \"%s\"",
14076 : getObjectDescription(&foundObject, false),
14077 : colName)));
14078 0 : break;
14079 :
14080 12 : case RewriteRelationId:
14081 :
14082 : /*
14083 : * View/rule bodies have pretty much the same issues as
14084 : * function bodies. FIXME someday.
14085 : */
14086 12 : if (subtype == AT_AlterColumnType)
14087 12 : ereport(ERROR,
14088 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14089 : errmsg("cannot alter type of a column used by a view or rule"),
14090 : errdetail("%s depends on column \"%s\"",
14091 : getObjectDescription(&foundObject, false),
14092 : colName)));
14093 0 : break;
14094 :
14095 0 : case TriggerRelationId:
14096 :
14097 : /*
14098 : * A trigger can depend on a column because the column is
14099 : * specified as an update target, or because the column is
14100 : * used in the trigger's WHEN condition. The first case would
14101 : * not require any extra work, but the second case would
14102 : * require updating the WHEN expression, which has the same
14103 : * issues as above. Since we can't easily tell which case
14104 : * applies, we punt for both. FIXME someday.
14105 : */
14106 0 : if (subtype == AT_AlterColumnType)
14107 0 : ereport(ERROR,
14108 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14109 : errmsg("cannot alter type of a column used in a trigger definition"),
14110 : errdetail("%s depends on column \"%s\"",
14111 : getObjectDescription(&foundObject, false),
14112 : colName)));
14113 0 : break;
14114 :
14115 0 : case PolicyRelationId:
14116 :
14117 : /*
14118 : * A policy can depend on a column because the column is
14119 : * specified in the policy's USING or WITH CHECK qual
14120 : * expressions. It might be possible to rewrite and recheck
14121 : * the policy expression, but punt for now. It's certainly
14122 : * easy enough to remove and recreate the policy; still, FIXME
14123 : * someday.
14124 : */
14125 0 : if (subtype == AT_AlterColumnType)
14126 0 : ereport(ERROR,
14127 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14128 : errmsg("cannot alter type of a column used in a policy definition"),
14129 : errdetail("%s depends on column \"%s\"",
14130 : getObjectDescription(&foundObject, false),
14131 : colName)));
14132 0 : break;
14133 :
14134 140 : case AttrDefaultRelationId:
14135 : {
14136 140 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14137 :
14138 140 : if (col.objectId == RelationGetRelid(rel) &&
14139 140 : col.objectSubId == attnum)
14140 : {
14141 : /*
14142 : * Ignore the column's own default expression. The
14143 : * caller deals with it.
14144 : */
14145 : }
14146 : else
14147 : {
14148 : /*
14149 : * This must be a reference from the expression of a
14150 : * generated column elsewhere in the same table.
14151 : * Changing the type/generated expression of a column
14152 : * that is used by a generated column is not allowed
14153 : * by SQL standard, so just punt for now. It might be
14154 : * doable with some thinking and effort.
14155 : */
14156 12 : if (subtype == AT_AlterColumnType)
14157 12 : ereport(ERROR,
14158 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14159 : errmsg("cannot alter type of a column used by a generated column"),
14160 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14161 : colName,
14162 : get_attname(col.objectId,
14163 : col.objectSubId,
14164 : false))));
14165 : }
14166 128 : break;
14167 : }
14168 :
14169 14 : case StatisticExtRelationId:
14170 :
14171 : /*
14172 : * Give the extended-stats machinery a chance to fix anything
14173 : * that this column type change would break.
14174 : */
14175 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14176 14 : break;
14177 :
14178 0 : case PublicationRelRelationId:
14179 :
14180 : /*
14181 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14182 : * clause. Same issues as above. FIXME someday.
14183 : */
14184 0 : if (subtype == AT_AlterColumnType)
14185 0 : ereport(ERROR,
14186 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14187 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
14188 : errdetail("%s depends on column \"%s\"",
14189 : getObjectDescription(&foundObject, false),
14190 : colName)));
14191 0 : break;
14192 :
14193 0 : default:
14194 :
14195 : /*
14196 : * We don't expect any other sorts of objects to depend on a
14197 : * column.
14198 : */
14199 0 : elog(ERROR, "unexpected object depending on column: %s",
14200 : getObjectDescription(&foundObject, false));
14201 : break;
14202 : }
14203 : }
14204 :
14205 1018 : systable_endscan(scan);
14206 1018 : table_close(depRel, NoLock);
14207 1018 : }
14208 :
14209 : /*
14210 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14211 : * needs to be reset.
14212 : */
14213 : static void
14214 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14215 : {
14216 444 : if (!get_index_isreplident(indoid))
14217 426 : return;
14218 :
14219 18 : if (tab->replicaIdentityIndex)
14220 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14221 :
14222 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14223 : }
14224 :
14225 : /*
14226 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14227 : */
14228 : static void
14229 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14230 : {
14231 444 : if (!get_index_isclustered(indoid))
14232 426 : return;
14233 :
14234 18 : if (tab->clusterOnIndex)
14235 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14236 :
14237 18 : tab->clusterOnIndex = get_rel_name(indoid);
14238 : }
14239 :
14240 : /*
14241 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14242 : * to be rebuilt (which we might already know).
14243 : */
14244 : static void
14245 686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14246 : {
14247 : /*
14248 : * This de-duplication check is critical for two independent reasons: we
14249 : * mustn't try to recreate the same constraint twice, and if a constraint
14250 : * depends on more than one column whose type is to be altered, we must
14251 : * capture its definition string before applying any of the column type
14252 : * changes. ruleutils.c will get confused if we ask again later.
14253 : */
14254 686 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14255 : {
14256 : /* OK, capture the constraint's existing definition string */
14257 596 : char *defstring = pg_get_constraintdef_command(conoid);
14258 : Oid indoid;
14259 :
14260 : /*
14261 : * It is critical to create not-null constraints ahead of primary key
14262 : * indexes; otherwise, the not-null constraint would be created by the
14263 : * primary key, and the constraint name would be wrong.
14264 : */
14265 596 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14266 : {
14267 198 : tab->changedConstraintOids = lcons_oid(conoid,
14268 : tab->changedConstraintOids);
14269 198 : tab->changedConstraintDefs = lcons(defstring,
14270 : tab->changedConstraintDefs);
14271 : }
14272 : else
14273 : {
14274 :
14275 398 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14276 : conoid);
14277 398 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14278 : defstring);
14279 : }
14280 :
14281 : /*
14282 : * For the index of a constraint, if any, remember if it is used for
14283 : * the table's replica identity or if it is a clustered index, so that
14284 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14285 : * those properties.
14286 : */
14287 596 : indoid = get_constraint_index(conoid);
14288 596 : if (OidIsValid(indoid))
14289 : {
14290 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
14291 228 : RememberClusterOnForRebuilding(indoid, tab);
14292 : }
14293 : }
14294 686 : }
14295 :
14296 : /*
14297 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14298 : * to be rebuilt (which we might already know).
14299 : */
14300 : static void
14301 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14302 : {
14303 : /*
14304 : * This de-duplication check is critical for two independent reasons: we
14305 : * mustn't try to recreate the same index twice, and if an index depends
14306 : * on more than one column whose type is to be altered, we must capture
14307 : * its definition string before applying any of the column type changes.
14308 : * ruleutils.c will get confused if we ask again later.
14309 : */
14310 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
14311 : {
14312 : /*
14313 : * Before adding it as an index-to-rebuild, we'd better see if it
14314 : * belongs to a constraint, and if so rebuild the constraint instead.
14315 : * Typically this check fails, because constraint indexes normally
14316 : * have only dependencies on their constraint. But it's possible for
14317 : * such an index to also have direct dependencies on table columns,
14318 : * for example with a partial exclusion constraint.
14319 : */
14320 228 : Oid conoid = get_index_constraint(indoid);
14321 :
14322 228 : if (OidIsValid(conoid))
14323 : {
14324 12 : RememberConstraintForRebuilding(conoid, tab);
14325 : }
14326 : else
14327 : {
14328 : /* OK, capture the index's existing definition string */
14329 216 : char *defstring = pg_get_indexdef_string(indoid);
14330 :
14331 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14332 : indoid);
14333 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14334 : defstring);
14335 :
14336 : /*
14337 : * Remember if this index is used for the table's replica identity
14338 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14339 : * can queue up commands necessary to restore those properties.
14340 : */
14341 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
14342 216 : RememberClusterOnForRebuilding(indoid, tab);
14343 : }
14344 : }
14345 236 : }
14346 :
14347 : /*
14348 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14349 : * needs to be rebuilt (which we might already know).
14350 : */
14351 : static void
14352 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14353 : {
14354 : /*
14355 : * This de-duplication check is critical for two independent reasons: we
14356 : * mustn't try to recreate the same statistics object twice, and if the
14357 : * statistics object depends on more than one column whose type is to be
14358 : * altered, we must capture its definition string before applying any of
14359 : * the type changes. ruleutils.c will get confused if we ask again later.
14360 : */
14361 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14362 : {
14363 : /* OK, capture the statistics object's existing definition string */
14364 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14365 :
14366 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14367 : stxoid);
14368 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14369 : defstring);
14370 : }
14371 14 : }
14372 :
14373 : /*
14374 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14375 : * operations for a particular relation. We have to drop and recreate all the
14376 : * indexes and constraints that depend on the altered columns. We do the
14377 : * actual dropping here, but re-creation is managed by adding work queue
14378 : * entries to do those steps later.
14379 : */
14380 : static void
14381 988 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14382 : {
14383 : ObjectAddress obj;
14384 : ObjectAddresses *objects;
14385 : ListCell *def_item;
14386 : ListCell *oid_item;
14387 :
14388 : /*
14389 : * Collect all the constraints and indexes to drop so we can process them
14390 : * in a single call. That way we don't have to worry about dependencies
14391 : * among them.
14392 : */
14393 988 : objects = new_object_addresses();
14394 :
14395 : /*
14396 : * Re-parse the index and constraint definitions, and attach them to the
14397 : * appropriate work queue entries. We do this before dropping because in
14398 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14399 : * lock on the table the constraint is attached to, and we need to get
14400 : * that before reparsing/dropping.
14401 : *
14402 : * We can't rely on the output of deparsing to tell us which relation to
14403 : * operate on, because concurrent activity might have made the name
14404 : * resolve differently. Instead, we've got to use the OID of the
14405 : * constraint or index we're processing to figure out which relation to
14406 : * operate on.
14407 : */
14408 1584 : forboth(oid_item, tab->changedConstraintOids,
14409 : def_item, tab->changedConstraintDefs)
14410 : {
14411 596 : Oid oldId = lfirst_oid(oid_item);
14412 : HeapTuple tup;
14413 : Form_pg_constraint con;
14414 : Oid relid;
14415 : Oid confrelid;
14416 : char contype;
14417 : bool conislocal;
14418 :
14419 596 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14420 596 : if (!HeapTupleIsValid(tup)) /* should not happen */
14421 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14422 596 : con = (Form_pg_constraint) GETSTRUCT(tup);
14423 596 : if (OidIsValid(con->conrelid))
14424 582 : relid = con->conrelid;
14425 : else
14426 : {
14427 : /* must be a domain constraint */
14428 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14429 14 : if (!OidIsValid(relid))
14430 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14431 : }
14432 596 : confrelid = con->confrelid;
14433 596 : contype = con->contype;
14434 596 : conislocal = con->conislocal;
14435 596 : ReleaseSysCache(tup);
14436 :
14437 596 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14438 596 : add_exact_object_address(&obj, objects);
14439 :
14440 : /*
14441 : * If the constraint is inherited (only), we don't want to inject a
14442 : * new definition here; it'll get recreated when
14443 : * ATAddCheckNNConstraint recurses from adding the parent table's
14444 : * constraint. But we had to carry the info this far so that we can
14445 : * drop the constraint below.
14446 : */
14447 596 : if (!conislocal)
14448 28 : continue;
14449 :
14450 : /*
14451 : * When rebuilding an FK constraint that references the table we're
14452 : * modifying, we might not yet have any lock on the FK's table, so get
14453 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14454 : * step, so there's no value in asking for anything weaker.
14455 : */
14456 568 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14457 36 : LockRelationOid(relid, AccessExclusiveLock);
14458 :
14459 568 : ATPostAlterTypeParse(oldId, relid, confrelid,
14460 568 : (char *) lfirst(def_item),
14461 568 : wqueue, lockmode, tab->rewrite);
14462 : }
14463 1204 : forboth(oid_item, tab->changedIndexOids,
14464 : def_item, tab->changedIndexDefs)
14465 : {
14466 216 : Oid oldId = lfirst_oid(oid_item);
14467 : Oid relid;
14468 :
14469 216 : relid = IndexGetRelation(oldId, false);
14470 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14471 216 : (char *) lfirst(def_item),
14472 216 : wqueue, lockmode, tab->rewrite);
14473 :
14474 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
14475 216 : add_exact_object_address(&obj, objects);
14476 : }
14477 :
14478 : /* add dependencies for new statistics */
14479 1002 : forboth(oid_item, tab->changedStatisticsOids,
14480 : def_item, tab->changedStatisticsDefs)
14481 : {
14482 14 : Oid oldId = lfirst_oid(oid_item);
14483 : Oid relid;
14484 :
14485 14 : relid = StatisticsGetRelation(oldId, false);
14486 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14487 14 : (char *) lfirst(def_item),
14488 14 : wqueue, lockmode, tab->rewrite);
14489 :
14490 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14491 14 : add_exact_object_address(&obj, objects);
14492 : }
14493 :
14494 : /*
14495 : * Queue up command to restore replica identity index marking
14496 : */
14497 988 : if (tab->replicaIdentityIndex)
14498 : {
14499 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14500 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14501 :
14502 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14503 18 : subcmd->name = tab->replicaIdentityIndex;
14504 18 : cmd->subtype = AT_ReplicaIdentity;
14505 18 : cmd->def = (Node *) subcmd;
14506 :
14507 : /* do it after indexes and constraints */
14508 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14509 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14510 : }
14511 :
14512 : /*
14513 : * Queue up command to restore marking of index used for cluster.
14514 : */
14515 988 : if (tab->clusterOnIndex)
14516 : {
14517 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14518 :
14519 18 : cmd->subtype = AT_ClusterOn;
14520 18 : cmd->name = tab->clusterOnIndex;
14521 :
14522 : /* do it after indexes and constraints */
14523 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14524 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14525 : }
14526 :
14527 : /*
14528 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14529 : * be depending on these objects.
14530 : */
14531 988 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14532 :
14533 988 : free_object_addresses(objects);
14534 :
14535 : /*
14536 : * The objects will get recreated during subsequent passes over the work
14537 : * queue.
14538 : */
14539 988 : }
14540 :
14541 : /*
14542 : * Parse the previously-saved definition string for a constraint, index or
14543 : * statistics object against the newly-established column data type(s), and
14544 : * queue up the resulting command parsetrees for execution.
14545 : *
14546 : * This might fail if, for example, you have a WHERE clause that uses an
14547 : * operator that's not available for the new column type.
14548 : */
14549 : static void
14550 798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14551 : List **wqueue, LOCKMODE lockmode, bool rewrite)
14552 : {
14553 : List *raw_parsetree_list;
14554 : List *querytree_list;
14555 : ListCell *list_item;
14556 : Relation rel;
14557 :
14558 : /*
14559 : * We expect that we will get only ALTER TABLE and CREATE INDEX
14560 : * statements. Hence, there is no need to pass them through
14561 : * parse_analyze_*() or the rewriter, but instead we need to pass them
14562 : * through parse_utilcmd.c to make them ready for execution.
14563 : */
14564 798 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14565 798 : querytree_list = NIL;
14566 1596 : foreach(list_item, raw_parsetree_list)
14567 : {
14568 798 : RawStmt *rs = lfirst_node(RawStmt, list_item);
14569 798 : Node *stmt = rs->stmt;
14570 :
14571 798 : if (IsA(stmt, IndexStmt))
14572 216 : querytree_list = lappend(querytree_list,
14573 216 : transformIndexStmt(oldRelId,
14574 : (IndexStmt *) stmt,
14575 : cmd));
14576 582 : else if (IsA(stmt, AlterTableStmt))
14577 : {
14578 : List *beforeStmts;
14579 : List *afterStmts;
14580 :
14581 554 : stmt = (Node *) transformAlterTableStmt(oldRelId,
14582 : (AlterTableStmt *) stmt,
14583 : cmd,
14584 : &beforeStmts,
14585 : &afterStmts);
14586 554 : querytree_list = list_concat(querytree_list, beforeStmts);
14587 554 : querytree_list = lappend(querytree_list, stmt);
14588 554 : querytree_list = list_concat(querytree_list, afterStmts);
14589 : }
14590 28 : else if (IsA(stmt, CreateStatsStmt))
14591 14 : querytree_list = lappend(querytree_list,
14592 14 : transformStatsStmt(oldRelId,
14593 : (CreateStatsStmt *) stmt,
14594 : cmd));
14595 : else
14596 14 : querytree_list = lappend(querytree_list, stmt);
14597 : }
14598 :
14599 : /* Caller should already have acquired whatever lock we need. */
14600 798 : rel = relation_open(oldRelId, NoLock);
14601 :
14602 : /*
14603 : * Attach each generated command to the proper place in the work queue.
14604 : * Note this could result in creation of entirely new work-queue entries.
14605 : *
14606 : * Also note that we have to tweak the command subtypes, because it turns
14607 : * out that re-creation of indexes and constraints has to act a bit
14608 : * differently from initial creation.
14609 : */
14610 1596 : foreach(list_item, querytree_list)
14611 : {
14612 798 : Node *stm = (Node *) lfirst(list_item);
14613 : AlteredTableInfo *tab;
14614 :
14615 798 : tab = ATGetQueueEntry(wqueue, rel);
14616 :
14617 798 : if (IsA(stm, IndexStmt))
14618 : {
14619 216 : IndexStmt *stmt = (IndexStmt *) stm;
14620 : AlterTableCmd *newcmd;
14621 :
14622 216 : if (!rewrite)
14623 56 : TryReuseIndex(oldId, stmt);
14624 216 : stmt->reset_default_tblspc = true;
14625 : /* keep the index's comment */
14626 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14627 :
14628 216 : newcmd = makeNode(AlterTableCmd);
14629 216 : newcmd->subtype = AT_ReAddIndex;
14630 216 : newcmd->def = (Node *) stmt;
14631 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
14632 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14633 : }
14634 582 : else if (IsA(stm, AlterTableStmt))
14635 : {
14636 554 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14637 : ListCell *lcmd;
14638 :
14639 1108 : foreach(lcmd, stmt->cmds)
14640 : {
14641 554 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14642 :
14643 554 : if (cmd->subtype == AT_AddIndex)
14644 : {
14645 : IndexStmt *indstmt;
14646 : Oid indoid;
14647 :
14648 228 : indstmt = castNode(IndexStmt, cmd->def);
14649 228 : indoid = get_constraint_index(oldId);
14650 :
14651 228 : if (!rewrite)
14652 48 : TryReuseIndex(indoid, indstmt);
14653 : /* keep any comment on the index */
14654 228 : indstmt->idxcomment = GetComment(indoid,
14655 : RelationRelationId, 0);
14656 228 : indstmt->reset_default_tblspc = true;
14657 :
14658 228 : cmd->subtype = AT_ReAddIndex;
14659 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
14660 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14661 :
14662 : /* recreate any comment on the constraint */
14663 228 : RebuildConstraintComment(tab,
14664 : AT_PASS_OLD_INDEX,
14665 : oldId,
14666 : rel,
14667 : NIL,
14668 228 : indstmt->idxname);
14669 : }
14670 326 : else if (cmd->subtype == AT_AddConstraint)
14671 : {
14672 326 : Constraint *con = castNode(Constraint, cmd->def);
14673 :
14674 326 : con->old_pktable_oid = refRelId;
14675 : /* rewriting neither side of a FK */
14676 326 : if (con->contype == CONSTR_FOREIGN &&
14677 72 : !rewrite && tab->rewrite == 0)
14678 6 : TryReuseForeignKey(oldId, con);
14679 326 : con->reset_default_tblspc = true;
14680 326 : cmd->subtype = AT_ReAddConstraint;
14681 326 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14682 326 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14683 :
14684 : /*
14685 : * Recreate any comment on the constraint. If we have
14686 : * recreated a primary key, then transformTableConstraint
14687 : * has added an unnamed not-null constraint here; skip
14688 : * this in that case.
14689 : */
14690 326 : if (con->conname)
14691 326 : RebuildConstraintComment(tab,
14692 : AT_PASS_OLD_CONSTR,
14693 : oldId,
14694 : rel,
14695 : NIL,
14696 326 : con->conname);
14697 : else
14698 : Assert(con->contype == CONSTR_NOTNULL);
14699 : }
14700 : else
14701 0 : elog(ERROR, "unexpected statement subtype: %d",
14702 : (int) cmd->subtype);
14703 : }
14704 : }
14705 28 : else if (IsA(stm, AlterDomainStmt))
14706 : {
14707 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14708 :
14709 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14710 : {
14711 14 : Constraint *con = castNode(Constraint, stmt->def);
14712 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14713 :
14714 14 : cmd->subtype = AT_ReAddDomainConstraint;
14715 14 : cmd->def = (Node *) stmt;
14716 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14717 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14718 :
14719 : /* recreate any comment on the constraint */
14720 14 : RebuildConstraintComment(tab,
14721 : AT_PASS_OLD_CONSTR,
14722 : oldId,
14723 : NULL,
14724 : stmt->typeName,
14725 14 : con->conname);
14726 : }
14727 : else
14728 0 : elog(ERROR, "unexpected statement subtype: %d",
14729 : (int) stmt->subtype);
14730 : }
14731 14 : else if (IsA(stm, CreateStatsStmt))
14732 : {
14733 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14734 : AlterTableCmd *newcmd;
14735 :
14736 : /* keep the statistics object's comment */
14737 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14738 :
14739 14 : newcmd = makeNode(AlterTableCmd);
14740 14 : newcmd->subtype = AT_ReAddStatistics;
14741 14 : newcmd->def = (Node *) stmt;
14742 14 : tab->subcmds[AT_PASS_MISC] =
14743 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14744 : }
14745 : else
14746 0 : elog(ERROR, "unexpected statement type: %d",
14747 : (int) nodeTag(stm));
14748 : }
14749 :
14750 798 : relation_close(rel, NoLock);
14751 798 : }
14752 :
14753 : /*
14754 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14755 : * for a table or domain constraint that is being rebuilt.
14756 : *
14757 : * objid is the OID of the constraint.
14758 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14759 : * as a string list) for a domain constraint.
14760 : * (We could dig that info, as well as the conname, out of the pg_constraint
14761 : * entry; but callers already have them so might as well pass them.)
14762 : */
14763 : static void
14764 568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14765 : Relation rel, List *domname,
14766 : const char *conname)
14767 : {
14768 : CommentStmt *cmd;
14769 : char *comment_str;
14770 : AlterTableCmd *newcmd;
14771 :
14772 : /* Look for comment for object wanted, and leave if none */
14773 568 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14774 568 : if (comment_str == NULL)
14775 478 : return;
14776 :
14777 : /* Build CommentStmt node, copying all input data for safety */
14778 90 : cmd = makeNode(CommentStmt);
14779 90 : if (rel)
14780 : {
14781 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
14782 78 : cmd->object = (Node *)
14783 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14784 : makeString(pstrdup(RelationGetRelationName(rel))),
14785 : makeString(pstrdup(conname)));
14786 : }
14787 : else
14788 : {
14789 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
14790 12 : cmd->object = (Node *)
14791 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
14792 : makeString(pstrdup(conname)));
14793 : }
14794 90 : cmd->comment = comment_str;
14795 :
14796 : /* Append it to list of commands */
14797 90 : newcmd = makeNode(AlterTableCmd);
14798 90 : newcmd->subtype = AT_ReAddComment;
14799 90 : newcmd->def = (Node *) cmd;
14800 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14801 : }
14802 :
14803 : /*
14804 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14805 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14806 : */
14807 : static void
14808 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14809 : {
14810 104 : if (CheckIndexCompatible(oldId,
14811 104 : stmt->accessMethod,
14812 104 : stmt->indexParams,
14813 104 : stmt->excludeOpNames,
14814 104 : stmt->iswithoutoverlaps))
14815 : {
14816 104 : Relation irel = index_open(oldId, NoLock);
14817 :
14818 : /* If it's a partitioned index, there is no storage to share. */
14819 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14820 : {
14821 74 : stmt->oldNumber = irel->rd_locator.relNumber;
14822 74 : stmt->oldCreateSubid = irel->rd_createSubid;
14823 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14824 : }
14825 104 : index_close(irel, NoLock);
14826 : }
14827 104 : }
14828 :
14829 : /*
14830 : * Subroutine for ATPostAlterTypeParse().
14831 : *
14832 : * Stash the old P-F equality operator into the Constraint node, for possible
14833 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14834 : * this constraint can be skipped.
14835 : */
14836 : static void
14837 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
14838 : {
14839 : HeapTuple tup;
14840 : Datum adatum;
14841 : ArrayType *arr;
14842 : Oid *rawarr;
14843 : int numkeys;
14844 : int i;
14845 :
14846 : Assert(con->contype == CONSTR_FOREIGN);
14847 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14848 :
14849 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14850 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
14851 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14852 :
14853 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14854 : Anum_pg_constraint_conpfeqop);
14855 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14856 6 : numkeys = ARR_DIMS(arr)[0];
14857 : /* test follows the one in ri_FetchConstraintInfo() */
14858 6 : if (ARR_NDIM(arr) != 1 ||
14859 6 : ARR_HASNULL(arr) ||
14860 6 : ARR_ELEMTYPE(arr) != OIDOID)
14861 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
14862 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14863 :
14864 : /* stash a List of the operator Oids in our Constraint node */
14865 12 : for (i = 0; i < numkeys; i++)
14866 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14867 :
14868 6 : ReleaseSysCache(tup);
14869 6 : }
14870 :
14871 : /*
14872 : * ALTER COLUMN .. OPTIONS ( ... )
14873 : *
14874 : * Returns the address of the modified column
14875 : */
14876 : static ObjectAddress
14877 172 : ATExecAlterColumnGenericOptions(Relation rel,
14878 : const char *colName,
14879 : List *options,
14880 : LOCKMODE lockmode)
14881 : {
14882 : Relation ftrel;
14883 : Relation attrel;
14884 : ForeignServer *server;
14885 : ForeignDataWrapper *fdw;
14886 : HeapTuple tuple;
14887 : HeapTuple newtuple;
14888 : bool isnull;
14889 : Datum repl_val[Natts_pg_attribute];
14890 : bool repl_null[Natts_pg_attribute];
14891 : bool repl_repl[Natts_pg_attribute];
14892 : Datum datum;
14893 : Form_pg_foreign_table fttableform;
14894 : Form_pg_attribute atttableform;
14895 : AttrNumber attnum;
14896 : ObjectAddress address;
14897 :
14898 172 : if (options == NIL)
14899 0 : return InvalidObjectAddress;
14900 :
14901 : /* First, determine FDW validator associated to the foreign table. */
14902 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14903 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14904 172 : if (!HeapTupleIsValid(tuple))
14905 0 : ereport(ERROR,
14906 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14907 : errmsg("foreign table \"%s\" does not exist",
14908 : RelationGetRelationName(rel))));
14909 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14910 172 : server = GetForeignServer(fttableform->ftserver);
14911 172 : fdw = GetForeignDataWrapper(server->fdwid);
14912 :
14913 172 : table_close(ftrel, AccessShareLock);
14914 172 : ReleaseSysCache(tuple);
14915 :
14916 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
14917 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14918 172 : if (!HeapTupleIsValid(tuple))
14919 0 : ereport(ERROR,
14920 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14921 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14922 : colName, RelationGetRelationName(rel))));
14923 :
14924 : /* Prevent them from altering a system attribute */
14925 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14926 172 : attnum = atttableform->attnum;
14927 172 : if (attnum <= 0)
14928 6 : ereport(ERROR,
14929 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14930 : errmsg("cannot alter system column \"%s\"", colName)));
14931 :
14932 :
14933 : /* Initialize buffers for new tuple values */
14934 166 : memset(repl_val, 0, sizeof(repl_val));
14935 166 : memset(repl_null, false, sizeof(repl_null));
14936 166 : memset(repl_repl, false, sizeof(repl_repl));
14937 :
14938 : /* Extract the current options */
14939 166 : datum = SysCacheGetAttr(ATTNAME,
14940 : tuple,
14941 : Anum_pg_attribute_attfdwoptions,
14942 : &isnull);
14943 166 : if (isnull)
14944 156 : datum = PointerGetDatum(NULL);
14945 :
14946 : /* Transform the options */
14947 166 : datum = transformGenericOptions(AttributeRelationId,
14948 : datum,
14949 : options,
14950 : fdw->fdwvalidator);
14951 :
14952 166 : if (PointerIsValid(DatumGetPointer(datum)))
14953 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14954 : else
14955 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14956 :
14957 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14958 :
14959 : /* Everything looks good - update the tuple */
14960 :
14961 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14962 : repl_val, repl_null, repl_repl);
14963 :
14964 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14965 :
14966 166 : InvokeObjectPostAlterHook(RelationRelationId,
14967 : RelationGetRelid(rel),
14968 : atttableform->attnum);
14969 166 : ObjectAddressSubSet(address, RelationRelationId,
14970 : RelationGetRelid(rel), attnum);
14971 :
14972 166 : ReleaseSysCache(tuple);
14973 :
14974 166 : table_close(attrel, RowExclusiveLock);
14975 :
14976 166 : heap_freetuple(newtuple);
14977 :
14978 166 : return address;
14979 : }
14980 :
14981 : /*
14982 : * ALTER TABLE OWNER
14983 : *
14984 : * recursing is true if we are recursing from a table to its indexes,
14985 : * sequences, or toast table. We don't allow the ownership of those things to
14986 : * be changed separately from the parent table. Also, we can skip permission
14987 : * checks (this is necessary not just an optimization, else we'd fail to
14988 : * handle toast tables properly).
14989 : *
14990 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14991 : * free-standing composite type.
14992 : */
14993 : void
14994 2022 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14995 : {
14996 : Relation target_rel;
14997 : Relation class_rel;
14998 : HeapTuple tuple;
14999 : Form_pg_class tuple_class;
15000 :
15001 : /*
15002 : * Get exclusive lock till end of transaction on the target table. Use
15003 : * relation_open so that we can work on indexes and sequences.
15004 : */
15005 2022 : target_rel = relation_open(relationOid, lockmode);
15006 :
15007 : /* Get its pg_class tuple, too */
15008 2022 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
15009 :
15010 2022 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15011 2022 : if (!HeapTupleIsValid(tuple))
15012 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
15013 2022 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15014 :
15015 : /* Can we change the ownership of this tuple? */
15016 2022 : switch (tuple_class->relkind)
15017 : {
15018 1756 : case RELKIND_RELATION:
15019 : case RELKIND_VIEW:
15020 : case RELKIND_MATVIEW:
15021 : case RELKIND_FOREIGN_TABLE:
15022 : case RELKIND_PARTITIONED_TABLE:
15023 : /* ok to change owner */
15024 1756 : break;
15025 96 : case RELKIND_INDEX:
15026 96 : if (!recursing)
15027 : {
15028 : /*
15029 : * Because ALTER INDEX OWNER used to be allowed, and in fact
15030 : * is generated by old versions of pg_dump, we give a warning
15031 : * and do nothing rather than erroring out. Also, to avoid
15032 : * unnecessary chatter while restoring those old dumps, say
15033 : * nothing at all if the command would be a no-op anyway.
15034 : */
15035 0 : if (tuple_class->relowner != newOwnerId)
15036 0 : ereport(WARNING,
15037 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15038 : errmsg("cannot change owner of index \"%s\"",
15039 : NameStr(tuple_class->relname)),
15040 : errhint("Change the ownership of the index's table instead.")));
15041 : /* quick hack to exit via the no-op path */
15042 0 : newOwnerId = tuple_class->relowner;
15043 : }
15044 96 : break;
15045 20 : case RELKIND_PARTITIONED_INDEX:
15046 20 : if (recursing)
15047 20 : break;
15048 0 : ereport(ERROR,
15049 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15050 : errmsg("cannot change owner of index \"%s\"",
15051 : NameStr(tuple_class->relname)),
15052 : errhint("Change the ownership of the index's table instead.")));
15053 : break;
15054 100 : case RELKIND_SEQUENCE:
15055 100 : if (!recursing &&
15056 52 : tuple_class->relowner != newOwnerId)
15057 : {
15058 : /* if it's an owned sequence, disallow changing it by itself */
15059 : Oid tableId;
15060 : int32 colId;
15061 :
15062 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15063 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15064 0 : ereport(ERROR,
15065 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15066 : errmsg("cannot change owner of sequence \"%s\"",
15067 : NameStr(tuple_class->relname)),
15068 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
15069 : NameStr(tuple_class->relname),
15070 : get_rel_name(tableId))));
15071 : }
15072 100 : break;
15073 8 : case RELKIND_COMPOSITE_TYPE:
15074 8 : if (recursing)
15075 8 : break;
15076 0 : ereport(ERROR,
15077 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15078 : errmsg("\"%s\" is a composite type",
15079 : NameStr(tuple_class->relname)),
15080 : /* translator: %s is an SQL ALTER command */
15081 : errhint("Use %s instead.",
15082 : "ALTER TYPE")));
15083 : break;
15084 42 : case RELKIND_TOASTVALUE:
15085 42 : if (recursing)
15086 42 : break;
15087 : /* FALL THRU */
15088 : default:
15089 0 : ereport(ERROR,
15090 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15091 : errmsg("cannot change owner of relation \"%s\"",
15092 : NameStr(tuple_class->relname)),
15093 : errdetail_relkind_not_supported(tuple_class->relkind)));
15094 : }
15095 :
15096 : /*
15097 : * If the new owner is the same as the existing owner, consider the
15098 : * command to have succeeded. This is for dump restoration purposes.
15099 : */
15100 2022 : if (tuple_class->relowner != newOwnerId)
15101 : {
15102 : Datum repl_val[Natts_pg_class];
15103 : bool repl_null[Natts_pg_class];
15104 : bool repl_repl[Natts_pg_class];
15105 : Acl *newAcl;
15106 : Datum aclDatum;
15107 : bool isNull;
15108 : HeapTuple newtuple;
15109 :
15110 : /* skip permission checks when recursing to index or toast table */
15111 498 : if (!recursing)
15112 : {
15113 : /* Superusers can always do it */
15114 280 : if (!superuser())
15115 : {
15116 42 : Oid namespaceOid = tuple_class->relnamespace;
15117 : AclResult aclresult;
15118 :
15119 : /* Otherwise, must be owner of the existing object */
15120 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15121 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15122 0 : RelationGetRelationName(target_rel));
15123 :
15124 : /* Must be able to become new owner */
15125 42 : check_can_set_role(GetUserId(), newOwnerId);
15126 :
15127 : /* New owner must have CREATE privilege on namespace */
15128 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15129 : ACL_CREATE);
15130 30 : if (aclresult != ACLCHECK_OK)
15131 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15132 0 : get_namespace_name(namespaceOid));
15133 : }
15134 : }
15135 :
15136 486 : memset(repl_null, false, sizeof(repl_null));
15137 486 : memset(repl_repl, false, sizeof(repl_repl));
15138 :
15139 486 : repl_repl[Anum_pg_class_relowner - 1] = true;
15140 486 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15141 :
15142 : /*
15143 : * Determine the modified ACL for the new owner. This is only
15144 : * necessary when the ACL is non-null.
15145 : */
15146 486 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15147 : Anum_pg_class_relacl,
15148 : &isNull);
15149 486 : if (!isNull)
15150 : {
15151 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15152 : tuple_class->relowner, newOwnerId);
15153 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
15154 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15155 : }
15156 :
15157 486 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15158 :
15159 486 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15160 :
15161 486 : heap_freetuple(newtuple);
15162 :
15163 : /*
15164 : * We must similarly update any per-column ACLs to reflect the new
15165 : * owner; for neatness reasons that's split out as a subroutine.
15166 : */
15167 486 : change_owner_fix_column_acls(relationOid,
15168 : tuple_class->relowner,
15169 : newOwnerId);
15170 :
15171 : /*
15172 : * Update owner dependency reference, if any. A composite type has
15173 : * none, because it's tracked for the pg_type entry instead of here;
15174 : * indexes and TOAST tables don't have their own entries either.
15175 : */
15176 486 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15177 478 : tuple_class->relkind != RELKIND_INDEX &&
15178 382 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15179 362 : tuple_class->relkind != RELKIND_TOASTVALUE)
15180 320 : changeDependencyOnOwner(RelationRelationId, relationOid,
15181 : newOwnerId);
15182 :
15183 : /*
15184 : * Also change the ownership of the table's row type, if it has one
15185 : */
15186 486 : if (OidIsValid(tuple_class->reltype))
15187 294 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15188 :
15189 : /*
15190 : * If we are operating on a table or materialized view, also change
15191 : * the ownership of any indexes and sequences that belong to the
15192 : * relation, as well as its toast table (if it has one).
15193 : */
15194 486 : if (tuple_class->relkind == RELKIND_RELATION ||
15195 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15196 224 : tuple_class->relkind == RELKIND_MATVIEW ||
15197 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
15198 : {
15199 : List *index_oid_list;
15200 : ListCell *i;
15201 :
15202 : /* Find all the indexes belonging to this relation */
15203 304 : index_oid_list = RelationGetIndexList(target_rel);
15204 :
15205 : /* For each index, recursively change its ownership */
15206 420 : foreach(i, index_oid_list)
15207 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15208 :
15209 304 : list_free(index_oid_list);
15210 : }
15211 :
15212 : /* If it has a toast table, recurse to change its ownership */
15213 486 : if (tuple_class->reltoastrelid != InvalidOid)
15214 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15215 : true, lockmode);
15216 :
15217 : /* If it has dependent sequences, recurse to change them too */
15218 486 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15219 : }
15220 :
15221 2010 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15222 :
15223 2010 : ReleaseSysCache(tuple);
15224 2010 : table_close(class_rel, RowExclusiveLock);
15225 2010 : relation_close(target_rel, NoLock);
15226 2010 : }
15227 :
15228 : /*
15229 : * change_owner_fix_column_acls
15230 : *
15231 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15232 : * and fix any non-null column ACLs to reflect the new owner.
15233 : */
15234 : static void
15235 486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15236 : {
15237 : Relation attRelation;
15238 : SysScanDesc scan;
15239 : ScanKeyData key[1];
15240 : HeapTuple attributeTuple;
15241 :
15242 486 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15243 486 : ScanKeyInit(&key[0],
15244 : Anum_pg_attribute_attrelid,
15245 : BTEqualStrategyNumber, F_OIDEQ,
15246 : ObjectIdGetDatum(relationOid));
15247 486 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15248 : true, NULL, 1, key);
15249 3372 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15250 : {
15251 2886 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15252 : Datum repl_val[Natts_pg_attribute];
15253 : bool repl_null[Natts_pg_attribute];
15254 : bool repl_repl[Natts_pg_attribute];
15255 : Acl *newAcl;
15256 : Datum aclDatum;
15257 : bool isNull;
15258 : HeapTuple newtuple;
15259 :
15260 : /* Ignore dropped columns */
15261 2886 : if (att->attisdropped)
15262 2884 : continue;
15263 :
15264 2886 : aclDatum = heap_getattr(attributeTuple,
15265 : Anum_pg_attribute_attacl,
15266 : RelationGetDescr(attRelation),
15267 : &isNull);
15268 : /* Null ACLs do not require changes */
15269 2886 : if (isNull)
15270 2884 : continue;
15271 :
15272 2 : memset(repl_null, false, sizeof(repl_null));
15273 2 : memset(repl_repl, false, sizeof(repl_repl));
15274 :
15275 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15276 : oldOwnerId, newOwnerId);
15277 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15278 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15279 :
15280 2 : newtuple = heap_modify_tuple(attributeTuple,
15281 : RelationGetDescr(attRelation),
15282 : repl_val, repl_null, repl_repl);
15283 :
15284 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15285 :
15286 2 : heap_freetuple(newtuple);
15287 : }
15288 486 : systable_endscan(scan);
15289 486 : table_close(attRelation, RowExclusiveLock);
15290 486 : }
15291 :
15292 : /*
15293 : * change_owner_recurse_to_sequences
15294 : *
15295 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15296 : * for sequences that are dependent on serial columns, and changes their
15297 : * ownership.
15298 : */
15299 : static void
15300 486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15301 : {
15302 : Relation depRel;
15303 : SysScanDesc scan;
15304 : ScanKeyData key[2];
15305 : HeapTuple tup;
15306 :
15307 : /*
15308 : * SERIAL sequences are those having an auto dependency on one of the
15309 : * table's columns (we don't care *which* column, exactly).
15310 : */
15311 486 : depRel = table_open(DependRelationId, AccessShareLock);
15312 :
15313 486 : ScanKeyInit(&key[0],
15314 : Anum_pg_depend_refclassid,
15315 : BTEqualStrategyNumber, F_OIDEQ,
15316 : ObjectIdGetDatum(RelationRelationId));
15317 486 : ScanKeyInit(&key[1],
15318 : Anum_pg_depend_refobjid,
15319 : BTEqualStrategyNumber, F_OIDEQ,
15320 : ObjectIdGetDatum(relationOid));
15321 : /* we leave refobjsubid unspecified */
15322 :
15323 486 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15324 : NULL, 2, key);
15325 :
15326 1374 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15327 : {
15328 888 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15329 : Relation seqRel;
15330 :
15331 : /* skip dependencies other than auto dependencies on columns */
15332 888 : if (depForm->refobjsubid == 0 ||
15333 352 : depForm->classid != RelationRelationId ||
15334 142 : depForm->objsubid != 0 ||
15335 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15336 746 : continue;
15337 :
15338 : /* Use relation_open just in case it's an index */
15339 142 : seqRel = relation_open(depForm->objid, lockmode);
15340 :
15341 : /* skip non-sequence relations */
15342 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15343 : {
15344 : /* No need to keep the lock */
15345 116 : relation_close(seqRel, lockmode);
15346 116 : continue;
15347 : }
15348 :
15349 : /* We don't need to close the sequence while we alter it. */
15350 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15351 :
15352 : /* Now we can close it. Keep the lock till end of transaction. */
15353 26 : relation_close(seqRel, NoLock);
15354 : }
15355 :
15356 486 : systable_endscan(scan);
15357 :
15358 486 : relation_close(depRel, AccessShareLock);
15359 486 : }
15360 :
15361 : /*
15362 : * ALTER TABLE CLUSTER ON
15363 : *
15364 : * The only thing we have to do is to change the indisclustered bits.
15365 : *
15366 : * Return the address of the new clustering index.
15367 : */
15368 : static ObjectAddress
15369 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15370 : {
15371 : Oid indexOid;
15372 : ObjectAddress address;
15373 :
15374 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15375 :
15376 64 : if (!OidIsValid(indexOid))
15377 0 : ereport(ERROR,
15378 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15379 : errmsg("index \"%s\" for table \"%s\" does not exist",
15380 : indexName, RelationGetRelationName(rel))));
15381 :
15382 : /* Check index is valid to cluster on */
15383 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15384 :
15385 : /* And do the work */
15386 64 : mark_index_clustered(rel, indexOid, false);
15387 :
15388 58 : ObjectAddressSet(address,
15389 : RelationRelationId, indexOid);
15390 :
15391 58 : return address;
15392 : }
15393 :
15394 : /*
15395 : * ALTER TABLE SET WITHOUT CLUSTER
15396 : *
15397 : * We have to find any indexes on the table that have indisclustered bit
15398 : * set and turn it off.
15399 : */
15400 : static void
15401 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15402 : {
15403 18 : mark_index_clustered(rel, InvalidOid, false);
15404 12 : }
15405 :
15406 : /*
15407 : * Preparation phase for SET ACCESS METHOD
15408 : *
15409 : * Check that the access method exists and determine whether a change is
15410 : * actually needed.
15411 : */
15412 : static void
15413 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15414 : {
15415 : Oid amoid;
15416 :
15417 : /*
15418 : * Look up the access method name and check that it differs from the
15419 : * table's current AM. If DEFAULT was specified for a partitioned table
15420 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15421 : */
15422 110 : if (amname != NULL)
15423 74 : amoid = get_table_am_oid(amname, false);
15424 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15425 18 : amoid = InvalidOid;
15426 : else
15427 18 : amoid = get_table_am_oid(default_table_access_method, false);
15428 :
15429 : /* if it's a match, phase 3 doesn't need to do anything */
15430 110 : if (rel->rd_rel->relam == amoid)
15431 12 : return;
15432 :
15433 : /* Save info for Phase 3 to do the real work */
15434 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15435 98 : tab->newAccessMethod = amoid;
15436 98 : tab->chgAccessMethod = true;
15437 : }
15438 :
15439 : /*
15440 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15441 : * storage that have an interest in preserving AM.
15442 : *
15443 : * Since these have no storage, setting the access method is a catalog only
15444 : * operation.
15445 : */
15446 : static void
15447 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15448 : {
15449 : Relation pg_class;
15450 : Oid oldAccessMethodId;
15451 : HeapTuple tuple;
15452 : Form_pg_class rd_rel;
15453 44 : Oid reloid = RelationGetRelid(rel);
15454 :
15455 : /*
15456 : * Shouldn't be called on relations having storage; these are processed in
15457 : * phase 3.
15458 : */
15459 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15460 :
15461 : /* Get a modifiable copy of the relation's pg_class row. */
15462 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15463 :
15464 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15465 44 : if (!HeapTupleIsValid(tuple))
15466 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15467 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15468 :
15469 : /* Update the pg_class row. */
15470 44 : oldAccessMethodId = rd_rel->relam;
15471 44 : rd_rel->relam = newAccessMethodId;
15472 :
15473 : /* Leave if no update required */
15474 44 : if (rd_rel->relam == oldAccessMethodId)
15475 : {
15476 0 : heap_freetuple(tuple);
15477 0 : table_close(pg_class, RowExclusiveLock);
15478 0 : return;
15479 : }
15480 :
15481 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15482 :
15483 : /*
15484 : * Update the dependency on the new access method. No dependency is added
15485 : * if the new access method is InvalidOid (default case). Be very careful
15486 : * that this has to compare the previous value stored in pg_class with the
15487 : * new one.
15488 : */
15489 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15490 20 : {
15491 : ObjectAddress relobj,
15492 : referenced;
15493 :
15494 : /*
15495 : * New access method is defined and there was no dependency
15496 : * previously, so record a new one.
15497 : */
15498 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15499 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15500 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15501 : }
15502 24 : else if (OidIsValid(oldAccessMethodId) &&
15503 24 : !OidIsValid(rd_rel->relam))
15504 : {
15505 : /*
15506 : * There was an access method defined, and no new one, so just remove
15507 : * the existing dependency.
15508 : */
15509 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15510 : AccessMethodRelationId,
15511 : DEPENDENCY_NORMAL);
15512 : }
15513 : else
15514 : {
15515 : Assert(OidIsValid(oldAccessMethodId) &&
15516 : OidIsValid(rd_rel->relam));
15517 :
15518 : /* Both are valid, so update the dependency */
15519 12 : changeDependencyFor(RelationRelationId, reloid,
15520 : AccessMethodRelationId,
15521 : oldAccessMethodId, rd_rel->relam);
15522 : }
15523 :
15524 : /* make the relam and dependency changes visible */
15525 44 : CommandCounterIncrement();
15526 :
15527 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15528 :
15529 44 : heap_freetuple(tuple);
15530 44 : table_close(pg_class, RowExclusiveLock);
15531 : }
15532 :
15533 : /*
15534 : * ALTER TABLE SET TABLESPACE
15535 : */
15536 : static void
15537 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15538 : {
15539 : Oid tablespaceId;
15540 :
15541 : /* Check that the tablespace exists */
15542 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
15543 :
15544 : /* Check permissions except when moving to database's default */
15545 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15546 : {
15547 : AclResult aclresult;
15548 :
15549 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15550 66 : if (aclresult != ACLCHECK_OK)
15551 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15552 : }
15553 :
15554 : /* Save info for Phase 3 to do the real work */
15555 158 : if (OidIsValid(tab->newTableSpace))
15556 0 : ereport(ERROR,
15557 : (errcode(ERRCODE_SYNTAX_ERROR),
15558 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
15559 :
15560 158 : tab->newTableSpace = tablespaceId;
15561 158 : }
15562 :
15563 : /*
15564 : * Set, reset, or replace reloptions.
15565 : */
15566 : static void
15567 946 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15568 : LOCKMODE lockmode)
15569 : {
15570 : Oid relid;
15571 : Relation pgclass;
15572 : HeapTuple tuple;
15573 : HeapTuple newtuple;
15574 : Datum datum;
15575 : bool isnull;
15576 : Datum newOptions;
15577 : Datum repl_val[Natts_pg_class];
15578 : bool repl_null[Natts_pg_class];
15579 : bool repl_repl[Natts_pg_class];
15580 946 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
15581 :
15582 946 : if (defList == NIL && operation != AT_ReplaceRelOptions)
15583 0 : return; /* nothing to do */
15584 :
15585 946 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
15586 :
15587 : /* Fetch heap tuple */
15588 946 : relid = RelationGetRelid(rel);
15589 946 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
15590 946 : if (!HeapTupleIsValid(tuple))
15591 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
15592 :
15593 946 : if (operation == AT_ReplaceRelOptions)
15594 : {
15595 : /*
15596 : * If we're supposed to replace the reloptions list, we just pretend
15597 : * there were none before.
15598 : */
15599 194 : datum = (Datum) 0;
15600 194 : isnull = true;
15601 : }
15602 : else
15603 : {
15604 : /* Get the old reloptions */
15605 752 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15606 : &isnull);
15607 : }
15608 :
15609 : /* Generate new proposed reloptions (text array) */
15610 946 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15611 : defList, NULL, validnsps, false,
15612 : operation == AT_ResetRelOptions);
15613 :
15614 : /* Validate */
15615 940 : switch (rel->rd_rel->relkind)
15616 : {
15617 524 : case RELKIND_RELATION:
15618 : case RELKIND_TOASTVALUE:
15619 : case RELKIND_MATVIEW:
15620 524 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15621 524 : break;
15622 6 : case RELKIND_PARTITIONED_TABLE:
15623 6 : (void) partitioned_table_reloptions(newOptions, true);
15624 0 : break;
15625 296 : case RELKIND_VIEW:
15626 296 : (void) view_reloptions(newOptions, true);
15627 278 : break;
15628 114 : case RELKIND_INDEX:
15629 : case RELKIND_PARTITIONED_INDEX:
15630 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15631 92 : break;
15632 0 : default:
15633 0 : ereport(ERROR,
15634 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15635 : errmsg("cannot set options for relation \"%s\"",
15636 : RelationGetRelationName(rel)),
15637 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15638 : break;
15639 : }
15640 :
15641 : /* Special-case validation of view options */
15642 894 : if (rel->rd_rel->relkind == RELKIND_VIEW)
15643 : {
15644 278 : Query *view_query = get_view_query(rel);
15645 278 : List *view_options = untransformRelOptions(newOptions);
15646 : ListCell *cell;
15647 278 : bool check_option = false;
15648 :
15649 380 : foreach(cell, view_options)
15650 : {
15651 102 : DefElem *defel = (DefElem *) lfirst(cell);
15652 :
15653 102 : if (strcmp(defel->defname, "check_option") == 0)
15654 24 : check_option = true;
15655 : }
15656 :
15657 : /*
15658 : * If the check option is specified, look to see if the view is
15659 : * actually auto-updatable or not.
15660 : */
15661 278 : if (check_option)
15662 : {
15663 : const char *view_updatable_error =
15664 24 : view_query_is_auto_updatable(view_query, true);
15665 :
15666 24 : if (view_updatable_error)
15667 0 : ereport(ERROR,
15668 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15669 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15670 : errhint("%s", _(view_updatable_error))));
15671 : }
15672 : }
15673 :
15674 : /*
15675 : * All we need do here is update the pg_class row; the new options will be
15676 : * propagated into relcaches during post-commit cache inval.
15677 : */
15678 894 : memset(repl_val, 0, sizeof(repl_val));
15679 894 : memset(repl_null, false, sizeof(repl_null));
15680 894 : memset(repl_repl, false, sizeof(repl_repl));
15681 :
15682 894 : if (newOptions != (Datum) 0)
15683 600 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15684 : else
15685 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
15686 :
15687 894 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15688 :
15689 894 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15690 : repl_val, repl_null, repl_repl);
15691 :
15692 894 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15693 894 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
15694 :
15695 894 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15696 :
15697 894 : heap_freetuple(newtuple);
15698 :
15699 894 : ReleaseSysCache(tuple);
15700 :
15701 : /* repeat the whole exercise for the toast table, if there's one */
15702 894 : if (OidIsValid(rel->rd_rel->reltoastrelid))
15703 : {
15704 : Relation toastrel;
15705 256 : Oid toastid = rel->rd_rel->reltoastrelid;
15706 :
15707 256 : toastrel = table_open(toastid, lockmode);
15708 :
15709 : /* Fetch heap tuple */
15710 256 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15711 256 : if (!HeapTupleIsValid(tuple))
15712 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15713 :
15714 256 : if (operation == AT_ReplaceRelOptions)
15715 : {
15716 : /*
15717 : * If we're supposed to replace the reloptions list, we just
15718 : * pretend there were none before.
15719 : */
15720 0 : datum = (Datum) 0;
15721 0 : isnull = true;
15722 : }
15723 : else
15724 : {
15725 : /* Get the old reloptions */
15726 256 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15727 : &isnull);
15728 : }
15729 :
15730 256 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15731 : defList, "toast", validnsps, false,
15732 : operation == AT_ResetRelOptions);
15733 :
15734 256 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15735 :
15736 256 : memset(repl_val, 0, sizeof(repl_val));
15737 256 : memset(repl_null, false, sizeof(repl_null));
15738 256 : memset(repl_repl, false, sizeof(repl_repl));
15739 :
15740 256 : if (newOptions != (Datum) 0)
15741 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15742 : else
15743 214 : repl_null[Anum_pg_class_reloptions - 1] = true;
15744 :
15745 256 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15746 :
15747 256 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15748 : repl_val, repl_null, repl_repl);
15749 :
15750 256 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15751 :
15752 256 : InvokeObjectPostAlterHookArg(RelationRelationId,
15753 : RelationGetRelid(toastrel), 0,
15754 : InvalidOid, true);
15755 :
15756 256 : heap_freetuple(newtuple);
15757 :
15758 256 : ReleaseSysCache(tuple);
15759 :
15760 256 : table_close(toastrel, NoLock);
15761 : }
15762 :
15763 894 : table_close(pgclass, RowExclusiveLock);
15764 : }
15765 :
15766 : /*
15767 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15768 : * rewriting to be done, so we just want to copy the data as fast as possible.
15769 : */
15770 : static void
15771 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15772 : {
15773 : Relation rel;
15774 : Oid reltoastrelid;
15775 : RelFileNumber newrelfilenumber;
15776 : RelFileLocator newrlocator;
15777 162 : List *reltoastidxids = NIL;
15778 : ListCell *lc;
15779 :
15780 : /*
15781 : * Need lock here in case we are recursing to toast table or index
15782 : */
15783 162 : rel = relation_open(tableOid, lockmode);
15784 :
15785 : /* Check first if relation can be moved to new tablespace */
15786 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15787 : {
15788 2 : InvokeObjectPostAlterHook(RelationRelationId,
15789 : RelationGetRelid(rel), 0);
15790 2 : relation_close(rel, NoLock);
15791 2 : return;
15792 : }
15793 :
15794 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
15795 : /* Fetch the list of indexes on toast relation if necessary */
15796 160 : if (OidIsValid(reltoastrelid))
15797 : {
15798 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15799 :
15800 20 : reltoastidxids = RelationGetIndexList(toastRel);
15801 20 : relation_close(toastRel, lockmode);
15802 : }
15803 :
15804 : /*
15805 : * Relfilenumbers are not unique in databases across tablespaces, so we
15806 : * need to allocate a new one in the new tablespace.
15807 : */
15808 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15809 160 : rel->rd_rel->relpersistence);
15810 :
15811 : /* Open old and new relation */
15812 160 : newrlocator = rel->rd_locator;
15813 160 : newrlocator.relNumber = newrelfilenumber;
15814 160 : newrlocator.spcOid = newTableSpace;
15815 :
15816 : /* hand off to AM to actually create new rel storage and copy the data */
15817 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
15818 : {
15819 62 : index_copy_data(rel, newrlocator);
15820 : }
15821 : else
15822 : {
15823 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15824 98 : table_relation_copy_data(rel, &newrlocator);
15825 : }
15826 :
15827 : /*
15828 : * Update the pg_class row.
15829 : *
15830 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15831 : * executed on pg_class or its indexes (the above copy wouldn't contain
15832 : * the updated pg_class entry), but that's forbidden with
15833 : * CheckRelationTableSpaceMove().
15834 : */
15835 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15836 :
15837 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15838 :
15839 160 : RelationAssumeNewRelfilelocator(rel);
15840 :
15841 160 : relation_close(rel, NoLock);
15842 :
15843 : /* Make sure the reltablespace change is visible */
15844 160 : CommandCounterIncrement();
15845 :
15846 : /* Move associated toast relation and/or indexes, too */
15847 160 : if (OidIsValid(reltoastrelid))
15848 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15849 180 : foreach(lc, reltoastidxids)
15850 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15851 :
15852 : /* Clean up */
15853 160 : list_free(reltoastidxids);
15854 : }
15855 :
15856 : /*
15857 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15858 : * storage that have an interest in preserving tablespace.
15859 : *
15860 : * Since these have no storage the tablespace can be updated with a simple
15861 : * metadata only operation to update the tablespace.
15862 : */
15863 : static void
15864 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15865 : {
15866 : /*
15867 : * Shouldn't be called on relations having storage; these are processed in
15868 : * phase 3.
15869 : */
15870 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15871 :
15872 : /* check if relation can be moved to its new tablespace */
15873 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15874 : {
15875 0 : InvokeObjectPostAlterHook(RelationRelationId,
15876 : RelationGetRelid(rel),
15877 : 0);
15878 0 : return;
15879 : }
15880 :
15881 : /* Update can be done, so change reltablespace */
15882 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15883 :
15884 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15885 :
15886 : /* Make sure the reltablespace change is visible */
15887 30 : CommandCounterIncrement();
15888 : }
15889 :
15890 : /*
15891 : * Alter Table ALL ... SET TABLESPACE
15892 : *
15893 : * Allows a user to move all objects of some type in a given tablespace in the
15894 : * current database to another tablespace. Objects can be chosen based on the
15895 : * owner of the object also, to allow users to move only their objects.
15896 : * The user must have CREATE rights on the new tablespace, as usual. The main
15897 : * permissions handling is done by the lower-level table move function.
15898 : *
15899 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
15900 : * lock can't be acquired then we ereport(ERROR).
15901 : */
15902 : Oid
15903 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15904 : {
15905 30 : List *relations = NIL;
15906 : ListCell *l;
15907 : ScanKeyData key[1];
15908 : Relation rel;
15909 : TableScanDesc scan;
15910 : HeapTuple tuple;
15911 : Oid orig_tablespaceoid;
15912 : Oid new_tablespaceoid;
15913 30 : List *role_oids = roleSpecsToIds(stmt->roles);
15914 :
15915 : /* Ensure we were not asked to move something we can't */
15916 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15917 12 : stmt->objtype != OBJECT_MATVIEW)
15918 0 : ereport(ERROR,
15919 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15920 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15921 :
15922 : /* Get the orig and new tablespace OIDs */
15923 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15924 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15925 :
15926 : /* Can't move shared relations in to or out of pg_global */
15927 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15928 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15929 : new_tablespaceoid == GLOBALTABLESPACE_OID)
15930 0 : ereport(ERROR,
15931 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15932 : errmsg("cannot move relations in to or out of pg_global tablespace")));
15933 :
15934 : /*
15935 : * Must have CREATE rights on the new tablespace, unless it is the
15936 : * database default tablespace (which all users implicitly have CREATE
15937 : * rights on).
15938 : */
15939 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15940 : {
15941 : AclResult aclresult;
15942 :
15943 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15944 : ACL_CREATE);
15945 0 : if (aclresult != ACLCHECK_OK)
15946 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
15947 0 : get_tablespace_name(new_tablespaceoid));
15948 : }
15949 :
15950 : /*
15951 : * Now that the checks are done, check if we should set either to
15952 : * InvalidOid because it is our database's default tablespace.
15953 : */
15954 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
15955 0 : orig_tablespaceoid = InvalidOid;
15956 :
15957 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
15958 30 : new_tablespaceoid = InvalidOid;
15959 :
15960 : /* no-op */
15961 30 : if (orig_tablespaceoid == new_tablespaceoid)
15962 0 : return new_tablespaceoid;
15963 :
15964 : /*
15965 : * Walk the list of objects in the tablespace and move them. This will
15966 : * only find objects in our database, of course.
15967 : */
15968 30 : ScanKeyInit(&key[0],
15969 : Anum_pg_class_reltablespace,
15970 : BTEqualStrategyNumber, F_OIDEQ,
15971 : ObjectIdGetDatum(orig_tablespaceoid));
15972 :
15973 30 : rel = table_open(RelationRelationId, AccessShareLock);
15974 30 : scan = table_beginscan_catalog(rel, 1, key);
15975 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15976 : {
15977 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15978 102 : Oid relOid = relForm->oid;
15979 :
15980 : /*
15981 : * Do not move objects in pg_catalog as part of this, if an admin
15982 : * really wishes to do so, they can issue the individual ALTER
15983 : * commands directly.
15984 : *
15985 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
15986 : * (TOAST will be moved with the main table).
15987 : */
15988 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
15989 204 : relForm->relisshared ||
15990 204 : isAnyTempNamespace(relForm->relnamespace) ||
15991 102 : IsToastNamespace(relForm->relnamespace))
15992 0 : continue;
15993 :
15994 : /* Only move the object type requested */
15995 102 : if ((stmt->objtype == OBJECT_TABLE &&
15996 60 : relForm->relkind != RELKIND_RELATION &&
15997 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15998 66 : (stmt->objtype == OBJECT_INDEX &&
15999 36 : relForm->relkind != RELKIND_INDEX &&
16000 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16001 60 : (stmt->objtype == OBJECT_MATVIEW &&
16002 6 : relForm->relkind != RELKIND_MATVIEW))
16003 42 : continue;
16004 :
16005 : /* Check if we are only moving objects owned by certain roles */
16006 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16007 0 : continue;
16008 :
16009 : /*
16010 : * Handle permissions-checking here since we are locking the tables
16011 : * and also to avoid doing a bunch of work only to fail part-way. Note
16012 : * that permissions will also be checked by AlterTableInternal().
16013 : *
16014 : * Caller must be considered an owner on the table to move it.
16015 : */
16016 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16017 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16018 0 : NameStr(relForm->relname));
16019 :
16020 60 : if (stmt->nowait &&
16021 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16022 0 : ereport(ERROR,
16023 : (errcode(ERRCODE_OBJECT_IN_USE),
16024 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
16025 : get_namespace_name(relForm->relnamespace),
16026 : NameStr(relForm->relname))));
16027 : else
16028 60 : LockRelationOid(relOid, AccessExclusiveLock);
16029 :
16030 : /* Add to our list of objects to move */
16031 60 : relations = lappend_oid(relations, relOid);
16032 : }
16033 :
16034 30 : table_endscan(scan);
16035 30 : table_close(rel, AccessShareLock);
16036 :
16037 30 : if (relations == NIL)
16038 12 : ereport(NOTICE,
16039 : (errcode(ERRCODE_NO_DATA_FOUND),
16040 : errmsg("no matching relations in tablespace \"%s\" found",
16041 : orig_tablespaceoid == InvalidOid ? "(database default)" :
16042 : get_tablespace_name(orig_tablespaceoid))));
16043 :
16044 : /* Everything is locked, loop through and move all of the relations. */
16045 90 : foreach(l, relations)
16046 : {
16047 60 : List *cmds = NIL;
16048 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16049 :
16050 60 : cmd->subtype = AT_SetTableSpace;
16051 60 : cmd->name = stmt->new_tablespacename;
16052 :
16053 60 : cmds = lappend(cmds, cmd);
16054 :
16055 60 : EventTriggerAlterTableStart((Node *) stmt);
16056 : /* OID is set by AlterTableInternal */
16057 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
16058 60 : EventTriggerAlterTableEnd();
16059 : }
16060 :
16061 30 : return new_tablespaceoid;
16062 : }
16063 :
16064 : static void
16065 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
16066 : {
16067 : SMgrRelation dstrel;
16068 :
16069 : /*
16070 : * Since we copy the file directly without looking at the shared buffers,
16071 : * we'd better first flush out any pages of the source relation that are
16072 : * in shared buffers. We assume no new changes will be made while we are
16073 : * holding exclusive lock on the rel.
16074 : */
16075 62 : FlushRelationBuffers(rel);
16076 :
16077 : /*
16078 : * Create and copy all forks of the relation, and schedule unlinking of
16079 : * old physical files.
16080 : *
16081 : * NOTE: any conflict in relfilenumber value will be caught in
16082 : * RelationCreateStorage().
16083 : */
16084 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16085 :
16086 : /* copy main fork */
16087 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16088 62 : rel->rd_rel->relpersistence);
16089 :
16090 : /* copy those extra forks that exist */
16091 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16092 186 : forkNum <= MAX_FORKNUM; forkNum++)
16093 : {
16094 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
16095 : {
16096 0 : smgrcreate(dstrel, forkNum, false);
16097 :
16098 : /*
16099 : * WAL log creation if the relation is persistent, or this is the
16100 : * init fork of an unlogged relation.
16101 : */
16102 0 : if (RelationIsPermanent(rel) ||
16103 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16104 : forkNum == INIT_FORKNUM))
16105 0 : log_smgrcreate(&newrlocator, forkNum);
16106 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16107 0 : rel->rd_rel->relpersistence);
16108 : }
16109 : }
16110 :
16111 : /* drop old relation, and close new one */
16112 62 : RelationDropStorage(rel);
16113 62 : smgrclose(dstrel);
16114 62 : }
16115 :
16116 : /*
16117 : * ALTER TABLE ENABLE/DISABLE TRIGGER
16118 : *
16119 : * We just pass this off to trigger.c.
16120 : */
16121 : static void
16122 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16123 : char fires_when, bool skip_system, bool recurse,
16124 : LOCKMODE lockmode)
16125 : {
16126 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
16127 : fires_when, skip_system, recurse,
16128 : lockmode);
16129 :
16130 340 : InvokeObjectPostAlterHook(RelationRelationId,
16131 : RelationGetRelid(rel), 0);
16132 340 : }
16133 :
16134 : /*
16135 : * ALTER TABLE ENABLE/DISABLE RULE
16136 : *
16137 : * We just pass this off to rewriteDefine.c.
16138 : */
16139 : static void
16140 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16141 : char fires_when, LOCKMODE lockmode)
16142 : {
16143 46 : EnableDisableRule(rel, rulename, fires_when);
16144 :
16145 46 : InvokeObjectPostAlterHook(RelationRelationId,
16146 : RelationGetRelid(rel), 0);
16147 46 : }
16148 :
16149 : /*
16150 : * ALTER TABLE INHERIT
16151 : *
16152 : * Add a parent to the child's parents. This verifies that all the columns and
16153 : * check constraints of the parent appear in the child and that they have the
16154 : * same data types and expressions.
16155 : */
16156 : static void
16157 346 : ATPrepAddInherit(Relation child_rel)
16158 : {
16159 346 : if (child_rel->rd_rel->reloftype)
16160 6 : ereport(ERROR,
16161 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16162 : errmsg("cannot change inheritance of typed table")));
16163 :
16164 340 : if (child_rel->rd_rel->relispartition)
16165 6 : ereport(ERROR,
16166 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16167 : errmsg("cannot change inheritance of a partition")));
16168 :
16169 334 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16170 6 : ereport(ERROR,
16171 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16172 : errmsg("cannot change inheritance of partitioned table")));
16173 328 : }
16174 :
16175 : /*
16176 : * Return the address of the new parent relation.
16177 : */
16178 : static ObjectAddress
16179 328 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16180 : {
16181 : Relation parent_rel;
16182 : List *children;
16183 : ObjectAddress address;
16184 : const char *trigger_name;
16185 :
16186 : /*
16187 : * A self-exclusive lock is needed here. See the similar case in
16188 : * MergeAttributes() for a full explanation.
16189 : */
16190 328 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16191 :
16192 : /*
16193 : * Must be owner of both parent and child -- child was checked by
16194 : * ATSimplePermissions call in ATPrepCmd
16195 : */
16196 328 : ATSimplePermissions(AT_AddInherit, parent_rel,
16197 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
16198 :
16199 : /* Permanent rels cannot inherit from temporary ones */
16200 328 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16201 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16202 0 : ereport(ERROR,
16203 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16204 : errmsg("cannot inherit from temporary relation \"%s\"",
16205 : RelationGetRelationName(parent_rel))));
16206 :
16207 : /* If parent rel is temp, it must belong to this session */
16208 328 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16209 6 : !parent_rel->rd_islocaltemp)
16210 0 : ereport(ERROR,
16211 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16212 : errmsg("cannot inherit from temporary relation of another session")));
16213 :
16214 : /* Ditto for the child */
16215 328 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16216 6 : !child_rel->rd_islocaltemp)
16217 0 : ereport(ERROR,
16218 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16219 : errmsg("cannot inherit to temporary relation of another session")));
16220 :
16221 : /* Prevent partitioned tables from becoming inheritance parents */
16222 328 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16223 6 : ereport(ERROR,
16224 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16225 : errmsg("cannot inherit from partitioned table \"%s\"",
16226 : parent->relname)));
16227 :
16228 : /* Likewise for partitions */
16229 322 : if (parent_rel->rd_rel->relispartition)
16230 6 : ereport(ERROR,
16231 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16232 : errmsg("cannot inherit from a partition")));
16233 :
16234 : /*
16235 : * Prevent circularity by seeing if proposed parent inherits from child.
16236 : * (In particular, this disallows making a rel inherit from itself.)
16237 : *
16238 : * This is not completely bulletproof because of race conditions: in
16239 : * multi-level inheritance trees, someone else could concurrently be
16240 : * making another inheritance link that closes the loop but does not join
16241 : * either of the rels we have locked. Preventing that seems to require
16242 : * exclusive locks on the entire inheritance tree, which is a cure worse
16243 : * than the disease. find_all_inheritors() will cope with circularity
16244 : * anyway, so don't sweat it too much.
16245 : *
16246 : * We use weakest lock we can on child's children, namely AccessShareLock.
16247 : */
16248 316 : children = find_all_inheritors(RelationGetRelid(child_rel),
16249 : AccessShareLock, NULL);
16250 :
16251 316 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16252 12 : ereport(ERROR,
16253 : (errcode(ERRCODE_DUPLICATE_TABLE),
16254 : errmsg("circular inheritance not allowed"),
16255 : errdetail("\"%s\" is already a child of \"%s\".",
16256 : parent->relname,
16257 : RelationGetRelationName(child_rel))));
16258 :
16259 : /*
16260 : * If child_rel has row-level triggers with transition tables, we
16261 : * currently don't allow it to become an inheritance child. See also
16262 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16263 : */
16264 304 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16265 304 : if (trigger_name != NULL)
16266 6 : ereport(ERROR,
16267 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16268 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16269 : trigger_name, RelationGetRelationName(child_rel)),
16270 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16271 :
16272 : /* OK to create inheritance */
16273 298 : CreateInheritance(child_rel, parent_rel, false);
16274 :
16275 232 : ObjectAddressSet(address, RelationRelationId,
16276 : RelationGetRelid(parent_rel));
16277 :
16278 : /* keep our lock on the parent relation until commit */
16279 232 : table_close(parent_rel, NoLock);
16280 :
16281 232 : return address;
16282 : }
16283 :
16284 : /*
16285 : * CreateInheritance
16286 : * Catalog manipulation portion of creating inheritance between a child
16287 : * table and a parent table.
16288 : *
16289 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16290 : */
16291 : static void
16292 2352 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16293 : {
16294 : Relation catalogRelation;
16295 : SysScanDesc scan;
16296 : ScanKeyData key;
16297 : HeapTuple inheritsTuple;
16298 : int32 inhseqno;
16299 :
16300 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16301 2352 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16302 :
16303 : /*
16304 : * Check for duplicates in the list of parents, and determine the highest
16305 : * inhseqno already present; we'll use the next one for the new parent.
16306 : * Also, if proposed child is a partition, it cannot already be
16307 : * inheriting.
16308 : *
16309 : * Note: we do not reject the case where the child already inherits from
16310 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16311 : */
16312 2352 : ScanKeyInit(&key,
16313 : Anum_pg_inherits_inhrelid,
16314 : BTEqualStrategyNumber, F_OIDEQ,
16315 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16316 2352 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16317 : true, NULL, 1, &key);
16318 :
16319 : /* inhseqno sequences start at 1 */
16320 2352 : inhseqno = 0;
16321 2398 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16322 : {
16323 52 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16324 :
16325 52 : if (inh->inhparent == RelationGetRelid(parent_rel))
16326 6 : ereport(ERROR,
16327 : (errcode(ERRCODE_DUPLICATE_TABLE),
16328 : errmsg("relation \"%s\" would be inherited from more than once",
16329 : RelationGetRelationName(parent_rel))));
16330 :
16331 46 : if (inh->inhseqno > inhseqno)
16332 46 : inhseqno = inh->inhseqno;
16333 : }
16334 2346 : systable_endscan(scan);
16335 :
16336 : /* Match up the columns and bump attinhcount as needed */
16337 2346 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16338 :
16339 : /* Match up the constraints and bump coninhcount as needed */
16340 2262 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16341 :
16342 : /*
16343 : * OK, it looks valid. Make the catalog entries that show inheritance.
16344 : */
16345 2214 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16346 : RelationGetRelid(parent_rel),
16347 : inhseqno + 1,
16348 : catalogRelation,
16349 2214 : parent_rel->rd_rel->relkind ==
16350 : RELKIND_PARTITIONED_TABLE);
16351 :
16352 : /* Now we're done with pg_inherits */
16353 2214 : table_close(catalogRelation, RowExclusiveLock);
16354 2214 : }
16355 :
16356 : /*
16357 : * Obtain the source-text form of the constraint expression for a check
16358 : * constraint, given its pg_constraint tuple
16359 : */
16360 : static char *
16361 184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16362 : {
16363 : Form_pg_constraint con;
16364 : bool isnull;
16365 : Datum attr;
16366 : Datum expr;
16367 :
16368 184 : con = (Form_pg_constraint) GETSTRUCT(contup);
16369 184 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16370 184 : if (isnull)
16371 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16372 :
16373 184 : expr = DirectFunctionCall2(pg_get_expr, attr,
16374 : ObjectIdGetDatum(con->conrelid));
16375 184 : return TextDatumGetCString(expr);
16376 : }
16377 :
16378 : /*
16379 : * Determine whether two check constraints are functionally equivalent
16380 : *
16381 : * The test we apply is to see whether they reverse-compile to the same
16382 : * source string. This insulates us from issues like whether attributes
16383 : * have the same physical column numbers in parent and child relations.
16384 : *
16385 : * Note that we ignore enforceability as there are cases where constraints
16386 : * with differing enforceability are allowed.
16387 : */
16388 : static bool
16389 92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16390 : {
16391 92 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16392 92 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16393 :
16394 92 : if (acon->condeferrable != bcon->condeferrable ||
16395 92 : acon->condeferred != bcon->condeferred ||
16396 92 : strcmp(decompile_conbin(a, tupleDesc),
16397 92 : decompile_conbin(b, tupleDesc)) != 0)
16398 6 : return false;
16399 : else
16400 86 : return true;
16401 : }
16402 :
16403 : /*
16404 : * Check columns in child table match up with columns in parent, and increment
16405 : * their attinhcount.
16406 : *
16407 : * Called by CreateInheritance
16408 : *
16409 : * Currently all parent columns must be found in child. Missing columns are an
16410 : * error. One day we might consider creating new columns like CREATE TABLE
16411 : * does. However, that is widely unpopular --- in the common use case of
16412 : * partitioned tables it's a foot-gun.
16413 : *
16414 : * The data type must match exactly. If the parent column is NOT NULL then
16415 : * the child must be as well. Defaults are not compared, however.
16416 : */
16417 : static void
16418 2346 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16419 : {
16420 : Relation attrrel;
16421 : TupleDesc parent_desc;
16422 :
16423 2346 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16424 2346 : parent_desc = RelationGetDescr(parent_rel);
16425 :
16426 7568 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16427 : {
16428 5306 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16429 5306 : char *parent_attname = NameStr(parent_att->attname);
16430 : HeapTuple tuple;
16431 :
16432 : /* Ignore dropped columns in the parent. */
16433 5306 : if (parent_att->attisdropped)
16434 296 : continue;
16435 :
16436 : /* Find same column in child (matching on column name). */
16437 5010 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16438 5010 : if (HeapTupleIsValid(tuple))
16439 : {
16440 4998 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16441 :
16442 4998 : if (parent_att->atttypid != child_att->atttypid ||
16443 4992 : parent_att->atttypmod != child_att->atttypmod)
16444 12 : ereport(ERROR,
16445 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16446 : errmsg("child table \"%s\" has different type for column \"%s\"",
16447 : RelationGetRelationName(child_rel), parent_attname)));
16448 :
16449 4986 : if (parent_att->attcollation != child_att->attcollation)
16450 6 : ereport(ERROR,
16451 : (errcode(ERRCODE_COLLATION_MISMATCH),
16452 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16453 : RelationGetRelationName(child_rel), parent_attname)));
16454 :
16455 : /*
16456 : * If the parent has a not-null constraint that's not NO INHERIT,
16457 : * make sure the child has one too.
16458 : *
16459 : * Other constraints are checked elsewhere.
16460 : */
16461 4980 : if (parent_att->attnotnull && !child_att->attnotnull)
16462 : {
16463 : HeapTuple contup;
16464 :
16465 30 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16466 30 : parent_att->attnum);
16467 30 : if (HeapTupleIsValid(contup) &&
16468 30 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16469 24 : ereport(ERROR,
16470 : errcode(ERRCODE_DATATYPE_MISMATCH),
16471 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16472 : parent_attname, RelationGetRelationName(child_rel)));
16473 : }
16474 :
16475 : /*
16476 : * Child column must be generated if and only if parent column is.
16477 : */
16478 4956 : if (parent_att->attgenerated && !child_att->attgenerated)
16479 18 : ereport(ERROR,
16480 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16481 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16482 4938 : if (child_att->attgenerated && !parent_att->attgenerated)
16483 12 : ereport(ERROR,
16484 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16485 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16486 :
16487 : /*
16488 : * Regular inheritance children are independent enough not to
16489 : * inherit identity columns. But partitions are integral part of
16490 : * a partitioned table and inherit identity column.
16491 : */
16492 4926 : if (ispartition)
16493 4386 : child_att->attidentity = parent_att->attidentity;
16494 :
16495 : /*
16496 : * OK, bump the child column's inheritance count. (If we fail
16497 : * later on, this change will just roll back.)
16498 : */
16499 4926 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
16500 : &child_att->attinhcount))
16501 0 : ereport(ERROR,
16502 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16503 : errmsg("too many inheritance parents"));
16504 :
16505 : /*
16506 : * In case of partitions, we must enforce that value of attislocal
16507 : * is same in all partitions. (Note: there are only inherited
16508 : * attributes in partitions)
16509 : */
16510 4926 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16511 : {
16512 : Assert(child_att->attinhcount == 1);
16513 4386 : child_att->attislocal = false;
16514 : }
16515 :
16516 4926 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16517 4926 : heap_freetuple(tuple);
16518 : }
16519 : else
16520 : {
16521 12 : ereport(ERROR,
16522 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16523 : errmsg("child table is missing column \"%s\"", parent_attname)));
16524 : }
16525 : }
16526 :
16527 2262 : table_close(attrrel, RowExclusiveLock);
16528 2262 : }
16529 :
16530 : /*
16531 : * Check constraints in child table match up with constraints in parent,
16532 : * and increment their coninhcount.
16533 : *
16534 : * Constraints that are marked ONLY in the parent are ignored.
16535 : *
16536 : * Called by CreateInheritance
16537 : *
16538 : * Currently all constraints in parent must be present in the child. One day we
16539 : * may consider adding new constraints like CREATE TABLE does.
16540 : *
16541 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
16542 : * constraints. As long as tables have more like 10 constraints it shouldn't be
16543 : * a problem though. Even 100 constraints ought not be the end of the world.
16544 : *
16545 : * XXX See MergeWithExistingConstraint too if you change this code.
16546 : */
16547 : static void
16548 2262 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16549 : {
16550 : Relation constraintrel;
16551 : SysScanDesc parent_scan;
16552 : ScanKeyData parent_key;
16553 : HeapTuple parent_tuple;
16554 2262 : Oid parent_relid = RelationGetRelid(parent_rel);
16555 : AttrMap *attmap;
16556 :
16557 2262 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16558 :
16559 : /* Outer loop scans through the parent's constraint definitions */
16560 2262 : ScanKeyInit(&parent_key,
16561 : Anum_pg_constraint_conrelid,
16562 : BTEqualStrategyNumber, F_OIDEQ,
16563 : ObjectIdGetDatum(parent_relid));
16564 2262 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16565 : true, NULL, 1, &parent_key);
16566 :
16567 2262 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16568 : RelationGetDescr(child_rel),
16569 : true);
16570 :
16571 3928 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16572 : {
16573 1714 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16574 : SysScanDesc child_scan;
16575 : ScanKeyData child_key;
16576 : HeapTuple child_tuple;
16577 : AttrNumber parent_attno;
16578 1714 : bool found = false;
16579 :
16580 1714 : if (parent_con->contype != CONSTRAINT_CHECK &&
16581 1578 : parent_con->contype != CONSTRAINT_NOTNULL)
16582 814 : continue;
16583 :
16584 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16585 932 : if (parent_con->connoinherit)
16586 32 : continue;
16587 :
16588 900 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16589 784 : parent_attno = extractNotNullColumn(parent_tuple);
16590 : else
16591 116 : parent_attno = InvalidAttrNumber;
16592 :
16593 : /* Search for a child constraint matching this one */
16594 900 : ScanKeyInit(&child_key,
16595 : Anum_pg_constraint_conrelid,
16596 : BTEqualStrategyNumber, F_OIDEQ,
16597 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16598 900 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16599 : true, NULL, 1, &child_key);
16600 :
16601 1546 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16602 : {
16603 1522 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16604 : HeapTuple child_copy;
16605 :
16606 1522 : if (child_con->contype != parent_con->contype)
16607 352 : continue;
16608 :
16609 : /*
16610 : * CHECK constraint are matched by constraint name, NOT NULL ones
16611 : * by attribute number.
16612 : */
16613 1170 : if (child_con->contype == CONSTRAINT_CHECK)
16614 : {
16615 122 : if (strcmp(NameStr(parent_con->conname),
16616 122 : NameStr(child_con->conname)) != 0)
16617 30 : continue;
16618 : }
16619 1048 : else if (child_con->contype == CONSTRAINT_NOTNULL)
16620 : {
16621 : Form_pg_attribute parent_attr;
16622 : Form_pg_attribute child_attr;
16623 : AttrNumber child_attno;
16624 :
16625 1048 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
16626 1048 : child_attno = extractNotNullColumn(child_tuple);
16627 1048 : if (parent_attno != attmap->attnums[child_attno - 1])
16628 264 : continue;
16629 :
16630 784 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
16631 : /* there shouldn't be constraints on dropped columns */
16632 784 : if (parent_attr->attisdropped || child_attr->attisdropped)
16633 0 : elog(ERROR, "found not-null constraint on dropped columns");
16634 : }
16635 :
16636 876 : if (child_con->contype == CONSTRAINT_CHECK &&
16637 92 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16638 6 : ereport(ERROR,
16639 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16640 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16641 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16642 :
16643 : /*
16644 : * If the child constraint is "no inherit" then cannot merge
16645 : */
16646 870 : if (child_con->connoinherit)
16647 12 : ereport(ERROR,
16648 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16649 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16650 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16651 :
16652 : /*
16653 : * If the child constraint is "not valid" then cannot merge with a
16654 : * valid parent constraint
16655 : */
16656 858 : if (parent_con->convalidated && child_con->conenforced &&
16657 846 : !child_con->convalidated)
16658 0 : ereport(ERROR,
16659 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16660 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16661 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16662 :
16663 : /*
16664 : * A non-enforced child constraint cannot be merged with an
16665 : * enforced parent constraint. However, the reverse is allowed,
16666 : * where the child constraint is enforced.
16667 : */
16668 858 : if (parent_con->conenforced && !child_con->conenforced)
16669 6 : ereport(ERROR,
16670 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16671 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
16672 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16673 :
16674 : /*
16675 : * OK, bump the child constraint's inheritance count. (If we fail
16676 : * later on, this change will just roll back.)
16677 : */
16678 852 : child_copy = heap_copytuple(child_tuple);
16679 852 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16680 :
16681 852 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
16682 : &child_con->coninhcount))
16683 0 : ereport(ERROR,
16684 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16685 : errmsg("too many inheritance parents"));
16686 :
16687 : /*
16688 : * In case of partitions, an inherited constraint must be
16689 : * inherited only once since it cannot have multiple parents and
16690 : * it is never considered local.
16691 : */
16692 852 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16693 : {
16694 : Assert(child_con->coninhcount == 1);
16695 740 : child_con->conislocal = false;
16696 : }
16697 :
16698 852 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16699 852 : heap_freetuple(child_copy);
16700 :
16701 852 : found = true;
16702 852 : break;
16703 : }
16704 :
16705 876 : systable_endscan(child_scan);
16706 :
16707 876 : if (!found)
16708 : {
16709 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16710 0 : ereport(ERROR,
16711 : errcode(ERRCODE_DATATYPE_MISMATCH),
16712 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16713 : get_attname(parent_relid,
16714 : extractNotNullColumn(parent_tuple),
16715 : false),
16716 : RelationGetRelationName(child_rel)));
16717 :
16718 24 : ereport(ERROR,
16719 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16720 : errmsg("child table is missing constraint \"%s\"",
16721 : NameStr(parent_con->conname))));
16722 : }
16723 : }
16724 :
16725 2214 : systable_endscan(parent_scan);
16726 2214 : table_close(constraintrel, RowExclusiveLock);
16727 2214 : }
16728 :
16729 : /*
16730 : * ALTER TABLE NO INHERIT
16731 : *
16732 : * Return value is the address of the relation that is no longer parent.
16733 : */
16734 : static ObjectAddress
16735 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16736 : {
16737 : ObjectAddress address;
16738 : Relation parent_rel;
16739 :
16740 86 : if (rel->rd_rel->relispartition)
16741 0 : ereport(ERROR,
16742 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16743 : errmsg("cannot change inheritance of a partition")));
16744 :
16745 : /*
16746 : * AccessShareLock on the parent is probably enough, seeing that DROP
16747 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
16748 : * be inspecting the parent's schema.
16749 : */
16750 86 : parent_rel = table_openrv(parent, AccessShareLock);
16751 :
16752 : /*
16753 : * We don't bother to check ownership of the parent table --- ownership of
16754 : * the child is presumed enough rights.
16755 : */
16756 :
16757 : /* Off to RemoveInheritance() where most of the work happens */
16758 86 : RemoveInheritance(rel, parent_rel, false);
16759 :
16760 80 : ObjectAddressSet(address, RelationRelationId,
16761 : RelationGetRelid(parent_rel));
16762 :
16763 : /* keep our lock on the parent relation until commit */
16764 80 : table_close(parent_rel, NoLock);
16765 :
16766 80 : return address;
16767 : }
16768 :
16769 : /*
16770 : * MarkInheritDetached
16771 : *
16772 : * Set inhdetachpending for a partition, for ATExecDetachPartition
16773 : * in concurrent mode. While at it, verify that no other partition is
16774 : * already pending detach.
16775 : */
16776 : static void
16777 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16778 : {
16779 : Relation catalogRelation;
16780 : SysScanDesc scan;
16781 : ScanKeyData key;
16782 : HeapTuple inheritsTuple;
16783 146 : bool found = false;
16784 :
16785 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16786 :
16787 : /*
16788 : * Find pg_inherits entries by inhparent. (We need to scan them all in
16789 : * order to verify that no other partition is pending detach.)
16790 : */
16791 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16792 146 : ScanKeyInit(&key,
16793 : Anum_pg_inherits_inhparent,
16794 : BTEqualStrategyNumber, F_OIDEQ,
16795 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16796 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16797 : true, NULL, 1, &key);
16798 :
16799 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16800 : {
16801 : Form_pg_inherits inhForm;
16802 :
16803 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16804 286 : if (inhForm->inhdetachpending)
16805 2 : ereport(ERROR,
16806 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16807 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16808 : get_rel_name(inhForm->inhrelid),
16809 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16810 : RelationGetRelationName(parent_rel)),
16811 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16812 :
16813 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16814 : {
16815 : HeapTuple newtup;
16816 :
16817 144 : newtup = heap_copytuple(inheritsTuple);
16818 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16819 :
16820 144 : CatalogTupleUpdate(catalogRelation,
16821 : &inheritsTuple->t_self,
16822 : newtup);
16823 144 : found = true;
16824 144 : heap_freetuple(newtup);
16825 : /* keep looking, to ensure we catch others pending detach */
16826 : }
16827 : }
16828 :
16829 : /* Done */
16830 144 : systable_endscan(scan);
16831 144 : table_close(catalogRelation, RowExclusiveLock);
16832 :
16833 144 : if (!found)
16834 0 : ereport(ERROR,
16835 : (errcode(ERRCODE_UNDEFINED_TABLE),
16836 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16837 : RelationGetRelationName(child_rel),
16838 : RelationGetRelationName(parent_rel))));
16839 144 : }
16840 :
16841 : /*
16842 : * RemoveInheritance
16843 : *
16844 : * Drop a parent from the child's parents. This just adjusts the attinhcount
16845 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16846 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16847 : *
16848 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16849 : * up attislocal stays true, which means if a child is ever removed from a
16850 : * parent then its columns will never be automatically dropped which may
16851 : * surprise. But at least we'll never surprise by dropping columns someone
16852 : * isn't expecting to be dropped which would actually mean data loss.
16853 : *
16854 : * coninhcount and conislocal for inherited constraints are adjusted in
16855 : * exactly the same way.
16856 : *
16857 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16858 : */
16859 : static void
16860 566 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16861 : {
16862 : Relation catalogRelation;
16863 : SysScanDesc scan;
16864 : ScanKeyData key[3];
16865 : HeapTuple attributeTuple,
16866 : constraintTuple;
16867 : AttrMap *attmap;
16868 : List *connames;
16869 : List *nncolumns;
16870 : bool found;
16871 : bool is_partitioning;
16872 :
16873 566 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16874 :
16875 566 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16876 : RelationGetRelid(parent_rel),
16877 : expect_detached,
16878 566 : RelationGetRelationName(child_rel));
16879 566 : if (!found)
16880 : {
16881 24 : if (is_partitioning)
16882 18 : ereport(ERROR,
16883 : (errcode(ERRCODE_UNDEFINED_TABLE),
16884 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16885 : RelationGetRelationName(child_rel),
16886 : RelationGetRelationName(parent_rel))));
16887 : else
16888 6 : ereport(ERROR,
16889 : (errcode(ERRCODE_UNDEFINED_TABLE),
16890 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16891 : RelationGetRelationName(parent_rel),
16892 : RelationGetRelationName(child_rel))));
16893 : }
16894 :
16895 : /*
16896 : * Search through child columns looking for ones matching parent rel
16897 : */
16898 542 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16899 542 : ScanKeyInit(&key[0],
16900 : Anum_pg_attribute_attrelid,
16901 : BTEqualStrategyNumber, F_OIDEQ,
16902 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16903 542 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16904 : true, NULL, 1, key);
16905 4844 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16906 : {
16907 4302 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16908 :
16909 : /* Ignore if dropped or not inherited */
16910 4302 : if (att->attisdropped)
16911 6 : continue;
16912 4296 : if (att->attinhcount <= 0)
16913 3282 : continue;
16914 :
16915 1014 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16916 1014 : NameStr(att->attname)))
16917 : {
16918 : /* Decrement inhcount and possibly set islocal to true */
16919 960 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
16920 960 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16921 :
16922 960 : copy_att->attinhcount--;
16923 960 : if (copy_att->attinhcount == 0)
16924 930 : copy_att->attislocal = true;
16925 :
16926 960 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16927 960 : heap_freetuple(copyTuple);
16928 : }
16929 : }
16930 542 : systable_endscan(scan);
16931 542 : table_close(catalogRelation, RowExclusiveLock);
16932 :
16933 : /*
16934 : * Likewise, find inherited check and not-null constraints and disinherit
16935 : * them. To do this, we first need a list of the names of the parent's
16936 : * check constraints. (We cheat a bit by only checking for name matches,
16937 : * assuming that the expressions will match.)
16938 : *
16939 : * For NOT NULL columns, we store column numbers to match, mapping them in
16940 : * to the child rel's attribute numbers.
16941 : */
16942 542 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
16943 : RelationGetDescr(parent_rel),
16944 : false);
16945 :
16946 542 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16947 542 : ScanKeyInit(&key[0],
16948 : Anum_pg_constraint_conrelid,
16949 : BTEqualStrategyNumber, F_OIDEQ,
16950 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16951 542 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16952 : true, NULL, 1, key);
16953 :
16954 542 : connames = NIL;
16955 542 : nncolumns = NIL;
16956 :
16957 1010 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16958 : {
16959 468 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16960 :
16961 468 : if (con->connoinherit)
16962 98 : continue;
16963 :
16964 370 : if (con->contype == CONSTRAINT_CHECK)
16965 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
16966 370 : if (con->contype == CONSTRAINT_NOTNULL)
16967 : {
16968 196 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
16969 :
16970 196 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
16971 : }
16972 : }
16973 :
16974 542 : systable_endscan(scan);
16975 :
16976 : /* Now scan the child's constraints to find matches */
16977 542 : ScanKeyInit(&key[0],
16978 : Anum_pg_constraint_conrelid,
16979 : BTEqualStrategyNumber, F_OIDEQ,
16980 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16981 542 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16982 : true, NULL, 1, key);
16983 :
16984 1194 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16985 : {
16986 652 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16987 652 : bool match = false;
16988 :
16989 : /*
16990 : * Match CHECK constraints by name, not-null constraints by column
16991 : * number, and ignore all others.
16992 : */
16993 652 : if (con->contype == CONSTRAINT_CHECK)
16994 : {
16995 350 : foreach_ptr(char, chkname, connames)
16996 : {
16997 18 : if (con->contype == CONSTRAINT_CHECK &&
16998 18 : strcmp(NameStr(con->conname), chkname) == 0)
16999 : {
17000 12 : match = true;
17001 12 : connames = foreach_delete_current(connames, chkname);
17002 12 : break;
17003 : }
17004 : }
17005 : }
17006 480 : else if (con->contype == CONSTRAINT_NOTNULL)
17007 : {
17008 256 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17009 :
17010 518 : foreach_int(prevattno, nncolumns)
17011 : {
17012 202 : if (prevattno == child_attno)
17013 : {
17014 196 : match = true;
17015 196 : nncolumns = foreach_delete_current(nncolumns, prevattno);
17016 196 : break;
17017 : }
17018 : }
17019 : }
17020 : else
17021 224 : continue;
17022 :
17023 428 : if (match)
17024 : {
17025 : /* Decrement inhcount and possibly set islocal to true */
17026 208 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
17027 208 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17028 :
17029 208 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
17030 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17031 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
17032 :
17033 208 : copy_con->coninhcount--;
17034 208 : if (copy_con->coninhcount == 0)
17035 190 : copy_con->conislocal = true;
17036 :
17037 208 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17038 208 : heap_freetuple(copyTuple);
17039 : }
17040 : }
17041 :
17042 : /* We should have matched all constraints */
17043 542 : if (connames != NIL || nncolumns != NIL)
17044 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
17045 : list_length(connames) + list_length(nncolumns),
17046 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
17047 :
17048 542 : systable_endscan(scan);
17049 542 : table_close(catalogRelation, RowExclusiveLock);
17050 :
17051 542 : drop_parent_dependency(RelationGetRelid(child_rel),
17052 : RelationRelationId,
17053 : RelationGetRelid(parent_rel),
17054 : child_dependency_type(is_partitioning));
17055 :
17056 : /*
17057 : * Post alter hook of this inherits. Since object_access_hook doesn't take
17058 : * multiple object identifiers, we relay oid of parent relation using
17059 : * auxiliary_id argument.
17060 : */
17061 542 : InvokeObjectPostAlterHookArg(InheritsRelationId,
17062 : RelationGetRelid(child_rel), 0,
17063 : RelationGetRelid(parent_rel), false);
17064 542 : }
17065 :
17066 : /*
17067 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17068 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17069 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17070 : * be TypeRelationId). There's no convenient way to do this, so go trawling
17071 : * through pg_depend.
17072 : */
17073 : static void
17074 554 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17075 : DependencyType deptype)
17076 : {
17077 : Relation catalogRelation;
17078 : SysScanDesc scan;
17079 : ScanKeyData key[3];
17080 : HeapTuple depTuple;
17081 :
17082 554 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17083 :
17084 554 : ScanKeyInit(&key[0],
17085 : Anum_pg_depend_classid,
17086 : BTEqualStrategyNumber, F_OIDEQ,
17087 : ObjectIdGetDatum(RelationRelationId));
17088 554 : ScanKeyInit(&key[1],
17089 : Anum_pg_depend_objid,
17090 : BTEqualStrategyNumber, F_OIDEQ,
17091 : ObjectIdGetDatum(relid));
17092 554 : ScanKeyInit(&key[2],
17093 : Anum_pg_depend_objsubid,
17094 : BTEqualStrategyNumber, F_INT4EQ,
17095 : Int32GetDatum(0));
17096 :
17097 554 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17098 : NULL, 3, key);
17099 :
17100 1722 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17101 : {
17102 1168 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17103 :
17104 1168 : if (dep->refclassid == refclassid &&
17105 596 : dep->refobjid == refobjid &&
17106 554 : dep->refobjsubid == 0 &&
17107 554 : dep->deptype == deptype)
17108 554 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17109 : }
17110 :
17111 554 : systable_endscan(scan);
17112 554 : table_close(catalogRelation, RowExclusiveLock);
17113 554 : }
17114 :
17115 : /*
17116 : * ALTER TABLE OF
17117 : *
17118 : * Attach a table to a composite type, as though it had been created with CREATE
17119 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17120 : * subject table must not have inheritance parents. These restrictions ensure
17121 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17122 : *
17123 : * The address of the type is returned.
17124 : */
17125 : static ObjectAddress
17126 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17127 : {
17128 66 : Oid relid = RelationGetRelid(rel);
17129 : Type typetuple;
17130 : Form_pg_type typeform;
17131 : Oid typeid;
17132 : Relation inheritsRelation,
17133 : relationRelation;
17134 : SysScanDesc scan;
17135 : ScanKeyData key;
17136 : AttrNumber table_attno,
17137 : type_attno;
17138 : TupleDesc typeTupleDesc,
17139 : tableTupleDesc;
17140 : ObjectAddress tableobj,
17141 : typeobj;
17142 : HeapTuple classtuple;
17143 :
17144 : /* Validate the type. */
17145 66 : typetuple = typenameType(NULL, ofTypename, NULL);
17146 66 : check_of_type(typetuple);
17147 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17148 66 : typeid = typeform->oid;
17149 :
17150 : /* Fail if the table has any inheritance parents. */
17151 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17152 66 : ScanKeyInit(&key,
17153 : Anum_pg_inherits_inhrelid,
17154 : BTEqualStrategyNumber, F_OIDEQ,
17155 : ObjectIdGetDatum(relid));
17156 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17157 : true, NULL, 1, &key);
17158 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17159 6 : ereport(ERROR,
17160 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17161 : errmsg("typed tables cannot inherit")));
17162 60 : systable_endscan(scan);
17163 60 : table_close(inheritsRelation, AccessShareLock);
17164 :
17165 : /*
17166 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17167 : * require that the order also match. However, attnotnull need not match.
17168 : */
17169 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17170 60 : tableTupleDesc = RelationGetDescr(rel);
17171 60 : table_attno = 1;
17172 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17173 : {
17174 : Form_pg_attribute type_attr,
17175 : table_attr;
17176 : const char *type_attname,
17177 : *table_attname;
17178 :
17179 : /* Get the next non-dropped type attribute. */
17180 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17181 154 : if (type_attr->attisdropped)
17182 44 : continue;
17183 110 : type_attname = NameStr(type_attr->attname);
17184 :
17185 : /* Get the next non-dropped table attribute. */
17186 : do
17187 : {
17188 122 : if (table_attno > tableTupleDesc->natts)
17189 6 : ereport(ERROR,
17190 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17191 : errmsg("table is missing column \"%s\"",
17192 : type_attname)));
17193 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17194 116 : table_attno++;
17195 116 : } while (table_attr->attisdropped);
17196 104 : table_attname = NameStr(table_attr->attname);
17197 :
17198 : /* Compare name. */
17199 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17200 6 : ereport(ERROR,
17201 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17202 : errmsg("table has column \"%s\" where type requires \"%s\"",
17203 : table_attname, type_attname)));
17204 :
17205 : /* Compare type. */
17206 98 : if (table_attr->atttypid != type_attr->atttypid ||
17207 92 : table_attr->atttypmod != type_attr->atttypmod ||
17208 86 : table_attr->attcollation != type_attr->attcollation)
17209 12 : ereport(ERROR,
17210 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17211 : errmsg("table \"%s\" has different type for column \"%s\"",
17212 : RelationGetRelationName(rel), type_attname)));
17213 : }
17214 36 : ReleaseTupleDesc(typeTupleDesc);
17215 :
17216 : /* Any remaining columns at the end of the table had better be dropped. */
17217 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17218 : {
17219 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17220 : table_attno - 1);
17221 :
17222 6 : if (!table_attr->attisdropped)
17223 6 : ereport(ERROR,
17224 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17225 : errmsg("table has extra column \"%s\"",
17226 : NameStr(table_attr->attname))));
17227 : }
17228 :
17229 : /* If the table was already typed, drop the existing dependency. */
17230 30 : if (rel->rd_rel->reloftype)
17231 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17232 : DEPENDENCY_NORMAL);
17233 :
17234 : /* Record a dependency on the new type. */
17235 30 : tableobj.classId = RelationRelationId;
17236 30 : tableobj.objectId = relid;
17237 30 : tableobj.objectSubId = 0;
17238 30 : typeobj.classId = TypeRelationId;
17239 30 : typeobj.objectId = typeid;
17240 30 : typeobj.objectSubId = 0;
17241 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17242 :
17243 : /* Update pg_class.reloftype */
17244 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17245 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17246 30 : if (!HeapTupleIsValid(classtuple))
17247 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17248 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17249 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17250 :
17251 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17252 :
17253 30 : heap_freetuple(classtuple);
17254 30 : table_close(relationRelation, RowExclusiveLock);
17255 :
17256 30 : ReleaseSysCache(typetuple);
17257 :
17258 30 : return typeobj;
17259 : }
17260 :
17261 : /*
17262 : * ALTER TABLE NOT OF
17263 : *
17264 : * Detach a typed table from its originating type. Just clear reloftype and
17265 : * remove the dependency.
17266 : */
17267 : static void
17268 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17269 : {
17270 6 : Oid relid = RelationGetRelid(rel);
17271 : Relation relationRelation;
17272 : HeapTuple tuple;
17273 :
17274 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17275 0 : ereport(ERROR,
17276 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17277 : errmsg("\"%s\" is not a typed table",
17278 : RelationGetRelationName(rel))));
17279 :
17280 : /*
17281 : * We don't bother to check ownership of the type --- ownership of the
17282 : * table is presumed enough rights. No lock required on the type, either.
17283 : */
17284 :
17285 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17286 : DEPENDENCY_NORMAL);
17287 :
17288 : /* Clear pg_class.reloftype */
17289 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17290 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17291 6 : if (!HeapTupleIsValid(tuple))
17292 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17293 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17294 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17295 :
17296 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17297 :
17298 6 : heap_freetuple(tuple);
17299 6 : table_close(relationRelation, RowExclusiveLock);
17300 6 : }
17301 :
17302 : /*
17303 : * relation_mark_replica_identity: Update a table's replica identity
17304 : *
17305 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17306 : * index. Otherwise, it must be InvalidOid.
17307 : *
17308 : * Caller had better hold an exclusive lock on the relation, as the results
17309 : * of running two of these concurrently wouldn't be pretty.
17310 : */
17311 : static void
17312 454 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17313 : bool is_internal)
17314 : {
17315 : Relation pg_index;
17316 : Relation pg_class;
17317 : HeapTuple pg_class_tuple;
17318 : HeapTuple pg_index_tuple;
17319 : Form_pg_class pg_class_form;
17320 : Form_pg_index pg_index_form;
17321 : ListCell *index;
17322 :
17323 : /*
17324 : * Check whether relreplident has changed, and update it if so.
17325 : */
17326 454 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17327 454 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17328 : ObjectIdGetDatum(RelationGetRelid(rel)));
17329 454 : if (!HeapTupleIsValid(pg_class_tuple))
17330 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17331 : RelationGetRelationName(rel));
17332 454 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17333 454 : if (pg_class_form->relreplident != ri_type)
17334 : {
17335 404 : pg_class_form->relreplident = ri_type;
17336 404 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17337 : }
17338 454 : table_close(pg_class, RowExclusiveLock);
17339 454 : heap_freetuple(pg_class_tuple);
17340 :
17341 : /*
17342 : * Update the per-index indisreplident flags correctly.
17343 : */
17344 454 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17345 1174 : foreach(index, RelationGetIndexList(rel))
17346 : {
17347 720 : Oid thisIndexOid = lfirst_oid(index);
17348 720 : bool dirty = false;
17349 :
17350 720 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17351 : ObjectIdGetDatum(thisIndexOid));
17352 720 : if (!HeapTupleIsValid(pg_index_tuple))
17353 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17354 720 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17355 :
17356 720 : if (thisIndexOid == indexOid)
17357 : {
17358 : /* Set the bit if not already set. */
17359 240 : if (!pg_index_form->indisreplident)
17360 : {
17361 222 : dirty = true;
17362 222 : pg_index_form->indisreplident = true;
17363 : }
17364 : }
17365 : else
17366 : {
17367 : /* Unset the bit if set. */
17368 480 : if (pg_index_form->indisreplident)
17369 : {
17370 52 : dirty = true;
17371 52 : pg_index_form->indisreplident = false;
17372 : }
17373 : }
17374 :
17375 720 : if (dirty)
17376 : {
17377 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17378 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17379 : InvalidOid, is_internal);
17380 :
17381 : /*
17382 : * Invalidate the relcache for the table, so that after we commit
17383 : * all sessions will refresh the table's replica identity index
17384 : * before attempting any UPDATE or DELETE on the table. (If we
17385 : * changed the table's pg_class row above, then a relcache inval
17386 : * is already queued due to that; but we might not have.)
17387 : */
17388 274 : CacheInvalidateRelcache(rel);
17389 : }
17390 720 : heap_freetuple(pg_index_tuple);
17391 : }
17392 :
17393 454 : table_close(pg_index, RowExclusiveLock);
17394 454 : }
17395 :
17396 : /*
17397 : * ALTER TABLE <name> REPLICA IDENTITY ...
17398 : */
17399 : static void
17400 502 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17401 : {
17402 : Oid indexOid;
17403 : Relation indexRel;
17404 : int key;
17405 :
17406 502 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17407 : {
17408 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17409 6 : return;
17410 : }
17411 496 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17412 : {
17413 160 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17414 160 : return;
17415 : }
17416 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17417 : {
17418 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17419 48 : return;
17420 : }
17421 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17422 : {
17423 : /* fallthrough */ ;
17424 : }
17425 : else
17426 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17427 :
17428 : /* Check that the index exists */
17429 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17430 288 : if (!OidIsValid(indexOid))
17431 0 : ereport(ERROR,
17432 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17433 : errmsg("index \"%s\" for table \"%s\" does not exist",
17434 : stmt->name, RelationGetRelationName(rel))));
17435 :
17436 288 : indexRel = index_open(indexOid, ShareLock);
17437 :
17438 : /* Check that the index is on the relation we're altering. */
17439 288 : if (indexRel->rd_index == NULL ||
17440 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17441 6 : ereport(ERROR,
17442 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17443 : errmsg("\"%s\" is not an index for table \"%s\"",
17444 : RelationGetRelationName(indexRel),
17445 : RelationGetRelationName(rel))));
17446 :
17447 : /*
17448 : * The AM must support uniqueness, and the index must in fact be unique.
17449 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17450 : * exclusion), we can use that too.
17451 : */
17452 282 : if ((!indexRel->rd_indam->amcanunique ||
17453 262 : !indexRel->rd_index->indisunique) &&
17454 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
17455 12 : ereport(ERROR,
17456 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17457 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17458 : RelationGetRelationName(indexRel))));
17459 : /* Deferred indexes are not guaranteed to be always unique. */
17460 270 : if (!indexRel->rd_index->indimmediate)
17461 12 : ereport(ERROR,
17462 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17463 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17464 : RelationGetRelationName(indexRel))));
17465 : /* Expression indexes aren't supported. */
17466 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
17467 6 : ereport(ERROR,
17468 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17469 : errmsg("cannot use expression index \"%s\" as replica identity",
17470 : RelationGetRelationName(indexRel))));
17471 : /* Predicate indexes aren't supported. */
17472 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
17473 6 : ereport(ERROR,
17474 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17475 : errmsg("cannot use partial index \"%s\" as replica identity",
17476 : RelationGetRelationName(indexRel))));
17477 :
17478 : /* Check index for nullable columns. */
17479 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17480 : {
17481 312 : int16 attno = indexRel->rd_index->indkey.values[key];
17482 : Form_pg_attribute attr;
17483 :
17484 : /*
17485 : * Reject any other system columns. (Going forward, we'll disallow
17486 : * indexes containing such columns in the first place, but they might
17487 : * exist in older branches.)
17488 : */
17489 312 : if (attno <= 0)
17490 0 : ereport(ERROR,
17491 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17492 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17493 : RelationGetRelationName(indexRel), attno)));
17494 :
17495 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17496 312 : if (!attr->attnotnull)
17497 6 : ereport(ERROR,
17498 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17499 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17500 : RelationGetRelationName(indexRel),
17501 : NameStr(attr->attname))));
17502 : }
17503 :
17504 : /* This index is suitable for use as a replica identity. Mark it. */
17505 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17506 :
17507 240 : index_close(indexRel, NoLock);
17508 : }
17509 :
17510 : /*
17511 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17512 : */
17513 : static void
17514 294 : ATExecSetRowSecurity(Relation rel, bool rls)
17515 : {
17516 : Relation pg_class;
17517 : Oid relid;
17518 : HeapTuple tuple;
17519 :
17520 294 : relid = RelationGetRelid(rel);
17521 :
17522 : /* Pull the record for this relation and update it */
17523 294 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17524 :
17525 294 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17526 :
17527 294 : if (!HeapTupleIsValid(tuple))
17528 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17529 :
17530 294 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17531 294 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17532 :
17533 294 : InvokeObjectPostAlterHook(RelationRelationId,
17534 : RelationGetRelid(rel), 0);
17535 :
17536 294 : table_close(pg_class, RowExclusiveLock);
17537 294 : heap_freetuple(tuple);
17538 294 : }
17539 :
17540 : /*
17541 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17542 : */
17543 : static void
17544 120 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17545 : {
17546 : Relation pg_class;
17547 : Oid relid;
17548 : HeapTuple tuple;
17549 :
17550 120 : relid = RelationGetRelid(rel);
17551 :
17552 120 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17553 :
17554 120 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17555 :
17556 120 : if (!HeapTupleIsValid(tuple))
17557 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17558 :
17559 120 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17560 120 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17561 :
17562 120 : InvokeObjectPostAlterHook(RelationRelationId,
17563 : RelationGetRelid(rel), 0);
17564 :
17565 120 : table_close(pg_class, RowExclusiveLock);
17566 120 : heap_freetuple(tuple);
17567 120 : }
17568 :
17569 : /*
17570 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
17571 : */
17572 : static void
17573 58 : ATExecGenericOptions(Relation rel, List *options)
17574 : {
17575 : Relation ftrel;
17576 : ForeignServer *server;
17577 : ForeignDataWrapper *fdw;
17578 : HeapTuple tuple;
17579 : bool isnull;
17580 : Datum repl_val[Natts_pg_foreign_table];
17581 : bool repl_null[Natts_pg_foreign_table];
17582 : bool repl_repl[Natts_pg_foreign_table];
17583 : Datum datum;
17584 : Form_pg_foreign_table tableform;
17585 :
17586 58 : if (options == NIL)
17587 0 : return;
17588 :
17589 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17590 :
17591 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17592 : ObjectIdGetDatum(rel->rd_id));
17593 58 : if (!HeapTupleIsValid(tuple))
17594 0 : ereport(ERROR,
17595 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17596 : errmsg("foreign table \"%s\" does not exist",
17597 : RelationGetRelationName(rel))));
17598 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17599 58 : server = GetForeignServer(tableform->ftserver);
17600 58 : fdw = GetForeignDataWrapper(server->fdwid);
17601 :
17602 58 : memset(repl_val, 0, sizeof(repl_val));
17603 58 : memset(repl_null, false, sizeof(repl_null));
17604 58 : memset(repl_repl, false, sizeof(repl_repl));
17605 :
17606 : /* Extract the current options */
17607 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
17608 : tuple,
17609 : Anum_pg_foreign_table_ftoptions,
17610 : &isnull);
17611 58 : if (isnull)
17612 4 : datum = PointerGetDatum(NULL);
17613 :
17614 : /* Transform the options */
17615 58 : datum = transformGenericOptions(ForeignTableRelationId,
17616 : datum,
17617 : options,
17618 : fdw->fdwvalidator);
17619 :
17620 56 : if (PointerIsValid(DatumGetPointer(datum)))
17621 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17622 : else
17623 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17624 :
17625 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17626 :
17627 : /* Everything looks good - update the tuple */
17628 :
17629 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17630 : repl_val, repl_null, repl_repl);
17631 :
17632 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17633 :
17634 : /*
17635 : * Invalidate relcache so that all sessions will refresh any cached plans
17636 : * that might depend on the old options.
17637 : */
17638 56 : CacheInvalidateRelcache(rel);
17639 :
17640 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
17641 : RelationGetRelid(rel), 0);
17642 :
17643 56 : table_close(ftrel, RowExclusiveLock);
17644 :
17645 56 : heap_freetuple(tuple);
17646 : }
17647 :
17648 : /*
17649 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
17650 : *
17651 : * Return value is the address of the modified column
17652 : */
17653 : static ObjectAddress
17654 68 : ATExecSetCompression(Relation rel,
17655 : const char *column,
17656 : Node *newValue,
17657 : LOCKMODE lockmode)
17658 : {
17659 : Relation attrel;
17660 : HeapTuple tuple;
17661 : Form_pg_attribute atttableform;
17662 : AttrNumber attnum;
17663 : char *compression;
17664 : char cmethod;
17665 : ObjectAddress address;
17666 :
17667 68 : compression = strVal(newValue);
17668 :
17669 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
17670 :
17671 : /* copy the cache entry so we can scribble on it below */
17672 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17673 68 : if (!HeapTupleIsValid(tuple))
17674 0 : ereport(ERROR,
17675 : (errcode(ERRCODE_UNDEFINED_COLUMN),
17676 : errmsg("column \"%s\" of relation \"%s\" does not exist",
17677 : column, RelationGetRelationName(rel))));
17678 :
17679 : /* prevent them from altering a system attribute */
17680 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17681 68 : attnum = atttableform->attnum;
17682 68 : if (attnum <= 0)
17683 0 : ereport(ERROR,
17684 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17685 : errmsg("cannot alter system column \"%s\"", column)));
17686 :
17687 : /*
17688 : * Check that column type is compressible, then get the attribute
17689 : * compression method code
17690 : */
17691 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17692 :
17693 : /* update pg_attribute entry */
17694 62 : atttableform->attcompression = cmethod;
17695 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17696 :
17697 62 : InvokeObjectPostAlterHook(RelationRelationId,
17698 : RelationGetRelid(rel),
17699 : attnum);
17700 :
17701 : /*
17702 : * Apply the change to indexes as well (only for simple index columns,
17703 : * matching behavior of index.c ConstructTupleDescriptor()).
17704 : */
17705 62 : SetIndexStorageProperties(rel, attrel, attnum,
17706 : false, 0,
17707 : true, cmethod,
17708 : lockmode);
17709 :
17710 62 : heap_freetuple(tuple);
17711 :
17712 62 : table_close(attrel, RowExclusiveLock);
17713 :
17714 : /* make changes visible */
17715 62 : CommandCounterIncrement();
17716 :
17717 62 : ObjectAddressSubSet(address, RelationRelationId,
17718 : RelationGetRelid(rel), attnum);
17719 62 : return address;
17720 : }
17721 :
17722 :
17723 : /*
17724 : * Preparation phase for SET LOGGED/UNLOGGED
17725 : *
17726 : * This verifies that we're not trying to change a temp table. Also,
17727 : * existing foreign key constraints are checked to avoid ending up with
17728 : * permanent tables referencing unlogged tables.
17729 : */
17730 : static void
17731 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
17732 : {
17733 : Relation pg_constraint;
17734 : HeapTuple tuple;
17735 : SysScanDesc scan;
17736 : ScanKeyData skey[1];
17737 :
17738 : /*
17739 : * Disallow changing status for a temp table. Also verify whether we can
17740 : * get away with doing nothing; in such cases we don't need to run the
17741 : * checks below, either.
17742 : */
17743 100 : switch (rel->rd_rel->relpersistence)
17744 : {
17745 0 : case RELPERSISTENCE_TEMP:
17746 0 : ereport(ERROR,
17747 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17748 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
17749 : RelationGetRelationName(rel)),
17750 : errtable(rel)));
17751 : break;
17752 56 : case RELPERSISTENCE_PERMANENT:
17753 56 : if (toLogged)
17754 : /* nothing to do */
17755 12 : return;
17756 50 : break;
17757 44 : case RELPERSISTENCE_UNLOGGED:
17758 44 : if (!toLogged)
17759 : /* nothing to do */
17760 6 : return;
17761 38 : break;
17762 : }
17763 :
17764 : /*
17765 : * Check that the table is not part of any publication when changing to
17766 : * UNLOGGED, as UNLOGGED tables can't be published.
17767 : */
17768 138 : if (!toLogged &&
17769 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
17770 0 : ereport(ERROR,
17771 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17772 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17773 : RelationGetRelationName(rel)),
17774 : errdetail("Unlogged relations cannot be replicated.")));
17775 :
17776 : /*
17777 : * Check existing foreign key constraints to preserve the invariant that
17778 : * permanent tables cannot reference unlogged ones. Self-referencing
17779 : * foreign keys can safely be ignored.
17780 : */
17781 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17782 :
17783 : /*
17784 : * Scan conrelid if changing to permanent, else confrelid. This also
17785 : * determines whether a useful index exists.
17786 : */
17787 88 : ScanKeyInit(&skey[0],
17788 : toLogged ? Anum_pg_constraint_conrelid :
17789 : Anum_pg_constraint_confrelid,
17790 : BTEqualStrategyNumber, F_OIDEQ,
17791 : ObjectIdGetDatum(RelationGetRelid(rel)));
17792 88 : scan = systable_beginscan(pg_constraint,
17793 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17794 : true, NULL, 1, skey);
17795 :
17796 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17797 : {
17798 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17799 :
17800 66 : if (con->contype == CONSTRAINT_FOREIGN)
17801 : {
17802 : Oid foreignrelid;
17803 : Relation foreignrel;
17804 :
17805 : /* the opposite end of what we used as scankey */
17806 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17807 :
17808 : /* ignore if self-referencing */
17809 30 : if (RelationGetRelid(rel) == foreignrelid)
17810 12 : continue;
17811 :
17812 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17813 :
17814 18 : if (toLogged)
17815 : {
17816 6 : if (!RelationIsPermanent(foreignrel))
17817 6 : ereport(ERROR,
17818 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17819 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17820 : RelationGetRelationName(rel),
17821 : RelationGetRelationName(foreignrel)),
17822 : errtableconstraint(rel, NameStr(con->conname))));
17823 : }
17824 : else
17825 : {
17826 12 : if (RelationIsPermanent(foreignrel))
17827 6 : ereport(ERROR,
17828 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17829 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17830 : RelationGetRelationName(rel),
17831 : RelationGetRelationName(foreignrel)),
17832 : errtableconstraint(rel, NameStr(con->conname))));
17833 : }
17834 :
17835 6 : relation_close(foreignrel, AccessShareLock);
17836 : }
17837 : }
17838 :
17839 76 : systable_endscan(scan);
17840 :
17841 76 : table_close(pg_constraint, AccessShareLock);
17842 :
17843 : /* force rewrite if necessary; see comment in ATRewriteTables */
17844 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
17845 76 : if (toLogged)
17846 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
17847 : else
17848 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
17849 76 : tab->chgPersistence = true;
17850 : }
17851 :
17852 : /*
17853 : * Execute ALTER TABLE SET SCHEMA
17854 : */
17855 : ObjectAddress
17856 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17857 : {
17858 : Relation rel;
17859 : Oid relid;
17860 : Oid oldNspOid;
17861 : Oid nspOid;
17862 : RangeVar *newrv;
17863 : ObjectAddresses *objsMoved;
17864 : ObjectAddress myself;
17865 :
17866 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17867 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
17868 : RangeVarCallbackForAlterRelation,
17869 : stmt);
17870 :
17871 102 : if (!OidIsValid(relid))
17872 : {
17873 12 : ereport(NOTICE,
17874 : (errmsg("relation \"%s\" does not exist, skipping",
17875 : stmt->relation->relname)));
17876 12 : return InvalidObjectAddress;
17877 : }
17878 :
17879 90 : rel = relation_open(relid, NoLock);
17880 :
17881 90 : oldNspOid = RelationGetNamespace(rel);
17882 :
17883 : /* If it's an owned sequence, disallow moving it by itself. */
17884 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17885 : {
17886 : Oid tableId;
17887 : int32 colId;
17888 :
17889 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17890 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17891 6 : ereport(ERROR,
17892 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17893 : errmsg("cannot move an owned sequence into another schema"),
17894 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
17895 : RelationGetRelationName(rel),
17896 : get_rel_name(tableId))));
17897 : }
17898 :
17899 : /* Get and lock schema OID and check its permissions. */
17900 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17901 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17902 :
17903 : /* common checks on switching namespaces */
17904 84 : CheckSetNamespace(oldNspOid, nspOid);
17905 :
17906 84 : objsMoved = new_object_addresses();
17907 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17908 84 : free_object_addresses(objsMoved);
17909 :
17910 84 : ObjectAddressSet(myself, RelationRelationId, relid);
17911 :
17912 84 : if (oldschema)
17913 84 : *oldschema = oldNspOid;
17914 :
17915 : /* close rel, but keep lock until commit */
17916 84 : relation_close(rel, NoLock);
17917 :
17918 84 : return myself;
17919 : }
17920 :
17921 : /*
17922 : * The guts of relocating a table or materialized view to another namespace:
17923 : * besides moving the relation itself, its dependent objects are relocated to
17924 : * the new schema.
17925 : */
17926 : void
17927 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17928 : ObjectAddresses *objsMoved)
17929 : {
17930 : Relation classRel;
17931 :
17932 : Assert(objsMoved != NULL);
17933 :
17934 : /* OK, modify the pg_class row and pg_depend entry */
17935 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
17936 :
17937 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17938 : nspOid, true, objsMoved);
17939 :
17940 : /* Fix the table's row type too, if it has one */
17941 86 : if (OidIsValid(rel->rd_rel->reltype))
17942 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
17943 : false, /* isImplicitArray */
17944 : false, /* ignoreDependent */
17945 : false, /* errorOnTableType */
17946 : objsMoved);
17947 :
17948 : /* Fix other dependent stuff */
17949 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17950 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17951 : objsMoved, AccessExclusiveLock);
17952 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17953 : false, objsMoved);
17954 :
17955 86 : table_close(classRel, RowExclusiveLock);
17956 86 : }
17957 :
17958 : /*
17959 : * The guts of relocating a relation to another namespace: fix the pg_class
17960 : * entry, and the pg_depend entry if any. Caller must already have
17961 : * opened and write-locked pg_class.
17962 : */
17963 : void
17964 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17965 : Oid oldNspOid, Oid newNspOid,
17966 : bool hasDependEntry,
17967 : ObjectAddresses *objsMoved)
17968 : {
17969 : HeapTuple classTup;
17970 : Form_pg_class classForm;
17971 : ObjectAddress thisobj;
17972 188 : bool already_done = false;
17973 :
17974 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
17975 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
17976 188 : if (!HeapTupleIsValid(classTup))
17977 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
17978 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
17979 :
17980 : Assert(classForm->relnamespace == oldNspOid);
17981 :
17982 188 : thisobj.classId = RelationRelationId;
17983 188 : thisobj.objectId = relOid;
17984 188 : thisobj.objectSubId = 0;
17985 :
17986 : /*
17987 : * If the object has already been moved, don't move it again. If it's
17988 : * already in the right place, don't move it, but still fire the object
17989 : * access hook.
17990 : */
17991 188 : already_done = object_address_present(&thisobj, objsMoved);
17992 188 : if (!already_done && oldNspOid != newNspOid)
17993 146 : {
17994 146 : ItemPointerData otid = classTup->t_self;
17995 :
17996 : /* check for duplicate name (more friendly than unique-index failure) */
17997 146 : if (get_relname_relid(NameStr(classForm->relname),
17998 : newNspOid) != InvalidOid)
17999 0 : ereport(ERROR,
18000 : (errcode(ERRCODE_DUPLICATE_TABLE),
18001 : errmsg("relation \"%s\" already exists in schema \"%s\"",
18002 : NameStr(classForm->relname),
18003 : get_namespace_name(newNspOid))));
18004 :
18005 : /* classTup is a copy, so OK to scribble on */
18006 146 : classForm->relnamespace = newNspOid;
18007 :
18008 146 : CatalogTupleUpdate(classRel, &otid, classTup);
18009 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
18010 :
18011 :
18012 : /* Update dependency on schema if caller said so */
18013 250 : if (hasDependEntry &&
18014 104 : changeDependencyFor(RelationRelationId,
18015 : relOid,
18016 : NamespaceRelationId,
18017 : oldNspOid,
18018 : newNspOid) != 1)
18019 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
18020 : NameStr(classForm->relname));
18021 : }
18022 : else
18023 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
18024 188 : if (!already_done)
18025 : {
18026 188 : add_exact_object_address(&thisobj, objsMoved);
18027 :
18028 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18029 : }
18030 :
18031 188 : heap_freetuple(classTup);
18032 188 : }
18033 :
18034 : /*
18035 : * Move all indexes for the specified relation to another namespace.
18036 : *
18037 : * Note: we assume adequate permission checking was done by the caller,
18038 : * and that the caller has a suitable lock on the owning relation.
18039 : */
18040 : static void
18041 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
18042 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18043 : {
18044 : List *indexList;
18045 : ListCell *l;
18046 :
18047 86 : indexList = RelationGetIndexList(rel);
18048 :
18049 132 : foreach(l, indexList)
18050 : {
18051 46 : Oid indexOid = lfirst_oid(l);
18052 : ObjectAddress thisobj;
18053 :
18054 46 : thisobj.classId = RelationRelationId;
18055 46 : thisobj.objectId = indexOid;
18056 46 : thisobj.objectSubId = 0;
18057 :
18058 : /*
18059 : * Note: currently, the index will not have its own dependency on the
18060 : * namespace, so we don't need to do changeDependencyFor(). There's no
18061 : * row type in pg_type, either.
18062 : *
18063 : * XXX this objsMoved test may be pointless -- surely we have a single
18064 : * dependency link from a relation to each index?
18065 : */
18066 46 : if (!object_address_present(&thisobj, objsMoved))
18067 : {
18068 46 : AlterRelationNamespaceInternal(classRel, indexOid,
18069 : oldNspOid, newNspOid,
18070 : false, objsMoved);
18071 46 : add_exact_object_address(&thisobj, objsMoved);
18072 : }
18073 : }
18074 :
18075 86 : list_free(indexList);
18076 86 : }
18077 :
18078 : /*
18079 : * Move all identity and SERIAL-column sequences of the specified relation to another
18080 : * namespace.
18081 : *
18082 : * Note: we assume adequate permission checking was done by the caller,
18083 : * and that the caller has a suitable lock on the owning relation.
18084 : */
18085 : static void
18086 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
18087 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18088 : LOCKMODE lockmode)
18089 : {
18090 : Relation depRel;
18091 : SysScanDesc scan;
18092 : ScanKeyData key[2];
18093 : HeapTuple tup;
18094 :
18095 : /*
18096 : * SERIAL sequences are those having an auto dependency on one of the
18097 : * table's columns (we don't care *which* column, exactly).
18098 : */
18099 86 : depRel = table_open(DependRelationId, AccessShareLock);
18100 :
18101 86 : ScanKeyInit(&key[0],
18102 : Anum_pg_depend_refclassid,
18103 : BTEqualStrategyNumber, F_OIDEQ,
18104 : ObjectIdGetDatum(RelationRelationId));
18105 86 : ScanKeyInit(&key[1],
18106 : Anum_pg_depend_refobjid,
18107 : BTEqualStrategyNumber, F_OIDEQ,
18108 : ObjectIdGetDatum(RelationGetRelid(rel)));
18109 : /* we leave refobjsubid unspecified */
18110 :
18111 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18112 : NULL, 2, key);
18113 :
18114 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
18115 : {
18116 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18117 : Relation seqRel;
18118 :
18119 : /* skip dependencies other than auto dependencies on columns */
18120 530 : if (depForm->refobjsubid == 0 ||
18121 382 : depForm->classid != RelationRelationId ||
18122 42 : depForm->objsubid != 0 ||
18123 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18124 488 : continue;
18125 :
18126 : /* Use relation_open just in case it's an index */
18127 42 : seqRel = relation_open(depForm->objid, lockmode);
18128 :
18129 : /* skip non-sequence relations */
18130 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18131 : {
18132 : /* No need to keep the lock */
18133 0 : relation_close(seqRel, lockmode);
18134 0 : continue;
18135 : }
18136 :
18137 : /* Fix the pg_class and pg_depend entries */
18138 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
18139 : oldNspOid, newNspOid,
18140 : true, objsMoved);
18141 :
18142 : /*
18143 : * Sequences used to have entries in pg_type, but no longer do. If we
18144 : * ever re-instate that, we'll need to move the pg_type entry to the
18145 : * new namespace, too (using AlterTypeNamespaceInternal).
18146 : */
18147 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18148 :
18149 : /* Now we can close it. Keep the lock till end of transaction. */
18150 42 : relation_close(seqRel, NoLock);
18151 : }
18152 :
18153 86 : systable_endscan(scan);
18154 :
18155 86 : relation_close(depRel, AccessShareLock);
18156 86 : }
18157 :
18158 :
18159 : /*
18160 : * This code supports
18161 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18162 : *
18163 : * Because we only support this for TEMP tables, it's sufficient to remember
18164 : * the state in a backend-local data structure.
18165 : */
18166 :
18167 : /*
18168 : * Register a newly-created relation's ON COMMIT action.
18169 : */
18170 : void
18171 166 : register_on_commit_action(Oid relid, OnCommitAction action)
18172 : {
18173 : OnCommitItem *oc;
18174 : MemoryContext oldcxt;
18175 :
18176 : /*
18177 : * We needn't bother registering the relation unless there is an ON COMMIT
18178 : * action we need to take.
18179 : */
18180 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18181 24 : return;
18182 :
18183 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18184 :
18185 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18186 142 : oc->relid = relid;
18187 142 : oc->oncommit = action;
18188 142 : oc->creating_subid = GetCurrentSubTransactionId();
18189 142 : oc->deleting_subid = InvalidSubTransactionId;
18190 :
18191 : /*
18192 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18193 : * order of registration. That might not be essential but it seems
18194 : * reasonable.
18195 : */
18196 142 : on_commits = lcons(oc, on_commits);
18197 :
18198 142 : MemoryContextSwitchTo(oldcxt);
18199 : }
18200 :
18201 : /*
18202 : * Unregister any ON COMMIT action when a relation is deleted.
18203 : *
18204 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18205 : */
18206 : void
18207 45446 : remove_on_commit_action(Oid relid)
18208 : {
18209 : ListCell *l;
18210 :
18211 45580 : foreach(l, on_commits)
18212 : {
18213 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18214 :
18215 264 : if (oc->relid == relid)
18216 : {
18217 130 : oc->deleting_subid = GetCurrentSubTransactionId();
18218 130 : break;
18219 : }
18220 : }
18221 45446 : }
18222 :
18223 : /*
18224 : * Perform ON COMMIT actions.
18225 : *
18226 : * This is invoked just before actually committing, since it's possible
18227 : * to encounter errors.
18228 : */
18229 : void
18230 739254 : PreCommit_on_commit_actions(void)
18231 : {
18232 : ListCell *l;
18233 739254 : List *oids_to_truncate = NIL;
18234 739254 : List *oids_to_drop = NIL;
18235 :
18236 739970 : foreach(l, on_commits)
18237 : {
18238 716 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18239 :
18240 : /* Ignore entry if already dropped in this xact */
18241 716 : if (oc->deleting_subid != InvalidSubTransactionId)
18242 68 : continue;
18243 :
18244 648 : switch (oc->oncommit)
18245 : {
18246 0 : case ONCOMMIT_NOOP:
18247 : case ONCOMMIT_PRESERVE_ROWS:
18248 : /* Do nothing (there shouldn't be such entries, actually) */
18249 0 : break;
18250 598 : case ONCOMMIT_DELETE_ROWS:
18251 :
18252 : /*
18253 : * If this transaction hasn't accessed any temporary
18254 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18255 : * tables, as they must still be empty.
18256 : */
18257 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18258 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18259 598 : break;
18260 50 : case ONCOMMIT_DROP:
18261 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18262 50 : break;
18263 : }
18264 716 : }
18265 :
18266 : /*
18267 : * Truncate relations before dropping so that all dependencies between
18268 : * relations are removed after they are worked on. Doing it like this
18269 : * might be a waste as it is possible that a relation being truncated will
18270 : * be dropped anyway due to its parent being dropped, but this makes the
18271 : * code more robust because of not having to re-check that the relation
18272 : * exists at truncation time.
18273 : */
18274 739254 : if (oids_to_truncate != NIL)
18275 334 : heap_truncate(oids_to_truncate);
18276 :
18277 739248 : if (oids_to_drop != NIL)
18278 : {
18279 44 : ObjectAddresses *targetObjects = new_object_addresses();
18280 :
18281 94 : foreach(l, oids_to_drop)
18282 : {
18283 : ObjectAddress object;
18284 :
18285 50 : object.classId = RelationRelationId;
18286 50 : object.objectId = lfirst_oid(l);
18287 50 : object.objectSubId = 0;
18288 :
18289 : Assert(!object_address_present(&object, targetObjects));
18290 :
18291 50 : add_exact_object_address(&object, targetObjects);
18292 : }
18293 :
18294 : /*
18295 : * Object deletion might involve toast table access (to clean up
18296 : * toasted catalog entries), so ensure we have a valid snapshot.
18297 : */
18298 44 : PushActiveSnapshot(GetTransactionSnapshot());
18299 :
18300 : /*
18301 : * Since this is an automatic drop, rather than one directly initiated
18302 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18303 : */
18304 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18305 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18306 :
18307 44 : PopActiveSnapshot();
18308 :
18309 : #ifdef USE_ASSERT_CHECKING
18310 :
18311 : /*
18312 : * Note that table deletion will call remove_on_commit_action, so the
18313 : * entry should get marked as deleted.
18314 : */
18315 : foreach(l, on_commits)
18316 : {
18317 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18318 :
18319 : if (oc->oncommit != ONCOMMIT_DROP)
18320 : continue;
18321 :
18322 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18323 : }
18324 : #endif
18325 : }
18326 739248 : }
18327 :
18328 : /*
18329 : * Post-commit or post-abort cleanup for ON COMMIT management.
18330 : *
18331 : * All we do here is remove no-longer-needed OnCommitItem entries.
18332 : *
18333 : * During commit, remove entries that were deleted during this transaction;
18334 : * during abort, remove those created during this transaction.
18335 : */
18336 : void
18337 785948 : AtEOXact_on_commit_actions(bool isCommit)
18338 : {
18339 : ListCell *cur_item;
18340 :
18341 786694 : foreach(cur_item, on_commits)
18342 : {
18343 746 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18344 :
18345 848 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18346 102 : oc->creating_subid != InvalidSubTransactionId)
18347 : {
18348 : /* cur_item must be removed */
18349 142 : on_commits = foreach_delete_current(on_commits, cur_item);
18350 142 : pfree(oc);
18351 : }
18352 : else
18353 : {
18354 : /* cur_item must be preserved */
18355 604 : oc->creating_subid = InvalidSubTransactionId;
18356 604 : oc->deleting_subid = InvalidSubTransactionId;
18357 : }
18358 : }
18359 785948 : }
18360 :
18361 : /*
18362 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18363 : *
18364 : * During subabort, we can immediately remove entries created during this
18365 : * subtransaction. During subcommit, just relabel entries marked during
18366 : * this subtransaction as being the parent's responsibility.
18367 : */
18368 : void
18369 20040 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18370 : SubTransactionId parentSubid)
18371 : {
18372 : ListCell *cur_item;
18373 :
18374 20040 : foreach(cur_item, on_commits)
18375 : {
18376 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18377 :
18378 0 : if (!isCommit && oc->creating_subid == mySubid)
18379 : {
18380 : /* cur_item must be removed */
18381 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18382 0 : pfree(oc);
18383 : }
18384 : else
18385 : {
18386 : /* cur_item must be preserved */
18387 0 : if (oc->creating_subid == mySubid)
18388 0 : oc->creating_subid = parentSubid;
18389 0 : if (oc->deleting_subid == mySubid)
18390 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18391 : }
18392 : }
18393 20040 : }
18394 :
18395 : /*
18396 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18397 : * the relation to be locked only if (1) it's a plain or partitioned table,
18398 : * materialized view, or TOAST table and (2) the current user is the owner (or
18399 : * the superuser) or has been granted MAINTAIN. This meets the
18400 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18401 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18402 : */
18403 : void
18404 1020 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18405 : Oid relId, Oid oldRelId, void *arg)
18406 : {
18407 : char relkind;
18408 : AclResult aclresult;
18409 :
18410 : /* Nothing to do if the relation was not found. */
18411 1020 : if (!OidIsValid(relId))
18412 6 : return;
18413 :
18414 : /*
18415 : * If the relation does exist, check whether it's an index. But note that
18416 : * the relation might have been dropped between the time we did the name
18417 : * lookup and now. In that case, there's nothing to do.
18418 : */
18419 1014 : relkind = get_rel_relkind(relId);
18420 1014 : if (!relkind)
18421 0 : return;
18422 1014 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18423 140 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18424 28 : ereport(ERROR,
18425 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18426 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18427 :
18428 : /* Check permissions */
18429 986 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18430 986 : if (aclresult != ACLCHECK_OK)
18431 30 : aclcheck_error(aclresult,
18432 30 : get_relkind_objtype(get_rel_relkind(relId)),
18433 30 : relation->relname);
18434 : }
18435 :
18436 : /*
18437 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18438 : */
18439 : static void
18440 2052 : RangeVarCallbackForTruncate(const RangeVar *relation,
18441 : Oid relId, Oid oldRelId, void *arg)
18442 : {
18443 : HeapTuple tuple;
18444 :
18445 : /* Nothing to do if the relation was not found. */
18446 2052 : if (!OidIsValid(relId))
18447 0 : return;
18448 :
18449 2052 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18450 2052 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18451 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18452 :
18453 2052 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18454 2046 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18455 :
18456 2014 : ReleaseSysCache(tuple);
18457 : }
18458 :
18459 : /*
18460 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18461 : * the owner of the relation, or superuser.
18462 : */
18463 : void
18464 15214 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18465 : Oid relId, Oid oldRelId, void *arg)
18466 : {
18467 : HeapTuple tuple;
18468 :
18469 : /* Nothing to do if the relation was not found. */
18470 15214 : if (!OidIsValid(relId))
18471 14 : return;
18472 :
18473 15200 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18474 15200 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18475 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18476 :
18477 15200 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18478 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18479 6 : relation->relname);
18480 :
18481 30268 : if (!allowSystemTableMods &&
18482 15074 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18483 2 : ereport(ERROR,
18484 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18485 : errmsg("permission denied: \"%s\" is a system catalog",
18486 : relation->relname)));
18487 :
18488 15192 : ReleaseSysCache(tuple);
18489 : }
18490 :
18491 : /*
18492 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18493 : * processing.
18494 : */
18495 : static void
18496 30728 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18497 : void *arg)
18498 : {
18499 30728 : Node *stmt = (Node *) arg;
18500 : ObjectType reltype;
18501 : HeapTuple tuple;
18502 : Form_pg_class classform;
18503 : AclResult aclresult;
18504 : char relkind;
18505 :
18506 30728 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18507 30728 : if (!HeapTupleIsValid(tuple))
18508 216 : return; /* concurrently dropped */
18509 30512 : classform = (Form_pg_class) GETSTRUCT(tuple);
18510 30512 : relkind = classform->relkind;
18511 :
18512 : /* Must own relation. */
18513 30512 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18514 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18515 :
18516 : /* No system table modifications unless explicitly allowed. */
18517 30452 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18518 28 : ereport(ERROR,
18519 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18520 : errmsg("permission denied: \"%s\" is a system catalog",
18521 : rv->relname)));
18522 :
18523 : /*
18524 : * Extract the specified relation type from the statement parse tree.
18525 : *
18526 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
18527 : * have CREATE rights on the containing namespace.
18528 : */
18529 30424 : if (IsA(stmt, RenameStmt))
18530 : {
18531 496 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18532 : GetUserId(), ACL_CREATE);
18533 496 : if (aclresult != ACLCHECK_OK)
18534 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
18535 0 : get_namespace_name(classform->relnamespace));
18536 496 : reltype = ((RenameStmt *) stmt)->renameType;
18537 : }
18538 29928 : else if (IsA(stmt, AlterObjectSchemaStmt))
18539 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18540 :
18541 29838 : else if (IsA(stmt, AlterTableStmt))
18542 29838 : reltype = ((AlterTableStmt *) stmt)->objtype;
18543 : else
18544 : {
18545 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18546 : reltype = OBJECT_TABLE; /* placate compiler */
18547 : }
18548 :
18549 : /*
18550 : * For compatibility with prior releases, we allow ALTER TABLE to be used
18551 : * with most other types of relations (but not composite types). We allow
18552 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
18553 : * otherwise. Otherwise, the user must select the correct form of the
18554 : * command for the relation at issue.
18555 : */
18556 30424 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18557 0 : ereport(ERROR,
18558 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18559 : errmsg("\"%s\" is not a sequence", rv->relname)));
18560 :
18561 30424 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18562 0 : ereport(ERROR,
18563 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18564 : errmsg("\"%s\" is not a view", rv->relname)));
18565 :
18566 30424 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18567 0 : ereport(ERROR,
18568 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18569 : errmsg("\"%s\" is not a materialized view", rv->relname)));
18570 :
18571 30424 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18572 0 : ereport(ERROR,
18573 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18574 : errmsg("\"%s\" is not a foreign table", rv->relname)));
18575 :
18576 30424 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18577 0 : ereport(ERROR,
18578 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18579 : errmsg("\"%s\" is not a composite type", rv->relname)));
18580 :
18581 30424 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18582 : relkind != RELKIND_PARTITIONED_INDEX
18583 38 : && !IsA(stmt, RenameStmt))
18584 6 : ereport(ERROR,
18585 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18586 : errmsg("\"%s\" is not an index", rv->relname)));
18587 :
18588 : /*
18589 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18590 : * TYPE for that.
18591 : */
18592 30418 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18593 0 : ereport(ERROR,
18594 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18595 : errmsg("\"%s\" is a composite type", rv->relname),
18596 : /* translator: %s is an SQL ALTER command */
18597 : errhint("Use %s instead.",
18598 : "ALTER TYPE")));
18599 :
18600 : /*
18601 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18602 : * to a different schema, such as indexes and TOAST tables.
18603 : */
18604 30418 : if (IsA(stmt, AlterObjectSchemaStmt))
18605 : {
18606 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18607 0 : ereport(ERROR,
18608 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18609 : errmsg("cannot change schema of index \"%s\"",
18610 : rv->relname),
18611 : errhint("Change the schema of the table instead.")));
18612 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
18613 0 : ereport(ERROR,
18614 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18615 : errmsg("cannot change schema of composite type \"%s\"",
18616 : rv->relname),
18617 : /* translator: %s is an SQL ALTER command */
18618 : errhint("Use %s instead.",
18619 : "ALTER TYPE")));
18620 90 : else if (relkind == RELKIND_TOASTVALUE)
18621 0 : ereport(ERROR,
18622 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18623 : errmsg("cannot change schema of TOAST table \"%s\"",
18624 : rv->relname),
18625 : errhint("Change the schema of the table instead.")));
18626 : }
18627 :
18628 30418 : ReleaseSysCache(tuple);
18629 : }
18630 :
18631 : /*
18632 : * Transform any expressions present in the partition key
18633 : *
18634 : * Returns a transformed PartitionSpec.
18635 : */
18636 : static PartitionSpec *
18637 4766 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18638 : {
18639 : PartitionSpec *newspec;
18640 : ParseState *pstate;
18641 : ParseNamespaceItem *nsitem;
18642 : ListCell *l;
18643 :
18644 4766 : newspec = makeNode(PartitionSpec);
18645 :
18646 4766 : newspec->strategy = partspec->strategy;
18647 4766 : newspec->partParams = NIL;
18648 4766 : newspec->location = partspec->location;
18649 :
18650 : /* Check valid number of columns for strategy */
18651 7190 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18652 2424 : list_length(partspec->partParams) != 1)
18653 6 : ereport(ERROR,
18654 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18655 : errmsg("cannot use \"list\" partition strategy with more than one column")));
18656 :
18657 : /*
18658 : * Create a dummy ParseState and insert the target relation as its sole
18659 : * rangetable entry. We need a ParseState for transformExpr.
18660 : */
18661 4760 : pstate = make_parsestate(NULL);
18662 4760 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18663 : NULL, false, true);
18664 4760 : addNSItemToQuery(pstate, nsitem, true, true, true);
18665 :
18666 : /* take care of any partition expressions */
18667 9928 : foreach(l, partspec->partParams)
18668 : {
18669 5192 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
18670 :
18671 5192 : if (pelem->expr)
18672 : {
18673 : /* Copy, to avoid scribbling on the input */
18674 298 : pelem = copyObject(pelem);
18675 :
18676 : /* Now do parse transformation of the expression */
18677 298 : pelem->expr = transformExpr(pstate, pelem->expr,
18678 : EXPR_KIND_PARTITION_EXPRESSION);
18679 :
18680 : /* we have to fix its collations too */
18681 274 : assign_expr_collations(pstate, pelem->expr);
18682 : }
18683 :
18684 5168 : newspec->partParams = lappend(newspec->partParams, pelem);
18685 : }
18686 :
18687 4736 : return newspec;
18688 : }
18689 :
18690 : /*
18691 : * Compute per-partition-column information from a list of PartitionElems.
18692 : * Expressions in the PartitionElems must be parse-analyzed already.
18693 : */
18694 : static void
18695 4736 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18696 : List **partexprs, Oid *partopclass, Oid *partcollation,
18697 : PartitionStrategy strategy)
18698 : {
18699 : int attn;
18700 : ListCell *lc;
18701 : Oid am_oid;
18702 :
18703 4736 : attn = 0;
18704 9820 : foreach(lc, partParams)
18705 : {
18706 5168 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18707 : Oid atttype;
18708 : Oid attcollation;
18709 :
18710 5168 : if (pelem->name != NULL)
18711 : {
18712 : /* Simple attribute reference */
18713 : HeapTuple atttuple;
18714 : Form_pg_attribute attform;
18715 :
18716 4894 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18717 4894 : pelem->name);
18718 4894 : if (!HeapTupleIsValid(atttuple))
18719 12 : ereport(ERROR,
18720 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18721 : errmsg("column \"%s\" named in partition key does not exist",
18722 : pelem->name),
18723 : parser_errposition(pstate, pelem->location)));
18724 4882 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18725 :
18726 4882 : if (attform->attnum <= 0)
18727 6 : ereport(ERROR,
18728 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18729 : errmsg("cannot use system column \"%s\" in partition key",
18730 : pelem->name),
18731 : parser_errposition(pstate, pelem->location)));
18732 :
18733 : /*
18734 : * Generated columns cannot work: They are computed after BEFORE
18735 : * triggers, but partition routing is done before all triggers.
18736 : */
18737 4876 : if (attform->attgenerated)
18738 6 : ereport(ERROR,
18739 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18740 : errmsg("cannot use generated column in partition key"),
18741 : errdetail("Column \"%s\" is a generated column.",
18742 : pelem->name),
18743 : parser_errposition(pstate, pelem->location)));
18744 :
18745 4870 : partattrs[attn] = attform->attnum;
18746 4870 : atttype = attform->atttypid;
18747 4870 : attcollation = attform->attcollation;
18748 4870 : ReleaseSysCache(atttuple);
18749 : }
18750 : else
18751 : {
18752 : /* Expression */
18753 274 : Node *expr = pelem->expr;
18754 : char partattname[16];
18755 :
18756 : Assert(expr != NULL);
18757 274 : atttype = exprType(expr);
18758 274 : attcollation = exprCollation(expr);
18759 :
18760 : /*
18761 : * The expression must be of a storable type (e.g., not RECORD).
18762 : * The test is the same as for whether a table column is of a safe
18763 : * type (which is why we needn't check for the non-expression
18764 : * case).
18765 : */
18766 274 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18767 274 : CheckAttributeType(partattname,
18768 : atttype, attcollation,
18769 : NIL, CHKATYPE_IS_PARTKEY);
18770 :
18771 : /*
18772 : * Strip any top-level COLLATE clause. This ensures that we treat
18773 : * "x COLLATE y" and "(x COLLATE y)" alike.
18774 : */
18775 262 : while (IsA(expr, CollateExpr))
18776 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
18777 :
18778 262 : if (IsA(expr, Var) &&
18779 12 : ((Var *) expr)->varattno > 0)
18780 : {
18781 : /*
18782 : * User wrote "(column)" or "(column COLLATE something)".
18783 : * Treat it like simple attribute anyway.
18784 : */
18785 6 : partattrs[attn] = ((Var *) expr)->varattno;
18786 : }
18787 : else
18788 : {
18789 256 : Bitmapset *expr_attrs = NULL;
18790 : int i;
18791 :
18792 256 : partattrs[attn] = 0; /* marks the column as expression */
18793 256 : *partexprs = lappend(*partexprs, expr);
18794 :
18795 : /*
18796 : * transformPartitionSpec() should have already rejected
18797 : * subqueries, aggregates, window functions, and SRFs, based
18798 : * on the EXPR_KIND_ for partition expressions.
18799 : */
18800 :
18801 : /*
18802 : * Cannot allow system column references, since that would
18803 : * make partition routing impossible: their values won't be
18804 : * known yet when we need to do that.
18805 : */
18806 256 : pull_varattnos(expr, 1, &expr_attrs);
18807 2048 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18808 : {
18809 1792 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18810 : expr_attrs))
18811 0 : ereport(ERROR,
18812 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18813 : errmsg("partition key expressions cannot contain system column references")));
18814 : }
18815 :
18816 : /*
18817 : * Generated columns cannot work: They are computed after
18818 : * BEFORE triggers, but partition routing is done before all
18819 : * triggers.
18820 : */
18821 256 : i = -1;
18822 564 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
18823 : {
18824 314 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18825 :
18826 314 : if (attno > 0 &&
18827 308 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18828 6 : ereport(ERROR,
18829 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18830 : errmsg("cannot use generated column in partition key"),
18831 : errdetail("Column \"%s\" is a generated column.",
18832 : get_attname(RelationGetRelid(rel), attno, false)),
18833 : parser_errposition(pstate, pelem->location)));
18834 : }
18835 :
18836 : /*
18837 : * Preprocess the expression before checking for mutability.
18838 : * This is essential for the reasons described in
18839 : * contain_mutable_functions_after_planning. However, we call
18840 : * expression_planner for ourselves rather than using that
18841 : * function, because if constant-folding reduces the
18842 : * expression to a constant, we'd like to know that so we can
18843 : * complain below.
18844 : *
18845 : * Like contain_mutable_functions_after_planning, assume that
18846 : * expression_planner won't scribble on its input, so this
18847 : * won't affect the partexprs entry we saved above.
18848 : */
18849 250 : expr = (Node *) expression_planner((Expr *) expr);
18850 :
18851 : /*
18852 : * Partition expressions cannot contain mutable functions,
18853 : * because a given row must always map to the same partition
18854 : * as long as there is no change in the partition boundary
18855 : * structure.
18856 : */
18857 250 : if (contain_mutable_functions(expr))
18858 6 : ereport(ERROR,
18859 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18860 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
18861 :
18862 : /*
18863 : * While it is not exactly *wrong* for a partition expression
18864 : * to be a constant, it seems better to reject such keys.
18865 : */
18866 244 : if (IsA(expr, Const))
18867 12 : ereport(ERROR,
18868 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18869 : errmsg("cannot use constant expression as partition key")));
18870 : }
18871 : }
18872 :
18873 : /*
18874 : * Apply collation override if any
18875 : */
18876 5108 : if (pelem->collation)
18877 54 : attcollation = get_collation_oid(pelem->collation, false);
18878 :
18879 : /*
18880 : * Check we have a collation iff it's a collatable type. The only
18881 : * expected failures here are (1) COLLATE applied to a noncollatable
18882 : * type, or (2) partition expression had an unresolved collation. But
18883 : * we might as well code this to be a complete consistency check.
18884 : */
18885 5108 : if (type_is_collatable(atttype))
18886 : {
18887 596 : if (!OidIsValid(attcollation))
18888 0 : ereport(ERROR,
18889 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
18890 : errmsg("could not determine which collation to use for partition expression"),
18891 : errhint("Use the COLLATE clause to set the collation explicitly.")));
18892 : }
18893 : else
18894 : {
18895 4512 : if (OidIsValid(attcollation))
18896 0 : ereport(ERROR,
18897 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18898 : errmsg("collations are not supported by type %s",
18899 : format_type_be(atttype))));
18900 : }
18901 :
18902 5108 : partcollation[attn] = attcollation;
18903 :
18904 : /*
18905 : * Identify the appropriate operator class. For list and range
18906 : * partitioning, we use a btree operator class; hash partitioning uses
18907 : * a hash operator class.
18908 : */
18909 5108 : if (strategy == PARTITION_STRATEGY_HASH)
18910 294 : am_oid = HASH_AM_OID;
18911 : else
18912 4814 : am_oid = BTREE_AM_OID;
18913 :
18914 5108 : if (!pelem->opclass)
18915 : {
18916 4976 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18917 :
18918 4976 : if (!OidIsValid(partopclass[attn]))
18919 : {
18920 12 : if (strategy == PARTITION_STRATEGY_HASH)
18921 0 : ereport(ERROR,
18922 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18923 : errmsg("data type %s has no default operator class for access method \"%s\"",
18924 : format_type_be(atttype), "hash"),
18925 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18926 : else
18927 12 : ereport(ERROR,
18928 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18929 : errmsg("data type %s has no default operator class for access method \"%s\"",
18930 : format_type_be(atttype), "btree"),
18931 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18932 : }
18933 : }
18934 : else
18935 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
18936 : atttype,
18937 : am_oid == HASH_AM_OID ? "hash" : "btree",
18938 : am_oid);
18939 :
18940 5084 : attn++;
18941 : }
18942 4652 : }
18943 :
18944 : /*
18945 : * PartConstraintImpliedByRelConstraint
18946 : * Do scanrel's existing constraints imply the partition constraint?
18947 : *
18948 : * "Existing constraints" include its check constraints and column-level
18949 : * not-null constraints. partConstraint describes the partition constraint,
18950 : * in implicit-AND form.
18951 : */
18952 : bool
18953 3032 : PartConstraintImpliedByRelConstraint(Relation scanrel,
18954 : List *partConstraint)
18955 : {
18956 3032 : List *existConstraint = NIL;
18957 3032 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18958 : int i;
18959 :
18960 3032 : if (constr && constr->has_not_null)
18961 : {
18962 754 : int natts = scanrel->rd_att->natts;
18963 :
18964 2440 : for (i = 1; i <= natts; i++)
18965 : {
18966 1686 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18967 :
18968 1686 : if (att->attnotnull && !att->attisdropped)
18969 : {
18970 1058 : NullTest *ntest = makeNode(NullTest);
18971 :
18972 1058 : ntest->arg = (Expr *) makeVar(1,
18973 : i,
18974 : att->atttypid,
18975 : att->atttypmod,
18976 : att->attcollation,
18977 : 0);
18978 1058 : ntest->nulltesttype = IS_NOT_NULL;
18979 :
18980 : /*
18981 : * argisrow=false is correct even for a composite column,
18982 : * because attnotnull does not represent a SQL-spec IS NOT
18983 : * NULL test in such a case, just IS DISTINCT FROM NULL.
18984 : */
18985 1058 : ntest->argisrow = false;
18986 1058 : ntest->location = -1;
18987 1058 : existConstraint = lappend(existConstraint, ntest);
18988 : }
18989 : }
18990 : }
18991 :
18992 3032 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18993 : }
18994 :
18995 : /*
18996 : * ConstraintImpliedByRelConstraint
18997 : * Do scanrel's existing constraints imply the given constraint?
18998 : *
18999 : * testConstraint is the constraint to validate. provenConstraint is a
19000 : * caller-provided list of conditions which this function may assume
19001 : * to be true. Both provenConstraint and testConstraint must be in
19002 : * implicit-AND form, must only contain immutable clauses, and must
19003 : * contain only Vars with varno = 1.
19004 : */
19005 : bool
19006 4180 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19007 : {
19008 4180 : List *existConstraint = list_copy(provenConstraint);
19009 4180 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19010 : int num_check,
19011 : i;
19012 :
19013 4180 : num_check = (constr != NULL) ? constr->num_check : 0;
19014 4696 : for (i = 0; i < num_check; i++)
19015 : {
19016 : Node *cexpr;
19017 :
19018 : /*
19019 : * If this constraint hasn't been fully validated yet, we must ignore
19020 : * it here.
19021 : */
19022 516 : if (!constr->check[i].ccvalid)
19023 6 : continue;
19024 :
19025 : /*
19026 : * NOT ENFORCED constraints are always marked as invalid, which should
19027 : * have been ignored.
19028 : */
19029 : Assert(constr->check[i].ccenforced);
19030 :
19031 510 : cexpr = stringToNode(constr->check[i].ccbin);
19032 :
19033 : /*
19034 : * Run each expression through const-simplification and
19035 : * canonicalization. It is necessary, because we will be comparing it
19036 : * to similarly-processed partition constraint expressions, and may
19037 : * fail to detect valid matches without this.
19038 : */
19039 510 : cexpr = eval_const_expressions(NULL, cexpr);
19040 510 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19041 :
19042 510 : existConstraint = list_concat(existConstraint,
19043 510 : make_ands_implicit((Expr *) cexpr));
19044 : }
19045 :
19046 : /*
19047 : * Try to make the proof. Since we are comparing CHECK constraints, we
19048 : * need to use weak implication, i.e., we assume existConstraint is
19049 : * not-false and try to prove the same for testConstraint.
19050 : *
19051 : * Note that predicate_implied_by assumes its first argument is known
19052 : * immutable. That should always be true for both NOT NULL and partition
19053 : * constraints, so we don't test it here.
19054 : */
19055 4180 : return predicate_implied_by(testConstraint, existConstraint, true);
19056 : }
19057 :
19058 : /*
19059 : * QueuePartitionConstraintValidation
19060 : *
19061 : * Add an entry to wqueue to have the given partition constraint validated by
19062 : * Phase 3, for the given relation, and all its children.
19063 : *
19064 : * We first verify whether the given constraint is implied by pre-existing
19065 : * relation constraints; if it is, there's no need to scan the table to
19066 : * validate, so don't queue in that case.
19067 : */
19068 : static void
19069 2402 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19070 : List *partConstraint,
19071 : bool validate_default)
19072 : {
19073 : /*
19074 : * Based on the table's existing constraints, determine whether or not we
19075 : * may skip scanning the table.
19076 : */
19077 2402 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19078 : {
19079 90 : if (!validate_default)
19080 68 : ereport(DEBUG1,
19081 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19082 : RelationGetRelationName(scanrel))));
19083 : else
19084 22 : ereport(DEBUG1,
19085 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19086 : RelationGetRelationName(scanrel))));
19087 90 : return;
19088 : }
19089 :
19090 : /*
19091 : * Constraints proved insufficient. For plain relations, queue a
19092 : * validation item now; for partitioned tables, recurse to process each
19093 : * partition.
19094 : */
19095 2312 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19096 : {
19097 : AlteredTableInfo *tab;
19098 :
19099 : /* Grab a work queue entry. */
19100 1924 : tab = ATGetQueueEntry(wqueue, scanrel);
19101 : Assert(tab->partition_constraint == NULL);
19102 1924 : tab->partition_constraint = (Expr *) linitial(partConstraint);
19103 1924 : tab->validate_default = validate_default;
19104 : }
19105 388 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19106 : {
19107 340 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19108 : int i;
19109 :
19110 710 : for (i = 0; i < partdesc->nparts; i++)
19111 : {
19112 : Relation part_rel;
19113 : List *thisPartConstraint;
19114 :
19115 : /*
19116 : * This is the minimum lock we need to prevent deadlocks.
19117 : */
19118 370 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19119 :
19120 : /*
19121 : * Adjust the constraint for scanrel so that it matches this
19122 : * partition's attribute numbers.
19123 : */
19124 : thisPartConstraint =
19125 370 : map_partition_varattnos(partConstraint, 1,
19126 : part_rel, scanrel);
19127 :
19128 370 : QueuePartitionConstraintValidation(wqueue, part_rel,
19129 : thisPartConstraint,
19130 : validate_default);
19131 370 : table_close(part_rel, NoLock); /* keep lock till commit */
19132 : }
19133 : }
19134 : }
19135 :
19136 : /*
19137 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19138 : *
19139 : * Return the address of the newly attached partition.
19140 : */
19141 : static ObjectAddress
19142 2204 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19143 : AlterTableUtilityContext *context)
19144 : {
19145 : Relation attachrel,
19146 : catalog;
19147 : List *attachrel_children;
19148 : List *partConstraint;
19149 : SysScanDesc scan;
19150 : ScanKeyData skey;
19151 : AttrNumber attno;
19152 : int natts;
19153 : TupleDesc tupleDesc;
19154 : ObjectAddress address;
19155 : const char *trigger_name;
19156 : Oid defaultPartOid;
19157 : List *partBoundConstraint;
19158 2204 : ParseState *pstate = make_parsestate(NULL);
19159 :
19160 2204 : pstate->p_sourcetext = context->queryString;
19161 :
19162 : /*
19163 : * We must lock the default partition if one exists, because attaching a
19164 : * new partition will change its partition constraint.
19165 : */
19166 : defaultPartOid =
19167 2204 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19168 2204 : if (OidIsValid(defaultPartOid))
19169 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19170 :
19171 2204 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19172 :
19173 : /*
19174 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19175 : * by FKs at this point also.
19176 : */
19177 :
19178 : /*
19179 : * Must be owner of both parent and source table -- parent was checked by
19180 : * ATSimplePermissions call in ATPrepCmd
19181 : */
19182 2198 : ATSimplePermissions(AT_AttachPartition, attachrel,
19183 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
19184 :
19185 : /* A partition can only have one parent */
19186 2192 : if (attachrel->rd_rel->relispartition)
19187 6 : ereport(ERROR,
19188 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19189 : errmsg("\"%s\" is already a partition",
19190 : RelationGetRelationName(attachrel))));
19191 :
19192 2186 : if (OidIsValid(attachrel->rd_rel->reloftype))
19193 6 : ereport(ERROR,
19194 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19195 : errmsg("cannot attach a typed table as partition")));
19196 :
19197 : /*
19198 : * Table being attached should not already be part of inheritance; either
19199 : * as a child table...
19200 : */
19201 2180 : catalog = table_open(InheritsRelationId, AccessShareLock);
19202 2180 : ScanKeyInit(&skey,
19203 : Anum_pg_inherits_inhrelid,
19204 : BTEqualStrategyNumber, F_OIDEQ,
19205 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19206 2180 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19207 : NULL, 1, &skey);
19208 2180 : if (HeapTupleIsValid(systable_getnext(scan)))
19209 6 : ereport(ERROR,
19210 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19211 : errmsg("cannot attach inheritance child as partition")));
19212 2174 : systable_endscan(scan);
19213 :
19214 : /* ...or as a parent table (except the case when it is partitioned) */
19215 2174 : ScanKeyInit(&skey,
19216 : Anum_pg_inherits_inhparent,
19217 : BTEqualStrategyNumber, F_OIDEQ,
19218 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19219 2174 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19220 : 1, &skey);
19221 2174 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19222 248 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19223 6 : ereport(ERROR,
19224 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19225 : errmsg("cannot attach inheritance parent as partition")));
19226 2168 : systable_endscan(scan);
19227 2168 : table_close(catalog, AccessShareLock);
19228 :
19229 : /*
19230 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19231 : * particular, this disallows making a rel a partition of itself.)
19232 : *
19233 : * We do that by checking if rel is a member of the list of attachrel's
19234 : * partitions provided the latter is partitioned at all. We want to avoid
19235 : * having to construct this list again, so we request the strongest lock
19236 : * on all partitions. We need the strongest lock, because we may decide
19237 : * to scan them if we find out that the table being attached (or its leaf
19238 : * partitions) may contain rows that violate the partition constraint. If
19239 : * the table has a constraint that would prevent such rows, which by
19240 : * definition is present in all the partitions, we need not scan the
19241 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19242 : * weaker lock now and the stronger one only when needed.
19243 : */
19244 2168 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19245 : AccessExclusiveLock, NULL);
19246 2168 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19247 12 : ereport(ERROR,
19248 : (errcode(ERRCODE_DUPLICATE_TABLE),
19249 : errmsg("circular inheritance not allowed"),
19250 : errdetail("\"%s\" is already a child of \"%s\".",
19251 : RelationGetRelationName(rel),
19252 : RelationGetRelationName(attachrel))));
19253 :
19254 : /* If the parent is permanent, so must be all of its partitions. */
19255 2156 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19256 2114 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19257 6 : ereport(ERROR,
19258 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19259 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19260 : RelationGetRelationName(rel))));
19261 :
19262 : /* Temp parent cannot have a partition that is itself not a temp */
19263 2150 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19264 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19265 18 : ereport(ERROR,
19266 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19267 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19268 : RelationGetRelationName(rel))));
19269 :
19270 : /* If the parent is temp, it must belong to this session */
19271 2132 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19272 24 : !rel->rd_islocaltemp)
19273 0 : ereport(ERROR,
19274 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19275 : errmsg("cannot attach as partition of temporary relation of another session")));
19276 :
19277 : /* Ditto for the partition */
19278 2132 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19279 24 : !attachrel->rd_islocaltemp)
19280 0 : ereport(ERROR,
19281 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19282 : errmsg("cannot attach temporary relation of another session as partition")));
19283 :
19284 : /*
19285 : * Check if attachrel has any identity columns or any columns that aren't
19286 : * in the parent.
19287 : */
19288 2132 : tupleDesc = RelationGetDescr(attachrel);
19289 2132 : natts = tupleDesc->natts;
19290 7302 : for (attno = 1; attno <= natts; attno++)
19291 : {
19292 5206 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19293 5206 : char *attributeName = NameStr(attribute->attname);
19294 :
19295 : /* Ignore dropped */
19296 5206 : if (attribute->attisdropped)
19297 580 : continue;
19298 :
19299 4626 : if (attribute->attidentity)
19300 18 : ereport(ERROR,
19301 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19302 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19303 : RelationGetRelationName(attachrel), attributeName),
19304 : errdetail("The new partition may not contain an identity column."));
19305 :
19306 : /* Try to find the column in parent (matching on column name) */
19307 4608 : if (!SearchSysCacheExists2(ATTNAME,
19308 : ObjectIdGetDatum(RelationGetRelid(rel)),
19309 : CStringGetDatum(attributeName)))
19310 18 : ereport(ERROR,
19311 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19312 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19313 : RelationGetRelationName(attachrel), attributeName,
19314 : RelationGetRelationName(rel)),
19315 : errdetail("The new partition may contain only the columns present in parent.")));
19316 : }
19317 :
19318 : /*
19319 : * If child_rel has row-level triggers with transition tables, we
19320 : * currently don't allow it to become a partition. See also prohibitions
19321 : * in ATExecAddInherit() and CreateTrigger().
19322 : */
19323 2096 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19324 2096 : if (trigger_name != NULL)
19325 6 : ereport(ERROR,
19326 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19327 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19328 : trigger_name, RelationGetRelationName(attachrel)),
19329 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19330 :
19331 : /*
19332 : * Check that the new partition's bound is valid and does not overlap any
19333 : * of existing partitions of the parent - note that it does not return on
19334 : * error.
19335 : */
19336 2090 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19337 : cmd->bound, pstate);
19338 :
19339 : /* OK to create inheritance. Rest of the checks performed there */
19340 2054 : CreateInheritance(attachrel, rel, true);
19341 :
19342 : /* Update the pg_class entry. */
19343 1982 : StorePartitionBound(attachrel, rel, cmd->bound);
19344 :
19345 : /* Ensure there exists a correct set of indexes in the partition. */
19346 1982 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19347 :
19348 : /* and triggers */
19349 1952 : CloneRowTriggersToPartition(rel, attachrel);
19350 :
19351 : /*
19352 : * Clone foreign key constraints. Callee is responsible for setting up
19353 : * for phase 3 constraint verification.
19354 : */
19355 1946 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19356 :
19357 : /*
19358 : * Generate partition constraint from the partition bound specification.
19359 : * If the parent itself is a partition, make sure to include its
19360 : * constraint as well.
19361 : */
19362 1934 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19363 :
19364 : /*
19365 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19366 : * since it's needed later to construct the constraint expression for
19367 : * validating against the default partition, if any.
19368 : */
19369 1934 : partConstraint = list_concat_copy(partBoundConstraint,
19370 1934 : RelationGetPartitionQual(rel));
19371 :
19372 : /* Skip validation if there are no constraints to validate. */
19373 1934 : if (partConstraint)
19374 : {
19375 : /*
19376 : * Run the partition quals through const-simplification similar to
19377 : * check constraints. We skip canonicalize_qual, though, because
19378 : * partition quals should be in canonical form already.
19379 : */
19380 : partConstraint =
19381 1886 : (List *) eval_const_expressions(NULL,
19382 : (Node *) partConstraint);
19383 :
19384 : /* XXX this sure looks wrong */
19385 1886 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19386 :
19387 : /*
19388 : * Adjust the generated constraint to match this partition's attribute
19389 : * numbers.
19390 : */
19391 1886 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19392 : rel);
19393 :
19394 : /* Validate partition constraints against the table being attached. */
19395 1886 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19396 : false);
19397 : }
19398 :
19399 : /*
19400 : * If we're attaching a partition other than the default partition and a
19401 : * default one exists, then that partition's partition constraint changes,
19402 : * so add an entry to the work queue to validate it, too. (We must not do
19403 : * this when the partition being attached is the default one; we already
19404 : * did it above!)
19405 : */
19406 1934 : if (OidIsValid(defaultPartOid))
19407 : {
19408 : Relation defaultrel;
19409 : List *defPartConstraint;
19410 :
19411 : Assert(!cmd->bound->is_default);
19412 :
19413 : /* we already hold a lock on the default partition */
19414 146 : defaultrel = table_open(defaultPartOid, NoLock);
19415 : defPartConstraint =
19416 146 : get_proposed_default_constraint(partBoundConstraint);
19417 :
19418 : /*
19419 : * Map the Vars in the constraint expression from rel's attnos to
19420 : * defaultrel's.
19421 : */
19422 : defPartConstraint =
19423 146 : map_partition_varattnos(defPartConstraint,
19424 : 1, defaultrel, rel);
19425 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19426 : defPartConstraint, true);
19427 :
19428 : /* keep our lock until commit. */
19429 146 : table_close(defaultrel, NoLock);
19430 : }
19431 :
19432 1934 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19433 :
19434 : /*
19435 : * If the partition we just attached is partitioned itself, invalidate
19436 : * relcache for all descendent partitions too to ensure that their
19437 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19438 : * the beginning of this function.
19439 : */
19440 1934 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19441 : {
19442 : ListCell *l;
19443 :
19444 960 : foreach(l, attachrel_children)
19445 : {
19446 644 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19447 : }
19448 : }
19449 :
19450 : /* keep our lock until commit */
19451 1934 : table_close(attachrel, NoLock);
19452 :
19453 1934 : return address;
19454 : }
19455 :
19456 : /*
19457 : * AttachPartitionEnsureIndexes
19458 : * subroutine for ATExecAttachPartition to create/match indexes
19459 : *
19460 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19461 : * PARTITION: every partition must have an index attached to each index on the
19462 : * partitioned table.
19463 : */
19464 : static void
19465 1982 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19466 : {
19467 : List *idxes;
19468 : List *attachRelIdxs;
19469 : Relation *attachrelIdxRels;
19470 : IndexInfo **attachInfos;
19471 : ListCell *cell;
19472 : MemoryContext cxt;
19473 : MemoryContext oldcxt;
19474 :
19475 1982 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19476 : "AttachPartitionEnsureIndexes",
19477 : ALLOCSET_DEFAULT_SIZES);
19478 1982 : oldcxt = MemoryContextSwitchTo(cxt);
19479 :
19480 1982 : idxes = RelationGetIndexList(rel);
19481 1982 : attachRelIdxs = RelationGetIndexList(attachrel);
19482 1982 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19483 1982 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19484 :
19485 : /* Build arrays of all existing indexes and their IndexInfos */
19486 4328 : foreach_oid(cldIdxId, attachRelIdxs)
19487 : {
19488 364 : int i = foreach_current_index(cldIdxId);
19489 :
19490 364 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19491 364 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19492 : }
19493 :
19494 : /*
19495 : * If we're attaching a foreign table, we must fail if any of the indexes
19496 : * is a constraint index; otherwise, there's nothing to do here. Do this
19497 : * before starting work, to avoid wasting the effort of building a few
19498 : * non-unique indexes before coming across a unique one.
19499 : */
19500 1982 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19501 : {
19502 86 : foreach(cell, idxes)
19503 : {
19504 36 : Oid idx = lfirst_oid(cell);
19505 36 : Relation idxRel = index_open(idx, AccessShareLock);
19506 :
19507 36 : if (idxRel->rd_index->indisunique ||
19508 24 : idxRel->rd_index->indisprimary)
19509 12 : ereport(ERROR,
19510 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19511 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19512 : RelationGetRelationName(attachrel),
19513 : RelationGetRelationName(rel)),
19514 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19515 : RelationGetRelationName(rel))));
19516 24 : index_close(idxRel, AccessShareLock);
19517 : }
19518 :
19519 50 : goto out;
19520 : }
19521 :
19522 : /*
19523 : * For each index on the partitioned table, find a matching one in the
19524 : * partition-to-be; if one is not found, create one.
19525 : */
19526 2336 : foreach(cell, idxes)
19527 : {
19528 434 : Oid idx = lfirst_oid(cell);
19529 434 : Relation idxRel = index_open(idx, AccessShareLock);
19530 : IndexInfo *info;
19531 : AttrMap *attmap;
19532 434 : bool found = false;
19533 : Oid constraintOid;
19534 :
19535 : /*
19536 : * Ignore indexes in the partitioned table other than partitioned
19537 : * indexes.
19538 : */
19539 434 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19540 : {
19541 0 : index_close(idxRel, AccessShareLock);
19542 0 : continue;
19543 : }
19544 :
19545 : /* construct an indexinfo to compare existing indexes against */
19546 434 : info = BuildIndexInfo(idxRel);
19547 434 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19548 : RelationGetDescr(rel),
19549 : false);
19550 434 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19551 :
19552 : /*
19553 : * Scan the list of existing indexes in the partition-to-be, and mark
19554 : * the first matching, valid, unattached one we find, if any, as
19555 : * partition of the parent index. If we find one, we're done.
19556 : */
19557 494 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19558 : {
19559 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19560 262 : Oid cldConstrOid = InvalidOid;
19561 :
19562 : /* does this index have a parent? if so, can't use it */
19563 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
19564 12 : continue;
19565 :
19566 : /* If this index is invalid, can't use it */
19567 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
19568 6 : continue;
19569 :
19570 244 : if (CompareIndexInfo(attachInfos[i], info,
19571 244 : attachrelIdxRels[i]->rd_indcollation,
19572 244 : idxRel->rd_indcollation,
19573 244 : attachrelIdxRels[i]->rd_opfamily,
19574 244 : idxRel->rd_opfamily,
19575 : attmap))
19576 : {
19577 : /*
19578 : * If this index is being created in the parent because of a
19579 : * constraint, then the child needs to have a constraint also,
19580 : * so look for one. If there is no such constraint, this
19581 : * index is no good, so keep looking.
19582 : */
19583 208 : if (OidIsValid(constraintOid))
19584 : {
19585 : cldConstrOid =
19586 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19587 : cldIdxId);
19588 : /* no dice */
19589 110 : if (!OidIsValid(cldConstrOid))
19590 6 : continue;
19591 :
19592 : /* Ensure they're both the same type of constraint */
19593 208 : if (get_constraint_type(constraintOid) !=
19594 104 : get_constraint_type(cldConstrOid))
19595 0 : continue;
19596 : }
19597 :
19598 : /* bingo. */
19599 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
19600 202 : if (OidIsValid(constraintOid))
19601 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19602 : RelationGetRelid(attachrel));
19603 202 : found = true;
19604 :
19605 202 : CommandCounterIncrement();
19606 202 : break;
19607 : }
19608 : }
19609 :
19610 : /*
19611 : * If no suitable index was found in the partition-to-be, create one
19612 : * now. Note that if this is a PK, not-null constraints must already
19613 : * exist.
19614 : */
19615 434 : if (!found)
19616 : {
19617 : IndexStmt *stmt;
19618 : Oid conOid;
19619 :
19620 232 : stmt = generateClonedIndexStmt(NULL,
19621 : idxRel, attmap,
19622 : &conOid);
19623 232 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19624 : RelationGetRelid(idxRel),
19625 : conOid,
19626 : -1,
19627 : true, false, false, false, false);
19628 : }
19629 :
19630 416 : index_close(idxRel, AccessShareLock);
19631 : }
19632 :
19633 1952 : out:
19634 : /* Clean up. */
19635 2304 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19636 352 : index_close(attachrelIdxRels[i], AccessShareLock);
19637 1952 : MemoryContextSwitchTo(oldcxt);
19638 1952 : MemoryContextDelete(cxt);
19639 1952 : }
19640 :
19641 : /*
19642 : * CloneRowTriggersToPartition
19643 : * subroutine for ATExecAttachPartition/DefineRelation to create row
19644 : * triggers on partitions
19645 : */
19646 : static void
19647 2360 : CloneRowTriggersToPartition(Relation parent, Relation partition)
19648 : {
19649 : Relation pg_trigger;
19650 : ScanKeyData key;
19651 : SysScanDesc scan;
19652 : HeapTuple tuple;
19653 : MemoryContext perTupCxt;
19654 :
19655 2360 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19656 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19657 2360 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19658 2360 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19659 : true, NULL, 1, &key);
19660 :
19661 2360 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19662 : "clone trig", ALLOCSET_SMALL_SIZES);
19663 :
19664 3868 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19665 : {
19666 1514 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19667 : CreateTrigStmt *trigStmt;
19668 1514 : Node *qual = NULL;
19669 : Datum value;
19670 : bool isnull;
19671 1514 : List *cols = NIL;
19672 1514 : List *trigargs = NIL;
19673 : MemoryContext oldcxt;
19674 :
19675 : /*
19676 : * Ignore statement-level triggers; those are not cloned.
19677 : */
19678 1514 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19679 1358 : continue;
19680 :
19681 : /*
19682 : * Don't clone internal triggers, because the constraint cloning code
19683 : * will.
19684 : */
19685 1514 : if (trigForm->tgisinternal)
19686 1358 : continue;
19687 :
19688 : /*
19689 : * Complain if we find an unexpected trigger type.
19690 : */
19691 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19692 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
19693 0 : elog(ERROR, "unexpected trigger \"%s\" found",
19694 : NameStr(trigForm->tgname));
19695 :
19696 : /* Use short-lived context for CREATE TRIGGER */
19697 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
19698 :
19699 : /*
19700 : * If there is a WHEN clause, generate a 'cooked' version of it that's
19701 : * appropriate for the partition.
19702 : */
19703 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19704 : RelationGetDescr(pg_trigger), &isnull);
19705 156 : if (!isnull)
19706 : {
19707 6 : qual = stringToNode(TextDatumGetCString(value));
19708 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19709 : partition, parent);
19710 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19711 : partition, parent);
19712 : }
19713 :
19714 : /*
19715 : * If there is a column list, transform it to a list of column names.
19716 : * Note we don't need to map this list in any way ...
19717 : */
19718 156 : if (trigForm->tgattr.dim1 > 0)
19719 : {
19720 : int i;
19721 :
19722 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
19723 : {
19724 : Form_pg_attribute col;
19725 :
19726 6 : col = TupleDescAttr(parent->rd_att,
19727 6 : trigForm->tgattr.values[i] - 1);
19728 6 : cols = lappend(cols,
19729 6 : makeString(pstrdup(NameStr(col->attname))));
19730 : }
19731 : }
19732 :
19733 : /* Reconstruct trigger arguments list. */
19734 156 : if (trigForm->tgnargs > 0)
19735 : {
19736 : char *p;
19737 :
19738 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19739 : RelationGetDescr(pg_trigger), &isnull);
19740 12 : if (isnull)
19741 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19742 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
19743 :
19744 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19745 :
19746 36 : for (int i = 0; i < trigForm->tgnargs; i++)
19747 : {
19748 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
19749 24 : p += strlen(p) + 1;
19750 : }
19751 : }
19752 :
19753 156 : trigStmt = makeNode(CreateTrigStmt);
19754 156 : trigStmt->replace = false;
19755 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19756 156 : trigStmt->trigname = NameStr(trigForm->tgname);
19757 156 : trigStmt->relation = NULL;
19758 156 : trigStmt->funcname = NULL; /* passed separately */
19759 156 : trigStmt->args = trigargs;
19760 156 : trigStmt->row = true;
19761 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19762 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19763 156 : trigStmt->columns = cols;
19764 156 : trigStmt->whenClause = NULL; /* passed separately */
19765 156 : trigStmt->transitionRels = NIL; /* not supported at present */
19766 156 : trigStmt->deferrable = trigForm->tgdeferrable;
19767 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
19768 156 : trigStmt->constrrel = NULL; /* passed separately */
19769 :
19770 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19771 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19772 : trigForm->tgfoid, trigForm->oid, qual,
19773 156 : false, true, trigForm->tgenabled);
19774 :
19775 150 : MemoryContextSwitchTo(oldcxt);
19776 150 : MemoryContextReset(perTupCxt);
19777 : }
19778 :
19779 2354 : MemoryContextDelete(perTupCxt);
19780 :
19781 2354 : systable_endscan(scan);
19782 2354 : table_close(pg_trigger, RowExclusiveLock);
19783 2354 : }
19784 :
19785 : /*
19786 : * ALTER TABLE DETACH PARTITION
19787 : *
19788 : * Return the address of the relation that is no longer a partition of rel.
19789 : *
19790 : * If concurrent mode is requested, we run in two transactions. A side-
19791 : * effect is that this command cannot run in a multi-part ALTER TABLE.
19792 : * Currently, that's enforced by the grammar.
19793 : *
19794 : * The strategy for concurrency is to first modify the partition's
19795 : * pg_inherit catalog row to make it visible to everyone that the
19796 : * partition is detached, lock the partition against writes, and commit
19797 : * the transaction; anyone who requests the partition descriptor from
19798 : * that point onwards has to ignore such a partition. In a second
19799 : * transaction, we wait until all transactions that could have seen the
19800 : * partition as attached are gone, then we remove the rest of partition
19801 : * metadata (pg_inherits and pg_class.relpartbounds).
19802 : */
19803 : static ObjectAddress
19804 546 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19805 : RangeVar *name, bool concurrent)
19806 : {
19807 : Relation partRel;
19808 : ObjectAddress address;
19809 : Oid defaultPartOid;
19810 :
19811 : /*
19812 : * We must lock the default partition, because detaching this partition
19813 : * will change its partition constraint.
19814 : */
19815 : defaultPartOid =
19816 546 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19817 546 : if (OidIsValid(defaultPartOid))
19818 : {
19819 : /*
19820 : * Concurrent detaching when a default partition exists is not
19821 : * supported. The main problem is that the default partition
19822 : * constraint would change. And there's a definitional problem: what
19823 : * should happen to the tuples that are being inserted that belong to
19824 : * the partition being detached? Putting them on the partition being
19825 : * detached would be wrong, since they'd become "lost" after the
19826 : * detaching completes but we cannot put them in the default partition
19827 : * either until we alter its partition constraint.
19828 : *
19829 : * I think we could solve this problem if we effected the constraint
19830 : * change before committing the first transaction. But the lock would
19831 : * have to remain AEL and it would cause concurrent query planning to
19832 : * be blocked, so changing it that way would be even worse.
19833 : */
19834 106 : if (concurrent)
19835 12 : ereport(ERROR,
19836 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19837 : errmsg("cannot detach partitions concurrently when a default partition exists")));
19838 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19839 : }
19840 :
19841 : /*
19842 : * In concurrent mode, the partition is locked with share-update-exclusive
19843 : * in the first transaction. This allows concurrent transactions to be
19844 : * doing DML to the partition.
19845 : */
19846 534 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19847 : AccessExclusiveLock);
19848 :
19849 : /*
19850 : * Check inheritance conditions and either delete the pg_inherits row (in
19851 : * non-concurrent mode) or just set the inhdetachpending flag.
19852 : */
19853 522 : if (!concurrent)
19854 376 : RemoveInheritance(partRel, rel, false);
19855 : else
19856 146 : MarkInheritDetached(partRel, rel);
19857 :
19858 : /*
19859 : * Ensure that foreign keys still hold after this detach. This keeps
19860 : * locks on the referencing tables, which prevents concurrent transactions
19861 : * from adding rows that we wouldn't see. For this to work in concurrent
19862 : * mode, it is critical that the partition appears as no longer attached
19863 : * for the RI queries as soon as the first transaction commits.
19864 : */
19865 502 : ATDetachCheckNoForeignKeyRefs(partRel);
19866 :
19867 : /*
19868 : * Concurrent mode has to work harder; first we add a new constraint to
19869 : * the partition that matches the partition constraint. Then we close our
19870 : * existing transaction, and in a new one wait for all processes to catch
19871 : * up on the catalog updates we've done so far; at that point we can
19872 : * complete the operation.
19873 : */
19874 468 : if (concurrent)
19875 : {
19876 : Oid partrelid,
19877 : parentrelid;
19878 : LOCKTAG tag;
19879 : char *parentrelname;
19880 : char *partrelname;
19881 :
19882 : /*
19883 : * Add a new constraint to the partition being detached, which
19884 : * supplants the partition constraint (unless there is one already).
19885 : */
19886 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
19887 :
19888 : /*
19889 : * We're almost done now; the only traces that remain are the
19890 : * pg_inherits tuple and the partition's relpartbounds. Before we can
19891 : * remove those, we need to wait until all transactions that know that
19892 : * this is a partition are gone.
19893 : */
19894 :
19895 : /*
19896 : * Remember relation OIDs to re-acquire them later; and relation names
19897 : * too, for error messages if something is dropped in between.
19898 : */
19899 140 : partrelid = RelationGetRelid(partRel);
19900 140 : parentrelid = RelationGetRelid(rel);
19901 140 : parentrelname = MemoryContextStrdup(PortalContext,
19902 140 : RelationGetRelationName(rel));
19903 140 : partrelname = MemoryContextStrdup(PortalContext,
19904 140 : RelationGetRelationName(partRel));
19905 :
19906 : /* Invalidate relcache entries for the parent -- must be before close */
19907 140 : CacheInvalidateRelcache(rel);
19908 :
19909 140 : table_close(partRel, NoLock);
19910 140 : table_close(rel, NoLock);
19911 140 : tab->rel = NULL;
19912 :
19913 : /* Make updated catalog entry visible */
19914 140 : PopActiveSnapshot();
19915 140 : CommitTransactionCommand();
19916 :
19917 140 : StartTransactionCommand();
19918 :
19919 : /*
19920 : * Now wait. This ensures that all queries that were planned
19921 : * including the partition are finished before we remove the rest of
19922 : * catalog entries. We don't need or indeed want to acquire this
19923 : * lock, though -- that would block later queries.
19924 : *
19925 : * We don't need to concern ourselves with waiting for a lock on the
19926 : * partition itself, since we will acquire AccessExclusiveLock below.
19927 : */
19928 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19929 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19930 :
19931 : /*
19932 : * Now acquire locks in both relations again. Note they may have been
19933 : * removed in the meantime, so care is required.
19934 : */
19935 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19936 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
19937 :
19938 : /* If the relations aren't there, something bad happened; bail out */
19939 90 : if (rel == NULL)
19940 : {
19941 0 : if (partRel != NULL) /* shouldn't happen */
19942 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19943 : partrelname);
19944 0 : ereport(ERROR,
19945 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19946 : errmsg("partitioned table \"%s\" was removed concurrently",
19947 : parentrelname)));
19948 : }
19949 90 : if (partRel == NULL)
19950 0 : ereport(ERROR,
19951 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19952 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
19953 :
19954 90 : tab->rel = rel;
19955 : }
19956 :
19957 : /* Do the final part of detaching */
19958 418 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19959 :
19960 416 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19961 :
19962 : /* keep our lock until commit */
19963 416 : table_close(partRel, NoLock);
19964 :
19965 416 : return address;
19966 : }
19967 :
19968 : /*
19969 : * Second part of ALTER TABLE .. DETACH.
19970 : *
19971 : * This is separate so that it can be run independently when the second
19972 : * transaction of the concurrent algorithm fails (crash or abort).
19973 : */
19974 : static void
19975 432 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19976 : Oid defaultPartOid)
19977 : {
19978 : Relation classRel;
19979 : List *fks;
19980 : ListCell *cell;
19981 : List *indexes;
19982 : Datum new_val[Natts_pg_class];
19983 : bool new_null[Natts_pg_class],
19984 : new_repl[Natts_pg_class];
19985 : HeapTuple tuple,
19986 : newtuple;
19987 432 : Relation trigrel = NULL;
19988 :
19989 432 : if (concurrent)
19990 : {
19991 : /*
19992 : * We can remove the pg_inherits row now. (In the non-concurrent case,
19993 : * this was already done).
19994 : */
19995 104 : RemoveInheritance(partRel, rel, true);
19996 : }
19997 :
19998 : /* Drop any triggers that were cloned on creation/attach. */
19999 432 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20000 :
20001 : /*
20002 : * Detach any foreign keys that are inherited. This includes creating
20003 : * additional action triggers.
20004 : */
20005 432 : fks = copyObject(RelationGetFKeyList(partRel));
20006 432 : if (fks != NIL)
20007 60 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20008 522 : foreach(cell, fks)
20009 : {
20010 90 : ForeignKeyCacheInfo *fk = lfirst(cell);
20011 : HeapTuple contup;
20012 : Form_pg_constraint conform;
20013 : Oid insertTriggerOid,
20014 : updateTriggerOid;
20015 :
20016 90 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20017 90 : if (!HeapTupleIsValid(contup))
20018 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20019 90 : conform = (Form_pg_constraint) GETSTRUCT(contup);
20020 :
20021 : /* consider only the inherited foreign keys */
20022 90 : if (conform->contype != CONSTRAINT_FOREIGN ||
20023 90 : !OidIsValid(conform->conparentid))
20024 : {
20025 18 : ReleaseSysCache(contup);
20026 18 : continue;
20027 : }
20028 :
20029 : /*
20030 : * The constraint on this table must be marked no longer a child of
20031 : * the parent's constraint, as do its check triggers.
20032 : */
20033 72 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20034 :
20035 : /*
20036 : * Also, look up the partition's "check" triggers corresponding to the
20037 : * constraint being detached and detach them from the parent triggers.
20038 : */
20039 72 : GetForeignKeyCheckTriggers(trigrel,
20040 : fk->conoid, fk->confrelid, fk->conrelid,
20041 : &insertTriggerOid, &updateTriggerOid);
20042 : Assert(OidIsValid(insertTriggerOid));
20043 72 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20044 : RelationGetRelid(partRel));
20045 : Assert(OidIsValid(updateTriggerOid));
20046 72 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20047 : RelationGetRelid(partRel));
20048 :
20049 : /*
20050 : * Lastly, create the action triggers on the referenced table, using
20051 : * addFkRecurseReferenced, which requires some elaborate setup (so put
20052 : * it in a separate block). While at it, if the table is partitioned,
20053 : * that function will recurse to create the pg_constraint rows and
20054 : * action triggers for each partition.
20055 : *
20056 : * Note there's no need to do addFkConstraint() here, because the
20057 : * pg_constraint row already exists.
20058 : */
20059 : {
20060 : Constraint *fkconstraint;
20061 : int numfks;
20062 : AttrNumber conkey[INDEX_MAX_KEYS];
20063 : AttrNumber confkey[INDEX_MAX_KEYS];
20064 : Oid conpfeqop[INDEX_MAX_KEYS];
20065 : Oid conppeqop[INDEX_MAX_KEYS];
20066 : Oid conffeqop[INDEX_MAX_KEYS];
20067 : int numfkdelsetcols;
20068 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
20069 : Relation refdRel;
20070 :
20071 72 : DeconstructFkConstraintRow(contup,
20072 : &numfks,
20073 : conkey,
20074 : confkey,
20075 : conpfeqop,
20076 : conppeqop,
20077 : conffeqop,
20078 : &numfkdelsetcols,
20079 : confdelsetcols);
20080 :
20081 : /* Create a synthetic node we'll use throughout */
20082 72 : fkconstraint = makeNode(Constraint);
20083 72 : fkconstraint->contype = CONSTRAINT_FOREIGN;
20084 72 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
20085 72 : fkconstraint->deferrable = conform->condeferrable;
20086 72 : fkconstraint->initdeferred = conform->condeferred;
20087 72 : fkconstraint->skip_validation = true;
20088 72 : fkconstraint->initially_valid = true;
20089 : /* a few irrelevant fields omitted here */
20090 72 : fkconstraint->pktable = NULL;
20091 72 : fkconstraint->fk_attrs = NIL;
20092 72 : fkconstraint->pk_attrs = NIL;
20093 72 : fkconstraint->fk_matchtype = conform->confmatchtype;
20094 72 : fkconstraint->fk_upd_action = conform->confupdtype;
20095 72 : fkconstraint->fk_del_action = conform->confdeltype;
20096 72 : fkconstraint->fk_del_set_cols = NIL;
20097 72 : fkconstraint->old_conpfeqop = NIL;
20098 72 : fkconstraint->old_pktable_oid = InvalidOid;
20099 72 : fkconstraint->location = -1;
20100 :
20101 : /* set up colnames, used to generate the constraint name */
20102 192 : for (int i = 0; i < numfks; i++)
20103 : {
20104 : Form_pg_attribute att;
20105 :
20106 120 : att = TupleDescAttr(RelationGetDescr(partRel),
20107 120 : conkey[i] - 1);
20108 :
20109 120 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
20110 120 : makeString(NameStr(att->attname)));
20111 : }
20112 :
20113 72 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
20114 :
20115 72 : addFkRecurseReferenced(fkconstraint, partRel,
20116 : refdRel,
20117 : conform->conindid,
20118 : fk->conoid,
20119 : numfks,
20120 : confkey,
20121 : conkey,
20122 : conpfeqop,
20123 : conppeqop,
20124 : conffeqop,
20125 : numfkdelsetcols,
20126 : confdelsetcols,
20127 : true,
20128 : InvalidOid, InvalidOid,
20129 72 : conform->conperiod);
20130 72 : table_close(refdRel, NoLock); /* keep lock till end of xact */
20131 : }
20132 :
20133 72 : ReleaseSysCache(contup);
20134 : }
20135 432 : list_free_deep(fks);
20136 432 : if (trigrel)
20137 60 : table_close(trigrel, RowExclusiveLock);
20138 :
20139 : /*
20140 : * Any sub-constraints that are in the referenced-side of a larger
20141 : * constraint have to be removed. This partition is no longer part of the
20142 : * key space of the constraint.
20143 : */
20144 468 : foreach(cell, GetParentedForeignKeyRefs(partRel))
20145 : {
20146 38 : Oid constrOid = lfirst_oid(cell);
20147 : ObjectAddress constraint;
20148 :
20149 38 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20150 38 : deleteDependencyRecordsForClass(ConstraintRelationId,
20151 : constrOid,
20152 : ConstraintRelationId,
20153 : DEPENDENCY_INTERNAL);
20154 38 : CommandCounterIncrement();
20155 :
20156 38 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20157 38 : performDeletion(&constraint, DROP_RESTRICT, 0);
20158 : }
20159 :
20160 : /* Now we can detach indexes */
20161 430 : indexes = RelationGetIndexList(partRel);
20162 612 : foreach(cell, indexes)
20163 : {
20164 182 : Oid idxid = lfirst_oid(cell);
20165 : Oid parentidx;
20166 : Relation idx;
20167 : Oid constrOid;
20168 : Oid parentConstrOid;
20169 :
20170 182 : if (!has_superclass(idxid))
20171 12 : continue;
20172 :
20173 170 : parentidx = get_partition_parent(idxid, false);
20174 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20175 :
20176 170 : idx = index_open(idxid, AccessExclusiveLock);
20177 170 : IndexSetParentIndex(idx, InvalidOid);
20178 :
20179 : /*
20180 : * If there's a constraint associated with the index, detach it too.
20181 : * Careful: it is possible for a constraint index in a partition to be
20182 : * the child of a non-constraint index, so verify whether the parent
20183 : * index does actually have a constraint.
20184 : */
20185 170 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20186 : idxid);
20187 170 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
20188 : parentidx);
20189 170 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20190 72 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20191 :
20192 170 : index_close(idx, NoLock);
20193 : }
20194 :
20195 : /* Update pg_class tuple */
20196 430 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20197 430 : tuple = SearchSysCacheCopy1(RELOID,
20198 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20199 430 : if (!HeapTupleIsValid(tuple))
20200 0 : elog(ERROR, "cache lookup failed for relation %u",
20201 : RelationGetRelid(partRel));
20202 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20203 :
20204 : /* Clear relpartbound and reset relispartition */
20205 430 : memset(new_val, 0, sizeof(new_val));
20206 430 : memset(new_null, false, sizeof(new_null));
20207 430 : memset(new_repl, false, sizeof(new_repl));
20208 430 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20209 430 : new_null[Anum_pg_class_relpartbound - 1] = true;
20210 430 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20211 430 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20212 : new_val, new_null, new_repl);
20213 :
20214 430 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20215 430 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20216 430 : heap_freetuple(newtuple);
20217 430 : table_close(classRel, RowExclusiveLock);
20218 :
20219 : /*
20220 : * Drop identity property from all identity columns of partition.
20221 : */
20222 1228 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20223 : {
20224 798 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20225 :
20226 798 : if (!attr->attisdropped && attr->attidentity)
20227 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20228 : AccessExclusiveLock, true, true);
20229 : }
20230 :
20231 430 : if (OidIsValid(defaultPartOid))
20232 : {
20233 : /*
20234 : * If the relation being detached is the default partition itself,
20235 : * remove it from the parent's pg_partitioned_table entry.
20236 : *
20237 : * If not, we must invalidate default partition's relcache entry, as
20238 : * in StorePartitionBound: its partition constraint depends on every
20239 : * other partition's partition constraint.
20240 : */
20241 46 : if (RelationGetRelid(partRel) == defaultPartOid)
20242 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20243 : else
20244 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20245 : }
20246 :
20247 : /*
20248 : * Invalidate the parent's relcache so that the partition is no longer
20249 : * included in its partition descriptor.
20250 : */
20251 430 : CacheInvalidateRelcache(rel);
20252 :
20253 : /*
20254 : * If the partition we just detached is partitioned itself, invalidate
20255 : * relcache for all descendent partitions too to ensure that their
20256 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20257 : * doing so, using the same lockmode as what partRel has been locked with
20258 : * by the caller.
20259 : */
20260 430 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20261 : {
20262 : List *children;
20263 :
20264 56 : children = find_all_inheritors(RelationGetRelid(partRel),
20265 : AccessExclusiveLock, NULL);
20266 180 : foreach(cell, children)
20267 : {
20268 124 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20269 : }
20270 : }
20271 430 : }
20272 :
20273 : /*
20274 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20275 : *
20276 : * To use when a DETACH PARTITION command previously did not run to
20277 : * completion; this completes the detaching process.
20278 : */
20279 : static ObjectAddress
20280 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20281 : {
20282 : Relation partRel;
20283 : ObjectAddress address;
20284 14 : Snapshot snap = GetActiveSnapshot();
20285 :
20286 14 : partRel = table_openrv(name, AccessExclusiveLock);
20287 :
20288 : /*
20289 : * Wait until existing snapshots are gone. This is important if the
20290 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20291 : * user could immediately run DETACH FINALIZE without actually waiting for
20292 : * existing transactions. We must not complete the detach action until
20293 : * all such queries are complete (otherwise we would present them with an
20294 : * inconsistent view of catalogs).
20295 : */
20296 14 : WaitForOlderSnapshots(snap->xmin, false);
20297 :
20298 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20299 :
20300 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20301 :
20302 14 : table_close(partRel, NoLock);
20303 :
20304 14 : return address;
20305 : }
20306 :
20307 : /*
20308 : * DetachAddConstraintIfNeeded
20309 : * Subroutine for ATExecDetachPartition. Create a constraint that
20310 : * takes the place of the partition constraint, but avoid creating
20311 : * a dupe if a constraint already exists which implies the needed
20312 : * constraint.
20313 : */
20314 : static void
20315 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20316 : {
20317 : List *constraintExpr;
20318 :
20319 140 : constraintExpr = RelationGetPartitionQual(partRel);
20320 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20321 :
20322 : /*
20323 : * Avoid adding a new constraint if the needed constraint is implied by an
20324 : * existing constraint
20325 : */
20326 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20327 : {
20328 : AlteredTableInfo *tab;
20329 : Constraint *n;
20330 :
20331 134 : tab = ATGetQueueEntry(wqueue, partRel);
20332 :
20333 : /* Add constraint on partition, equivalent to the partition constraint */
20334 134 : n = makeNode(Constraint);
20335 134 : n->contype = CONSTR_CHECK;
20336 134 : n->conname = NULL;
20337 134 : n->location = -1;
20338 134 : n->is_no_inherit = false;
20339 134 : n->raw_expr = NULL;
20340 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20341 134 : n->is_enforced = true;
20342 134 : n->initially_valid = true;
20343 134 : n->skip_validation = true;
20344 : /* It's a re-add, since it nominally already exists */
20345 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20346 : true, false, true, ShareUpdateExclusiveLock);
20347 : }
20348 140 : }
20349 :
20350 : /*
20351 : * DropClonedTriggersFromPartition
20352 : * subroutine for ATExecDetachPartition to remove any triggers that were
20353 : * cloned to the partition when it was created-as-partition or attached.
20354 : * This undoes what CloneRowTriggersToPartition did.
20355 : */
20356 : static void
20357 432 : DropClonedTriggersFromPartition(Oid partitionId)
20358 : {
20359 : ScanKeyData skey;
20360 : SysScanDesc scan;
20361 : HeapTuple trigtup;
20362 : Relation tgrel;
20363 : ObjectAddresses *objects;
20364 :
20365 432 : objects = new_object_addresses();
20366 :
20367 : /*
20368 : * Scan pg_trigger to search for all triggers on this rel.
20369 : */
20370 432 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20371 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20372 432 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20373 432 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20374 : true, NULL, 1, &skey);
20375 706 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20376 : {
20377 274 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20378 : ObjectAddress trig;
20379 :
20380 : /* Ignore triggers that weren't cloned */
20381 274 : if (!OidIsValid(pg_trigger->tgparentid))
20382 256 : continue;
20383 :
20384 : /*
20385 : * Ignore internal triggers that are implementation objects of foreign
20386 : * keys, because these will be detached when the foreign keys
20387 : * themselves are.
20388 : */
20389 238 : if (OidIsValid(pg_trigger->tgconstrrelid))
20390 220 : continue;
20391 :
20392 : /*
20393 : * This is ugly, but necessary: remove the dependency markings on the
20394 : * trigger so that it can be removed.
20395 : */
20396 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20397 : TriggerRelationId,
20398 : DEPENDENCY_PARTITION_PRI);
20399 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20400 : RelationRelationId,
20401 : DEPENDENCY_PARTITION_SEC);
20402 :
20403 : /* remember this trigger to remove it below */
20404 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20405 18 : add_exact_object_address(&trig, objects);
20406 : }
20407 :
20408 : /* make the dependency removal visible to the deletion below */
20409 432 : CommandCounterIncrement();
20410 432 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20411 :
20412 : /* done */
20413 432 : free_object_addresses(objects);
20414 432 : systable_endscan(scan);
20415 432 : table_close(tgrel, RowExclusiveLock);
20416 432 : }
20417 :
20418 : /*
20419 : * Before acquiring lock on an index, acquire the same lock on the owning
20420 : * table.
20421 : */
20422 : struct AttachIndexCallbackState
20423 : {
20424 : Oid partitionOid;
20425 : Oid parentTblOid;
20426 : bool lockedParentTbl;
20427 : };
20428 :
20429 : static void
20430 400 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20431 : void *arg)
20432 : {
20433 : struct AttachIndexCallbackState *state;
20434 : Form_pg_class classform;
20435 : HeapTuple tuple;
20436 :
20437 400 : state = (struct AttachIndexCallbackState *) arg;
20438 :
20439 400 : if (!state->lockedParentTbl)
20440 : {
20441 392 : LockRelationOid(state->parentTblOid, AccessShareLock);
20442 392 : state->lockedParentTbl = true;
20443 : }
20444 :
20445 : /*
20446 : * If we previously locked some other heap, and the name we're looking up
20447 : * no longer refers to an index on that relation, release the now-useless
20448 : * lock. XXX maybe we should do *after* we verify whether the index does
20449 : * not actually belong to the same relation ...
20450 : */
20451 400 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20452 : {
20453 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20454 0 : state->partitionOid = InvalidOid;
20455 : }
20456 :
20457 : /* Didn't find a relation, so no need for locking or permission checks. */
20458 400 : if (!OidIsValid(relOid))
20459 6 : return;
20460 :
20461 394 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20462 394 : if (!HeapTupleIsValid(tuple))
20463 0 : return; /* concurrently dropped, so nothing to do */
20464 394 : classform = (Form_pg_class) GETSTRUCT(tuple);
20465 394 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20466 302 : classform->relkind != RELKIND_INDEX)
20467 6 : ereport(ERROR,
20468 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20469 : errmsg("\"%s\" is not an index", rv->relname)));
20470 388 : ReleaseSysCache(tuple);
20471 :
20472 : /*
20473 : * Since we need only examine the heap's tupledesc, an access share lock
20474 : * on it (preventing any DDL) is sufficient.
20475 : */
20476 388 : state->partitionOid = IndexGetRelation(relOid, false);
20477 388 : LockRelationOid(state->partitionOid, AccessShareLock);
20478 : }
20479 :
20480 : /*
20481 : * ALTER INDEX i1 ATTACH PARTITION i2
20482 : */
20483 : static ObjectAddress
20484 392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20485 : {
20486 : Relation partIdx;
20487 : Relation partTbl;
20488 : Relation parentTbl;
20489 : ObjectAddress address;
20490 : Oid partIdxId;
20491 : Oid currParent;
20492 : struct AttachIndexCallbackState state;
20493 :
20494 : /*
20495 : * We need to obtain lock on the index 'name' to modify it, but we also
20496 : * need to read its owning table's tuple descriptor -- so we need to lock
20497 : * both. To avoid deadlocks, obtain lock on the table before doing so on
20498 : * the index. Furthermore, we need to examine the parent table of the
20499 : * partition, so lock that one too.
20500 : */
20501 392 : state.partitionOid = InvalidOid;
20502 392 : state.parentTblOid = parentIdx->rd_index->indrelid;
20503 392 : state.lockedParentTbl = false;
20504 : partIdxId =
20505 392 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20506 : RangeVarCallbackForAttachIndex,
20507 : &state);
20508 : /* Not there? */
20509 380 : if (!OidIsValid(partIdxId))
20510 0 : ereport(ERROR,
20511 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20512 : errmsg("index \"%s\" does not exist", name->relname)));
20513 :
20514 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20515 380 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
20516 :
20517 : /* we already hold locks on both tables, so this is safe: */
20518 380 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20519 380 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20520 :
20521 380 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20522 :
20523 : /* Silently do nothing if already in the right state */
20524 760 : currParent = partIdx->rd_rel->relispartition ?
20525 380 : get_partition_parent(partIdxId, false) : InvalidOid;
20526 380 : if (currParent != RelationGetRelid(parentIdx))
20527 : {
20528 : IndexInfo *childInfo;
20529 : IndexInfo *parentInfo;
20530 : AttrMap *attmap;
20531 : bool found;
20532 : int i;
20533 : PartitionDesc partDesc;
20534 : Oid constraintOid,
20535 356 : cldConstrId = InvalidOid;
20536 :
20537 : /*
20538 : * If this partition already has an index attached, refuse the
20539 : * operation.
20540 : */
20541 356 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20542 :
20543 350 : if (OidIsValid(currParent))
20544 0 : ereport(ERROR,
20545 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20546 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20547 : RelationGetRelationName(partIdx),
20548 : RelationGetRelationName(parentIdx)),
20549 : errdetail("Index \"%s\" is already attached to another index.",
20550 : RelationGetRelationName(partIdx))));
20551 :
20552 : /* Make sure it indexes a partition of the other index's table */
20553 350 : partDesc = RelationGetPartitionDesc(parentTbl, true);
20554 350 : found = false;
20555 552 : for (i = 0; i < partDesc->nparts; i++)
20556 : {
20557 546 : if (partDesc->oids[i] == state.partitionOid)
20558 : {
20559 344 : found = true;
20560 344 : break;
20561 : }
20562 : }
20563 350 : if (!found)
20564 6 : ereport(ERROR,
20565 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20566 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20567 : RelationGetRelationName(partIdx),
20568 : RelationGetRelationName(parentIdx)),
20569 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20570 : RelationGetRelationName(partIdx),
20571 : RelationGetRelationName(parentTbl))));
20572 :
20573 : /* Ensure the indexes are compatible */
20574 344 : childInfo = BuildIndexInfo(partIdx);
20575 344 : parentInfo = BuildIndexInfo(parentIdx);
20576 344 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20577 : RelationGetDescr(parentTbl),
20578 : false);
20579 344 : if (!CompareIndexInfo(childInfo, parentInfo,
20580 344 : partIdx->rd_indcollation,
20581 344 : parentIdx->rd_indcollation,
20582 344 : partIdx->rd_opfamily,
20583 344 : parentIdx->rd_opfamily,
20584 : attmap))
20585 42 : ereport(ERROR,
20586 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20587 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20588 : RelationGetRelationName(partIdx),
20589 : RelationGetRelationName(parentIdx)),
20590 : errdetail("The index definitions do not match.")));
20591 :
20592 : /*
20593 : * If there is a constraint in the parent, make sure there is one in
20594 : * the child too.
20595 : */
20596 302 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20597 : RelationGetRelid(parentIdx));
20598 :
20599 302 : if (OidIsValid(constraintOid))
20600 : {
20601 122 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20602 : partIdxId);
20603 122 : if (!OidIsValid(cldConstrId))
20604 6 : ereport(ERROR,
20605 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20606 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20607 : RelationGetRelationName(partIdx),
20608 : RelationGetRelationName(parentIdx)),
20609 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20610 : RelationGetRelationName(parentIdx),
20611 : RelationGetRelationName(parentTbl),
20612 : RelationGetRelationName(partIdx))));
20613 : }
20614 :
20615 : /*
20616 : * If it's a primary key, make sure the columns in the partition are
20617 : * NOT NULL.
20618 : */
20619 296 : if (parentIdx->rd_index->indisprimary)
20620 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
20621 :
20622 : /* All good -- do it */
20623 296 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20624 296 : if (OidIsValid(constraintOid))
20625 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
20626 : RelationGetRelid(partTbl));
20627 :
20628 296 : free_attrmap(attmap);
20629 :
20630 296 : validatePartitionedIndex(parentIdx, parentTbl);
20631 : }
20632 :
20633 320 : relation_close(parentTbl, AccessShareLock);
20634 : /* keep these locks till commit */
20635 320 : relation_close(partTbl, NoLock);
20636 320 : relation_close(partIdx, NoLock);
20637 :
20638 320 : return address;
20639 : }
20640 :
20641 : /*
20642 : * Verify whether the given partition already contains an index attached
20643 : * to the given partitioned index. If so, raise an error.
20644 : */
20645 : static void
20646 356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20647 : {
20648 : Oid existingIdx;
20649 :
20650 356 : existingIdx = index_get_partition(partitionTbl,
20651 : RelationGetRelid(parentIdx));
20652 356 : if (OidIsValid(existingIdx))
20653 6 : ereport(ERROR,
20654 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20655 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20656 : RelationGetRelationName(partIdx),
20657 : RelationGetRelationName(parentIdx)),
20658 : errdetail("Another index is already attached for partition \"%s\".",
20659 : RelationGetRelationName(partitionTbl))));
20660 350 : }
20661 :
20662 : /*
20663 : * Verify whether the set of attached partition indexes to a parent index on
20664 : * a partitioned table is complete. If it is, mark the parent index valid.
20665 : *
20666 : * This should be called each time a partition index is attached.
20667 : */
20668 : static void
20669 338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20670 : {
20671 : Relation inheritsRel;
20672 : SysScanDesc scan;
20673 : ScanKeyData key;
20674 338 : int tuples = 0;
20675 : HeapTuple inhTup;
20676 338 : bool updated = false;
20677 :
20678 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20679 :
20680 : /*
20681 : * Scan pg_inherits for this parent index. Count each valid index we find
20682 : * (verifying the pg_index entry for each), and if we reach the total
20683 : * amount we expect, we can mark this parent index as valid.
20684 : */
20685 338 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20686 338 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20687 : BTEqualStrategyNumber, F_OIDEQ,
20688 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20689 338 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20690 : NULL, 1, &key);
20691 880 : while ((inhTup = systable_getnext(scan)) != NULL)
20692 : {
20693 542 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20694 : HeapTuple indTup;
20695 : Form_pg_index indexForm;
20696 :
20697 542 : indTup = SearchSysCache1(INDEXRELID,
20698 : ObjectIdGetDatum(inhForm->inhrelid));
20699 542 : if (!HeapTupleIsValid(indTup))
20700 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20701 542 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20702 542 : if (indexForm->indisvalid)
20703 484 : tuples += 1;
20704 542 : ReleaseSysCache(indTup);
20705 : }
20706 :
20707 : /* Done with pg_inherits */
20708 338 : systable_endscan(scan);
20709 338 : table_close(inheritsRel, AccessShareLock);
20710 :
20711 : /*
20712 : * If we found as many inherited indexes as the partitioned table has
20713 : * partitions, we're good; update pg_index to set indisvalid.
20714 : */
20715 338 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20716 : {
20717 : Relation idxRel;
20718 : HeapTuple indTup;
20719 : Form_pg_index indexForm;
20720 :
20721 168 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
20722 168 : indTup = SearchSysCacheCopy1(INDEXRELID,
20723 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20724 168 : if (!HeapTupleIsValid(indTup))
20725 0 : elog(ERROR, "cache lookup failed for index %u",
20726 : RelationGetRelid(partedIdx));
20727 168 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20728 :
20729 168 : indexForm->indisvalid = true;
20730 168 : updated = true;
20731 :
20732 168 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20733 :
20734 168 : table_close(idxRel, RowExclusiveLock);
20735 168 : heap_freetuple(indTup);
20736 : }
20737 :
20738 : /*
20739 : * If this index is in turn a partition of a larger index, validating it
20740 : * might cause the parent to become valid also. Try that.
20741 : */
20742 338 : if (updated && partedIdx->rd_rel->relispartition)
20743 : {
20744 : Oid parentIdxId,
20745 : parentTblId;
20746 : Relation parentIdx,
20747 : parentTbl;
20748 :
20749 : /* make sure we see the validation we just did */
20750 42 : CommandCounterIncrement();
20751 :
20752 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20753 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20754 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20755 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20756 : Assert(!parentIdx->rd_index->indisvalid);
20757 :
20758 42 : validatePartitionedIndex(parentIdx, parentTbl);
20759 :
20760 42 : relation_close(parentIdx, AccessExclusiveLock);
20761 42 : relation_close(parentTbl, AccessExclusiveLock);
20762 : }
20763 338 : }
20764 :
20765 : /*
20766 : * When attaching an index as a partition of a partitioned index which is a
20767 : * primary key, verify that all the columns in the partition are marked NOT
20768 : * NULL.
20769 : */
20770 : static void
20771 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20772 : {
20773 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20774 : {
20775 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20776 94 : iinfo->ii_IndexAttrNumbers[i] - 1);
20777 :
20778 94 : if (!att->attnotnull)
20779 0 : ereport(ERROR,
20780 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20781 : errmsg("invalid primary key definition"),
20782 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20783 : NameStr(att->attname),
20784 : RelationGetRelationName(partition)));
20785 : }
20786 92 : }
20787 :
20788 : /*
20789 : * Return an OID list of constraints that reference the given relation
20790 : * that are marked as having a parent constraints.
20791 : */
20792 : static List *
20793 934 : GetParentedForeignKeyRefs(Relation partition)
20794 : {
20795 : Relation pg_constraint;
20796 : HeapTuple tuple;
20797 : SysScanDesc scan;
20798 : ScanKeyData key[2];
20799 934 : List *constraints = NIL;
20800 :
20801 : /*
20802 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
20803 : * scan.
20804 : */
20805 1338 : if (RelationGetIndexList(partition) == NIL ||
20806 404 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
20807 : INDEX_ATTR_BITMAP_KEY)))
20808 686 : return NIL;
20809 :
20810 : /* Search for constraints referencing this table */
20811 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20812 248 : ScanKeyInit(&key[0],
20813 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20814 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20815 248 : ScanKeyInit(&key[1],
20816 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
20817 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20818 :
20819 : /* XXX This is a seqscan, as we don't have a usable index */
20820 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20821 372 : while ((tuple = systable_getnext(scan)) != NULL)
20822 : {
20823 124 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20824 :
20825 : /*
20826 : * We only need to process constraints that are part of larger ones.
20827 : */
20828 124 : if (!OidIsValid(constrForm->conparentid))
20829 0 : continue;
20830 :
20831 124 : constraints = lappend_oid(constraints, constrForm->oid);
20832 : }
20833 :
20834 248 : systable_endscan(scan);
20835 248 : table_close(pg_constraint, AccessShareLock);
20836 :
20837 248 : return constraints;
20838 : }
20839 :
20840 : /*
20841 : * During DETACH PARTITION, verify that any foreign keys pointing to the
20842 : * partitioned table would not become invalid. An error is raised if any
20843 : * referenced values exist.
20844 : */
20845 : static void
20846 502 : ATDetachCheckNoForeignKeyRefs(Relation partition)
20847 : {
20848 : List *constraints;
20849 : ListCell *cell;
20850 :
20851 502 : constraints = GetParentedForeignKeyRefs(partition);
20852 :
20853 554 : foreach(cell, constraints)
20854 : {
20855 86 : Oid constrOid = lfirst_oid(cell);
20856 : HeapTuple tuple;
20857 : Form_pg_constraint constrForm;
20858 : Relation rel;
20859 86 : Trigger trig = {0};
20860 :
20861 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20862 86 : if (!HeapTupleIsValid(tuple))
20863 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20864 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20865 :
20866 : Assert(OidIsValid(constrForm->conparentid));
20867 : Assert(constrForm->confrelid == RelationGetRelid(partition));
20868 :
20869 : /* prevent data changes into the referencing table until commit */
20870 86 : rel = table_open(constrForm->conrelid, ShareLock);
20871 :
20872 86 : trig.tgoid = InvalidOid;
20873 86 : trig.tgname = NameStr(constrForm->conname);
20874 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20875 86 : trig.tgisinternal = true;
20876 86 : trig.tgconstrrelid = RelationGetRelid(partition);
20877 86 : trig.tgconstrindid = constrForm->conindid;
20878 86 : trig.tgconstraint = constrForm->oid;
20879 86 : trig.tgdeferrable = false;
20880 86 : trig.tginitdeferred = false;
20881 : /* we needn't fill in remaining fields */
20882 :
20883 86 : RI_PartitionRemove_Check(&trig, rel, partition);
20884 :
20885 52 : ReleaseSysCache(tuple);
20886 :
20887 52 : table_close(rel, NoLock);
20888 : }
20889 468 : }
20890 :
20891 : /*
20892 : * resolve column compression specification to compression method.
20893 : */
20894 : static char
20895 231688 : GetAttributeCompression(Oid atttypid, const char *compression)
20896 : {
20897 : char cmethod;
20898 :
20899 231688 : if (compression == NULL || strcmp(compression, "default") == 0)
20900 231538 : return InvalidCompressionMethod;
20901 :
20902 : /*
20903 : * To specify a nondefault method, the column data type must be toastable.
20904 : * Note this says nothing about whether the column's attstorage setting
20905 : * permits compression; we intentionally allow attstorage and
20906 : * attcompression to be independent. But with a non-toastable type,
20907 : * attstorage could not be set to a value that would permit compression.
20908 : *
20909 : * We don't actually need to enforce this, since nothing bad would happen
20910 : * if attcompression were non-default; it would never be consulted. But
20911 : * it seems more user-friendly to complain about a certainly-useless
20912 : * attempt to set the property.
20913 : */
20914 150 : if (!TypeIsToastable(atttypid))
20915 6 : ereport(ERROR,
20916 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20917 : errmsg("column data type %s does not support compression",
20918 : format_type_be(atttypid))));
20919 :
20920 144 : cmethod = CompressionNameToMethod(compression);
20921 144 : if (!CompressionMethodIsValid(cmethod))
20922 12 : ereport(ERROR,
20923 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20924 : errmsg("invalid compression method \"%s\"", compression)));
20925 :
20926 132 : return cmethod;
20927 : }
20928 :
20929 : /*
20930 : * resolve column storage specification
20931 : */
20932 : static char
20933 242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
20934 : {
20935 242 : char cstorage = 0;
20936 :
20937 242 : if (pg_strcasecmp(storagemode, "plain") == 0)
20938 50 : cstorage = TYPSTORAGE_PLAIN;
20939 192 : else if (pg_strcasecmp(storagemode, "external") == 0)
20940 156 : cstorage = TYPSTORAGE_EXTERNAL;
20941 36 : else if (pg_strcasecmp(storagemode, "extended") == 0)
20942 16 : cstorage = TYPSTORAGE_EXTENDED;
20943 20 : else if (pg_strcasecmp(storagemode, "main") == 0)
20944 14 : cstorage = TYPSTORAGE_MAIN;
20945 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
20946 6 : cstorage = get_typstorage(atttypid);
20947 : else
20948 0 : ereport(ERROR,
20949 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20950 : errmsg("invalid storage type \"%s\"",
20951 : storagemode)));
20952 :
20953 : /*
20954 : * safety check: do not allow toasted storage modes unless column datatype
20955 : * is TOAST-aware.
20956 : */
20957 242 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20958 6 : ereport(ERROR,
20959 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20960 : errmsg("column data type %s can only have storage PLAIN",
20961 : format_type_be(atttypid))));
20962 :
20963 236 : return cstorage;
20964 : }
|