/src/postgres/src/backend/parser/parse_utilcmd.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * parse_utilcmd.c |
4 | | * Perform parse analysis work for various utility commands |
5 | | * |
6 | | * Formerly we did this work during parse_analyze_*() in analyze.c. However |
7 | | * that is fairly unsafe in the presence of querytree caching, since any |
8 | | * database state that we depend on in making the transformations might be |
9 | | * obsolete by the time the utility command is executed; and utility commands |
10 | | * have no infrastructure for holding locks or rechecking plan validity. |
11 | | * Hence these functions are now called at the start of execution of their |
12 | | * respective utility commands. |
13 | | * |
14 | | * |
15 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
16 | | * Portions Copyright (c) 1994, Regents of the University of California |
17 | | * |
18 | | * src/backend/parser/parse_utilcmd.c |
19 | | * |
20 | | *------------------------------------------------------------------------- |
21 | | */ |
22 | | |
23 | | #include "postgres.h" |
24 | | |
25 | | #include "access/amapi.h" |
26 | | #include "access/attmap.h" |
27 | | #include "access/htup_details.h" |
28 | | #include "access/relation.h" |
29 | | #include "access/reloptions.h" |
30 | | #include "access/table.h" |
31 | | #include "access/toast_compression.h" |
32 | | #include "catalog/dependency.h" |
33 | | #include "catalog/heap.h" |
34 | | #include "catalog/index.h" |
35 | | #include "catalog/namespace.h" |
36 | | #include "catalog/pg_am.h" |
37 | | #include "catalog/pg_collation.h" |
38 | | #include "catalog/pg_constraint.h" |
39 | | #include "catalog/pg_opclass.h" |
40 | | #include "catalog/pg_operator.h" |
41 | | #include "catalog/pg_statistic_ext.h" |
42 | | #include "catalog/pg_type.h" |
43 | | #include "commands/comment.h" |
44 | | #include "commands/defrem.h" |
45 | | #include "commands/sequence.h" |
46 | | #include "commands/tablecmds.h" |
47 | | #include "commands/tablespace.h" |
48 | | #include "miscadmin.h" |
49 | | #include "nodes/makefuncs.h" |
50 | | #include "nodes/nodeFuncs.h" |
51 | | #include "optimizer/optimizer.h" |
52 | | #include "parser/analyze.h" |
53 | | #include "parser/parse_clause.h" |
54 | | #include "parser/parse_coerce.h" |
55 | | #include "parser/parse_collate.h" |
56 | | #include "parser/parse_expr.h" |
57 | | #include "parser/parse_relation.h" |
58 | | #include "parser/parse_target.h" |
59 | | #include "parser/parse_type.h" |
60 | | #include "parser/parse_utilcmd.h" |
61 | | #include "parser/parser.h" |
62 | | #include "rewrite/rewriteManip.h" |
63 | | #include "utils/acl.h" |
64 | | #include "utils/builtins.h" |
65 | | #include "utils/lsyscache.h" |
66 | | #include "utils/partcache.h" |
67 | | #include "utils/rel.h" |
68 | | #include "utils/ruleutils.h" |
69 | | #include "utils/syscache.h" |
70 | | #include "utils/typcache.h" |
71 | | |
72 | | |
73 | | /* State shared by transformCreateStmt and its subroutines */ |
74 | | typedef struct |
75 | | { |
76 | | ParseState *pstate; /* overall parser state */ |
77 | | const char *stmtType; /* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */ |
78 | | RangeVar *relation; /* relation to create */ |
79 | | Relation rel; /* opened/locked rel, if ALTER */ |
80 | | List *inhRelations; /* relations to inherit from */ |
81 | | bool isforeign; /* true if CREATE/ALTER FOREIGN TABLE */ |
82 | | bool isalter; /* true if altering existing table */ |
83 | | List *columns; /* ColumnDef items */ |
84 | | List *ckconstraints; /* CHECK constraints */ |
85 | | List *nnconstraints; /* NOT NULL constraints */ |
86 | | List *fkconstraints; /* FOREIGN KEY constraints */ |
87 | | List *ixconstraints; /* index-creating constraints */ |
88 | | List *likeclauses; /* LIKE clauses that need post-processing */ |
89 | | List *blist; /* "before list" of things to do before |
90 | | * creating the table */ |
91 | | List *alist; /* "after list" of things to do after creating |
92 | | * the table */ |
93 | | IndexStmt *pkey; /* PRIMARY KEY index, if any */ |
94 | | bool ispartitioned; /* true if table is partitioned */ |
95 | | PartitionBoundSpec *partbound; /* transformed FOR VALUES */ |
96 | | bool ofType; /* true if statement contains OF typename */ |
97 | | } CreateStmtContext; |
98 | | |
99 | | /* State shared by transformCreateSchemaStmtElements and its subroutines */ |
100 | | typedef struct |
101 | | { |
102 | | ParseState *pstate; /* overall parse state */ |
103 | | const char *schemaname; /* name of schema */ |
104 | | List *sequences; /* CREATE SEQUENCE items */ |
105 | | List *tables; /* CREATE TABLE items */ |
106 | | List *views; /* CREATE VIEW items */ |
107 | | List *indexes; /* CREATE INDEX items */ |
108 | | List *triggers; /* CREATE TRIGGER items */ |
109 | | List *grants; /* GRANT items */ |
110 | | List *foreign_keys; /* generated ALTER ADD FOREIGN KEY items */ |
111 | | } CreateSchemaStmtContext; |
112 | | |
113 | | |
114 | | static void transformColumnDefinition(CreateStmtContext *cxt, |
115 | | ColumnDef *column); |
116 | | static void transformTableConstraint(CreateStmtContext *cxt, |
117 | | Constraint *constraint); |
118 | | static void transformTableLikeClause(CreateStmtContext *cxt, |
119 | | TableLikeClause *table_like_clause); |
120 | | static void transformOfType(CreateStmtContext *cxt, |
121 | | TypeName *ofTypename); |
122 | | static CreateStatsStmt *generateClonedExtStatsStmt(RangeVar *heapRel, |
123 | | Oid heapRelid, |
124 | | Oid source_statsid, |
125 | | const AttrMap *attmap); |
126 | | static List *get_collation(Oid collation, Oid actual_datatype); |
127 | | static List *get_opclass(Oid opclass, Oid actual_datatype); |
128 | | static void transformIndexConstraints(CreateStmtContext *cxt); |
129 | | static IndexStmt *transformIndexConstraint(Constraint *constraint, |
130 | | CreateStmtContext *cxt); |
131 | | static void transformFKConstraints(CreateStmtContext *cxt, |
132 | | bool skipValidation, |
133 | | bool isAddConstraint); |
134 | | static void transformCheckConstraints(CreateStmtContext *cxt, |
135 | | bool skipValidation); |
136 | | static void transformConstraintAttrs(ParseState *pstate, |
137 | | List *constraintList); |
138 | | static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); |
139 | | static void checkSchemaNameRV(CreateSchemaStmtContext *cxt, RangeVar *relation); |
140 | | static CreateStmt *transformCreateSchemaCreateTable(ParseState *pstate, |
141 | | CreateStmt *stmt, |
142 | | List **fk_elements); |
143 | | static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd); |
144 | | static List *transformPartitionRangeBounds(ParseState *pstate, List *blist, |
145 | | Relation parent); |
146 | | static void validateInfiniteBounds(ParseState *pstate, List *blist); |
147 | | static Const *transformPartitionBoundValue(ParseState *pstate, Node *val, |
148 | | const char *colName, Oid colType, int32 colTypmod, |
149 | | Oid partCollation); |
150 | | |
151 | | |
152 | | /* |
153 | | * transformCreateStmt - |
154 | | * parse analysis for CREATE TABLE |
155 | | * |
156 | | * Returns a List of utility commands to be done in sequence. One of these |
157 | | * will be the transformed CreateStmt, but there may be additional actions |
158 | | * to be done before and after the actual DefineRelation() call. |
159 | | * In addition to normal utility commands such as AlterTableStmt and |
160 | | * IndexStmt, the result list may contain TableLikeClause(s), representing |
161 | | * the need to perform additional parse analysis after DefineRelation(). |
162 | | * |
163 | | * SQL allows constraints to be scattered all over, so thumb through |
164 | | * the columns and collect all constraints into one place. |
165 | | * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY) |
166 | | * then expand those into multiple IndexStmt blocks. |
167 | | * - thomas 1997-12-02 |
168 | | */ |
169 | | List * |
170 | | transformCreateStmt(CreateStmt *stmt, const char *queryString) |
171 | 0 | { |
172 | 0 | ParseState *pstate; |
173 | 0 | CreateStmtContext cxt; |
174 | 0 | List *result; |
175 | 0 | List *save_alist; |
176 | 0 | ListCell *elements; |
177 | 0 | Oid namespaceid; |
178 | 0 | Oid existing_relid; |
179 | 0 | ParseCallbackState pcbstate; |
180 | | |
181 | | /* Set up pstate */ |
182 | 0 | pstate = make_parsestate(NULL); |
183 | 0 | pstate->p_sourcetext = queryString; |
184 | | |
185 | | /* |
186 | | * Look up the creation namespace. This also checks permissions on the |
187 | | * target namespace, locks it against concurrent drops, checks for a |
188 | | * preexisting relation in that namespace with the same name, and updates |
189 | | * stmt->relation->relpersistence if the selected namespace is temporary. |
190 | | */ |
191 | 0 | setup_parser_errposition_callback(&pcbstate, pstate, |
192 | 0 | stmt->relation->location); |
193 | 0 | namespaceid = |
194 | 0 | RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, |
195 | 0 | &existing_relid); |
196 | 0 | cancel_parser_errposition_callback(&pcbstate); |
197 | | |
198 | | /* |
199 | | * If the relation already exists and the user specified "IF NOT EXISTS", |
200 | | * bail out with a NOTICE. |
201 | | */ |
202 | 0 | if (stmt->if_not_exists && OidIsValid(existing_relid)) |
203 | 0 | { |
204 | | /* |
205 | | * If we are in an extension script, insist that the pre-existing |
206 | | * object be a member of the extension, to avoid security risks. |
207 | | */ |
208 | 0 | ObjectAddress address; |
209 | |
|
210 | 0 | ObjectAddressSet(address, RelationRelationId, existing_relid); |
211 | 0 | checkMembershipInCurrentExtension(&address); |
212 | | |
213 | | /* OK to skip */ |
214 | 0 | ereport(NOTICE, |
215 | 0 | (errcode(ERRCODE_DUPLICATE_TABLE), |
216 | 0 | errmsg("relation \"%s\" already exists, skipping", |
217 | 0 | stmt->relation->relname))); |
218 | 0 | return NIL; |
219 | 0 | } |
220 | | |
221 | | /* |
222 | | * If the target relation name isn't schema-qualified, make it so. This |
223 | | * prevents some corner cases in which added-on rewritten commands might |
224 | | * think they should apply to other relations that have the same name and |
225 | | * are earlier in the search path. But a local temp table is effectively |
226 | | * specified to be in pg_temp, so no need for anything extra in that case. |
227 | | */ |
228 | 0 | if (stmt->relation->schemaname == NULL |
229 | 0 | && stmt->relation->relpersistence != RELPERSISTENCE_TEMP) |
230 | 0 | stmt->relation->schemaname = get_namespace_name(namespaceid); |
231 | | |
232 | | /* Set up CreateStmtContext */ |
233 | 0 | cxt.pstate = pstate; |
234 | 0 | if (IsA(stmt, CreateForeignTableStmt)) |
235 | 0 | { |
236 | 0 | cxt.stmtType = "CREATE FOREIGN TABLE"; |
237 | 0 | cxt.isforeign = true; |
238 | 0 | } |
239 | 0 | else |
240 | 0 | { |
241 | 0 | cxt.stmtType = "CREATE TABLE"; |
242 | 0 | cxt.isforeign = false; |
243 | 0 | } |
244 | 0 | cxt.relation = stmt->relation; |
245 | 0 | cxt.rel = NULL; |
246 | 0 | cxt.inhRelations = stmt->inhRelations; |
247 | 0 | cxt.isalter = false; |
248 | 0 | cxt.columns = NIL; |
249 | 0 | cxt.ckconstraints = NIL; |
250 | 0 | cxt.nnconstraints = NIL; |
251 | 0 | cxt.fkconstraints = NIL; |
252 | 0 | cxt.ixconstraints = NIL; |
253 | 0 | cxt.likeclauses = NIL; |
254 | 0 | cxt.blist = NIL; |
255 | 0 | cxt.alist = NIL; |
256 | 0 | cxt.pkey = NULL; |
257 | 0 | cxt.ispartitioned = stmt->partspec != NULL; |
258 | 0 | cxt.partbound = stmt->partbound; |
259 | 0 | cxt.ofType = (stmt->ofTypename != NULL); |
260 | |
|
261 | 0 | Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ |
262 | |
|
263 | 0 | if (stmt->ofTypename) |
264 | 0 | transformOfType(&cxt, stmt->ofTypename); |
265 | |
|
266 | 0 | if (stmt->partspec) |
267 | 0 | { |
268 | 0 | if (stmt->inhRelations && !stmt->partbound) |
269 | 0 | ereport(ERROR, |
270 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
271 | 0 | errmsg("cannot create partitioned table as inheritance child"))); |
272 | 0 | } |
273 | | |
274 | | /* |
275 | | * Run through each primary element in the table creation clause. Separate |
276 | | * column defs from constraints, and do preliminary analysis. |
277 | | */ |
278 | 0 | foreach(elements, stmt->tableElts) |
279 | 0 | { |
280 | 0 | Node *element = lfirst(elements); |
281 | |
|
282 | 0 | switch (nodeTag(element)) |
283 | 0 | { |
284 | 0 | case T_ColumnDef: |
285 | 0 | transformColumnDefinition(&cxt, (ColumnDef *) element); |
286 | 0 | break; |
287 | | |
288 | 0 | case T_Constraint: |
289 | 0 | transformTableConstraint(&cxt, (Constraint *) element); |
290 | 0 | break; |
291 | | |
292 | 0 | case T_TableLikeClause: |
293 | 0 | transformTableLikeClause(&cxt, (TableLikeClause *) element); |
294 | 0 | break; |
295 | | |
296 | 0 | default: |
297 | 0 | elog(ERROR, "unrecognized node type: %d", |
298 | 0 | (int) nodeTag(element)); |
299 | 0 | break; |
300 | 0 | } |
301 | 0 | } |
302 | | |
303 | | /* |
304 | | * Transfer anything we already have in cxt.alist into save_alist, to keep |
305 | | * it separate from the output of transformIndexConstraints. (This may |
306 | | * not be necessary anymore, but we'll keep doing it to preserve the |
307 | | * historical order of execution of the alist commands.) |
308 | | */ |
309 | 0 | save_alist = cxt.alist; |
310 | 0 | cxt.alist = NIL; |
311 | |
|
312 | 0 | Assert(stmt->constraints == NIL); |
313 | | |
314 | | /* |
315 | | * Before processing index constraints, which could include a primary key, |
316 | | * we must scan all not-null constraints to propagate the is_not_null flag |
317 | | * to each corresponding ColumnDef. This is necessary because table-level |
318 | | * not-null constraints have not been marked in each ColumnDef, and the PK |
319 | | * processing code needs to know whether one constraint has already been |
320 | | * declared in order not to declare a redundant one. |
321 | | */ |
322 | 0 | foreach_node(Constraint, nn, cxt.nnconstraints) |
323 | 0 | { |
324 | 0 | char *colname = strVal(linitial(nn->keys)); |
325 | |
|
326 | 0 | foreach_node(ColumnDef, cd, cxt.columns) |
327 | 0 | { |
328 | | /* not our column? */ |
329 | 0 | if (strcmp(cd->colname, colname) != 0) |
330 | 0 | continue; |
331 | | /* Already marked not-null? Nothing to do */ |
332 | 0 | if (cd->is_not_null) |
333 | 0 | break; |
334 | | /* Bingo, we're done for this constraint */ |
335 | 0 | cd->is_not_null = true; |
336 | 0 | break; |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | | /* |
341 | | * Postprocess constraints that give rise to index definitions. |
342 | | */ |
343 | 0 | transformIndexConstraints(&cxt); |
344 | | |
345 | | /* |
346 | | * Re-consideration of LIKE clauses should happen after creation of |
347 | | * indexes, but before creation of foreign keys. This order is critical |
348 | | * because a LIKE clause may attempt to create a primary key. If there's |
349 | | * also a pkey in the main CREATE TABLE list, creation of that will not |
350 | | * check for a duplicate at runtime (since index_check_primary_key() |
351 | | * expects that we rejected dups here). Creation of the LIKE-generated |
352 | | * pkey behaves like ALTER TABLE ADD, so it will check, but obviously that |
353 | | * only works if it happens second. On the other hand, we want to make |
354 | | * pkeys before foreign key constraints, in case the user tries to make a |
355 | | * self-referential FK. |
356 | | */ |
357 | 0 | cxt.alist = list_concat(cxt.alist, cxt.likeclauses); |
358 | | |
359 | | /* |
360 | | * Postprocess foreign-key constraints. |
361 | | */ |
362 | 0 | transformFKConstraints(&cxt, true, false); |
363 | | |
364 | | /* |
365 | | * Postprocess check constraints. |
366 | | * |
367 | | * For regular tables all constraints can be marked valid immediately, |
368 | | * because the table is new therefore empty. Not so for foreign tables. |
369 | | */ |
370 | 0 | transformCheckConstraints(&cxt, !cxt.isforeign); |
371 | | |
372 | | /* |
373 | | * Output results. |
374 | | */ |
375 | 0 | stmt->tableElts = cxt.columns; |
376 | 0 | stmt->constraints = cxt.ckconstraints; |
377 | 0 | stmt->nnconstraints = cxt.nnconstraints; |
378 | |
|
379 | 0 | result = lappend(cxt.blist, stmt); |
380 | 0 | result = list_concat(result, cxt.alist); |
381 | 0 | result = list_concat(result, save_alist); |
382 | |
|
383 | 0 | return result; |
384 | 0 | } |
385 | | |
386 | | /* |
387 | | * generateSerialExtraStmts |
388 | | * Generate CREATE SEQUENCE and ALTER SEQUENCE ... OWNED BY statements |
389 | | * to create the sequence for a serial or identity column. |
390 | | * |
391 | | * This includes determining the name the sequence will have. The caller |
392 | | * can ask to get back the name components by passing non-null pointers |
393 | | * for snamespace_p and sname_p. |
394 | | */ |
395 | | static void |
396 | | generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, |
397 | | Oid seqtypid, List *seqoptions, |
398 | | bool for_identity, bool col_exists, |
399 | | char **snamespace_p, char **sname_p) |
400 | 0 | { |
401 | 0 | ListCell *option; |
402 | 0 | DefElem *nameEl = NULL; |
403 | 0 | DefElem *loggedEl = NULL; |
404 | 0 | Oid snamespaceid; |
405 | 0 | char *snamespace; |
406 | 0 | char *sname; |
407 | 0 | char seqpersistence; |
408 | 0 | CreateSeqStmt *seqstmt; |
409 | 0 | AlterSeqStmt *altseqstmt; |
410 | 0 | List *attnamelist; |
411 | | |
412 | | /* Make a copy of this as we may end up modifying it in the code below */ |
413 | 0 | seqoptions = list_copy(seqoptions); |
414 | | |
415 | | /* |
416 | | * Check for non-SQL-standard options (not supported within CREATE |
417 | | * SEQUENCE, because they'd be redundant), and remove them from the |
418 | | * seqoptions list if found. |
419 | | */ |
420 | 0 | foreach(option, seqoptions) |
421 | 0 | { |
422 | 0 | DefElem *defel = lfirst_node(DefElem, option); |
423 | |
|
424 | 0 | if (strcmp(defel->defname, "sequence_name") == 0) |
425 | 0 | { |
426 | 0 | if (nameEl) |
427 | 0 | errorConflictingDefElem(defel, cxt->pstate); |
428 | 0 | nameEl = defel; |
429 | 0 | seqoptions = foreach_delete_current(seqoptions, option); |
430 | 0 | } |
431 | 0 | else if (strcmp(defel->defname, "logged") == 0 || |
432 | 0 | strcmp(defel->defname, "unlogged") == 0) |
433 | 0 | { |
434 | 0 | if (loggedEl) |
435 | 0 | errorConflictingDefElem(defel, cxt->pstate); |
436 | 0 | loggedEl = defel; |
437 | 0 | seqoptions = foreach_delete_current(seqoptions, option); |
438 | 0 | } |
439 | 0 | } |
440 | | |
441 | | /* |
442 | | * Determine namespace and name to use for the sequence. |
443 | | */ |
444 | 0 | if (nameEl) |
445 | 0 | { |
446 | | /* Use specified name */ |
447 | 0 | RangeVar *rv = makeRangeVarFromNameList(castNode(List, nameEl->arg)); |
448 | |
|
449 | 0 | snamespace = rv->schemaname; |
450 | 0 | if (!snamespace) |
451 | 0 | { |
452 | | /* Given unqualified SEQUENCE NAME, select namespace */ |
453 | 0 | if (cxt->rel) |
454 | 0 | snamespaceid = RelationGetNamespace(cxt->rel); |
455 | 0 | else |
456 | 0 | snamespaceid = RangeVarGetCreationNamespace(cxt->relation); |
457 | 0 | snamespace = get_namespace_name(snamespaceid); |
458 | 0 | } |
459 | 0 | sname = rv->relname; |
460 | 0 | } |
461 | 0 | else |
462 | 0 | { |
463 | | /* |
464 | | * Generate a name. |
465 | | * |
466 | | * Although we use ChooseRelationName, it's not guaranteed that the |
467 | | * selected sequence name won't conflict; given sufficiently long |
468 | | * field names, two different serial columns in the same table could |
469 | | * be assigned the same sequence name, and we'd not notice since we |
470 | | * aren't creating the sequence quite yet. In practice this seems |
471 | | * quite unlikely to be a problem, especially since few people would |
472 | | * need two serial columns in one table. |
473 | | */ |
474 | 0 | if (cxt->rel) |
475 | 0 | snamespaceid = RelationGetNamespace(cxt->rel); |
476 | 0 | else |
477 | 0 | { |
478 | 0 | snamespaceid = RangeVarGetCreationNamespace(cxt->relation); |
479 | 0 | RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid); |
480 | 0 | } |
481 | 0 | snamespace = get_namespace_name(snamespaceid); |
482 | 0 | sname = ChooseRelationName(cxt->relation->relname, |
483 | 0 | column->colname, |
484 | 0 | "seq", |
485 | 0 | snamespaceid, |
486 | 0 | false); |
487 | 0 | } |
488 | |
|
489 | 0 | ereport(DEBUG1, |
490 | 0 | (errmsg_internal("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"", |
491 | 0 | cxt->stmtType, sname, |
492 | 0 | cxt->relation->relname, column->colname))); |
493 | | |
494 | | /* |
495 | | * Determine the persistence of the sequence. By default we copy the |
496 | | * persistence of the table, but if LOGGED or UNLOGGED was specified, use |
497 | | * that (as long as the table isn't TEMP). |
498 | | * |
499 | | * For CREATE TABLE, we get the persistence from cxt->relation, which |
500 | | * comes from the CreateStmt in progress. For ALTER TABLE, the parser |
501 | | * won't set cxt->relation->relpersistence, but we have cxt->rel as the |
502 | | * existing table, so we copy the persistence from there. |
503 | | */ |
504 | 0 | seqpersistence = cxt->rel ? cxt->rel->rd_rel->relpersistence : cxt->relation->relpersistence; |
505 | 0 | if (loggedEl) |
506 | 0 | { |
507 | 0 | if (seqpersistence == RELPERSISTENCE_TEMP) |
508 | 0 | ereport(ERROR, |
509 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
510 | 0 | errmsg("cannot set logged status of a temporary sequence"), |
511 | 0 | parser_errposition(cxt->pstate, loggedEl->location))); |
512 | 0 | else if (strcmp(loggedEl->defname, "logged") == 0) |
513 | 0 | seqpersistence = RELPERSISTENCE_PERMANENT; |
514 | 0 | else |
515 | 0 | seqpersistence = RELPERSISTENCE_UNLOGGED; |
516 | 0 | } |
517 | | |
518 | | /* |
519 | | * Build a CREATE SEQUENCE command to create the sequence object, and add |
520 | | * it to the list of things to be done before this CREATE/ALTER TABLE. |
521 | | */ |
522 | 0 | seqstmt = makeNode(CreateSeqStmt); |
523 | 0 | seqstmt->for_identity = for_identity; |
524 | 0 | seqstmt->sequence = makeRangeVar(snamespace, sname, -1); |
525 | 0 | seqstmt->sequence->relpersistence = seqpersistence; |
526 | 0 | seqstmt->options = seqoptions; |
527 | | |
528 | | /* |
529 | | * If a sequence data type was specified, add it to the options. Prepend |
530 | | * to the list rather than append; in case a user supplied their own AS |
531 | | * clause, the "redundant options" error will point to their occurrence, |
532 | | * not our synthetic one. |
533 | | */ |
534 | 0 | if (seqtypid) |
535 | 0 | seqstmt->options = lcons(makeDefElem("as", |
536 | 0 | (Node *) makeTypeNameFromOid(seqtypid, -1), |
537 | 0 | -1), |
538 | 0 | seqstmt->options); |
539 | | |
540 | | /* |
541 | | * If this is ALTER ADD COLUMN, make sure the sequence will be owned by |
542 | | * the table's owner. The current user might be someone else (perhaps a |
543 | | * superuser, or someone who's only a member of the owning role), but the |
544 | | * SEQUENCE OWNED BY mechanisms will bleat unless table and sequence have |
545 | | * exactly the same owning role. |
546 | | */ |
547 | 0 | if (cxt->rel) |
548 | 0 | seqstmt->ownerId = cxt->rel->rd_rel->relowner; |
549 | 0 | else |
550 | 0 | seqstmt->ownerId = InvalidOid; |
551 | |
|
552 | 0 | cxt->blist = lappend(cxt->blist, seqstmt); |
553 | | |
554 | | /* |
555 | | * Store the identity sequence name that we decided on. ALTER TABLE ... |
556 | | * ADD COLUMN ... IDENTITY needs this so that it can fill the new column |
557 | | * with values from the sequence, while the association of the sequence |
558 | | * with the table is not set until after the ALTER TABLE. |
559 | | */ |
560 | 0 | column->identitySequence = seqstmt->sequence; |
561 | | |
562 | | /* |
563 | | * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence as |
564 | | * owned by this column, and add it to the appropriate list of things to |
565 | | * be done along with this CREATE/ALTER TABLE. In a CREATE or ALTER ADD |
566 | | * COLUMN, it must be done after the statement because we don't know the |
567 | | * column's attnum yet. But if we do have the attnum (in AT_AddIdentity), |
568 | | * we can do the marking immediately, which improves some ALTER TABLE |
569 | | * behaviors. |
570 | | */ |
571 | 0 | altseqstmt = makeNode(AlterSeqStmt); |
572 | 0 | altseqstmt->sequence = makeRangeVar(snamespace, sname, -1); |
573 | 0 | attnamelist = list_make3(makeString(snamespace), |
574 | 0 | makeString(cxt->relation->relname), |
575 | 0 | makeString(column->colname)); |
576 | 0 | altseqstmt->options = list_make1(makeDefElem("owned_by", |
577 | 0 | (Node *) attnamelist, -1)); |
578 | 0 | altseqstmt->for_identity = for_identity; |
579 | |
|
580 | 0 | if (col_exists) |
581 | 0 | cxt->blist = lappend(cxt->blist, altseqstmt); |
582 | 0 | else |
583 | 0 | cxt->alist = lappend(cxt->alist, altseqstmt); |
584 | |
|
585 | 0 | if (snamespace_p) |
586 | 0 | *snamespace_p = snamespace; |
587 | 0 | if (sname_p) |
588 | 0 | *sname_p = sname; |
589 | 0 | } |
590 | | |
591 | | /* |
592 | | * transformColumnDefinition - |
593 | | * transform a single ColumnDef within CREATE TABLE |
594 | | * Also used in ALTER TABLE ADD COLUMN |
595 | | */ |
596 | | static void |
597 | | transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) |
598 | 0 | { |
599 | 0 | bool is_serial; |
600 | 0 | bool saw_nullable; |
601 | 0 | bool saw_default; |
602 | 0 | bool saw_identity; |
603 | 0 | bool saw_generated; |
604 | 0 | bool need_notnull = false; |
605 | 0 | bool disallow_noinherit_notnull = false; |
606 | 0 | Constraint *notnull_constraint = NULL; |
607 | |
|
608 | 0 | cxt->columns = lappend(cxt->columns, column); |
609 | | |
610 | | /* Check for SERIAL pseudo-types */ |
611 | 0 | is_serial = false; |
612 | 0 | if (column->typeName |
613 | 0 | && list_length(column->typeName->names) == 1 |
614 | 0 | && !column->typeName->pct_type) |
615 | 0 | { |
616 | 0 | char *typname = strVal(linitial(column->typeName->names)); |
617 | |
|
618 | 0 | if (strcmp(typname, "smallserial") == 0 || |
619 | 0 | strcmp(typname, "serial2") == 0) |
620 | 0 | { |
621 | 0 | is_serial = true; |
622 | 0 | column->typeName->names = NIL; |
623 | 0 | column->typeName->typeOid = INT2OID; |
624 | 0 | } |
625 | 0 | else if (strcmp(typname, "serial") == 0 || |
626 | 0 | strcmp(typname, "serial4") == 0) |
627 | 0 | { |
628 | 0 | is_serial = true; |
629 | 0 | column->typeName->names = NIL; |
630 | 0 | column->typeName->typeOid = INT4OID; |
631 | 0 | } |
632 | 0 | else if (strcmp(typname, "bigserial") == 0 || |
633 | 0 | strcmp(typname, "serial8") == 0) |
634 | 0 | { |
635 | 0 | is_serial = true; |
636 | 0 | column->typeName->names = NIL; |
637 | 0 | column->typeName->typeOid = INT8OID; |
638 | 0 | } |
639 | | |
640 | | /* |
641 | | * We have to reject "serial[]" explicitly, because once we've set |
642 | | * typeid, LookupTypeName won't notice arrayBounds. We don't need any |
643 | | * special coding for serial(typmod) though. |
644 | | */ |
645 | 0 | if (is_serial && column->typeName->arrayBounds != NIL) |
646 | 0 | ereport(ERROR, |
647 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
648 | 0 | errmsg("array of serial is not implemented"), |
649 | 0 | parser_errposition(cxt->pstate, |
650 | 0 | column->typeName->location))); |
651 | 0 | } |
652 | | |
653 | | /* Do necessary work on the column type declaration */ |
654 | 0 | if (column->typeName) |
655 | 0 | transformColumnType(cxt, column); |
656 | | |
657 | | /* Special actions for SERIAL pseudo-types */ |
658 | 0 | if (is_serial) |
659 | 0 | { |
660 | 0 | char *snamespace; |
661 | 0 | char *sname; |
662 | 0 | char *qstring; |
663 | 0 | A_Const *snamenode; |
664 | 0 | TypeCast *castnode; |
665 | 0 | FuncCall *funccallnode; |
666 | 0 | Constraint *constraint; |
667 | |
|
668 | 0 | generateSerialExtraStmts(cxt, column, |
669 | 0 | column->typeName->typeOid, NIL, |
670 | 0 | false, false, |
671 | 0 | &snamespace, &sname); |
672 | | |
673 | | /* |
674 | | * Create appropriate constraints for SERIAL. We do this in full, |
675 | | * rather than shortcutting, so that we will detect any conflicting |
676 | | * constraints the user wrote (like a different DEFAULT). |
677 | | * |
678 | | * Create an expression tree representing the function call |
679 | | * nextval('sequencename'). We cannot reduce the raw tree to cooked |
680 | | * form until after the sequence is created, but there's no need to do |
681 | | * so. |
682 | | */ |
683 | 0 | qstring = quote_qualified_identifier(snamespace, sname); |
684 | 0 | snamenode = makeNode(A_Const); |
685 | 0 | snamenode->val.node.type = T_String; |
686 | 0 | snamenode->val.sval.sval = qstring; |
687 | 0 | snamenode->location = -1; |
688 | 0 | castnode = makeNode(TypeCast); |
689 | 0 | castnode->typeName = SystemTypeName("regclass"); |
690 | 0 | castnode->arg = (Node *) snamenode; |
691 | 0 | castnode->location = -1; |
692 | 0 | funccallnode = makeFuncCall(SystemFuncName("nextval"), |
693 | 0 | list_make1(castnode), |
694 | 0 | COERCE_EXPLICIT_CALL, |
695 | 0 | -1); |
696 | 0 | constraint = makeNode(Constraint); |
697 | 0 | constraint->contype = CONSTR_DEFAULT; |
698 | 0 | constraint->location = -1; |
699 | 0 | constraint->raw_expr = (Node *) funccallnode; |
700 | 0 | constraint->cooked_expr = NULL; |
701 | 0 | column->constraints = lappend(column->constraints, constraint); |
702 | | |
703 | | /* have a not-null constraint added later */ |
704 | 0 | need_notnull = true; |
705 | 0 | disallow_noinherit_notnull = true; |
706 | 0 | } |
707 | | |
708 | | /* Process column constraints, if any... */ |
709 | 0 | transformConstraintAttrs(cxt->pstate, column->constraints); |
710 | | |
711 | | /* |
712 | | * First, scan the column's constraints to see if a not-null constraint |
713 | | * that we add must be prevented from being NO INHERIT. This should be |
714 | | * enforced only for PRIMARY KEY, not IDENTITY or SERIAL. However, if the |
715 | | * not-null constraint is specified as a table constraint rather than as a |
716 | | * column constraint, AddRelationNotNullConstraints would raise an error |
717 | | * if a NO INHERIT mismatch is found. To avoid inconsistently disallowing |
718 | | * it in the table constraint case but not the column constraint case, we |
719 | | * disallow it here as well. Maybe AddRelationNotNullConstraints can be |
720 | | * improved someday, so that it doesn't complain, and then we can remove |
721 | | * the restriction for SERIAL and IDENTITY here as well. |
722 | | */ |
723 | 0 | if (!disallow_noinherit_notnull) |
724 | 0 | { |
725 | 0 | foreach_node(Constraint, constraint, column->constraints) |
726 | 0 | { |
727 | 0 | switch (constraint->contype) |
728 | 0 | { |
729 | 0 | case CONSTR_IDENTITY: |
730 | 0 | case CONSTR_PRIMARY: |
731 | 0 | disallow_noinherit_notnull = true; |
732 | 0 | break; |
733 | 0 | default: |
734 | 0 | break; |
735 | 0 | } |
736 | 0 | } |
737 | 0 | } |
738 | | |
739 | | /* Now scan them again to do full processing */ |
740 | 0 | saw_nullable = false; |
741 | 0 | saw_default = false; |
742 | 0 | saw_identity = false; |
743 | 0 | saw_generated = false; |
744 | |
|
745 | 0 | foreach_node(Constraint, constraint, column->constraints) |
746 | 0 | { |
747 | 0 | switch (constraint->contype) |
748 | 0 | { |
749 | 0 | case CONSTR_NULL: |
750 | 0 | if ((saw_nullable && column->is_not_null) || need_notnull) |
751 | 0 | ereport(ERROR, |
752 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
753 | 0 | errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"", |
754 | 0 | column->colname, cxt->relation->relname), |
755 | 0 | parser_errposition(cxt->pstate, |
756 | 0 | constraint->location))); |
757 | 0 | column->is_not_null = false; |
758 | 0 | saw_nullable = true; |
759 | 0 | break; |
760 | | |
761 | 0 | case CONSTR_NOTNULL: |
762 | 0 | if (cxt->ispartitioned && constraint->is_no_inherit) |
763 | 0 | ereport(ERROR, |
764 | 0 | errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
765 | 0 | errmsg("not-null constraints on partitioned tables cannot be NO INHERIT")); |
766 | | |
767 | | /* Disallow conflicting [NOT] NULL markings */ |
768 | 0 | if (saw_nullable && !column->is_not_null) |
769 | 0 | ereport(ERROR, |
770 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
771 | 0 | errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"", |
772 | 0 | column->colname, cxt->relation->relname), |
773 | 0 | parser_errposition(cxt->pstate, |
774 | 0 | constraint->location))); |
775 | | |
776 | 0 | if (disallow_noinherit_notnull && constraint->is_no_inherit) |
777 | 0 | ereport(ERROR, |
778 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
779 | 0 | errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"", |
780 | 0 | column->colname)); |
781 | | |
782 | | /* |
783 | | * If this is the first time we see this column being marked |
784 | | * not-null, add the constraint entry and keep track of it. |
785 | | * Also, remove previous markings that we need one. |
786 | | * |
787 | | * If this is a redundant not-null specification, just check |
788 | | * that it doesn't conflict with what was specified earlier. |
789 | | * |
790 | | * Any conflicts with table constraints will be further |
791 | | * checked in AddRelationNotNullConstraints(). |
792 | | */ |
793 | 0 | if (!column->is_not_null) |
794 | 0 | { |
795 | 0 | column->is_not_null = true; |
796 | 0 | saw_nullable = true; |
797 | 0 | need_notnull = false; |
798 | |
|
799 | 0 | constraint->keys = list_make1(makeString(column->colname)); |
800 | 0 | notnull_constraint = constraint; |
801 | 0 | cxt->nnconstraints = lappend(cxt->nnconstraints, constraint); |
802 | 0 | } |
803 | 0 | else if (notnull_constraint) |
804 | 0 | { |
805 | 0 | if (constraint->conname && |
806 | 0 | notnull_constraint->conname && |
807 | 0 | strcmp(notnull_constraint->conname, constraint->conname) != 0) |
808 | 0 | ereport(ERROR, |
809 | 0 | errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
810 | 0 | errmsg("conflicting not-null constraint names \"%s\" and \"%s\"", |
811 | 0 | notnull_constraint->conname, constraint->conname)); |
812 | | |
813 | 0 | if (notnull_constraint->is_no_inherit != constraint->is_no_inherit) |
814 | 0 | ereport(ERROR, |
815 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
816 | 0 | errmsg("conflicting NO INHERIT declarations for not-null constraints on column \"%s\"", |
817 | 0 | column->colname)); |
818 | | |
819 | 0 | if (!notnull_constraint->conname && constraint->conname) |
820 | 0 | notnull_constraint->conname = constraint->conname; |
821 | 0 | } |
822 | | |
823 | 0 | break; |
824 | | |
825 | 0 | case CONSTR_DEFAULT: |
826 | 0 | if (saw_default) |
827 | 0 | ereport(ERROR, |
828 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
829 | 0 | errmsg("multiple default values specified for column \"%s\" of table \"%s\"", |
830 | 0 | column->colname, cxt->relation->relname), |
831 | 0 | parser_errposition(cxt->pstate, |
832 | 0 | constraint->location))); |
833 | 0 | column->raw_default = constraint->raw_expr; |
834 | 0 | Assert(constraint->cooked_expr == NULL); |
835 | 0 | saw_default = true; |
836 | 0 | break; |
837 | | |
838 | 0 | case CONSTR_IDENTITY: |
839 | 0 | { |
840 | 0 | Type ctype; |
841 | 0 | Oid typeOid; |
842 | |
|
843 | 0 | if (cxt->ofType) |
844 | 0 | ereport(ERROR, |
845 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
846 | 0 | errmsg("identity columns are not supported on typed tables"))); |
847 | 0 | if (cxt->partbound) |
848 | 0 | ereport(ERROR, |
849 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
850 | 0 | errmsg("identity columns are not supported on partitions"))); |
851 | | |
852 | 0 | ctype = typenameType(cxt->pstate, column->typeName, NULL); |
853 | 0 | typeOid = ((Form_pg_type) GETSTRUCT(ctype))->oid; |
854 | 0 | ReleaseSysCache(ctype); |
855 | |
|
856 | 0 | if (saw_identity) |
857 | 0 | ereport(ERROR, |
858 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
859 | 0 | errmsg("multiple identity specifications for column \"%s\" of table \"%s\"", |
860 | 0 | column->colname, cxt->relation->relname), |
861 | 0 | parser_errposition(cxt->pstate, |
862 | 0 | constraint->location))); |
863 | | |
864 | 0 | generateSerialExtraStmts(cxt, column, |
865 | 0 | typeOid, constraint->options, |
866 | 0 | true, false, |
867 | 0 | NULL, NULL); |
868 | |
|
869 | 0 | column->identity = constraint->generated_when; |
870 | 0 | saw_identity = true; |
871 | | |
872 | | /* |
873 | | * Identity columns are always NOT NULL, but we may have a |
874 | | * constraint already. |
875 | | */ |
876 | 0 | if (!saw_nullable) |
877 | 0 | need_notnull = true; |
878 | 0 | else if (!column->is_not_null) |
879 | 0 | ereport(ERROR, |
880 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
881 | 0 | errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"", |
882 | 0 | column->colname, cxt->relation->relname), |
883 | 0 | parser_errposition(cxt->pstate, |
884 | 0 | constraint->location))); |
885 | 0 | break; |
886 | 0 | } |
887 | | |
888 | 0 | case CONSTR_GENERATED: |
889 | 0 | if (cxt->ofType) |
890 | 0 | ereport(ERROR, |
891 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
892 | 0 | errmsg("generated columns are not supported on typed tables"))); |
893 | 0 | if (saw_generated) |
894 | 0 | ereport(ERROR, |
895 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
896 | 0 | errmsg("multiple generation clauses specified for column \"%s\" of table \"%s\"", |
897 | 0 | column->colname, cxt->relation->relname), |
898 | 0 | parser_errposition(cxt->pstate, |
899 | 0 | constraint->location))); |
900 | 0 | column->generated = constraint->generated_kind; |
901 | 0 | column->raw_default = constraint->raw_expr; |
902 | 0 | Assert(constraint->cooked_expr == NULL); |
903 | 0 | saw_generated = true; |
904 | 0 | break; |
905 | | |
906 | 0 | case CONSTR_CHECK: |
907 | 0 | cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); |
908 | 0 | break; |
909 | | |
910 | 0 | case CONSTR_PRIMARY: |
911 | 0 | if (saw_nullable && !column->is_not_null) |
912 | 0 | ereport(ERROR, |
913 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
914 | 0 | errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"", |
915 | 0 | column->colname, cxt->relation->relname), |
916 | 0 | parser_errposition(cxt->pstate, |
917 | 0 | constraint->location))); |
918 | 0 | need_notnull = true; |
919 | |
|
920 | 0 | if (cxt->isforeign) |
921 | 0 | ereport(ERROR, |
922 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
923 | 0 | errmsg("primary key constraints are not supported on foreign tables"), |
924 | 0 | parser_errposition(cxt->pstate, |
925 | 0 | constraint->location))); |
926 | 0 | pg_fallthrough; |
927 | |
|
928 | 0 | case CONSTR_UNIQUE: |
929 | 0 | if (cxt->isforeign) |
930 | 0 | ereport(ERROR, |
931 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
932 | 0 | errmsg("unique constraints are not supported on foreign tables"), |
933 | 0 | parser_errposition(cxt->pstate, |
934 | 0 | constraint->location))); |
935 | 0 | if (constraint->keys == NIL) |
936 | 0 | constraint->keys = list_make1(makeString(column->colname)); |
937 | 0 | cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); |
938 | 0 | break; |
939 | | |
940 | 0 | case CONSTR_EXCLUSION: |
941 | | /* grammar does not allow EXCLUDE as a column constraint */ |
942 | 0 | elog(ERROR, "column exclusion constraints are not supported"); |
943 | 0 | break; |
944 | | |
945 | 0 | case CONSTR_FOREIGN: |
946 | 0 | if (cxt->isforeign) |
947 | 0 | ereport(ERROR, |
948 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
949 | 0 | errmsg("foreign key constraints are not supported on foreign tables"), |
950 | 0 | parser_errposition(cxt->pstate, |
951 | 0 | constraint->location))); |
952 | | |
953 | | /* |
954 | | * Fill in the current attribute's name and throw it into the |
955 | | * list of FK constraints to be processed later. |
956 | | */ |
957 | 0 | constraint->fk_attrs = list_make1(makeString(column->colname)); |
958 | 0 | cxt->fkconstraints = lappend(cxt->fkconstraints, constraint); |
959 | 0 | break; |
960 | | |
961 | 0 | case CONSTR_ATTR_DEFERRABLE: |
962 | 0 | case CONSTR_ATTR_NOT_DEFERRABLE: |
963 | 0 | case CONSTR_ATTR_DEFERRED: |
964 | 0 | case CONSTR_ATTR_IMMEDIATE: |
965 | 0 | case CONSTR_ATTR_ENFORCED: |
966 | 0 | case CONSTR_ATTR_NOT_ENFORCED: |
967 | | /* transformConstraintAttrs took care of these */ |
968 | 0 | break; |
969 | | |
970 | 0 | default: |
971 | 0 | elog(ERROR, "unrecognized constraint type: %d", |
972 | 0 | constraint->contype); |
973 | 0 | break; |
974 | 0 | } |
975 | | |
976 | 0 | if (saw_default && saw_identity) |
977 | 0 | ereport(ERROR, |
978 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
979 | 0 | errmsg("both default and identity specified for column \"%s\" of table \"%s\"", |
980 | 0 | column->colname, cxt->relation->relname), |
981 | 0 | parser_errposition(cxt->pstate, |
982 | 0 | constraint->location))); |
983 | | |
984 | 0 | if (saw_default && saw_generated) |
985 | 0 | ereport(ERROR, |
986 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
987 | 0 | errmsg("both default and generation expression specified for column \"%s\" of table \"%s\"", |
988 | 0 | column->colname, cxt->relation->relname), |
989 | 0 | parser_errposition(cxt->pstate, |
990 | 0 | constraint->location))); |
991 | | |
992 | 0 | if (saw_identity && saw_generated) |
993 | 0 | ereport(ERROR, |
994 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
995 | 0 | errmsg("both identity and generation expression specified for column \"%s\" of table \"%s\"", |
996 | 0 | column->colname, cxt->relation->relname), |
997 | 0 | parser_errposition(cxt->pstate, |
998 | 0 | constraint->location))); |
999 | 0 | } |
1000 | | |
1001 | | /* |
1002 | | * If we need a not-null constraint for PRIMARY KEY, SERIAL or IDENTITY, |
1003 | | * and one was not explicitly specified, add one now. |
1004 | | */ |
1005 | 0 | if (need_notnull && !(saw_nullable && column->is_not_null)) |
1006 | 0 | { |
1007 | 0 | column->is_not_null = true; |
1008 | 0 | notnull_constraint = makeNotNullConstraint(makeString(column->colname)); |
1009 | 0 | cxt->nnconstraints = lappend(cxt->nnconstraints, notnull_constraint); |
1010 | 0 | } |
1011 | | |
1012 | | /* |
1013 | | * If needed, generate ALTER FOREIGN TABLE ALTER COLUMN statement to add |
1014 | | * per-column foreign data wrapper options to this column after creation. |
1015 | | */ |
1016 | 0 | if (column->fdwoptions != NIL) |
1017 | 0 | { |
1018 | 0 | AlterTableStmt *stmt; |
1019 | 0 | AlterTableCmd *cmd; |
1020 | |
|
1021 | 0 | cmd = makeNode(AlterTableCmd); |
1022 | 0 | cmd->subtype = AT_AlterColumnGenericOptions; |
1023 | 0 | cmd->name = column->colname; |
1024 | 0 | cmd->def = (Node *) column->fdwoptions; |
1025 | 0 | cmd->behavior = DROP_RESTRICT; |
1026 | 0 | cmd->missing_ok = false; |
1027 | |
|
1028 | 0 | stmt = makeNode(AlterTableStmt); |
1029 | 0 | stmt->relation = cxt->relation; |
1030 | 0 | stmt->cmds = NIL; |
1031 | 0 | stmt->objtype = OBJECT_FOREIGN_TABLE; |
1032 | 0 | stmt->cmds = lappend(stmt->cmds, cmd); |
1033 | |
|
1034 | 0 | cxt->alist = lappend(cxt->alist, stmt); |
1035 | 0 | } |
1036 | 0 | } |
1037 | | |
1038 | | /* |
1039 | | * transformTableConstraint |
1040 | | * transform a Constraint node within CREATE TABLE or ALTER TABLE |
1041 | | */ |
1042 | | static void |
1043 | | transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) |
1044 | 0 | { |
1045 | 0 | switch (constraint->contype) |
1046 | 0 | { |
1047 | 0 | case CONSTR_PRIMARY: |
1048 | 0 | if (cxt->isforeign) |
1049 | 0 | ereport(ERROR, |
1050 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1051 | 0 | errmsg("primary key constraints are not supported on foreign tables"), |
1052 | 0 | parser_errposition(cxt->pstate, |
1053 | 0 | constraint->location))); |
1054 | 0 | cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); |
1055 | 0 | break; |
1056 | | |
1057 | 0 | case CONSTR_UNIQUE: |
1058 | 0 | if (cxt->isforeign) |
1059 | 0 | ereport(ERROR, |
1060 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1061 | 0 | errmsg("unique constraints are not supported on foreign tables"), |
1062 | 0 | parser_errposition(cxt->pstate, |
1063 | 0 | constraint->location))); |
1064 | 0 | cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); |
1065 | 0 | break; |
1066 | | |
1067 | 0 | case CONSTR_EXCLUSION: |
1068 | 0 | if (cxt->isforeign) |
1069 | 0 | ereport(ERROR, |
1070 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1071 | 0 | errmsg("exclusion constraints are not supported on foreign tables"), |
1072 | 0 | parser_errposition(cxt->pstate, |
1073 | 0 | constraint->location))); |
1074 | 0 | cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); |
1075 | 0 | break; |
1076 | | |
1077 | 0 | case CONSTR_CHECK: |
1078 | 0 | cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); |
1079 | 0 | break; |
1080 | | |
1081 | 0 | case CONSTR_NOTNULL: |
1082 | 0 | if (cxt->ispartitioned && constraint->is_no_inherit) |
1083 | 0 | ereport(ERROR, |
1084 | 0 | errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1085 | 0 | errmsg("not-null constraints on partitioned tables cannot be NO INHERIT")); |
1086 | | |
1087 | 0 | cxt->nnconstraints = lappend(cxt->nnconstraints, constraint); |
1088 | 0 | break; |
1089 | | |
1090 | 0 | case CONSTR_FOREIGN: |
1091 | 0 | if (cxt->isforeign) |
1092 | 0 | ereport(ERROR, |
1093 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1094 | 0 | errmsg("foreign key constraints are not supported on foreign tables"), |
1095 | 0 | parser_errposition(cxt->pstate, |
1096 | 0 | constraint->location))); |
1097 | 0 | cxt->fkconstraints = lappend(cxt->fkconstraints, constraint); |
1098 | 0 | break; |
1099 | | |
1100 | 0 | case CONSTR_NULL: |
1101 | 0 | case CONSTR_DEFAULT: |
1102 | 0 | case CONSTR_ATTR_DEFERRABLE: |
1103 | 0 | case CONSTR_ATTR_NOT_DEFERRABLE: |
1104 | 0 | case CONSTR_ATTR_DEFERRED: |
1105 | 0 | case CONSTR_ATTR_IMMEDIATE: |
1106 | 0 | case CONSTR_ATTR_ENFORCED: |
1107 | 0 | case CONSTR_ATTR_NOT_ENFORCED: |
1108 | 0 | elog(ERROR, "invalid context for constraint type %d", |
1109 | 0 | constraint->contype); |
1110 | 0 | break; |
1111 | | |
1112 | 0 | default: |
1113 | 0 | elog(ERROR, "unrecognized constraint type: %d", |
1114 | 0 | constraint->contype); |
1115 | 0 | break; |
1116 | 0 | } |
1117 | 0 | } |
1118 | | |
1119 | | /* |
1120 | | * transformTableLikeClause |
1121 | | * |
1122 | | * Change the LIKE <srctable> portion of a CREATE TABLE statement into |
1123 | | * column definitions that recreate the user defined column portions of |
1124 | | * <srctable>. Also, if there are any LIKE options that we can't fully |
1125 | | * process at this point, add the TableLikeClause to cxt->likeclauses, which |
1126 | | * will cause utility.c to call expandTableLikeClause() after the new |
1127 | | * table has been created. |
1128 | | * |
1129 | | * Some options are ignored. For example, as foreign tables have no storage, |
1130 | | * these INCLUDING options have no effect: STORAGE, COMPRESSION, IDENTITY |
1131 | | * and INDEXES. Similarly, INCLUDING INDEXES is ignored from a view. |
1132 | | */ |
1133 | | static void |
1134 | | transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause) |
1135 | 0 | { |
1136 | 0 | AttrNumber parent_attno; |
1137 | 0 | Relation relation; |
1138 | 0 | TupleDesc tupleDesc; |
1139 | 0 | AclResult aclresult; |
1140 | 0 | char *comment; |
1141 | 0 | ParseCallbackState pcbstate; |
1142 | |
|
1143 | 0 | setup_parser_errposition_callback(&pcbstate, cxt->pstate, |
1144 | 0 | table_like_clause->relation->location); |
1145 | | |
1146 | | /* Open the relation referenced by the LIKE clause */ |
1147 | 0 | relation = relation_openrv(table_like_clause->relation, AccessShareLock); |
1148 | |
|
1149 | 0 | if (relation->rd_rel->relkind != RELKIND_RELATION && |
1150 | 0 | relation->rd_rel->relkind != RELKIND_VIEW && |
1151 | 0 | relation->rd_rel->relkind != RELKIND_MATVIEW && |
1152 | 0 | relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE && |
1153 | 0 | relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE && |
1154 | 0 | relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) |
1155 | 0 | ereport(ERROR, |
1156 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1157 | 0 | errmsg("relation \"%s\" is invalid in LIKE clause", |
1158 | 0 | RelationGetRelationName(relation)), |
1159 | 0 | errdetail_relkind_not_supported(relation->rd_rel->relkind))); |
1160 | | |
1161 | 0 | cancel_parser_errposition_callback(&pcbstate); |
1162 | | |
1163 | | /* |
1164 | | * Check for privileges |
1165 | | */ |
1166 | 0 | if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) |
1167 | 0 | { |
1168 | 0 | aclresult = object_aclcheck(TypeRelationId, relation->rd_rel->reltype, GetUserId(), |
1169 | 0 | ACL_USAGE); |
1170 | 0 | if (aclresult != ACLCHECK_OK) |
1171 | 0 | aclcheck_error(aclresult, OBJECT_TYPE, |
1172 | 0 | RelationGetRelationName(relation)); |
1173 | 0 | } |
1174 | 0 | else |
1175 | 0 | { |
1176 | 0 | aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(), |
1177 | 0 | ACL_SELECT); |
1178 | 0 | if (aclresult != ACLCHECK_OK) |
1179 | 0 | aclcheck_error(aclresult, get_relkind_objtype(relation->rd_rel->relkind), |
1180 | 0 | RelationGetRelationName(relation)); |
1181 | 0 | } |
1182 | |
|
1183 | 0 | tupleDesc = RelationGetDescr(relation); |
1184 | | |
1185 | | /* |
1186 | | * Insert the copied attributes into the cxt for the new table definition. |
1187 | | * We must do this now so that they appear in the table in the relative |
1188 | | * position where the LIKE clause is, as required by SQL99. |
1189 | | */ |
1190 | 0 | for (parent_attno = 1; parent_attno <= tupleDesc->natts; |
1191 | 0 | parent_attno++) |
1192 | 0 | { |
1193 | 0 | Form_pg_attribute attribute = TupleDescAttr(tupleDesc, |
1194 | 0 | parent_attno - 1); |
1195 | 0 | ColumnDef *def; |
1196 | | |
1197 | | /* |
1198 | | * Ignore dropped columns in the parent. |
1199 | | */ |
1200 | 0 | if (attribute->attisdropped) |
1201 | 0 | continue; |
1202 | | |
1203 | | /* |
1204 | | * Create a new column definition |
1205 | | */ |
1206 | 0 | def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid, |
1207 | 0 | attribute->atttypmod, attribute->attcollation); |
1208 | | |
1209 | | /* |
1210 | | * Add to column list |
1211 | | */ |
1212 | 0 | cxt->columns = lappend(cxt->columns, def); |
1213 | | |
1214 | | /* |
1215 | | * Although we don't transfer the column's default/generation |
1216 | | * expression now, we need to mark it GENERATED if appropriate. |
1217 | | */ |
1218 | 0 | if (attribute->atthasdef && attribute->attgenerated && |
1219 | 0 | (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED)) |
1220 | 0 | def->generated = attribute->attgenerated; |
1221 | | |
1222 | | /* |
1223 | | * Copy identity if requested |
1224 | | */ |
1225 | 0 | if (attribute->attidentity && |
1226 | 0 | (table_like_clause->options & CREATE_TABLE_LIKE_IDENTITY) && |
1227 | 0 | !cxt->isforeign) |
1228 | 0 | { |
1229 | 0 | Oid seq_relid; |
1230 | 0 | List *seq_options; |
1231 | | |
1232 | | /* |
1233 | | * find sequence owned by old column; extract sequence parameters; |
1234 | | * build new create sequence command |
1235 | | */ |
1236 | 0 | seq_relid = getIdentitySequence(relation, attribute->attnum, false); |
1237 | 0 | seq_options = sequence_options(seq_relid); |
1238 | 0 | generateSerialExtraStmts(cxt, def, |
1239 | 0 | InvalidOid, seq_options, |
1240 | 0 | true, false, |
1241 | 0 | NULL, NULL); |
1242 | 0 | def->identity = attribute->attidentity; |
1243 | 0 | } |
1244 | | |
1245 | | /* Likewise, copy storage if requested */ |
1246 | 0 | if ((table_like_clause->options & CREATE_TABLE_LIKE_STORAGE) && |
1247 | 0 | !cxt->isforeign) |
1248 | 0 | def->storage = attribute->attstorage; |
1249 | 0 | else |
1250 | 0 | def->storage = 0; |
1251 | | |
1252 | | /* Likewise, copy compression if requested */ |
1253 | 0 | if ((table_like_clause->options & CREATE_TABLE_LIKE_COMPRESSION) != 0 && |
1254 | 0 | CompressionMethodIsValid(attribute->attcompression) && |
1255 | 0 | !cxt->isforeign) |
1256 | 0 | def->compression = |
1257 | 0 | pstrdup(GetCompressionMethodName(attribute->attcompression)); |
1258 | 0 | else |
1259 | 0 | def->compression = NULL; |
1260 | | |
1261 | | /* Likewise, copy comment if requested */ |
1262 | 0 | if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) && |
1263 | 0 | (comment = GetComment(attribute->attrelid, |
1264 | 0 | RelationRelationId, |
1265 | 0 | attribute->attnum)) != NULL) |
1266 | 0 | { |
1267 | 0 | CommentStmt *stmt = makeNode(CommentStmt); |
1268 | |
|
1269 | 0 | stmt->objtype = OBJECT_COLUMN; |
1270 | 0 | stmt->object = (Node *) list_make3(makeString(cxt->relation->schemaname), |
1271 | 0 | makeString(cxt->relation->relname), |
1272 | 0 | makeString(def->colname)); |
1273 | 0 | stmt->comment = comment; |
1274 | |
|
1275 | 0 | cxt->alist = lappend(cxt->alist, stmt); |
1276 | 0 | } |
1277 | 0 | } |
1278 | | |
1279 | | /* |
1280 | | * Reproduce not-null constraints, if any, by copying them. We do this |
1281 | | * regardless of options given. |
1282 | | */ |
1283 | 0 | if (tupleDesc->constr && tupleDesc->constr->has_not_null) |
1284 | 0 | { |
1285 | 0 | List *lst; |
1286 | |
|
1287 | 0 | lst = RelationGetNotNullConstraints(RelationGetRelid(relation), false, |
1288 | 0 | true); |
1289 | 0 | cxt->nnconstraints = list_concat(cxt->nnconstraints, lst); |
1290 | | |
1291 | | /* Copy comments on not-null constraints */ |
1292 | 0 | if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) |
1293 | 0 | { |
1294 | 0 | foreach_node(Constraint, nnconstr, lst) |
1295 | 0 | { |
1296 | 0 | if ((comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation), |
1297 | 0 | nnconstr->conname, false), |
1298 | 0 | ConstraintRelationId, |
1299 | 0 | 0)) != NULL) |
1300 | 0 | { |
1301 | 0 | CommentStmt *stmt = makeNode(CommentStmt); |
1302 | |
|
1303 | 0 | stmt->objtype = OBJECT_TABCONSTRAINT; |
1304 | 0 | stmt->object = (Node *) list_make3(makeString(cxt->relation->schemaname), |
1305 | 0 | makeString(cxt->relation->relname), |
1306 | 0 | makeString(nnconstr->conname)); |
1307 | 0 | stmt->comment = comment; |
1308 | 0 | cxt->alist = lappend(cxt->alist, stmt); |
1309 | 0 | } |
1310 | 0 | } |
1311 | 0 | } |
1312 | 0 | } |
1313 | | |
1314 | | /* |
1315 | | * We cannot yet deal with defaults, CHECK constraints, indexes, or |
1316 | | * statistics, since we don't yet know what column numbers the copied |
1317 | | * columns will have in the finished table. If any of those options are |
1318 | | * specified, add the LIKE clause to cxt->likeclauses so that |
1319 | | * expandTableLikeClause will be called after we do know that. |
1320 | | * |
1321 | | * In order for this to work, we remember the relation OID so that |
1322 | | * expandTableLikeClause is certain to open the same table. |
1323 | | */ |
1324 | 0 | if (table_like_clause->options & |
1325 | 0 | (CREATE_TABLE_LIKE_DEFAULTS | |
1326 | 0 | CREATE_TABLE_LIKE_GENERATED | |
1327 | 0 | CREATE_TABLE_LIKE_CONSTRAINTS | |
1328 | 0 | CREATE_TABLE_LIKE_INDEXES | |
1329 | 0 | CREATE_TABLE_LIKE_STATISTICS)) |
1330 | 0 | { |
1331 | 0 | table_like_clause->relationOid = RelationGetRelid(relation); |
1332 | 0 | cxt->likeclauses = lappend(cxt->likeclauses, table_like_clause); |
1333 | 0 | } |
1334 | | |
1335 | | /* |
1336 | | * Close the parent rel, but keep our AccessShareLock on it until xact |
1337 | | * commit. That will prevent someone else from deleting or ALTERing the |
1338 | | * parent before we can run expandTableLikeClause. |
1339 | | */ |
1340 | 0 | table_close(relation, NoLock); |
1341 | 0 | } |
1342 | | |
1343 | | /* |
1344 | | * expandTableLikeClause |
1345 | | * |
1346 | | * Process LIKE options that require knowing the final column numbers |
1347 | | * assigned to the new table's columns. This executes after we have |
1348 | | * run DefineRelation for the new table. It returns a list of utility |
1349 | | * commands that should be run to generate indexes etc. |
1350 | | */ |
1351 | | List * |
1352 | | expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) |
1353 | 0 | { |
1354 | 0 | List *result = NIL; |
1355 | 0 | List *atsubcmds = NIL; |
1356 | 0 | AttrNumber parent_attno; |
1357 | 0 | Relation relation; |
1358 | 0 | Relation childrel; |
1359 | 0 | TupleDesc tupleDesc; |
1360 | 0 | TupleConstr *constr; |
1361 | 0 | AttrMap *attmap; |
1362 | 0 | char *comment; |
1363 | | |
1364 | | /* |
1365 | | * Open the relation referenced by the LIKE clause. We should still have |
1366 | | * the table lock obtained by transformTableLikeClause (and this'll throw |
1367 | | * an assertion failure if not). Hence, no need to recheck privileges |
1368 | | * etc. We must open the rel by OID not name, to be sure we get the same |
1369 | | * table. |
1370 | | */ |
1371 | 0 | if (!OidIsValid(table_like_clause->relationOid)) |
1372 | 0 | elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause"); |
1373 | | |
1374 | 0 | relation = relation_open(table_like_clause->relationOid, NoLock); |
1375 | |
|
1376 | 0 | tupleDesc = RelationGetDescr(relation); |
1377 | 0 | constr = tupleDesc->constr; |
1378 | | |
1379 | | /* |
1380 | | * Open the newly-created child relation; we have lock on that too. |
1381 | | */ |
1382 | 0 | childrel = relation_openrv(heapRel, NoLock); |
1383 | | |
1384 | | /* |
1385 | | * Construct a map from the LIKE relation's attnos to the child rel's. |
1386 | | * This re-checks type match etc, although it shouldn't be possible to |
1387 | | * have a failure since both tables are locked. |
1388 | | */ |
1389 | 0 | attmap = build_attrmap_by_name(RelationGetDescr(childrel), |
1390 | 0 | tupleDesc, |
1391 | 0 | false); |
1392 | | |
1393 | | /* |
1394 | | * Process defaults, if required. |
1395 | | */ |
1396 | 0 | if ((table_like_clause->options & |
1397 | 0 | (CREATE_TABLE_LIKE_DEFAULTS | CREATE_TABLE_LIKE_GENERATED)) && |
1398 | 0 | constr != NULL) |
1399 | 0 | { |
1400 | 0 | for (parent_attno = 1; parent_attno <= tupleDesc->natts; |
1401 | 0 | parent_attno++) |
1402 | 0 | { |
1403 | 0 | Form_pg_attribute attribute = TupleDescAttr(tupleDesc, |
1404 | 0 | parent_attno - 1); |
1405 | | |
1406 | | /* |
1407 | | * Ignore dropped columns in the parent. |
1408 | | */ |
1409 | 0 | if (attribute->attisdropped) |
1410 | 0 | continue; |
1411 | | |
1412 | | /* |
1413 | | * Copy default, if present and it should be copied. We have |
1414 | | * separate options for plain default expressions and GENERATED |
1415 | | * defaults. |
1416 | | */ |
1417 | 0 | if (attribute->atthasdef && |
1418 | 0 | (attribute->attgenerated ? |
1419 | 0 | (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) : |
1420 | 0 | (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS))) |
1421 | 0 | { |
1422 | 0 | Node *this_default; |
1423 | 0 | AlterTableCmd *atsubcmd; |
1424 | 0 | bool found_whole_row; |
1425 | |
|
1426 | 0 | this_default = TupleDescGetDefault(tupleDesc, parent_attno); |
1427 | 0 | if (this_default == NULL) |
1428 | 0 | elog(ERROR, "default expression not found for attribute %d of relation \"%s\"", |
1429 | 0 | parent_attno, RelationGetRelationName(relation)); |
1430 | | |
1431 | 0 | atsubcmd = makeNode(AlterTableCmd); |
1432 | 0 | atsubcmd->subtype = AT_CookedColumnDefault; |
1433 | 0 | atsubcmd->num = attmap->attnums[parent_attno - 1]; |
1434 | 0 | atsubcmd->def = map_variable_attnos(this_default, |
1435 | 0 | 1, 0, |
1436 | 0 | attmap, |
1437 | 0 | InvalidOid, |
1438 | 0 | &found_whole_row); |
1439 | | |
1440 | | /* |
1441 | | * Prevent this for the same reason as for constraints below. |
1442 | | * Note that defaults cannot contain any vars, so it's OK that |
1443 | | * the error message refers to generated columns. |
1444 | | */ |
1445 | 0 | if (found_whole_row) |
1446 | 0 | ereport(ERROR, |
1447 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1448 | 0 | errmsg("cannot convert whole-row table reference"), |
1449 | 0 | errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".", |
1450 | 0 | NameStr(attribute->attname), |
1451 | 0 | RelationGetRelationName(relation)))); |
1452 | | |
1453 | 0 | atsubcmds = lappend(atsubcmds, atsubcmd); |
1454 | 0 | } |
1455 | 0 | } |
1456 | 0 | } |
1457 | | |
1458 | | /* |
1459 | | * Copy CHECK constraints if requested, being careful to adjust attribute |
1460 | | * numbers so they match the child. |
1461 | | */ |
1462 | 0 | if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) && |
1463 | 0 | constr != NULL) |
1464 | 0 | { |
1465 | 0 | int ccnum; |
1466 | |
|
1467 | 0 | for (ccnum = 0; ccnum < constr->num_check; ccnum++) |
1468 | 0 | { |
1469 | 0 | char *ccname = constr->check[ccnum].ccname; |
1470 | 0 | char *ccbin = constr->check[ccnum].ccbin; |
1471 | 0 | bool ccenforced = constr->check[ccnum].ccenforced; |
1472 | 0 | bool ccnoinherit = constr->check[ccnum].ccnoinherit; |
1473 | 0 | Node *ccbin_node; |
1474 | 0 | bool found_whole_row; |
1475 | 0 | Constraint *n; |
1476 | 0 | AlterTableCmd *atsubcmd; |
1477 | |
|
1478 | 0 | ccbin_node = map_variable_attnos(stringToNode(ccbin), |
1479 | 0 | 1, 0, |
1480 | 0 | attmap, |
1481 | 0 | InvalidOid, &found_whole_row); |
1482 | | |
1483 | | /* |
1484 | | * We reject whole-row variables because the whole point of LIKE |
1485 | | * is that the new table's rowtype might later diverge from the |
1486 | | * parent's. So, while translation might be possible right now, |
1487 | | * it wouldn't be possible to guarantee it would work in future. |
1488 | | */ |
1489 | 0 | if (found_whole_row) |
1490 | 0 | ereport(ERROR, |
1491 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1492 | 0 | errmsg("cannot convert whole-row table reference"), |
1493 | 0 | errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".", |
1494 | 0 | ccname, |
1495 | 0 | RelationGetRelationName(relation)))); |
1496 | | |
1497 | | /* |
1498 | | * Copying a CHECK constraint adds new references. Since the |
1499 | | * constraint arrives pre-cooked, it bypasses the checks in |
1500 | | * AddRelationNewConstraints(), so we must check for USAGE on |
1501 | | * types here. |
1502 | | */ |
1503 | 0 | CheckUsageOnTypesInSingleRelExpr(stringToNode(ccbin), |
1504 | 0 | RelationGetRelid(relation), |
1505 | 0 | GetUserId()); |
1506 | |
|
1507 | 0 | n = makeNode(Constraint); |
1508 | 0 | n->contype = CONSTR_CHECK; |
1509 | 0 | n->conname = pstrdup(ccname); |
1510 | 0 | n->location = -1; |
1511 | 0 | n->is_enforced = ccenforced; |
1512 | 0 | n->initially_valid = ccenforced; /* sic */ |
1513 | 0 | n->is_no_inherit = ccnoinherit; |
1514 | 0 | n->raw_expr = NULL; |
1515 | 0 | n->cooked_expr = nodeToString(ccbin_node); |
1516 | | |
1517 | | /* We can skip validation, since the new table should be empty. */ |
1518 | 0 | n->skip_validation = true; |
1519 | |
|
1520 | 0 | atsubcmd = makeNode(AlterTableCmd); |
1521 | 0 | atsubcmd->subtype = AT_AddConstraint; |
1522 | 0 | atsubcmd->def = (Node *) n; |
1523 | 0 | atsubcmds = lappend(atsubcmds, atsubcmd); |
1524 | | |
1525 | | /* Copy comment on constraint */ |
1526 | 0 | if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) && |
1527 | 0 | (comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation), |
1528 | 0 | n->conname, false), |
1529 | 0 | ConstraintRelationId, |
1530 | 0 | 0)) != NULL) |
1531 | 0 | { |
1532 | 0 | CommentStmt *stmt = makeNode(CommentStmt); |
1533 | |
|
1534 | 0 | stmt->objtype = OBJECT_TABCONSTRAINT; |
1535 | 0 | stmt->object = (Node *) list_make3(makeString(heapRel->schemaname), |
1536 | 0 | makeString(heapRel->relname), |
1537 | 0 | makeString(n->conname)); |
1538 | 0 | stmt->comment = comment; |
1539 | |
|
1540 | 0 | result = lappend(result, stmt); |
1541 | 0 | } |
1542 | 0 | } |
1543 | 0 | } |
1544 | | |
1545 | | /* |
1546 | | * If we generated any ALTER TABLE actions above, wrap them into a single |
1547 | | * ALTER TABLE command. Stick it at the front of the result, so it runs |
1548 | | * before any CommentStmts we made above. |
1549 | | */ |
1550 | 0 | if (atsubcmds) |
1551 | 0 | { |
1552 | 0 | AlterTableStmt *atcmd = makeNode(AlterTableStmt); |
1553 | |
|
1554 | 0 | atcmd->relation = copyObject(heapRel); |
1555 | 0 | atcmd->cmds = atsubcmds; |
1556 | 0 | atcmd->objtype = OBJECT_TABLE; |
1557 | 0 | atcmd->missing_ok = false; |
1558 | 0 | result = lcons(atcmd, result); |
1559 | 0 | } |
1560 | | |
1561 | | /* |
1562 | | * Process indexes if required. |
1563 | | */ |
1564 | 0 | if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) && |
1565 | 0 | relation->rd_rel->relhasindex && |
1566 | 0 | childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) |
1567 | 0 | { |
1568 | 0 | List *parent_indexes; |
1569 | 0 | ListCell *l; |
1570 | |
|
1571 | 0 | parent_indexes = RelationGetIndexList(relation); |
1572 | |
|
1573 | 0 | foreach(l, parent_indexes) |
1574 | 0 | { |
1575 | 0 | Oid parent_index_oid = lfirst_oid(l); |
1576 | 0 | Relation parent_index; |
1577 | 0 | IndexStmt *index_stmt; |
1578 | |
|
1579 | 0 | parent_index = index_open(parent_index_oid, AccessShareLock); |
1580 | | |
1581 | | /* Build CREATE INDEX statement to recreate the parent_index */ |
1582 | 0 | index_stmt = generateClonedIndexStmt(heapRel, |
1583 | 0 | parent_index, |
1584 | 0 | attmap, |
1585 | 0 | NULL); |
1586 | | |
1587 | | /* Copy comment on index, if requested */ |
1588 | 0 | if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) |
1589 | 0 | { |
1590 | 0 | comment = GetComment(parent_index_oid, RelationRelationId, 0); |
1591 | | |
1592 | | /* |
1593 | | * We make use of IndexStmt's idxcomment option, so as not to |
1594 | | * need to know now what name the index will have. |
1595 | | */ |
1596 | 0 | index_stmt->idxcomment = comment; |
1597 | 0 | } |
1598 | |
|
1599 | 0 | result = lappend(result, index_stmt); |
1600 | |
|
1601 | 0 | index_close(parent_index, AccessShareLock); |
1602 | 0 | } |
1603 | 0 | } |
1604 | | |
1605 | | /* |
1606 | | * Process extended statistics if required. |
1607 | | */ |
1608 | 0 | if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS) |
1609 | 0 | { |
1610 | 0 | List *parent_extstats; |
1611 | 0 | ListCell *l; |
1612 | |
|
1613 | 0 | parent_extstats = RelationGetStatExtList(relation); |
1614 | |
|
1615 | 0 | foreach(l, parent_extstats) |
1616 | 0 | { |
1617 | 0 | Oid parent_stat_oid = lfirst_oid(l); |
1618 | 0 | CreateStatsStmt *stats_stmt; |
1619 | |
|
1620 | 0 | stats_stmt = generateClonedExtStatsStmt(heapRel, |
1621 | 0 | RelationGetRelid(childrel), |
1622 | 0 | parent_stat_oid, |
1623 | 0 | attmap); |
1624 | | |
1625 | | /* Copy comment on statistics object, if requested */ |
1626 | 0 | if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) |
1627 | 0 | { |
1628 | 0 | comment = GetComment(parent_stat_oid, StatisticExtRelationId, 0); |
1629 | | |
1630 | | /* |
1631 | | * We make use of CreateStatsStmt's stxcomment option, so as |
1632 | | * not to need to know now what name the statistics will have. |
1633 | | */ |
1634 | 0 | stats_stmt->stxcomment = comment; |
1635 | 0 | } |
1636 | |
|
1637 | 0 | result = lappend(result, stats_stmt); |
1638 | 0 | } |
1639 | |
|
1640 | 0 | list_free(parent_extstats); |
1641 | 0 | } |
1642 | | |
1643 | | /* Done with child rel */ |
1644 | 0 | table_close(childrel, NoLock); |
1645 | | |
1646 | | /* |
1647 | | * Close the parent rel, but keep our AccessShareLock on it until xact |
1648 | | * commit. That will prevent someone else from deleting or ALTERing the |
1649 | | * parent before the child is committed. |
1650 | | */ |
1651 | 0 | table_close(relation, NoLock); |
1652 | |
|
1653 | 0 | return result; |
1654 | 0 | } |
1655 | | |
1656 | | static void |
1657 | | transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) |
1658 | 0 | { |
1659 | 0 | HeapTuple tuple; |
1660 | 0 | TupleDesc tupdesc; |
1661 | 0 | int i; |
1662 | 0 | Oid ofTypeId; |
1663 | |
|
1664 | 0 | Assert(ofTypename); |
1665 | |
|
1666 | 0 | tuple = typenameType(cxt->pstate, ofTypename, NULL); |
1667 | 0 | check_of_type(tuple); |
1668 | 0 | ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid; |
1669 | 0 | ofTypename->typeOid = ofTypeId; /* cached for later */ |
1670 | |
|
1671 | 0 | tupdesc = lookup_rowtype_tupdesc(ofTypeId, -1); |
1672 | 0 | for (i = 0; i < tupdesc->natts; i++) |
1673 | 0 | { |
1674 | 0 | Form_pg_attribute attr = TupleDescAttr(tupdesc, i); |
1675 | 0 | ColumnDef *n; |
1676 | |
|
1677 | 0 | if (attr->attisdropped) |
1678 | 0 | continue; |
1679 | | |
1680 | 0 | n = makeColumnDef(NameStr(attr->attname), attr->atttypid, |
1681 | 0 | attr->atttypmod, attr->attcollation); |
1682 | 0 | n->is_from_type = true; |
1683 | |
|
1684 | 0 | cxt->columns = lappend(cxt->columns, n); |
1685 | 0 | } |
1686 | 0 | ReleaseTupleDesc(tupdesc); |
1687 | |
|
1688 | 0 | ReleaseSysCache(tuple); |
1689 | 0 | } |
1690 | | |
1691 | | /* |
1692 | | * Generate an IndexStmt node using information from an already existing index |
1693 | | * "source_idx". |
1694 | | * |
1695 | | * heapRel is stored into the IndexStmt's relation field, but we don't use it |
1696 | | * otherwise; some callers pass NULL, if they don't need it to be valid. |
1697 | | * (The target relation might not exist yet, so we mustn't try to access it.) |
1698 | | * |
1699 | | * Attribute numbers in expression Vars are adjusted according to attmap. |
1700 | | * |
1701 | | * If constraintOid isn't NULL, we store the OID of any constraint associated |
1702 | | * with the index there. |
1703 | | * |
1704 | | * Unlike transformIndexConstraint, we don't make any effort to force primary |
1705 | | * key columns to be not-null. The larger cloning process this is part of |
1706 | | * should have cloned their not-null status separately (and DefineIndex will |
1707 | | * complain if that fails to happen). |
1708 | | */ |
1709 | | IndexStmt * |
1710 | | generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, |
1711 | | const AttrMap *attmap, |
1712 | | Oid *constraintOid) |
1713 | 0 | { |
1714 | 0 | Oid source_relid = RelationGetRelid(source_idx); |
1715 | 0 | HeapTuple ht_idxrel; |
1716 | 0 | HeapTuple ht_idx; |
1717 | 0 | HeapTuple ht_am; |
1718 | 0 | Form_pg_class idxrelrec; |
1719 | 0 | Form_pg_index idxrec; |
1720 | 0 | Form_pg_am amrec; |
1721 | 0 | oidvector *indcollation; |
1722 | 0 | oidvector *indclass; |
1723 | 0 | IndexStmt *index; |
1724 | 0 | List *indexprs; |
1725 | 0 | ListCell *indexpr_item; |
1726 | 0 | Oid indrelid; |
1727 | 0 | int keyno; |
1728 | 0 | Oid keycoltype; |
1729 | 0 | Datum datum; |
1730 | 0 | bool isnull; |
1731 | |
|
1732 | 0 | if (constraintOid) |
1733 | 0 | *constraintOid = InvalidOid; |
1734 | | |
1735 | | /* |
1736 | | * Fetch pg_class tuple of source index. We can't use the copy in the |
1737 | | * relcache entry because it doesn't include optional fields. |
1738 | | */ |
1739 | 0 | ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(source_relid)); |
1740 | 0 | if (!HeapTupleIsValid(ht_idxrel)) |
1741 | 0 | elog(ERROR, "cache lookup failed for relation %u", source_relid); |
1742 | 0 | idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); |
1743 | | |
1744 | | /* Fetch pg_index tuple for source index from relcache entry */ |
1745 | 0 | ht_idx = source_idx->rd_indextuple; |
1746 | 0 | idxrec = (Form_pg_index) GETSTRUCT(ht_idx); |
1747 | 0 | indrelid = idxrec->indrelid; |
1748 | | |
1749 | | /* Fetch the pg_am tuple of the index' access method */ |
1750 | 0 | ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam)); |
1751 | 0 | if (!HeapTupleIsValid(ht_am)) |
1752 | 0 | elog(ERROR, "cache lookup failed for access method %u", |
1753 | 0 | idxrelrec->relam); |
1754 | 0 | amrec = (Form_pg_am) GETSTRUCT(ht_am); |
1755 | | |
1756 | | /* Extract indcollation from the pg_index tuple */ |
1757 | 0 | datum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx, |
1758 | 0 | Anum_pg_index_indcollation); |
1759 | 0 | indcollation = (oidvector *) DatumGetPointer(datum); |
1760 | | |
1761 | | /* Extract indclass from the pg_index tuple */ |
1762 | 0 | datum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx, Anum_pg_index_indclass); |
1763 | 0 | indclass = (oidvector *) DatumGetPointer(datum); |
1764 | | |
1765 | | /* Begin building the IndexStmt */ |
1766 | 0 | index = makeNode(IndexStmt); |
1767 | 0 | index->relation = heapRel; |
1768 | 0 | index->accessMethod = pstrdup(NameStr(amrec->amname)); |
1769 | 0 | if (OidIsValid(idxrelrec->reltablespace)) |
1770 | 0 | index->tableSpace = get_tablespace_name(idxrelrec->reltablespace); |
1771 | 0 | else |
1772 | 0 | index->tableSpace = NULL; |
1773 | 0 | index->excludeOpNames = NIL; |
1774 | 0 | index->idxcomment = NULL; |
1775 | 0 | index->indexOid = InvalidOid; |
1776 | 0 | index->oldNumber = InvalidRelFileNumber; |
1777 | 0 | index->oldCreateSubid = InvalidSubTransactionId; |
1778 | 0 | index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId; |
1779 | 0 | index->unique = idxrec->indisunique; |
1780 | 0 | index->nulls_not_distinct = idxrec->indnullsnotdistinct; |
1781 | 0 | index->primary = idxrec->indisprimary; |
1782 | 0 | index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion; |
1783 | 0 | index->transformed = true; /* don't need transformIndexStmt */ |
1784 | 0 | index->concurrent = false; |
1785 | 0 | index->if_not_exists = false; |
1786 | 0 | index->reset_default_tblspc = false; |
1787 | | |
1788 | | /* |
1789 | | * We don't try to preserve the name of the source index; instead, just |
1790 | | * let DefineIndex() choose a reasonable name. (If we tried to preserve |
1791 | | * the name, we'd get duplicate-relation-name failures unless the source |
1792 | | * table was in a different schema.) |
1793 | | */ |
1794 | 0 | index->idxname = NULL; |
1795 | | |
1796 | | /* |
1797 | | * If the index is marked PRIMARY or has an exclusion condition, it's |
1798 | | * certainly from a constraint; else, if it's not marked UNIQUE, it |
1799 | | * certainly isn't. If it is or might be from a constraint, we have to |
1800 | | * fetch the pg_constraint record. |
1801 | | */ |
1802 | 0 | if (index->primary || index->unique || idxrec->indisexclusion) |
1803 | 0 | { |
1804 | 0 | Oid constraintId = get_index_constraint(source_relid); |
1805 | |
|
1806 | 0 | if (OidIsValid(constraintId)) |
1807 | 0 | { |
1808 | 0 | HeapTuple ht_constr; |
1809 | 0 | Form_pg_constraint conrec; |
1810 | |
|
1811 | 0 | if (constraintOid) |
1812 | 0 | *constraintOid = constraintId; |
1813 | |
|
1814 | 0 | ht_constr = SearchSysCache1(CONSTROID, |
1815 | 0 | ObjectIdGetDatum(constraintId)); |
1816 | 0 | if (!HeapTupleIsValid(ht_constr)) |
1817 | 0 | elog(ERROR, "cache lookup failed for constraint %u", |
1818 | 0 | constraintId); |
1819 | 0 | conrec = (Form_pg_constraint) GETSTRUCT(ht_constr); |
1820 | |
|
1821 | 0 | index->isconstraint = true; |
1822 | 0 | index->deferrable = conrec->condeferrable; |
1823 | 0 | index->initdeferred = conrec->condeferred; |
1824 | | |
1825 | | /* If it's an exclusion constraint, we need the operator names */ |
1826 | 0 | if (idxrec->indisexclusion) |
1827 | 0 | { |
1828 | 0 | Datum *elems; |
1829 | 0 | int nElems; |
1830 | 0 | int i; |
1831 | |
|
1832 | 0 | Assert(conrec->contype == CONSTRAINT_EXCLUSION || |
1833 | 0 | (index->iswithoutoverlaps && |
1834 | 0 | (conrec->contype == CONSTRAINT_PRIMARY || conrec->contype == CONSTRAINT_UNIQUE))); |
1835 | | /* Extract operator OIDs from the pg_constraint tuple */ |
1836 | 0 | datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr, |
1837 | 0 | Anum_pg_constraint_conexclop); |
1838 | 0 | deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &elems, NULL, &nElems); |
1839 | |
|
1840 | 0 | for (i = 0; i < nElems; i++) |
1841 | 0 | { |
1842 | 0 | Oid operid = DatumGetObjectId(elems[i]); |
1843 | 0 | HeapTuple opertup; |
1844 | 0 | Form_pg_operator operform; |
1845 | 0 | char *oprname; |
1846 | 0 | char *nspname; |
1847 | 0 | List *namelist; |
1848 | |
|
1849 | 0 | opertup = SearchSysCache1(OPEROID, |
1850 | 0 | ObjectIdGetDatum(operid)); |
1851 | 0 | if (!HeapTupleIsValid(opertup)) |
1852 | 0 | elog(ERROR, "cache lookup failed for operator %u", |
1853 | 0 | operid); |
1854 | 0 | operform = (Form_pg_operator) GETSTRUCT(opertup); |
1855 | 0 | oprname = pstrdup(NameStr(operform->oprname)); |
1856 | | /* For simplicity we always schema-qualify the op name */ |
1857 | 0 | nspname = get_namespace_name(operform->oprnamespace); |
1858 | 0 | namelist = list_make2(makeString(nspname), |
1859 | 0 | makeString(oprname)); |
1860 | 0 | index->excludeOpNames = lappend(index->excludeOpNames, |
1861 | 0 | namelist); |
1862 | 0 | ReleaseSysCache(opertup); |
1863 | 0 | } |
1864 | 0 | } |
1865 | | |
1866 | 0 | ReleaseSysCache(ht_constr); |
1867 | 0 | } |
1868 | 0 | else |
1869 | 0 | index->isconstraint = false; |
1870 | 0 | } |
1871 | 0 | else |
1872 | 0 | index->isconstraint = false; |
1873 | | |
1874 | | /* Get the index expressions, if any */ |
1875 | 0 | datum = SysCacheGetAttr(INDEXRELID, ht_idx, |
1876 | 0 | Anum_pg_index_indexprs, &isnull); |
1877 | 0 | if (!isnull) |
1878 | 0 | { |
1879 | 0 | char *exprsString; |
1880 | |
|
1881 | 0 | exprsString = TextDatumGetCString(datum); |
1882 | 0 | indexprs = (List *) stringToNode(exprsString); |
1883 | 0 | } |
1884 | 0 | else |
1885 | 0 | indexprs = NIL; |
1886 | | |
1887 | | /* Build the list of IndexElem */ |
1888 | 0 | index->indexParams = NIL; |
1889 | 0 | index->indexIncludingParams = NIL; |
1890 | |
|
1891 | 0 | indexpr_item = list_head(indexprs); |
1892 | 0 | for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++) |
1893 | 0 | { |
1894 | 0 | IndexElem *iparam; |
1895 | 0 | AttrNumber attnum = idxrec->indkey.values[keyno]; |
1896 | 0 | Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(source_idx), |
1897 | 0 | keyno); |
1898 | 0 | int16 opt = source_idx->rd_indoption[keyno]; |
1899 | |
|
1900 | 0 | iparam = makeNode(IndexElem); |
1901 | |
|
1902 | 0 | if (AttributeNumberIsValid(attnum)) |
1903 | 0 | { |
1904 | | /* Simple index column */ |
1905 | 0 | char *attname; |
1906 | |
|
1907 | 0 | attname = get_attname(indrelid, attnum, false); |
1908 | 0 | keycoltype = get_atttype(indrelid, attnum); |
1909 | |
|
1910 | 0 | iparam->name = attname; |
1911 | 0 | iparam->expr = NULL; |
1912 | 0 | } |
1913 | 0 | else |
1914 | 0 | { |
1915 | | /* Expressional index */ |
1916 | 0 | Node *indexkey; |
1917 | 0 | bool found_whole_row; |
1918 | |
|
1919 | 0 | if (indexpr_item == NULL) |
1920 | 0 | elog(ERROR, "too few entries in indexprs list"); |
1921 | 0 | indexkey = (Node *) lfirst(indexpr_item); |
1922 | 0 | indexpr_item = lnext(indexprs, indexpr_item); |
1923 | | |
1924 | | /* Adjust Vars to match new table's column numbering */ |
1925 | 0 | indexkey = map_variable_attnos(indexkey, |
1926 | 0 | 1, 0, |
1927 | 0 | attmap, |
1928 | 0 | InvalidOid, &found_whole_row); |
1929 | | |
1930 | | /* As in expandTableLikeClause, reject whole-row variables */ |
1931 | 0 | if (found_whole_row) |
1932 | 0 | ereport(ERROR, |
1933 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1934 | 0 | errmsg("cannot convert whole-row table reference"), |
1935 | 0 | errdetail("Index \"%s\" contains a whole-row table reference.", |
1936 | 0 | RelationGetRelationName(source_idx)))); |
1937 | | |
1938 | 0 | iparam->name = NULL; |
1939 | 0 | iparam->expr = indexkey; |
1940 | |
|
1941 | 0 | keycoltype = exprType(indexkey); |
1942 | 0 | } |
1943 | | |
1944 | | /* Copy the original index column name */ |
1945 | 0 | iparam->indexcolname = pstrdup(NameStr(attr->attname)); |
1946 | | |
1947 | | /* Add the collation name, if non-default */ |
1948 | 0 | iparam->collation = get_collation(indcollation->values[keyno], keycoltype); |
1949 | | |
1950 | | /* Add the operator class name, if non-default */ |
1951 | 0 | iparam->opclass = get_opclass(indclass->values[keyno], keycoltype); |
1952 | 0 | iparam->opclassopts = |
1953 | 0 | untransformRelOptions(get_attoptions(source_relid, keyno + 1)); |
1954 | |
|
1955 | 0 | iparam->ordering = SORTBY_DEFAULT; |
1956 | 0 | iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; |
1957 | | |
1958 | | /* Adjust options if necessary */ |
1959 | 0 | if (source_idx->rd_indam->amcanorder) |
1960 | 0 | { |
1961 | | /* |
1962 | | * If it supports sort ordering, copy DESC and NULLS opts. Don't |
1963 | | * set non-default settings unnecessarily, though, so as to |
1964 | | * improve the chance of recognizing equivalence to constraint |
1965 | | * indexes. |
1966 | | */ |
1967 | 0 | if (opt & INDOPTION_DESC) |
1968 | 0 | { |
1969 | 0 | iparam->ordering = SORTBY_DESC; |
1970 | 0 | if ((opt & INDOPTION_NULLS_FIRST) == 0) |
1971 | 0 | iparam->nulls_ordering = SORTBY_NULLS_LAST; |
1972 | 0 | } |
1973 | 0 | else |
1974 | 0 | { |
1975 | 0 | if (opt & INDOPTION_NULLS_FIRST) |
1976 | 0 | iparam->nulls_ordering = SORTBY_NULLS_FIRST; |
1977 | 0 | } |
1978 | 0 | } |
1979 | |
|
1980 | 0 | iparam->location = -1; |
1981 | |
|
1982 | 0 | index->indexParams = lappend(index->indexParams, iparam); |
1983 | 0 | } |
1984 | | |
1985 | | /* Handle included columns separately */ |
1986 | 0 | for (keyno = idxrec->indnkeyatts; keyno < idxrec->indnatts; keyno++) |
1987 | 0 | { |
1988 | 0 | IndexElem *iparam; |
1989 | 0 | AttrNumber attnum = idxrec->indkey.values[keyno]; |
1990 | 0 | Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(source_idx), |
1991 | 0 | keyno); |
1992 | |
|
1993 | 0 | iparam = makeNode(IndexElem); |
1994 | |
|
1995 | 0 | if (AttributeNumberIsValid(attnum)) |
1996 | 0 | { |
1997 | | /* Simple index column */ |
1998 | 0 | char *attname; |
1999 | |
|
2000 | 0 | attname = get_attname(indrelid, attnum, false); |
2001 | |
|
2002 | 0 | iparam->name = attname; |
2003 | 0 | iparam->expr = NULL; |
2004 | 0 | } |
2005 | 0 | else |
2006 | 0 | ereport(ERROR, |
2007 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2008 | 0 | errmsg("expressions are not supported in included columns"))); |
2009 | | |
2010 | | /* Copy the original index column name */ |
2011 | 0 | iparam->indexcolname = pstrdup(NameStr(attr->attname)); |
2012 | |
|
2013 | 0 | iparam->location = -1; |
2014 | |
|
2015 | 0 | index->indexIncludingParams = lappend(index->indexIncludingParams, iparam); |
2016 | 0 | } |
2017 | | /* Copy reloptions if any */ |
2018 | 0 | datum = SysCacheGetAttr(RELOID, ht_idxrel, |
2019 | 0 | Anum_pg_class_reloptions, &isnull); |
2020 | 0 | if (!isnull) |
2021 | 0 | index->options = untransformRelOptions(datum); |
2022 | | |
2023 | | /* If it's a partial index, decompile and append the predicate */ |
2024 | 0 | datum = SysCacheGetAttr(INDEXRELID, ht_idx, |
2025 | 0 | Anum_pg_index_indpred, &isnull); |
2026 | 0 | if (!isnull) |
2027 | 0 | { |
2028 | 0 | char *pred_str; |
2029 | 0 | Node *pred_tree; |
2030 | 0 | bool found_whole_row; |
2031 | | |
2032 | | /* Convert text string to node tree */ |
2033 | 0 | pred_str = TextDatumGetCString(datum); |
2034 | 0 | pred_tree = (Node *) stringToNode(pred_str); |
2035 | | |
2036 | | /* Adjust Vars to match new table's column numbering */ |
2037 | 0 | pred_tree = map_variable_attnos(pred_tree, |
2038 | 0 | 1, 0, |
2039 | 0 | attmap, |
2040 | 0 | InvalidOid, &found_whole_row); |
2041 | | |
2042 | | /* As in expandTableLikeClause, reject whole-row variables */ |
2043 | 0 | if (found_whole_row) |
2044 | 0 | ereport(ERROR, |
2045 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2046 | 0 | errmsg("cannot convert whole-row table reference"), |
2047 | 0 | errdetail("Index \"%s\" contains a whole-row table reference.", |
2048 | 0 | RelationGetRelationName(source_idx)))); |
2049 | | |
2050 | 0 | index->whereClause = pred_tree; |
2051 | 0 | } |
2052 | | |
2053 | | /* Clean up */ |
2054 | 0 | ReleaseSysCache(ht_idxrel); |
2055 | 0 | ReleaseSysCache(ht_am); |
2056 | |
|
2057 | 0 | return index; |
2058 | 0 | } |
2059 | | |
2060 | | /* |
2061 | | * Generate a CreateStatsStmt node using information from an already existing |
2062 | | * extended statistic "source_statsid", for the rel identified by heapRel and |
2063 | | * heapRelid. |
2064 | | * |
2065 | | * stxkeys in the source statistic holds attribute numbers from the parent |
2066 | | * relation. Those attnums, along with the attribute numbers referenced by |
2067 | | * Vars inside the expression tree, are remapped to the new relation's |
2068 | | * numbering according to attmap. |
2069 | | */ |
2070 | | static CreateStatsStmt * |
2071 | | generateClonedExtStatsStmt(RangeVar *heapRel, Oid heapRelid, |
2072 | | Oid source_statsid, const AttrMap *attmap) |
2073 | 0 | { |
2074 | 0 | HeapTuple ht_stats; |
2075 | 0 | Form_pg_statistic_ext statsrec; |
2076 | 0 | CreateStatsStmt *stats; |
2077 | 0 | List *stat_types = NIL; |
2078 | 0 | List *def_names = NIL; |
2079 | 0 | bool isnull; |
2080 | 0 | Datum datum; |
2081 | 0 | ArrayType *arr; |
2082 | 0 | char *enabled; |
2083 | 0 | int i; |
2084 | |
|
2085 | 0 | Assert(OidIsValid(heapRelid)); |
2086 | 0 | Assert(heapRel != NULL); |
2087 | | |
2088 | | /* |
2089 | | * Fetch pg_statistic_ext tuple of source statistics object. |
2090 | | */ |
2091 | 0 | ht_stats = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(source_statsid)); |
2092 | 0 | if (!HeapTupleIsValid(ht_stats)) |
2093 | 0 | elog(ERROR, "cache lookup failed for statistics object %u", source_statsid); |
2094 | 0 | statsrec = (Form_pg_statistic_ext) GETSTRUCT(ht_stats); |
2095 | | |
2096 | | /* Determine which statistics types exist */ |
2097 | 0 | datum = SysCacheGetAttrNotNull(STATEXTOID, ht_stats, |
2098 | 0 | Anum_pg_statistic_ext_stxkind); |
2099 | 0 | arr = DatumGetArrayTypeP(datum); |
2100 | 0 | if (ARR_NDIM(arr) != 1 || |
2101 | 0 | ARR_HASNULL(arr) || |
2102 | 0 | ARR_ELEMTYPE(arr) != CHAROID) |
2103 | 0 | elog(ERROR, "stxkind is not a 1-D char array"); |
2104 | 0 | enabled = (char *) ARR_DATA_PTR(arr); |
2105 | 0 | for (i = 0; i < ARR_DIMS(arr)[0]; i++) |
2106 | 0 | { |
2107 | 0 | if (enabled[i] == STATS_EXT_NDISTINCT) |
2108 | 0 | stat_types = lappend(stat_types, makeString("ndistinct")); |
2109 | 0 | else if (enabled[i] == STATS_EXT_DEPENDENCIES) |
2110 | 0 | stat_types = lappend(stat_types, makeString("dependencies")); |
2111 | 0 | else if (enabled[i] == STATS_EXT_MCV) |
2112 | 0 | stat_types = lappend(stat_types, makeString("mcv")); |
2113 | 0 | else if (enabled[i] == STATS_EXT_EXPRESSIONS) |
2114 | | /* expression stats are not exposed to users */ |
2115 | 0 | continue; |
2116 | 0 | else |
2117 | 0 | elog(ERROR, "unrecognized statistics kind %c", enabled[i]); |
2118 | 0 | } |
2119 | | |
2120 | | /* Determine which columns the statistics are on */ |
2121 | 0 | for (i = 0; i < statsrec->stxkeys.dim1; i++) |
2122 | 0 | { |
2123 | 0 | StatsElem *selem = makeNode(StatsElem); |
2124 | 0 | AttrNumber attnum = statsrec->stxkeys.values[i]; |
2125 | |
|
2126 | 0 | selem->name = |
2127 | 0 | get_attname(heapRelid, attmap->attnums[attnum - 1], false); |
2128 | 0 | selem->expr = NULL; |
2129 | |
|
2130 | 0 | def_names = lappend(def_names, selem); |
2131 | 0 | } |
2132 | | |
2133 | | /* |
2134 | | * Now handle expressions, if there are any. The order (with respect to |
2135 | | * regular attributes) does not really matter for extended stats, so we |
2136 | | * simply append them after simple column references. |
2137 | | * |
2138 | | * XXX Some places during build/estimation treat expressions as if they |
2139 | | * are before attributes, but for the CREATE command that's entirely |
2140 | | * irrelevant. |
2141 | | */ |
2142 | 0 | datum = SysCacheGetAttr(STATEXTOID, ht_stats, |
2143 | 0 | Anum_pg_statistic_ext_stxexprs, &isnull); |
2144 | |
|
2145 | 0 | if (!isnull) |
2146 | 0 | { |
2147 | 0 | ListCell *lc; |
2148 | 0 | List *exprs = NIL; |
2149 | 0 | char *exprsString; |
2150 | |
|
2151 | 0 | exprsString = TextDatumGetCString(datum); |
2152 | 0 | exprs = (List *) stringToNode(exprsString); |
2153 | |
|
2154 | 0 | foreach(lc, exprs) |
2155 | 0 | { |
2156 | 0 | Node *expr = (Node *) lfirst(lc); |
2157 | 0 | StatsElem *selem = makeNode(StatsElem); |
2158 | 0 | bool found_whole_row; |
2159 | | |
2160 | | /* Adjust Vars to match new table's column numbering */ |
2161 | 0 | expr = map_variable_attnos(expr, |
2162 | 0 | 1, 0, |
2163 | 0 | attmap, |
2164 | 0 | InvalidOid, |
2165 | 0 | &found_whole_row); |
2166 | |
|
2167 | 0 | selem->name = NULL; |
2168 | 0 | selem->expr = expr; |
2169 | |
|
2170 | 0 | def_names = lappend(def_names, selem); |
2171 | 0 | } |
2172 | |
|
2173 | 0 | pfree(exprsString); |
2174 | 0 | } |
2175 | | |
2176 | | /* finally, build the output node */ |
2177 | 0 | stats = makeNode(CreateStatsStmt); |
2178 | 0 | stats->defnames = NULL; |
2179 | 0 | stats->stat_types = stat_types; |
2180 | 0 | stats->exprs = def_names; |
2181 | 0 | stats->relations = list_make1(heapRel); |
2182 | 0 | stats->stxcomment = NULL; |
2183 | 0 | stats->transformed = true; /* don't need transformStatsStmt again */ |
2184 | 0 | stats->if_not_exists = false; |
2185 | | |
2186 | | /* Clean up */ |
2187 | 0 | ReleaseSysCache(ht_stats); |
2188 | |
|
2189 | 0 | return stats; |
2190 | 0 | } |
2191 | | |
2192 | | /* |
2193 | | * get_collation - fetch qualified name of a collation |
2194 | | * |
2195 | | * If collation is InvalidOid or is the default for the given actual_datatype, |
2196 | | * then the return value is NIL. |
2197 | | */ |
2198 | | static List * |
2199 | | get_collation(Oid collation, Oid actual_datatype) |
2200 | 0 | { |
2201 | 0 | List *result; |
2202 | 0 | HeapTuple ht_coll; |
2203 | 0 | Form_pg_collation coll_rec; |
2204 | 0 | char *nsp_name; |
2205 | 0 | char *coll_name; |
2206 | |
|
2207 | 0 | if (!OidIsValid(collation)) |
2208 | 0 | return NIL; /* easy case */ |
2209 | 0 | if (collation == get_typcollation(actual_datatype)) |
2210 | 0 | return NIL; /* just let it default */ |
2211 | | |
2212 | 0 | ht_coll = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation)); |
2213 | 0 | if (!HeapTupleIsValid(ht_coll)) |
2214 | 0 | elog(ERROR, "cache lookup failed for collation %u", collation); |
2215 | 0 | coll_rec = (Form_pg_collation) GETSTRUCT(ht_coll); |
2216 | | |
2217 | | /* For simplicity, we always schema-qualify the name */ |
2218 | 0 | nsp_name = get_namespace_name(coll_rec->collnamespace); |
2219 | 0 | coll_name = pstrdup(NameStr(coll_rec->collname)); |
2220 | 0 | result = list_make2(makeString(nsp_name), makeString(coll_name)); |
2221 | |
|
2222 | 0 | ReleaseSysCache(ht_coll); |
2223 | 0 | return result; |
2224 | 0 | } |
2225 | | |
2226 | | /* |
2227 | | * get_opclass - fetch qualified name of an index operator class |
2228 | | * |
2229 | | * If the opclass is the default for the given actual_datatype, then |
2230 | | * the return value is NIL. |
2231 | | */ |
2232 | | static List * |
2233 | | get_opclass(Oid opclass, Oid actual_datatype) |
2234 | 0 | { |
2235 | 0 | List *result = NIL; |
2236 | 0 | HeapTuple ht_opc; |
2237 | 0 | Form_pg_opclass opc_rec; |
2238 | |
|
2239 | 0 | ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); |
2240 | 0 | if (!HeapTupleIsValid(ht_opc)) |
2241 | 0 | elog(ERROR, "cache lookup failed for opclass %u", opclass); |
2242 | 0 | opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc); |
2243 | |
|
2244 | 0 | if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass) |
2245 | 0 | { |
2246 | | /* For simplicity, we always schema-qualify the name */ |
2247 | 0 | char *nsp_name = get_namespace_name(opc_rec->opcnamespace); |
2248 | 0 | char *opc_name = pstrdup(NameStr(opc_rec->opcname)); |
2249 | |
|
2250 | 0 | result = list_make2(makeString(nsp_name), makeString(opc_name)); |
2251 | 0 | } |
2252 | |
|
2253 | 0 | ReleaseSysCache(ht_opc); |
2254 | 0 | return result; |
2255 | 0 | } |
2256 | | |
2257 | | |
2258 | | /* |
2259 | | * transformIndexConstraints |
2260 | | * Handle UNIQUE, PRIMARY KEY, EXCLUDE constraints, which create indexes. |
2261 | | * We also merge in any index definitions arising from |
2262 | | * LIKE ... INCLUDING INDEXES. |
2263 | | */ |
2264 | | static void |
2265 | | transformIndexConstraints(CreateStmtContext *cxt) |
2266 | 0 | { |
2267 | 0 | IndexStmt *index; |
2268 | 0 | List *indexlist = NIL; |
2269 | 0 | List *finalindexlist = NIL; |
2270 | 0 | ListCell *lc; |
2271 | | |
2272 | | /* |
2273 | | * Run through the constraints that need to generate an index, and do so. |
2274 | | * |
2275 | | * For PRIMARY KEY, this queues not-null constraints for each column, if |
2276 | | * needed. |
2277 | | */ |
2278 | 0 | foreach(lc, cxt->ixconstraints) |
2279 | 0 | { |
2280 | 0 | Constraint *constraint = lfirst_node(Constraint, lc); |
2281 | |
|
2282 | 0 | Assert(constraint->contype == CONSTR_PRIMARY || |
2283 | 0 | constraint->contype == CONSTR_UNIQUE || |
2284 | 0 | constraint->contype == CONSTR_EXCLUSION); |
2285 | |
|
2286 | 0 | index = transformIndexConstraint(constraint, cxt); |
2287 | |
|
2288 | 0 | indexlist = lappend(indexlist, index); |
2289 | 0 | } |
2290 | | |
2291 | | /* |
2292 | | * Scan the index list and remove any redundant index specifications. This |
2293 | | * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A |
2294 | | * strict reading of SQL would suggest raising an error instead, but that |
2295 | | * strikes me as too anal-retentive. - tgl 2001-02-14 |
2296 | | * |
2297 | | * XXX in ALTER TABLE case, it'd be nice to look for duplicate |
2298 | | * pre-existing indexes, too. |
2299 | | */ |
2300 | 0 | if (cxt->pkey != NULL) |
2301 | 0 | { |
2302 | | /* Make sure we keep the PKEY index in preference to others... */ |
2303 | 0 | finalindexlist = list_make1(cxt->pkey); |
2304 | 0 | } |
2305 | |
|
2306 | 0 | foreach(lc, indexlist) |
2307 | 0 | { |
2308 | 0 | bool keep = true; |
2309 | 0 | ListCell *k; |
2310 | |
|
2311 | 0 | index = lfirst(lc); |
2312 | | |
2313 | | /* if it's pkey, it's already in finalindexlist */ |
2314 | 0 | if (index == cxt->pkey) |
2315 | 0 | continue; |
2316 | | |
2317 | 0 | foreach(k, finalindexlist) |
2318 | 0 | { |
2319 | 0 | IndexStmt *priorindex = lfirst(k); |
2320 | |
|
2321 | 0 | if (equal(index->indexParams, priorindex->indexParams) && |
2322 | 0 | equal(index->indexIncludingParams, priorindex->indexIncludingParams) && |
2323 | 0 | equal(index->whereClause, priorindex->whereClause) && |
2324 | 0 | equal(index->excludeOpNames, priorindex->excludeOpNames) && |
2325 | 0 | strcmp(index->accessMethod, priorindex->accessMethod) == 0 && |
2326 | 0 | index->nulls_not_distinct == priorindex->nulls_not_distinct && |
2327 | 0 | index->deferrable == priorindex->deferrable && |
2328 | 0 | index->initdeferred == priorindex->initdeferred) |
2329 | 0 | { |
2330 | 0 | priorindex->unique |= index->unique; |
2331 | | |
2332 | | /* |
2333 | | * If the prior index is as yet unnamed, and this one is |
2334 | | * named, then transfer the name to the prior index. This |
2335 | | * ensures that if we have named and unnamed constraints, |
2336 | | * we'll use (at least one of) the names for the index. |
2337 | | */ |
2338 | 0 | if (priorindex->idxname == NULL) |
2339 | 0 | priorindex->idxname = index->idxname; |
2340 | 0 | keep = false; |
2341 | 0 | break; |
2342 | 0 | } |
2343 | 0 | } |
2344 | |
|
2345 | 0 | if (keep) |
2346 | 0 | finalindexlist = lappend(finalindexlist, index); |
2347 | 0 | } |
2348 | | |
2349 | | /* |
2350 | | * Now append all the IndexStmts to cxt->alist. |
2351 | | */ |
2352 | 0 | cxt->alist = list_concat(cxt->alist, finalindexlist); |
2353 | 0 | } |
2354 | | |
2355 | | /* |
2356 | | * transformIndexConstraint |
2357 | | * Transform one UNIQUE, PRIMARY KEY, or EXCLUDE constraint for |
2358 | | * transformIndexConstraints. An IndexStmt is returned. |
2359 | | * |
2360 | | * For a PRIMARY KEY constraint, we additionally create not-null constraints |
2361 | | * for columns that don't already have them. |
2362 | | */ |
2363 | | static IndexStmt * |
2364 | | transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) |
2365 | 0 | { |
2366 | 0 | IndexStmt *index; |
2367 | 0 | ListCell *lc; |
2368 | |
|
2369 | 0 | index = makeNode(IndexStmt); |
2370 | |
|
2371 | 0 | index->unique = (constraint->contype != CONSTR_EXCLUSION); |
2372 | 0 | index->primary = (constraint->contype == CONSTR_PRIMARY); |
2373 | 0 | if (index->primary) |
2374 | 0 | { |
2375 | 0 | if (cxt->pkey != NULL) |
2376 | 0 | ereport(ERROR, |
2377 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
2378 | 0 | errmsg("multiple primary keys for table \"%s\" are not allowed", |
2379 | 0 | cxt->relation->relname), |
2380 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2381 | 0 | cxt->pkey = index; |
2382 | | |
2383 | | /* |
2384 | | * In ALTER TABLE case, a primary index might already exist, but |
2385 | | * DefineIndex will check for it. |
2386 | | */ |
2387 | 0 | } |
2388 | 0 | index->nulls_not_distinct = constraint->nulls_not_distinct; |
2389 | 0 | index->isconstraint = true; |
2390 | 0 | index->iswithoutoverlaps = constraint->without_overlaps; |
2391 | 0 | index->deferrable = constraint->deferrable; |
2392 | 0 | index->initdeferred = constraint->initdeferred; |
2393 | |
|
2394 | 0 | if (constraint->conname != NULL) |
2395 | 0 | index->idxname = pstrdup(constraint->conname); |
2396 | 0 | else |
2397 | 0 | index->idxname = NULL; /* DefineIndex will choose name */ |
2398 | |
|
2399 | 0 | index->relation = cxt->relation; |
2400 | 0 | index->accessMethod = constraint->access_method ? constraint->access_method : DEFAULT_INDEX_TYPE; |
2401 | 0 | index->options = constraint->options; |
2402 | 0 | index->tableSpace = constraint->indexspace; |
2403 | 0 | index->whereClause = constraint->where_clause; |
2404 | 0 | index->indexParams = NIL; |
2405 | 0 | index->indexIncludingParams = NIL; |
2406 | 0 | index->excludeOpNames = NIL; |
2407 | 0 | index->idxcomment = NULL; |
2408 | 0 | index->indexOid = InvalidOid; |
2409 | 0 | index->oldNumber = InvalidRelFileNumber; |
2410 | 0 | index->oldCreateSubid = InvalidSubTransactionId; |
2411 | 0 | index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId; |
2412 | 0 | index->transformed = false; |
2413 | 0 | index->concurrent = false; |
2414 | 0 | index->if_not_exists = false; |
2415 | 0 | index->reset_default_tblspc = constraint->reset_default_tblspc; |
2416 | | |
2417 | | /* |
2418 | | * If it's ALTER TABLE ADD CONSTRAINT USING INDEX, look up the index and |
2419 | | * verify it's usable, then extract the implied column name list. (We |
2420 | | * will not actually need the column name list at runtime, but we need it |
2421 | | * now to check for duplicate column entries below.) |
2422 | | */ |
2423 | 0 | if (constraint->indexname != NULL) |
2424 | 0 | { |
2425 | 0 | char *index_name = constraint->indexname; |
2426 | 0 | Relation heap_rel = cxt->rel; |
2427 | 0 | Oid index_oid; |
2428 | 0 | Relation index_rel; |
2429 | 0 | Form_pg_index index_form; |
2430 | 0 | oidvector *indclass; |
2431 | 0 | Datum indclassDatum; |
2432 | 0 | int i; |
2433 | | |
2434 | | /* Grammar should not allow this with explicit column list */ |
2435 | 0 | Assert(constraint->keys == NIL); |
2436 | | |
2437 | | /* Grammar should only allow PRIMARY and UNIQUE constraints */ |
2438 | 0 | Assert(constraint->contype == CONSTR_PRIMARY || |
2439 | 0 | constraint->contype == CONSTR_UNIQUE); |
2440 | | |
2441 | | /* Must be ALTER, not CREATE, but grammar doesn't enforce that */ |
2442 | 0 | if (!cxt->isalter) |
2443 | 0 | ereport(ERROR, |
2444 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2445 | 0 | errmsg("cannot use an existing index in CREATE TABLE"), |
2446 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2447 | | |
2448 | | /* Look for the index in the same schema as the table */ |
2449 | 0 | index_oid = get_relname_relid(index_name, RelationGetNamespace(heap_rel)); |
2450 | |
|
2451 | 0 | if (!OidIsValid(index_oid)) |
2452 | 0 | ereport(ERROR, |
2453 | 0 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
2454 | 0 | errmsg("index \"%s\" does not exist", index_name), |
2455 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2456 | | |
2457 | | /* Open the index (this will throw an error if it is not an index) */ |
2458 | 0 | index_rel = index_open(index_oid, AccessShareLock); |
2459 | 0 | index_form = index_rel->rd_index; |
2460 | | |
2461 | | /* Check that it does not have an associated constraint already */ |
2462 | 0 | if (OidIsValid(get_index_constraint(index_oid))) |
2463 | 0 | ereport(ERROR, |
2464 | 0 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), |
2465 | 0 | errmsg("index \"%s\" is already associated with a constraint", |
2466 | 0 | index_name), |
2467 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2468 | | |
2469 | | /* Perform validity checks on the index */ |
2470 | 0 | if (index_form->indrelid != RelationGetRelid(heap_rel)) |
2471 | 0 | ereport(ERROR, |
2472 | 0 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), |
2473 | 0 | errmsg("index \"%s\" does not belong to table \"%s\"", |
2474 | 0 | index_name, RelationGetRelationName(heap_rel)), |
2475 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2476 | | |
2477 | 0 | if (!index_form->indisvalid) |
2478 | 0 | ereport(ERROR, |
2479 | 0 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), |
2480 | 0 | errmsg("index \"%s\" is not valid", index_name), |
2481 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2482 | | |
2483 | | /* |
2484 | | * Today we forbid non-unique indexes, but we could permit GiST |
2485 | | * indexes whose last entry is a range type and use that to create a |
2486 | | * WITHOUT OVERLAPS constraint (i.e. a temporal constraint). |
2487 | | */ |
2488 | 0 | if (!index_form->indisunique) |
2489 | 0 | ereport(ERROR, |
2490 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2491 | 0 | errmsg("\"%s\" is not a unique index", index_name), |
2492 | 0 | errdetail("Cannot create a primary key or unique constraint using such an index."), |
2493 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2494 | | |
2495 | 0 | if (RelationGetIndexExpressions(index_rel) != NIL) |
2496 | 0 | ereport(ERROR, |
2497 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2498 | 0 | errmsg("index \"%s\" contains expressions", index_name), |
2499 | 0 | errdetail("Cannot create a primary key or unique constraint using such an index."), |
2500 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2501 | | |
2502 | 0 | if (RelationGetIndexPredicate(index_rel) != NIL) |
2503 | 0 | ereport(ERROR, |
2504 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2505 | 0 | errmsg("\"%s\" is a partial index", index_name), |
2506 | 0 | errdetail("Cannot create a primary key or unique constraint using such an index."), |
2507 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2508 | | |
2509 | | /* |
2510 | | * It's probably unsafe to change a deferred index to non-deferred. (A |
2511 | | * non-constraint index couldn't be deferred anyway, so this case |
2512 | | * should never occur; no need to sweat, but let's check it.) |
2513 | | */ |
2514 | 0 | if (!index_form->indimmediate && !constraint->deferrable) |
2515 | 0 | ereport(ERROR, |
2516 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2517 | 0 | errmsg("\"%s\" is a deferrable index", index_name), |
2518 | 0 | errdetail("Cannot create a non-deferrable constraint using a deferrable index."), |
2519 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2520 | | |
2521 | | /* |
2522 | | * Insist on it being a btree. We must have an index that exactly |
2523 | | * matches what you'd get from plain ADD CONSTRAINT syntax, else dump |
2524 | | * and reload will produce a different index (breaking pg_upgrade in |
2525 | | * particular). |
2526 | | */ |
2527 | 0 | if (index_rel->rd_rel->relam != get_index_am_oid(DEFAULT_INDEX_TYPE, false)) |
2528 | 0 | ereport(ERROR, |
2529 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2530 | 0 | errmsg("index \"%s\" is not a btree", index_name), |
2531 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2532 | | |
2533 | | /* Must get indclass the hard way */ |
2534 | 0 | indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, |
2535 | 0 | index_rel->rd_indextuple, |
2536 | 0 | Anum_pg_index_indclass); |
2537 | 0 | indclass = (oidvector *) DatumGetPointer(indclassDatum); |
2538 | |
|
2539 | 0 | for (i = 0; i < index_form->indnatts; i++) |
2540 | 0 | { |
2541 | 0 | int16 attnum = index_form->indkey.values[i]; |
2542 | 0 | const FormData_pg_attribute *attform; |
2543 | 0 | char *attname; |
2544 | 0 | Oid defopclass; |
2545 | | |
2546 | | /* |
2547 | | * We shouldn't see attnum == 0 here, since we already rejected |
2548 | | * expression indexes. If we do, SystemAttributeDefinition will |
2549 | | * throw an error. |
2550 | | */ |
2551 | 0 | if (attnum > 0) |
2552 | 0 | { |
2553 | 0 | Assert(attnum <= heap_rel->rd_att->natts); |
2554 | 0 | attform = TupleDescAttr(heap_rel->rd_att, attnum - 1); |
2555 | 0 | } |
2556 | 0 | else |
2557 | 0 | attform = SystemAttributeDefinition(attnum); |
2558 | 0 | attname = pstrdup(NameStr(attform->attname)); |
2559 | |
|
2560 | 0 | if (i < index_form->indnkeyatts) |
2561 | 0 | { |
2562 | | /* |
2563 | | * Insist on default opclass, collation, and sort options. |
2564 | | * While the index would still work as a constraint with |
2565 | | * non-default settings, it might not provide exactly the same |
2566 | | * uniqueness semantics as you'd get from a normally-created |
2567 | | * constraint; and there's also the dump/reload problem |
2568 | | * mentioned above. |
2569 | | */ |
2570 | 0 | Datum attoptions = |
2571 | 0 | get_attoptions(RelationGetRelid(index_rel), i + 1); |
2572 | |
|
2573 | 0 | defopclass = GetDefaultOpClass(attform->atttypid, |
2574 | 0 | index_rel->rd_rel->relam); |
2575 | 0 | if (indclass->values[i] != defopclass || |
2576 | 0 | attform->attcollation != index_rel->rd_indcollation[i] || |
2577 | 0 | attoptions != (Datum) 0 || |
2578 | 0 | index_rel->rd_indoption[i] != 0) |
2579 | 0 | ereport(ERROR, |
2580 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2581 | 0 | errmsg("index \"%s\" column number %d does not have default sorting behavior", index_name, i + 1), |
2582 | 0 | errdetail("Cannot create a primary key or unique constraint using such an index."), |
2583 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2584 | | |
2585 | | /* If a PK, ensure the columns get not null constraints */ |
2586 | 0 | if (constraint->contype == CONSTR_PRIMARY) |
2587 | 0 | cxt->nnconstraints = |
2588 | 0 | lappend(cxt->nnconstraints, |
2589 | 0 | makeNotNullConstraint(makeString(attname))); |
2590 | |
|
2591 | 0 | constraint->keys = lappend(constraint->keys, makeString(attname)); |
2592 | 0 | } |
2593 | 0 | else |
2594 | 0 | constraint->including = lappend(constraint->including, makeString(attname)); |
2595 | 0 | } |
2596 | | |
2597 | | /* Close the index relation but keep the lock */ |
2598 | 0 | index_close(index_rel, NoLock); |
2599 | |
|
2600 | 0 | index->indexOid = index_oid; |
2601 | 0 | } |
2602 | | |
2603 | | /* |
2604 | | * If it's an EXCLUDE constraint, the grammar returns a list of pairs of |
2605 | | * IndexElems and operator names. We have to break that apart into |
2606 | | * separate lists. |
2607 | | */ |
2608 | 0 | if (constraint->contype == CONSTR_EXCLUSION) |
2609 | 0 | { |
2610 | 0 | foreach(lc, constraint->exclusions) |
2611 | 0 | { |
2612 | 0 | List *pair = (List *) lfirst(lc); |
2613 | 0 | IndexElem *elem; |
2614 | 0 | List *opname; |
2615 | |
|
2616 | 0 | Assert(list_length(pair) == 2); |
2617 | 0 | elem = linitial_node(IndexElem, pair); |
2618 | 0 | opname = lsecond_node(List, pair); |
2619 | |
|
2620 | 0 | index->indexParams = lappend(index->indexParams, elem); |
2621 | 0 | index->excludeOpNames = lappend(index->excludeOpNames, opname); |
2622 | 0 | } |
2623 | 0 | } |
2624 | | |
2625 | | /* |
2626 | | * For UNIQUE and PRIMARY KEY, we just have a list of column names. |
2627 | | * |
2628 | | * Make sure referenced keys exist. If we are making a PRIMARY KEY index, |
2629 | | * also make sure they are not-null. For WITHOUT OVERLAPS constraints, we |
2630 | | * make sure the last part is a range or multirange. |
2631 | | */ |
2632 | 0 | else |
2633 | 0 | { |
2634 | 0 | foreach(lc, constraint->keys) |
2635 | 0 | { |
2636 | 0 | char *key = strVal(lfirst(lc)); |
2637 | 0 | bool found = false; |
2638 | 0 | ColumnDef *column = NULL; |
2639 | 0 | ListCell *columns; |
2640 | 0 | IndexElem *iparam; |
2641 | 0 | Oid typid = InvalidOid; |
2642 | | |
2643 | | /* Make sure referenced column exists. */ |
2644 | 0 | foreach(columns, cxt->columns) |
2645 | 0 | { |
2646 | 0 | column = lfirst_node(ColumnDef, columns); |
2647 | 0 | if (strcmp(column->colname, key) == 0) |
2648 | 0 | { |
2649 | 0 | found = true; |
2650 | 0 | break; |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | if (!found) |
2654 | 0 | column = NULL; |
2655 | |
|
2656 | 0 | if (found) |
2657 | 0 | { |
2658 | | /* |
2659 | | * column is defined in the new table. For CREATE TABLE with |
2660 | | * a PRIMARY KEY, we can apply the not-null constraint cheaply |
2661 | | * here. If the not-null constraint already exists, we can |
2662 | | * (albeit not so cheaply) verify that it's not a NO INHERIT |
2663 | | * constraint. |
2664 | | * |
2665 | | * Note that ALTER TABLE never needs either check, because |
2666 | | * those constraints have already been added by |
2667 | | * ATPrepAddPrimaryKey. |
2668 | | */ |
2669 | 0 | if (constraint->contype == CONSTR_PRIMARY && |
2670 | 0 | !cxt->isalter) |
2671 | 0 | { |
2672 | 0 | if (column->is_not_null) |
2673 | 0 | { |
2674 | 0 | foreach_node(Constraint, nn, cxt->nnconstraints) |
2675 | 0 | { |
2676 | 0 | if (strcmp(strVal(linitial(nn->keys)), key) == 0) |
2677 | 0 | { |
2678 | 0 | if (nn->is_no_inherit) |
2679 | 0 | ereport(ERROR, |
2680 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
2681 | 0 | errmsg("conflicting NO INHERIT declaration for not-null constraint on column \"%s\"", |
2682 | 0 | key)); |
2683 | 0 | break; |
2684 | 0 | } |
2685 | 0 | } |
2686 | 0 | } |
2687 | 0 | else |
2688 | 0 | { |
2689 | 0 | column->is_not_null = true; |
2690 | 0 | cxt->nnconstraints = |
2691 | 0 | lappend(cxt->nnconstraints, |
2692 | 0 | makeNotNullConstraint(makeString(key))); |
2693 | 0 | } |
2694 | 0 | } |
2695 | 0 | else if (constraint->contype == CONSTR_PRIMARY) |
2696 | 0 | Assert(column->is_not_null); |
2697 | 0 | } |
2698 | 0 | else if (SystemAttributeByName(key) != NULL) |
2699 | 0 | { |
2700 | | /* |
2701 | | * column will be a system column in the new table, so accept |
2702 | | * it. System columns can't ever be null, so no need to worry |
2703 | | * about PRIMARY/NOT NULL constraint. |
2704 | | */ |
2705 | 0 | found = true; |
2706 | 0 | } |
2707 | 0 | else if (cxt->inhRelations) |
2708 | 0 | { |
2709 | | /* try inherited tables */ |
2710 | 0 | ListCell *inher; |
2711 | |
|
2712 | 0 | foreach(inher, cxt->inhRelations) |
2713 | 0 | { |
2714 | 0 | RangeVar *inh = lfirst_node(RangeVar, inher); |
2715 | 0 | Relation rel; |
2716 | 0 | int count; |
2717 | |
|
2718 | 0 | rel = table_openrv(inh, AccessShareLock); |
2719 | | /* check user requested inheritance from valid relkind */ |
2720 | 0 | if (rel->rd_rel->relkind != RELKIND_RELATION && |
2721 | 0 | rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && |
2722 | 0 | rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) |
2723 | 0 | ereport(ERROR, |
2724 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2725 | 0 | errmsg("inherited relation \"%s\" is not a table or foreign table", |
2726 | 0 | inh->relname))); |
2727 | 0 | for (count = 0; count < rel->rd_att->natts; count++) |
2728 | 0 | { |
2729 | 0 | Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, |
2730 | 0 | count); |
2731 | 0 | char *inhname = NameStr(inhattr->attname); |
2732 | |
|
2733 | 0 | if (inhattr->attisdropped) |
2734 | 0 | continue; |
2735 | 0 | if (strcmp(key, inhname) == 0) |
2736 | 0 | { |
2737 | 0 | found = true; |
2738 | 0 | typid = inhattr->atttypid; |
2739 | |
|
2740 | 0 | if (constraint->contype == CONSTR_PRIMARY) |
2741 | 0 | cxt->nnconstraints = |
2742 | 0 | lappend(cxt->nnconstraints, |
2743 | 0 | makeNotNullConstraint(makeString(pstrdup(inhname)))); |
2744 | 0 | break; |
2745 | 0 | } |
2746 | 0 | } |
2747 | 0 | table_close(rel, NoLock); |
2748 | 0 | if (found) |
2749 | 0 | break; |
2750 | 0 | } |
2751 | 0 | } |
2752 | | |
2753 | | /* |
2754 | | * In the ALTER TABLE case, don't complain about index keys not |
2755 | | * created in the command; they may well exist already. |
2756 | | * DefineIndex will complain about them if not. |
2757 | | */ |
2758 | 0 | if (!found && !cxt->isalter) |
2759 | 0 | ereport(ERROR, |
2760 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
2761 | 0 | errmsg("column \"%s\" named in key does not exist", key), |
2762 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2763 | | |
2764 | | /* Check for PRIMARY KEY(foo, foo) */ |
2765 | 0 | foreach(columns, index->indexParams) |
2766 | 0 | { |
2767 | 0 | iparam = (IndexElem *) lfirst(columns); |
2768 | 0 | if (iparam->name && strcmp(key, iparam->name) == 0) |
2769 | 0 | { |
2770 | 0 | if (index->primary) |
2771 | 0 | ereport(ERROR, |
2772 | 0 | (errcode(ERRCODE_DUPLICATE_COLUMN), |
2773 | 0 | errmsg("column \"%s\" appears twice in primary key constraint", |
2774 | 0 | key), |
2775 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2776 | 0 | else |
2777 | 0 | ereport(ERROR, |
2778 | 0 | (errcode(ERRCODE_DUPLICATE_COLUMN), |
2779 | 0 | errmsg("column \"%s\" appears twice in unique constraint", |
2780 | 0 | key), |
2781 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2782 | 0 | } |
2783 | 0 | } |
2784 | | |
2785 | | /* |
2786 | | * The WITHOUT OVERLAPS part (if any) must be a range or |
2787 | | * multirange type, or a domain over such a type. |
2788 | | */ |
2789 | 0 | if (constraint->without_overlaps && lc == list_last_cell(constraint->keys)) |
2790 | 0 | { |
2791 | 0 | if (!found && cxt->isalter) |
2792 | 0 | { |
2793 | | /* |
2794 | | * Look up the column type on existing table. If we can't |
2795 | | * find it, let things fail in DefineIndex. |
2796 | | */ |
2797 | 0 | Relation rel = cxt->rel; |
2798 | |
|
2799 | 0 | for (int i = 0; i < rel->rd_att->natts; i++) |
2800 | 0 | { |
2801 | 0 | Form_pg_attribute attr = TupleDescAttr(rel->rd_att, i); |
2802 | 0 | const char *attname; |
2803 | |
|
2804 | 0 | if (attr->attisdropped) |
2805 | 0 | continue; |
2806 | 0 | attname = NameStr(attr->attname); |
2807 | 0 | if (strcmp(attname, key) == 0) |
2808 | 0 | { |
2809 | 0 | found = true; |
2810 | 0 | typid = attr->atttypid; |
2811 | 0 | break; |
2812 | 0 | } |
2813 | 0 | } |
2814 | 0 | } |
2815 | 0 | if (found) |
2816 | 0 | { |
2817 | | /* Look up column type if we didn't already */ |
2818 | 0 | if (!OidIsValid(typid) && column) |
2819 | 0 | typid = typenameTypeId(cxt->pstate, |
2820 | 0 | column->typeName); |
2821 | | /* Look through any domain */ |
2822 | 0 | if (OidIsValid(typid)) |
2823 | 0 | typid = getBaseType(typid); |
2824 | | /* Complain if not range/multirange */ |
2825 | 0 | if (!OidIsValid(typid) || |
2826 | 0 | !(type_is_range(typid) || type_is_multirange(typid))) |
2827 | 0 | ereport(ERROR, |
2828 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
2829 | 0 | errmsg("column \"%s\" in WITHOUT OVERLAPS is not a range or multirange type", key), |
2830 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2831 | 0 | } |
2832 | 0 | } |
2833 | | |
2834 | | /* OK, add it to the index definition */ |
2835 | 0 | iparam = makeNode(IndexElem); |
2836 | 0 | iparam->name = pstrdup(key); |
2837 | 0 | iparam->expr = NULL; |
2838 | 0 | iparam->indexcolname = NULL; |
2839 | 0 | iparam->collation = NIL; |
2840 | 0 | iparam->opclass = NIL; |
2841 | 0 | iparam->opclassopts = NIL; |
2842 | 0 | iparam->ordering = SORTBY_DEFAULT; |
2843 | 0 | iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; |
2844 | 0 | iparam->location = -1; |
2845 | 0 | index->indexParams = lappend(index->indexParams, iparam); |
2846 | 0 | } |
2847 | | |
2848 | 0 | if (constraint->without_overlaps) |
2849 | 0 | { |
2850 | | /* |
2851 | | * This enforces that there is at least one equality column |
2852 | | * besides the WITHOUT OVERLAPS columns. This is per SQL |
2853 | | * standard. XXX Do we need this? |
2854 | | */ |
2855 | 0 | if (list_length(constraint->keys) < 2) |
2856 | 0 | ereport(ERROR, |
2857 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
2858 | 0 | errmsg("constraint using WITHOUT OVERLAPS needs at least two columns")); |
2859 | | |
2860 | | /* WITHOUT OVERLAPS requires a GiST index */ |
2861 | 0 | index->accessMethod = "gist"; |
2862 | 0 | } |
2863 | |
|
2864 | 0 | } |
2865 | | |
2866 | | /* |
2867 | | * Add included columns to index definition. This is much like the |
2868 | | * simple-column-name-list code above, except that we don't worry about |
2869 | | * NOT NULL marking; included columns in a primary key should not be |
2870 | | * forced NOT NULL. We don't complain about duplicate columns, either, |
2871 | | * though maybe we should? |
2872 | | */ |
2873 | 0 | foreach(lc, constraint->including) |
2874 | 0 | { |
2875 | 0 | char *key = strVal(lfirst(lc)); |
2876 | 0 | bool found = false; |
2877 | 0 | ColumnDef *column = NULL; |
2878 | 0 | ListCell *columns; |
2879 | 0 | IndexElem *iparam; |
2880 | |
|
2881 | 0 | foreach(columns, cxt->columns) |
2882 | 0 | { |
2883 | 0 | column = lfirst_node(ColumnDef, columns); |
2884 | 0 | if (strcmp(column->colname, key) == 0) |
2885 | 0 | { |
2886 | 0 | found = true; |
2887 | 0 | break; |
2888 | 0 | } |
2889 | 0 | } |
2890 | |
|
2891 | 0 | if (!found) |
2892 | 0 | { |
2893 | 0 | if (SystemAttributeByName(key) != NULL) |
2894 | 0 | { |
2895 | | /* |
2896 | | * column will be a system column in the new table, so accept |
2897 | | * it. |
2898 | | */ |
2899 | 0 | found = true; |
2900 | 0 | } |
2901 | 0 | else if (cxt->inhRelations) |
2902 | 0 | { |
2903 | | /* try inherited tables */ |
2904 | 0 | ListCell *inher; |
2905 | |
|
2906 | 0 | foreach(inher, cxt->inhRelations) |
2907 | 0 | { |
2908 | 0 | RangeVar *inh = lfirst_node(RangeVar, inher); |
2909 | 0 | Relation rel; |
2910 | 0 | int count; |
2911 | |
|
2912 | 0 | rel = table_openrv(inh, AccessShareLock); |
2913 | | /* check user requested inheritance from valid relkind */ |
2914 | 0 | if (rel->rd_rel->relkind != RELKIND_RELATION && |
2915 | 0 | rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && |
2916 | 0 | rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) |
2917 | 0 | ereport(ERROR, |
2918 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2919 | 0 | errmsg("inherited relation \"%s\" is not a table or foreign table", |
2920 | 0 | inh->relname))); |
2921 | 0 | for (count = 0; count < rel->rd_att->natts; count++) |
2922 | 0 | { |
2923 | 0 | Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, |
2924 | 0 | count); |
2925 | 0 | char *inhname = NameStr(inhattr->attname); |
2926 | |
|
2927 | 0 | if (inhattr->attisdropped) |
2928 | 0 | continue; |
2929 | 0 | if (strcmp(key, inhname) == 0) |
2930 | 0 | { |
2931 | 0 | found = true; |
2932 | 0 | break; |
2933 | 0 | } |
2934 | 0 | } |
2935 | 0 | table_close(rel, NoLock); |
2936 | 0 | if (found) |
2937 | 0 | break; |
2938 | 0 | } |
2939 | 0 | } |
2940 | 0 | } |
2941 | | |
2942 | | /* |
2943 | | * In the ALTER TABLE case, don't complain about index keys not |
2944 | | * created in the command; they may well exist already. DefineIndex |
2945 | | * will complain about them if not. |
2946 | | */ |
2947 | 0 | if (!found && !cxt->isalter) |
2948 | 0 | ereport(ERROR, |
2949 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
2950 | 0 | errmsg("column \"%s\" named in key does not exist", key), |
2951 | 0 | parser_errposition(cxt->pstate, constraint->location))); |
2952 | | |
2953 | | /* OK, add it to the index definition */ |
2954 | 0 | iparam = makeNode(IndexElem); |
2955 | 0 | iparam->name = pstrdup(key); |
2956 | 0 | iparam->expr = NULL; |
2957 | 0 | iparam->indexcolname = NULL; |
2958 | 0 | iparam->collation = NIL; |
2959 | 0 | iparam->opclass = NIL; |
2960 | 0 | iparam->opclassopts = NIL; |
2961 | 0 | iparam->location = -1; |
2962 | 0 | index->indexIncludingParams = lappend(index->indexIncludingParams, iparam); |
2963 | 0 | } |
2964 | | |
2965 | 0 | return index; |
2966 | 0 | } |
2967 | | |
2968 | | /* |
2969 | | * transformCheckConstraints |
2970 | | * handle CHECK constraints |
2971 | | * |
2972 | | * Right now, there's nothing to do here when called from ALTER TABLE, |
2973 | | * but the other constraint-transformation functions are called in both |
2974 | | * the CREATE TABLE and ALTER TABLE paths, so do the same here, and just |
2975 | | * don't do anything if we're not authorized to skip validation. |
2976 | | */ |
2977 | | static void |
2978 | | transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation) |
2979 | 0 | { |
2980 | 0 | ListCell *ckclist; |
2981 | |
|
2982 | 0 | if (cxt->ckconstraints == NIL) |
2983 | 0 | return; |
2984 | | |
2985 | | /* |
2986 | | * When creating a new table (but not a foreign table), we can safely skip |
2987 | | * the validation of check constraints and mark them as valid based on the |
2988 | | * constraint enforcement flag, since NOT ENFORCED constraints must always |
2989 | | * be marked as NOT VALID. (This will override any user-supplied NOT VALID |
2990 | | * flag.) |
2991 | | */ |
2992 | 0 | if (skipValidation) |
2993 | 0 | { |
2994 | 0 | foreach(ckclist, cxt->ckconstraints) |
2995 | 0 | { |
2996 | 0 | Constraint *constraint = (Constraint *) lfirst(ckclist); |
2997 | |
|
2998 | 0 | constraint->skip_validation = true; |
2999 | 0 | constraint->initially_valid = constraint->is_enforced; |
3000 | 0 | } |
3001 | 0 | } |
3002 | 0 | } |
3003 | | |
3004 | | /* |
3005 | | * transformFKConstraints |
3006 | | * handle FOREIGN KEY constraints |
3007 | | */ |
3008 | | static void |
3009 | | transformFKConstraints(CreateStmtContext *cxt, |
3010 | | bool skipValidation, bool isAddConstraint) |
3011 | 0 | { |
3012 | 0 | ListCell *fkclist; |
3013 | |
|
3014 | 0 | if (cxt->fkconstraints == NIL) |
3015 | 0 | return; |
3016 | | |
3017 | | /* |
3018 | | * If CREATE TABLE or adding a column with NULL default, we can safely |
3019 | | * skip validation of FK constraints, and mark them as valid based on the |
3020 | | * constraint enforcement flag, since NOT ENFORCED constraints must always |
3021 | | * be marked as NOT VALID. (This will override any user-supplied NOT VALID |
3022 | | * flag.) |
3023 | | */ |
3024 | 0 | if (skipValidation) |
3025 | 0 | { |
3026 | 0 | foreach(fkclist, cxt->fkconstraints) |
3027 | 0 | { |
3028 | 0 | Constraint *constraint = (Constraint *) lfirst(fkclist); |
3029 | |
|
3030 | 0 | constraint->skip_validation = true; |
3031 | 0 | constraint->initially_valid = constraint->is_enforced; |
3032 | 0 | } |
3033 | 0 | } |
3034 | | |
3035 | | /* |
3036 | | * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD |
3037 | | * CONSTRAINT command to execute after the basic command is complete. (If |
3038 | | * called from ADD CONSTRAINT, that routine will add the FK constraints to |
3039 | | * its own subcommand list.) |
3040 | | * |
3041 | | * Note: the ADD CONSTRAINT command must also execute after any index |
3042 | | * creation commands. Thus, this should run after |
3043 | | * transformIndexConstraints, so that the CREATE INDEX commands are |
3044 | | * already in cxt->alist. See also the handling of cxt->likeclauses. |
3045 | | */ |
3046 | 0 | if (!isAddConstraint) |
3047 | 0 | { |
3048 | 0 | AlterTableStmt *alterstmt = makeNode(AlterTableStmt); |
3049 | |
|
3050 | 0 | alterstmt->relation = cxt->relation; |
3051 | 0 | alterstmt->cmds = NIL; |
3052 | 0 | alterstmt->objtype = OBJECT_TABLE; |
3053 | |
|
3054 | 0 | foreach(fkclist, cxt->fkconstraints) |
3055 | 0 | { |
3056 | 0 | Constraint *constraint = (Constraint *) lfirst(fkclist); |
3057 | 0 | AlterTableCmd *altercmd = makeNode(AlterTableCmd); |
3058 | |
|
3059 | 0 | altercmd->subtype = AT_AddConstraint; |
3060 | 0 | altercmd->name = NULL; |
3061 | 0 | altercmd->def = (Node *) constraint; |
3062 | 0 | alterstmt->cmds = lappend(alterstmt->cmds, altercmd); |
3063 | 0 | } |
3064 | |
|
3065 | 0 | cxt->alist = lappend(cxt->alist, alterstmt); |
3066 | 0 | } |
3067 | 0 | } |
3068 | | |
3069 | | /* |
3070 | | * transformIndexStmt - parse analysis for CREATE INDEX and ALTER TABLE |
3071 | | * |
3072 | | * Note: this is a no-op for an index not using either index expressions or |
3073 | | * a predicate expression. There are several code paths that create indexes |
3074 | | * without bothering to call this, because they know they don't have any |
3075 | | * such expressions to deal with. |
3076 | | * |
3077 | | * To avoid race conditions, it's important that this function rely only on |
3078 | | * the passed-in relid (and not on stmt->relation) to determine the target |
3079 | | * relation. |
3080 | | */ |
3081 | | IndexStmt * |
3082 | | transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) |
3083 | 0 | { |
3084 | 0 | ParseState *pstate; |
3085 | 0 | ParseNamespaceItem *nsitem; |
3086 | 0 | ListCell *l; |
3087 | 0 | Relation rel; |
3088 | | |
3089 | | /* Nothing to do if statement already transformed. */ |
3090 | 0 | if (stmt->transformed) |
3091 | 0 | return stmt; |
3092 | | |
3093 | | /* Set up pstate */ |
3094 | 0 | pstate = make_parsestate(NULL); |
3095 | 0 | pstate->p_sourcetext = queryString; |
3096 | | |
3097 | | /* |
3098 | | * Put the parent table into the rtable so that the expressions can refer |
3099 | | * to its fields without qualification. Caller is responsible for locking |
3100 | | * relation, but we still need to open it. |
3101 | | */ |
3102 | 0 | rel = relation_open(relid, NoLock); |
3103 | 0 | nsitem = addRangeTableEntryForRelation(pstate, rel, |
3104 | 0 | AccessShareLock, |
3105 | 0 | NULL, false, true); |
3106 | | |
3107 | | /* no to join list, yes to namespaces */ |
3108 | 0 | addNSItemToQuery(pstate, nsitem, false, true, true); |
3109 | | |
3110 | | /* take care of the where clause */ |
3111 | 0 | if (stmt->whereClause) |
3112 | 0 | { |
3113 | 0 | stmt->whereClause = transformWhereClause(pstate, |
3114 | 0 | stmt->whereClause, |
3115 | 0 | EXPR_KIND_INDEX_PREDICATE, |
3116 | 0 | "WHERE"); |
3117 | | /* we have to fix its collations too */ |
3118 | 0 | assign_expr_collations(pstate, stmt->whereClause); |
3119 | 0 | } |
3120 | | |
3121 | | /* take care of any index expressions */ |
3122 | 0 | foreach(l, stmt->indexParams) |
3123 | 0 | { |
3124 | 0 | IndexElem *ielem = (IndexElem *) lfirst(l); |
3125 | |
|
3126 | 0 | if (ielem->expr) |
3127 | 0 | { |
3128 | | /* Do parse transformation of the expression */ |
3129 | 0 | ielem->expr = transformExpr(pstate, ielem->expr, |
3130 | 0 | EXPR_KIND_INDEX_EXPRESSION); |
3131 | | |
3132 | | /* We have to fix its collations too */ |
3133 | 0 | assign_expr_collations(pstate, ielem->expr); |
3134 | | |
3135 | | /* |
3136 | | * transformExpr() should have already rejected subqueries, |
3137 | | * aggregates, window functions, and SRFs, based on the EXPR_KIND_ |
3138 | | * for an index expression. |
3139 | | * |
3140 | | * DefineIndex() will make more checks. |
3141 | | */ |
3142 | 0 | } |
3143 | 0 | } |
3144 | | |
3145 | | /* |
3146 | | * Likewise take care of any expressions in INCLUDING. (At this writing, |
3147 | | * those will be rejected later on, but probably someday we'll wish to |
3148 | | * support them.) |
3149 | | */ |
3150 | 0 | foreach(l, stmt->indexIncludingParams) |
3151 | 0 | { |
3152 | 0 | IndexElem *ielem = (IndexElem *) lfirst(l); |
3153 | |
|
3154 | 0 | if (ielem->expr) |
3155 | 0 | { |
3156 | | /* Do parse transformation of the expression */ |
3157 | 0 | ielem->expr = transformExpr(pstate, ielem->expr, |
3158 | 0 | EXPR_KIND_INDEX_EXPRESSION); |
3159 | | |
3160 | | /* We have to fix its collations too */ |
3161 | 0 | assign_expr_collations(pstate, ielem->expr); |
3162 | 0 | } |
3163 | 0 | } |
3164 | | |
3165 | | /* |
3166 | | * Check that only the base rel is mentioned. (This should be dead code |
3167 | | * now that add_missing_from is history.) |
3168 | | */ |
3169 | 0 | if (list_length(pstate->p_rtable) != 1) |
3170 | 0 | ereport(ERROR, |
3171 | 0 | (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), |
3172 | 0 | errmsg("index expressions and predicates can refer only to the table being indexed"))); |
3173 | | |
3174 | 0 | free_parsestate(pstate); |
3175 | | |
3176 | | /* Close relation */ |
3177 | 0 | table_close(rel, NoLock); |
3178 | | |
3179 | | /* Mark statement as successfully transformed */ |
3180 | 0 | stmt->transformed = true; |
3181 | |
|
3182 | 0 | return stmt; |
3183 | 0 | } |
3184 | | |
3185 | | /* |
3186 | | * transformStatsStmt - parse analysis for CREATE STATISTICS |
3187 | | * |
3188 | | * To avoid race conditions, it's important that this function relies only on |
3189 | | * the passed-in relid (and not on stmt->relation) to determine the target |
3190 | | * relation. |
3191 | | */ |
3192 | | CreateStatsStmt * |
3193 | | transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString) |
3194 | 0 | { |
3195 | 0 | ParseState *pstate; |
3196 | 0 | ParseNamespaceItem *nsitem; |
3197 | 0 | ListCell *l; |
3198 | 0 | Relation rel; |
3199 | | |
3200 | | /* Nothing to do if statement already transformed. */ |
3201 | 0 | if (stmt->transformed) |
3202 | 0 | return stmt; |
3203 | | |
3204 | | /* Set up pstate */ |
3205 | 0 | pstate = make_parsestate(NULL); |
3206 | 0 | pstate->p_sourcetext = queryString; |
3207 | | |
3208 | | /* |
3209 | | * Put the parent table into the rtable so that the expressions can refer |
3210 | | * to its fields without qualification. Caller is responsible for locking |
3211 | | * relation, but we still need to open it. |
3212 | | */ |
3213 | 0 | rel = relation_open(relid, NoLock); |
3214 | 0 | nsitem = addRangeTableEntryForRelation(pstate, rel, |
3215 | 0 | AccessShareLock, |
3216 | 0 | NULL, false, true); |
3217 | | |
3218 | | /* no to join list, yes to namespaces */ |
3219 | 0 | addNSItemToQuery(pstate, nsitem, false, true, true); |
3220 | | |
3221 | | /* take care of any expressions */ |
3222 | 0 | foreach(l, stmt->exprs) |
3223 | 0 | { |
3224 | 0 | StatsElem *selem = (StatsElem *) lfirst(l); |
3225 | |
|
3226 | 0 | if (selem->expr) |
3227 | 0 | { |
3228 | | /* Now do parse transformation of the expression */ |
3229 | 0 | selem->expr = transformExpr(pstate, selem->expr, |
3230 | 0 | EXPR_KIND_STATS_EXPRESSION); |
3231 | | |
3232 | | /* We have to fix its collations too */ |
3233 | 0 | assign_expr_collations(pstate, selem->expr); |
3234 | 0 | } |
3235 | 0 | } |
3236 | | |
3237 | | /* |
3238 | | * Check that only the base rel is mentioned. (This should be dead code |
3239 | | * now that add_missing_from is history.) |
3240 | | */ |
3241 | 0 | if (list_length(pstate->p_rtable) != 1) |
3242 | 0 | ereport(ERROR, |
3243 | 0 | (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), |
3244 | 0 | errmsg("statistics expressions can refer only to the table being referenced"))); |
3245 | | |
3246 | 0 | free_parsestate(pstate); |
3247 | | |
3248 | | /* Close relation */ |
3249 | 0 | table_close(rel, NoLock); |
3250 | | |
3251 | | /* Mark statement as successfully transformed */ |
3252 | 0 | stmt->transformed = true; |
3253 | |
|
3254 | 0 | return stmt; |
3255 | 0 | } |
3256 | | |
3257 | | |
3258 | | /* |
3259 | | * transformRuleStmt - |
3260 | | * transform a CREATE RULE Statement. The action is a list of parse |
3261 | | * trees which is transformed into a list of query trees, and we also |
3262 | | * transform the WHERE clause if any. |
3263 | | * |
3264 | | * actions and whereClause are output parameters that receive the |
3265 | | * transformed results. |
3266 | | */ |
3267 | | void |
3268 | | transformRuleStmt(RuleStmt *stmt, const char *queryString, |
3269 | | List **actions, Node **whereClause) |
3270 | 0 | { |
3271 | 0 | Relation rel; |
3272 | 0 | ParseState *pstate; |
3273 | 0 | ParseNamespaceItem *oldnsitem; |
3274 | 0 | ParseNamespaceItem *newnsitem; |
3275 | | |
3276 | | /* |
3277 | | * To avoid deadlock, make sure the first thing we do is grab |
3278 | | * AccessExclusiveLock on the target relation. This will be needed by |
3279 | | * DefineQueryRewrite(), and we don't want to grab a lesser lock |
3280 | | * beforehand. |
3281 | | */ |
3282 | 0 | rel = table_openrv(stmt->relation, AccessExclusiveLock); |
3283 | |
|
3284 | 0 | if (rel->rd_rel->relkind == RELKIND_MATVIEW) |
3285 | 0 | ereport(ERROR, |
3286 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
3287 | 0 | errmsg("rules on materialized views are not supported"))); |
3288 | | |
3289 | | /* Set up pstate */ |
3290 | 0 | pstate = make_parsestate(NULL); |
3291 | 0 | pstate->p_sourcetext = queryString; |
3292 | | |
3293 | | /* |
3294 | | * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2. |
3295 | | * Set up their ParseNamespaceItems in the main pstate for use in parsing |
3296 | | * the rule qualification. |
3297 | | */ |
3298 | 0 | oldnsitem = addRangeTableEntryForRelation(pstate, rel, |
3299 | 0 | AccessShareLock, |
3300 | 0 | makeAlias("old", NIL), |
3301 | 0 | false, false); |
3302 | 0 | newnsitem = addRangeTableEntryForRelation(pstate, rel, |
3303 | 0 | AccessShareLock, |
3304 | 0 | makeAlias("new", NIL), |
3305 | 0 | false, false); |
3306 | | |
3307 | | /* |
3308 | | * They must be in the namespace too for lookup purposes, but only add the |
3309 | | * one(s) that are relevant for the current kind of rule. In an UPDATE |
3310 | | * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but |
3311 | | * there's no need to be so picky for INSERT & DELETE. We do not add them |
3312 | | * to the joinlist. |
3313 | | */ |
3314 | 0 | switch (stmt->event) |
3315 | 0 | { |
3316 | 0 | case CMD_SELECT: |
3317 | 0 | addNSItemToQuery(pstate, oldnsitem, false, true, true); |
3318 | 0 | break; |
3319 | 0 | case CMD_UPDATE: |
3320 | 0 | addNSItemToQuery(pstate, oldnsitem, false, true, true); |
3321 | 0 | addNSItemToQuery(pstate, newnsitem, false, true, true); |
3322 | 0 | break; |
3323 | 0 | case CMD_INSERT: |
3324 | 0 | addNSItemToQuery(pstate, newnsitem, false, true, true); |
3325 | 0 | break; |
3326 | 0 | case CMD_DELETE: |
3327 | 0 | addNSItemToQuery(pstate, oldnsitem, false, true, true); |
3328 | 0 | break; |
3329 | 0 | default: |
3330 | 0 | elog(ERROR, "unrecognized event type: %d", |
3331 | 0 | (int) stmt->event); |
3332 | 0 | break; |
3333 | 0 | } |
3334 | | |
3335 | | /* take care of the where clause */ |
3336 | 0 | *whereClause = transformWhereClause(pstate, |
3337 | 0 | stmt->whereClause, |
3338 | 0 | EXPR_KIND_WHERE, |
3339 | 0 | "WHERE"); |
3340 | | /* we have to fix its collations too */ |
3341 | 0 | assign_expr_collations(pstate, *whereClause); |
3342 | | |
3343 | | /* this is probably dead code without add_missing_from: */ |
3344 | 0 | if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */ |
3345 | 0 | ereport(ERROR, |
3346 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
3347 | 0 | errmsg("rule WHERE condition cannot contain references to other relations"))); |
3348 | | |
3349 | | /* |
3350 | | * 'instead nothing' rules with a qualification need a query rangetable so |
3351 | | * the rewrite handler can add the negated rule qualification to the |
3352 | | * original query. We create a query with the new command type CMD_NOTHING |
3353 | | * here that is treated specially by the rewrite system. |
3354 | | */ |
3355 | 0 | if (stmt->actions == NIL) |
3356 | 0 | { |
3357 | 0 | Query *nothing_qry = makeNode(Query); |
3358 | |
|
3359 | 0 | nothing_qry->commandType = CMD_NOTHING; |
3360 | 0 | nothing_qry->rtable = pstate->p_rtable; |
3361 | 0 | nothing_qry->rteperminfos = pstate->p_rteperminfos; |
3362 | 0 | nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ |
3363 | |
|
3364 | 0 | *actions = list_make1(nothing_qry); |
3365 | 0 | } |
3366 | 0 | else |
3367 | 0 | { |
3368 | 0 | ListCell *l; |
3369 | 0 | List *newactions = NIL; |
3370 | | |
3371 | | /* |
3372 | | * transform each statement, like parse_sub_analyze() |
3373 | | */ |
3374 | 0 | foreach(l, stmt->actions) |
3375 | 0 | { |
3376 | 0 | Node *action = (Node *) lfirst(l); |
3377 | 0 | ParseState *sub_pstate = make_parsestate(NULL); |
3378 | 0 | Query *sub_qry, |
3379 | 0 | *top_subqry; |
3380 | 0 | bool has_old, |
3381 | 0 | has_new; |
3382 | | |
3383 | | /* |
3384 | | * Since outer ParseState isn't parent of inner, have to pass down |
3385 | | * the query text by hand. |
3386 | | */ |
3387 | 0 | sub_pstate->p_sourcetext = queryString; |
3388 | | |
3389 | | /* |
3390 | | * Set up OLD/NEW in the rtable for this statement. The entries |
3391 | | * are added only to relnamespace, not varnamespace, because we |
3392 | | * don't want them to be referred to by unqualified field names |
3393 | | * nor "*" in the rule actions. We decide later whether to put |
3394 | | * them in the joinlist. |
3395 | | */ |
3396 | 0 | oldnsitem = addRangeTableEntryForRelation(sub_pstate, rel, |
3397 | 0 | AccessShareLock, |
3398 | 0 | makeAlias("old", NIL), |
3399 | 0 | false, false); |
3400 | 0 | newnsitem = addRangeTableEntryForRelation(sub_pstate, rel, |
3401 | 0 | AccessShareLock, |
3402 | 0 | makeAlias("new", NIL), |
3403 | 0 | false, false); |
3404 | 0 | addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); |
3405 | 0 | addNSItemToQuery(sub_pstate, newnsitem, false, true, false); |
3406 | | |
3407 | | /* Transform the rule action statement */ |
3408 | 0 | top_subqry = transformStmt(sub_pstate, action); |
3409 | | |
3410 | | /* |
3411 | | * We cannot support utility-statement actions (eg NOTIFY) with |
3412 | | * nonempty rule WHERE conditions, because there's no way to make |
3413 | | * the utility action execute conditionally. |
3414 | | */ |
3415 | 0 | if (top_subqry->commandType == CMD_UTILITY && |
3416 | 0 | *whereClause != NULL) |
3417 | 0 | ereport(ERROR, |
3418 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
3419 | 0 | errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions"))); |
3420 | | |
3421 | | /* |
3422 | | * If the action is INSERT...SELECT, OLD/NEW have been pushed down |
3423 | | * into the SELECT, and that's what we need to look at. (Ugly |
3424 | | * kluge ... try to fix this when we redesign querytrees.) |
3425 | | */ |
3426 | 0 | sub_qry = getInsertSelectQuery(top_subqry, NULL); |
3427 | | |
3428 | | /* |
3429 | | * If the sub_qry is a setop, we cannot attach any qualifications |
3430 | | * to it, because the planner won't notice them. This could |
3431 | | * perhaps be relaxed someday, but for now, we may as well reject |
3432 | | * such a rule immediately. |
3433 | | */ |
3434 | 0 | if (sub_qry->setOperations != NULL && *whereClause != NULL) |
3435 | 0 | ereport(ERROR, |
3436 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
3437 | 0 | errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented"))); |
3438 | | |
3439 | | /* |
3440 | | * Validate action's use of OLD/NEW, qual too |
3441 | | */ |
3442 | 0 | has_old = |
3443 | 0 | rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) || |
3444 | 0 | rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0); |
3445 | 0 | has_new = |
3446 | 0 | rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) || |
3447 | 0 | rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0); |
3448 | |
|
3449 | 0 | switch (stmt->event) |
3450 | 0 | { |
3451 | 0 | case CMD_SELECT: |
3452 | 0 | if (has_old) |
3453 | 0 | ereport(ERROR, |
3454 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
3455 | 0 | errmsg("ON SELECT rule cannot use OLD"))); |
3456 | 0 | if (has_new) |
3457 | 0 | ereport(ERROR, |
3458 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
3459 | 0 | errmsg("ON SELECT rule cannot use NEW"))); |
3460 | 0 | break; |
3461 | 0 | case CMD_UPDATE: |
3462 | | /* both are OK */ |
3463 | 0 | break; |
3464 | 0 | case CMD_INSERT: |
3465 | 0 | if (has_old) |
3466 | 0 | ereport(ERROR, |
3467 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
3468 | 0 | errmsg("ON INSERT rule cannot use OLD"))); |
3469 | 0 | break; |
3470 | 0 | case CMD_DELETE: |
3471 | 0 | if (has_new) |
3472 | 0 | ereport(ERROR, |
3473 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
3474 | 0 | errmsg("ON DELETE rule cannot use NEW"))); |
3475 | 0 | break; |
3476 | 0 | default: |
3477 | 0 | elog(ERROR, "unrecognized event type: %d", |
3478 | 0 | (int) stmt->event); |
3479 | 0 | break; |
3480 | 0 | } |
3481 | | |
3482 | | /* |
3483 | | * OLD/NEW are not allowed in WITH queries, because they would |
3484 | | * amount to outer references for the WITH, which we disallow. |
3485 | | * However, they were already in the outer rangetable when we |
3486 | | * analyzed the query, so we have to check. |
3487 | | * |
3488 | | * Note that in the INSERT...SELECT case, we need to examine the |
3489 | | * CTE lists of both top_subqry and sub_qry. |
3490 | | * |
3491 | | * Note that we aren't digging into the body of the query looking |
3492 | | * for WITHs in nested sub-SELECTs. A WITH down there can |
3493 | | * legitimately refer to OLD/NEW, because it'd be an |
3494 | | * indirect-correlated outer reference. |
3495 | | */ |
3496 | 0 | if (rangeTableEntry_used((Node *) top_subqry->cteList, |
3497 | 0 | PRS2_OLD_VARNO, 0) || |
3498 | 0 | rangeTableEntry_used((Node *) sub_qry->cteList, |
3499 | 0 | PRS2_OLD_VARNO, 0)) |
3500 | 0 | ereport(ERROR, |
3501 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
3502 | 0 | errmsg("cannot refer to OLD within WITH query"))); |
3503 | 0 | if (rangeTableEntry_used((Node *) top_subqry->cteList, |
3504 | 0 | PRS2_NEW_VARNO, 0) || |
3505 | 0 | rangeTableEntry_used((Node *) sub_qry->cteList, |
3506 | 0 | PRS2_NEW_VARNO, 0)) |
3507 | 0 | ereport(ERROR, |
3508 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
3509 | 0 | errmsg("cannot refer to NEW within WITH query"))); |
3510 | | |
3511 | | /* |
3512 | | * For efficiency's sake, add OLD to the rule action's jointree |
3513 | | * only if it was actually referenced in the statement or qual. |
3514 | | * |
3515 | | * For INSERT, NEW is not really a relation (only a reference to |
3516 | | * the to-be-inserted tuple) and should never be added to the |
3517 | | * jointree. |
3518 | | * |
3519 | | * For UPDATE, we treat NEW as being another kind of reference to |
3520 | | * OLD, because it represents references to *transformed* tuples |
3521 | | * of the existing relation. It would be wrong to enter NEW |
3522 | | * separately in the jointree, since that would cause a double |
3523 | | * join of the updated relation. It's also wrong to fail to make |
3524 | | * a jointree entry if only NEW and not OLD is mentioned. |
3525 | | */ |
3526 | 0 | if (has_old || (has_new && stmt->event == CMD_UPDATE)) |
3527 | 0 | { |
3528 | 0 | RangeTblRef *rtr; |
3529 | | |
3530 | | /* |
3531 | | * If sub_qry is a setop, manipulating its jointree will do no |
3532 | | * good at all, because the jointree is dummy. (This should be |
3533 | | * a can't-happen case because of prior tests.) |
3534 | | */ |
3535 | 0 | if (sub_qry->setOperations != NULL) |
3536 | 0 | ereport(ERROR, |
3537 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
3538 | 0 | errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented"))); |
3539 | | /* hackishly add OLD to the already-built FROM clause */ |
3540 | 0 | rtr = makeNode(RangeTblRef); |
3541 | 0 | rtr->rtindex = oldnsitem->p_rtindex; |
3542 | 0 | sub_qry->jointree->fromlist = |
3543 | 0 | lappend(sub_qry->jointree->fromlist, rtr); |
3544 | 0 | } |
3545 | | |
3546 | 0 | newactions = lappend(newactions, top_subqry); |
3547 | |
|
3548 | 0 | free_parsestate(sub_pstate); |
3549 | 0 | } |
3550 | | |
3551 | 0 | *actions = newactions; |
3552 | 0 | } |
3553 | | |
3554 | 0 | free_parsestate(pstate); |
3555 | | |
3556 | | /* Close relation, but keep the exclusive lock */ |
3557 | 0 | table_close(rel, NoLock); |
3558 | 0 | } |
3559 | | |
3560 | | |
3561 | | /* |
3562 | | * transformAlterTableStmt - |
3563 | | * parse analysis for ALTER TABLE |
3564 | | * |
3565 | | * Returns the transformed AlterTableStmt. There may be additional actions |
3566 | | * to be done before and after the transformed statement, which are returned |
3567 | | * in *beforeStmts and *afterStmts as lists of utility command parsetrees. |
3568 | | * |
3569 | | * To avoid race conditions, it's important that this function rely only on |
3570 | | * the passed-in relid (and not on stmt->relation) to determine the target |
3571 | | * relation. |
3572 | | */ |
3573 | | AlterTableStmt * |
3574 | | transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, |
3575 | | const char *queryString, |
3576 | | List **beforeStmts, List **afterStmts) |
3577 | 0 | { |
3578 | 0 | Relation rel; |
3579 | 0 | TupleDesc tupdesc; |
3580 | 0 | ParseState *pstate; |
3581 | 0 | CreateStmtContext cxt; |
3582 | 0 | List *save_alist; |
3583 | 0 | ListCell *lcmd, |
3584 | 0 | *l; |
3585 | 0 | List *newcmds = NIL; |
3586 | 0 | bool skipValidation = true; |
3587 | 0 | AlterTableCmd *newcmd; |
3588 | 0 | ParseNamespaceItem *nsitem; |
3589 | | |
3590 | | /* Caller is responsible for locking the relation */ |
3591 | 0 | rel = relation_open(relid, NoLock); |
3592 | 0 | tupdesc = RelationGetDescr(rel); |
3593 | | |
3594 | | /* Set up pstate */ |
3595 | 0 | pstate = make_parsestate(NULL); |
3596 | 0 | pstate->p_sourcetext = queryString; |
3597 | 0 | nsitem = addRangeTableEntryForRelation(pstate, |
3598 | 0 | rel, |
3599 | 0 | AccessShareLock, |
3600 | 0 | NULL, |
3601 | 0 | false, |
3602 | 0 | true); |
3603 | 0 | addNSItemToQuery(pstate, nsitem, false, true, true); |
3604 | | |
3605 | | /* Set up CreateStmtContext */ |
3606 | 0 | cxt.pstate = pstate; |
3607 | 0 | if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) |
3608 | 0 | { |
3609 | 0 | cxt.stmtType = "ALTER FOREIGN TABLE"; |
3610 | 0 | cxt.isforeign = true; |
3611 | 0 | } |
3612 | 0 | else |
3613 | 0 | { |
3614 | 0 | cxt.stmtType = "ALTER TABLE"; |
3615 | 0 | cxt.isforeign = false; |
3616 | 0 | } |
3617 | 0 | cxt.relation = stmt->relation; |
3618 | 0 | cxt.rel = rel; |
3619 | 0 | cxt.inhRelations = NIL; |
3620 | 0 | cxt.isalter = true; |
3621 | 0 | cxt.columns = NIL; |
3622 | 0 | cxt.ckconstraints = NIL; |
3623 | 0 | cxt.nnconstraints = NIL; |
3624 | 0 | cxt.fkconstraints = NIL; |
3625 | 0 | cxt.ixconstraints = NIL; |
3626 | 0 | cxt.likeclauses = NIL; |
3627 | 0 | cxt.blist = NIL; |
3628 | 0 | cxt.alist = NIL; |
3629 | 0 | cxt.pkey = NULL; |
3630 | 0 | cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); |
3631 | 0 | cxt.partbound = NULL; |
3632 | 0 | cxt.ofType = false; |
3633 | | |
3634 | | /* |
3635 | | * Transform ALTER subcommands that need it (most don't). These largely |
3636 | | * re-use code from CREATE TABLE. |
3637 | | */ |
3638 | 0 | foreach(lcmd, stmt->cmds) |
3639 | 0 | { |
3640 | 0 | AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); |
3641 | |
|
3642 | 0 | switch (cmd->subtype) |
3643 | 0 | { |
3644 | 0 | case AT_AddColumn: |
3645 | 0 | { |
3646 | 0 | ColumnDef *def = castNode(ColumnDef, cmd->def); |
3647 | |
|
3648 | 0 | transformColumnDefinition(&cxt, def); |
3649 | | |
3650 | | /* |
3651 | | * If the column has a non-null default, we can't skip |
3652 | | * validation of foreign keys. |
3653 | | */ |
3654 | 0 | if (def->raw_default != NULL) |
3655 | 0 | skipValidation = false; |
3656 | | |
3657 | | /* |
3658 | | * All constraints are processed in other ways. Remove the |
3659 | | * original list |
3660 | | */ |
3661 | 0 | def->constraints = NIL; |
3662 | |
|
3663 | 0 | newcmds = lappend(newcmds, cmd); |
3664 | 0 | break; |
3665 | 0 | } |
3666 | | |
3667 | 0 | case AT_AddConstraint: |
3668 | | |
3669 | | /* |
3670 | | * The original AddConstraint cmd node doesn't go to newcmds |
3671 | | */ |
3672 | 0 | if (IsA(cmd->def, Constraint)) |
3673 | 0 | { |
3674 | 0 | transformTableConstraint(&cxt, (Constraint *) cmd->def); |
3675 | 0 | if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN) |
3676 | 0 | skipValidation = false; |
3677 | 0 | } |
3678 | 0 | else |
3679 | 0 | elog(ERROR, "unrecognized node type: %d", |
3680 | 0 | (int) nodeTag(cmd->def)); |
3681 | 0 | break; |
3682 | | |
3683 | 0 | case AT_AlterColumnType: |
3684 | 0 | { |
3685 | 0 | ColumnDef *def = castNode(ColumnDef, cmd->def); |
3686 | 0 | AttrNumber attnum; |
3687 | | |
3688 | | /* |
3689 | | * For ALTER COLUMN TYPE, transform the USING clause if |
3690 | | * one was specified. |
3691 | | */ |
3692 | 0 | if (def->raw_default) |
3693 | 0 | { |
3694 | 0 | def->cooked_default = |
3695 | 0 | transformExpr(pstate, def->raw_default, |
3696 | 0 | EXPR_KIND_ALTER_COL_TRANSFORM); |
3697 | 0 | } |
3698 | | |
3699 | | /* |
3700 | | * For identity column, create ALTER SEQUENCE command to |
3701 | | * change the data type of the sequence. Identity sequence |
3702 | | * is associated with the top level partitioned table. |
3703 | | * Hence ignore partitions. |
3704 | | */ |
3705 | 0 | if (!RelationGetForm(rel)->relispartition) |
3706 | 0 | { |
3707 | 0 | attnum = get_attnum(relid, cmd->name); |
3708 | 0 | if (attnum == InvalidAttrNumber) |
3709 | 0 | ereport(ERROR, |
3710 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
3711 | 0 | errmsg("column \"%s\" of relation \"%s\" does not exist", |
3712 | 0 | cmd->name, RelationGetRelationName(rel)))); |
3713 | | |
3714 | 0 | if (attnum > 0 && |
3715 | 0 | TupleDescAttr(tupdesc, attnum - 1)->attidentity) |
3716 | 0 | { |
3717 | 0 | Oid seq_relid = getIdentitySequence(rel, attnum, false); |
3718 | 0 | Oid typeOid = typenameTypeId(pstate, def->typeName); |
3719 | 0 | AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt); |
3720 | |
|
3721 | 0 | altseqstmt->sequence |
3722 | 0 | = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)), |
3723 | 0 | get_rel_name(seq_relid), |
3724 | 0 | -1); |
3725 | 0 | altseqstmt->options = list_make1(makeDefElem("as", |
3726 | 0 | (Node *) makeTypeNameFromOid(typeOid, -1), |
3727 | 0 | -1)); |
3728 | 0 | altseqstmt->for_identity = true; |
3729 | 0 | cxt.blist = lappend(cxt.blist, altseqstmt); |
3730 | 0 | } |
3731 | 0 | } |
3732 | | |
3733 | 0 | newcmds = lappend(newcmds, cmd); |
3734 | 0 | break; |
3735 | 0 | } |
3736 | | |
3737 | 0 | case AT_AddIdentity: |
3738 | 0 | { |
3739 | 0 | Constraint *def = castNode(Constraint, cmd->def); |
3740 | 0 | ColumnDef *newdef = makeNode(ColumnDef); |
3741 | 0 | AttrNumber attnum; |
3742 | |
|
3743 | 0 | newdef->colname = cmd->name; |
3744 | 0 | newdef->identity = def->generated_when; |
3745 | 0 | cmd->def = (Node *) newdef; |
3746 | |
|
3747 | 0 | attnum = get_attnum(relid, cmd->name); |
3748 | 0 | if (attnum == InvalidAttrNumber) |
3749 | 0 | ereport(ERROR, |
3750 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
3751 | 0 | errmsg("column \"%s\" of relation \"%s\" does not exist", |
3752 | 0 | cmd->name, RelationGetRelationName(rel)))); |
3753 | | |
3754 | 0 | generateSerialExtraStmts(&cxt, newdef, |
3755 | 0 | get_atttype(relid, attnum), |
3756 | 0 | def->options, true, true, |
3757 | 0 | NULL, NULL); |
3758 | |
|
3759 | 0 | newcmds = lappend(newcmds, cmd); |
3760 | 0 | break; |
3761 | 0 | } |
3762 | | |
3763 | 0 | case AT_SetIdentity: |
3764 | 0 | { |
3765 | | /* |
3766 | | * Create an ALTER SEQUENCE statement for the internal |
3767 | | * sequence of the identity column. |
3768 | | */ |
3769 | 0 | ListCell *lc; |
3770 | 0 | List *newseqopts = NIL; |
3771 | 0 | List *newdef = NIL; |
3772 | 0 | AttrNumber attnum; |
3773 | 0 | Oid seq_relid; |
3774 | | |
3775 | | /* |
3776 | | * Split options into those handled by ALTER SEQUENCE and |
3777 | | * those for ALTER TABLE proper. |
3778 | | */ |
3779 | 0 | foreach(lc, castNode(List, cmd->def)) |
3780 | 0 | { |
3781 | 0 | DefElem *def = lfirst_node(DefElem, lc); |
3782 | |
|
3783 | 0 | if (strcmp(def->defname, "generated") == 0) |
3784 | 0 | newdef = lappend(newdef, def); |
3785 | 0 | else |
3786 | 0 | newseqopts = lappend(newseqopts, def); |
3787 | 0 | } |
3788 | |
|
3789 | 0 | attnum = get_attnum(relid, cmd->name); |
3790 | 0 | if (attnum == InvalidAttrNumber) |
3791 | 0 | ereport(ERROR, |
3792 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
3793 | 0 | errmsg("column \"%s\" of relation \"%s\" does not exist", |
3794 | 0 | cmd->name, RelationGetRelationName(rel)))); |
3795 | | |
3796 | 0 | seq_relid = getIdentitySequence(rel, attnum, true); |
3797 | |
|
3798 | 0 | if (seq_relid) |
3799 | 0 | { |
3800 | 0 | AlterSeqStmt *seqstmt; |
3801 | |
|
3802 | 0 | seqstmt = makeNode(AlterSeqStmt); |
3803 | 0 | seqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)), |
3804 | 0 | get_rel_name(seq_relid), -1); |
3805 | 0 | seqstmt->options = newseqopts; |
3806 | 0 | seqstmt->for_identity = true; |
3807 | 0 | seqstmt->missing_ok = false; |
3808 | |
|
3809 | 0 | cxt.blist = lappend(cxt.blist, seqstmt); |
3810 | 0 | } |
3811 | | |
3812 | | /* |
3813 | | * If column was not an identity column, we just let the |
3814 | | * ALTER TABLE command error out later. (There are cases |
3815 | | * this fails to cover, but we'll need to restructure |
3816 | | * where creation of the sequence dependency linkage |
3817 | | * happens before we can fix it.) |
3818 | | */ |
3819 | |
|
3820 | 0 | cmd->def = (Node *) newdef; |
3821 | 0 | newcmds = lappend(newcmds, cmd); |
3822 | 0 | break; |
3823 | 0 | } |
3824 | | |
3825 | 0 | case AT_AttachPartition: |
3826 | 0 | case AT_DetachPartition: |
3827 | 0 | { |
3828 | 0 | PartitionCmd *partcmd = (PartitionCmd *) cmd->def; |
3829 | |
|
3830 | 0 | transformPartitionCmd(&cxt, partcmd); |
3831 | | /* assign transformed value of the partition bound */ |
3832 | 0 | partcmd->bound = cxt.partbound; |
3833 | 0 | } |
3834 | |
|
3835 | 0 | newcmds = lappend(newcmds, cmd); |
3836 | 0 | break; |
3837 | | |
3838 | 0 | default: |
3839 | | |
3840 | | /* |
3841 | | * Currently, we shouldn't actually get here for subcommand |
3842 | | * types that don't require transformation; but if we do, just |
3843 | | * emit them unchanged. |
3844 | | */ |
3845 | 0 | newcmds = lappend(newcmds, cmd); |
3846 | 0 | break; |
3847 | 0 | } |
3848 | 0 | } |
3849 | | |
3850 | | /* |
3851 | | * Transfer anything we already have in cxt.alist into save_alist, to keep |
3852 | | * it separate from the output of transformIndexConstraints. |
3853 | | */ |
3854 | 0 | save_alist = cxt.alist; |
3855 | 0 | cxt.alist = NIL; |
3856 | | |
3857 | | /* Postprocess constraints */ |
3858 | 0 | transformIndexConstraints(&cxt); |
3859 | 0 | transformFKConstraints(&cxt, skipValidation, true); |
3860 | 0 | transformCheckConstraints(&cxt, false); |
3861 | | |
3862 | | /* |
3863 | | * Push any index-creation commands into the ALTER, so that they can be |
3864 | | * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that |
3865 | | * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint |
3866 | | * subcommand has already been through transformIndexStmt. |
3867 | | */ |
3868 | 0 | foreach(l, cxt.alist) |
3869 | 0 | { |
3870 | 0 | Node *istmt = (Node *) lfirst(l); |
3871 | | |
3872 | | /* |
3873 | | * We assume here that cxt.alist contains only IndexStmts generated |
3874 | | * from primary key constraints. |
3875 | | */ |
3876 | 0 | if (IsA(istmt, IndexStmt)) |
3877 | 0 | { |
3878 | 0 | IndexStmt *idxstmt = (IndexStmt *) istmt; |
3879 | |
|
3880 | 0 | idxstmt = transformIndexStmt(relid, idxstmt, queryString); |
3881 | 0 | newcmd = makeNode(AlterTableCmd); |
3882 | 0 | newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex; |
3883 | 0 | newcmd->def = (Node *) idxstmt; |
3884 | 0 | newcmds = lappend(newcmds, newcmd); |
3885 | 0 | } |
3886 | 0 | else |
3887 | 0 | elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt)); |
3888 | 0 | } |
3889 | 0 | cxt.alist = NIL; |
3890 | | |
3891 | | /* Append any CHECK, NOT NULL or FK constraints to the commands list */ |
3892 | 0 | foreach_node(Constraint, def, cxt.ckconstraints) |
3893 | 0 | { |
3894 | 0 | newcmd = makeNode(AlterTableCmd); |
3895 | 0 | newcmd->subtype = AT_AddConstraint; |
3896 | 0 | newcmd->def = (Node *) def; |
3897 | 0 | newcmds = lappend(newcmds, newcmd); |
3898 | 0 | } |
3899 | 0 | foreach_node(Constraint, def, cxt.nnconstraints) |
3900 | 0 | { |
3901 | 0 | newcmd = makeNode(AlterTableCmd); |
3902 | 0 | newcmd->subtype = AT_AddConstraint; |
3903 | 0 | newcmd->def = (Node *) def; |
3904 | 0 | newcmds = lappend(newcmds, newcmd); |
3905 | 0 | } |
3906 | 0 | foreach_node(Constraint, def, cxt.fkconstraints) |
3907 | 0 | { |
3908 | 0 | newcmd = makeNode(AlterTableCmd); |
3909 | 0 | newcmd->subtype = AT_AddConstraint; |
3910 | 0 | newcmd->def = (Node *) def; |
3911 | 0 | newcmds = lappend(newcmds, newcmd); |
3912 | 0 | } |
3913 | | |
3914 | | /* Close rel */ |
3915 | 0 | relation_close(rel, NoLock); |
3916 | | |
3917 | | /* |
3918 | | * Output results. |
3919 | | */ |
3920 | 0 | stmt->cmds = newcmds; |
3921 | |
|
3922 | 0 | *beforeStmts = cxt.blist; |
3923 | 0 | *afterStmts = list_concat(cxt.alist, save_alist); |
3924 | |
|
3925 | 0 | return stmt; |
3926 | 0 | } |
3927 | | |
3928 | | |
3929 | | /* |
3930 | | * Preprocess a list of column constraint clauses |
3931 | | * to attach constraint attributes to their primary constraint nodes |
3932 | | * and detect inconsistent/misplaced constraint attributes. |
3933 | | * |
3934 | | * NOTE: currently, attributes are only supported for FOREIGN KEY, UNIQUE, |
3935 | | * EXCLUSION, and PRIMARY KEY constraints, but someday they ought to be |
3936 | | * supported for other constraint types. |
3937 | | * |
3938 | | * NOTE: this must be idempotent in non-error cases; see |
3939 | | * transformCreateSchemaCreateTable. |
3940 | | */ |
3941 | | static void |
3942 | | transformConstraintAttrs(ParseState *pstate, List *constraintList) |
3943 | 0 | { |
3944 | 0 | Constraint *lastprimarycon = NULL; |
3945 | 0 | bool saw_deferrability = false; |
3946 | 0 | bool saw_initially = false; |
3947 | 0 | bool saw_enforced = false; |
3948 | 0 | ListCell *clist; |
3949 | |
|
3950 | 0 | #define SUPPORTS_ATTRS(node) \ |
3951 | 0 | ((node) != NULL && \ |
3952 | 0 | ((node)->contype == CONSTR_PRIMARY || \ |
3953 | 0 | (node)->contype == CONSTR_UNIQUE || \ |
3954 | 0 | (node)->contype == CONSTR_EXCLUSION || \ |
3955 | 0 | (node)->contype == CONSTR_FOREIGN)) |
3956 | |
|
3957 | 0 | foreach(clist, constraintList) |
3958 | 0 | { |
3959 | 0 | Constraint *con = (Constraint *) lfirst(clist); |
3960 | |
|
3961 | 0 | if (!IsA(con, Constraint)) |
3962 | 0 | elog(ERROR, "unrecognized node type: %d", |
3963 | 0 | (int) nodeTag(con)); |
3964 | 0 | switch (con->contype) |
3965 | 0 | { |
3966 | 0 | case CONSTR_ATTR_DEFERRABLE: |
3967 | 0 | if (!SUPPORTS_ATTRS(lastprimarycon)) |
3968 | 0 | ereport(ERROR, |
3969 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
3970 | 0 | errmsg("misplaced DEFERRABLE clause"), |
3971 | 0 | parser_errposition(pstate, con->location))); |
3972 | 0 | if (saw_deferrability) |
3973 | 0 | ereport(ERROR, |
3974 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
3975 | 0 | errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"), |
3976 | 0 | parser_errposition(pstate, con->location))); |
3977 | 0 | saw_deferrability = true; |
3978 | 0 | lastprimarycon->deferrable = true; |
3979 | 0 | break; |
3980 | | |
3981 | 0 | case CONSTR_ATTR_NOT_DEFERRABLE: |
3982 | 0 | if (!SUPPORTS_ATTRS(lastprimarycon)) |
3983 | 0 | ereport(ERROR, |
3984 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
3985 | 0 | errmsg("misplaced NOT DEFERRABLE clause"), |
3986 | 0 | parser_errposition(pstate, con->location))); |
3987 | 0 | if (saw_deferrability) |
3988 | 0 | ereport(ERROR, |
3989 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
3990 | 0 | errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"), |
3991 | 0 | parser_errposition(pstate, con->location))); |
3992 | 0 | saw_deferrability = true; |
3993 | 0 | lastprimarycon->deferrable = false; |
3994 | 0 | if (saw_initially && |
3995 | 0 | lastprimarycon->initdeferred) |
3996 | 0 | ereport(ERROR, |
3997 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
3998 | 0 | errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), |
3999 | 0 | parser_errposition(pstate, con->location))); |
4000 | 0 | break; |
4001 | | |
4002 | 0 | case CONSTR_ATTR_DEFERRED: |
4003 | 0 | if (!SUPPORTS_ATTRS(lastprimarycon)) |
4004 | 0 | ereport(ERROR, |
4005 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4006 | 0 | errmsg("misplaced INITIALLY DEFERRED clause"), |
4007 | 0 | parser_errposition(pstate, con->location))); |
4008 | 0 | if (saw_initially) |
4009 | 0 | ereport(ERROR, |
4010 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4011 | 0 | errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"), |
4012 | 0 | parser_errposition(pstate, con->location))); |
4013 | 0 | saw_initially = true; |
4014 | 0 | lastprimarycon->initdeferred = true; |
4015 | | |
4016 | | /* |
4017 | | * If only INITIALLY DEFERRED appears, assume DEFERRABLE |
4018 | | */ |
4019 | 0 | if (!saw_deferrability) |
4020 | 0 | lastprimarycon->deferrable = true; |
4021 | 0 | else if (!lastprimarycon->deferrable) |
4022 | 0 | ereport(ERROR, |
4023 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4024 | 0 | errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), |
4025 | 0 | parser_errposition(pstate, con->location))); |
4026 | 0 | break; |
4027 | | |
4028 | 0 | case CONSTR_ATTR_IMMEDIATE: |
4029 | 0 | if (!SUPPORTS_ATTRS(lastprimarycon)) |
4030 | 0 | ereport(ERROR, |
4031 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4032 | 0 | errmsg("misplaced INITIALLY IMMEDIATE clause"), |
4033 | 0 | parser_errposition(pstate, con->location))); |
4034 | 0 | if (saw_initially) |
4035 | 0 | ereport(ERROR, |
4036 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4037 | 0 | errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"), |
4038 | 0 | parser_errposition(pstate, con->location))); |
4039 | 0 | saw_initially = true; |
4040 | 0 | lastprimarycon->initdeferred = false; |
4041 | 0 | break; |
4042 | | |
4043 | 0 | case CONSTR_ATTR_ENFORCED: |
4044 | 0 | if (lastprimarycon == NULL || |
4045 | 0 | (lastprimarycon->contype != CONSTR_CHECK && |
4046 | 0 | lastprimarycon->contype != CONSTR_FOREIGN)) |
4047 | 0 | ereport(ERROR, |
4048 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4049 | 0 | errmsg("misplaced ENFORCED clause"), |
4050 | 0 | parser_errposition(pstate, con->location))); |
4051 | 0 | if (saw_enforced) |
4052 | 0 | ereport(ERROR, |
4053 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4054 | 0 | errmsg("multiple ENFORCED/NOT ENFORCED clauses not allowed"), |
4055 | 0 | parser_errposition(pstate, con->location))); |
4056 | 0 | saw_enforced = true; |
4057 | 0 | lastprimarycon->is_enforced = true; |
4058 | 0 | break; |
4059 | | |
4060 | 0 | case CONSTR_ATTR_NOT_ENFORCED: |
4061 | 0 | if (lastprimarycon == NULL || |
4062 | 0 | (lastprimarycon->contype != CONSTR_CHECK && |
4063 | 0 | lastprimarycon->contype != CONSTR_FOREIGN)) |
4064 | 0 | ereport(ERROR, |
4065 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4066 | 0 | errmsg("misplaced NOT ENFORCED clause"), |
4067 | 0 | parser_errposition(pstate, con->location))); |
4068 | 0 | if (saw_enforced) |
4069 | 0 | ereport(ERROR, |
4070 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
4071 | 0 | errmsg("multiple ENFORCED/NOT ENFORCED clauses not allowed"), |
4072 | 0 | parser_errposition(pstate, con->location))); |
4073 | 0 | saw_enforced = true; |
4074 | 0 | lastprimarycon->is_enforced = false; |
4075 | | |
4076 | | /* A NOT ENFORCED constraint must be marked as invalid. */ |
4077 | 0 | lastprimarycon->skip_validation = true; |
4078 | 0 | lastprimarycon->initially_valid = false; |
4079 | 0 | break; |
4080 | | |
4081 | 0 | default: |
4082 | | /* Otherwise it's not an attribute */ |
4083 | 0 | lastprimarycon = con; |
4084 | | /* reset flags for new primary node */ |
4085 | 0 | saw_deferrability = false; |
4086 | 0 | saw_initially = false; |
4087 | 0 | saw_enforced = false; |
4088 | 0 | break; |
4089 | 0 | } |
4090 | 0 | } |
4091 | 0 | } |
4092 | | |
4093 | | /* |
4094 | | * Special handling of type definition for a column |
4095 | | */ |
4096 | | static void |
4097 | | transformColumnType(CreateStmtContext *cxt, ColumnDef *column) |
4098 | 0 | { |
4099 | | /* |
4100 | | * All we really need to do here is verify that the type is valid, |
4101 | | * including any collation spec that might be present. |
4102 | | */ |
4103 | 0 | Type ctype = typenameType(cxt->pstate, column->typeName, NULL); |
4104 | |
|
4105 | 0 | if (column->collClause) |
4106 | 0 | { |
4107 | 0 | Form_pg_type typtup = (Form_pg_type) GETSTRUCT(ctype); |
4108 | |
|
4109 | 0 | LookupCollation(cxt->pstate, |
4110 | 0 | column->collClause->collname, |
4111 | 0 | column->collClause->location); |
4112 | | /* Complain if COLLATE is applied to an uncollatable type */ |
4113 | 0 | if (!OidIsValid(typtup->typcollation)) |
4114 | 0 | ereport(ERROR, |
4115 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
4116 | 0 | errmsg("collations are not supported by type %s", |
4117 | 0 | format_type_be(typtup->oid)), |
4118 | 0 | parser_errposition(cxt->pstate, |
4119 | 0 | column->collClause->location))); |
4120 | 0 | } |
4121 | | |
4122 | 0 | ReleaseSysCache(ctype); |
4123 | 0 | } |
4124 | | |
4125 | | |
4126 | | /* |
4127 | | * transformCreateSchemaStmtElements - |
4128 | | * analyzes the elements of a CREATE SCHEMA statement |
4129 | | * |
4130 | | * This presently has two responsibilities. We verify that no subcommands |
4131 | | * are trying to create objects outside the new schema. We also attempt to |
4132 | | * re-order the subcommands such that there are no forward references |
4133 | | * (e.g. GRANT to a table created later in the list). Note that the logic |
4134 | | * we use for determining forward references is presently quite incomplete, |
4135 | | * and it's unlikely that we can do significantly better while working with |
4136 | | * non-parse-analyzed commands. The only case that the SQL standard calls |
4137 | | * out as required is to support forward references in foreign-key constraint |
4138 | | * clauses in CREATE TABLE subcommands. We do handle that, by pulling out |
4139 | | * such clauses and converting them to ALTER TABLE ADD CONSTRAINT commands |
4140 | | * appended to the list. |
4141 | | * |
4142 | | * "schemaName" is the name of the schema that will be used for the creation |
4143 | | * of the objects listed. It may be obtained from the schema name defined |
4144 | | * in the statement or a role specification. |
4145 | | * |
4146 | | * The result is a list of parse nodes that still need to be analyzed --- |
4147 | | * but we can't analyze the later commands until we've executed the earlier |
4148 | | * ones, because of possible inter-object references. |
4149 | | * |
4150 | | * Note it's important that we not modify the input data structure. We create |
4151 | | * a new result List, and we copy any CREATE TABLE subcommands that we might |
4152 | | * modify. |
4153 | | */ |
4154 | | List * |
4155 | | transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, |
4156 | | const char *schemaName) |
4157 | 0 | { |
4158 | 0 | CreateSchemaStmtContext cxt; |
4159 | 0 | List *result; |
4160 | 0 | ListCell *elements; |
4161 | |
|
4162 | 0 | cxt.pstate = pstate; |
4163 | 0 | cxt.schemaname = schemaName; |
4164 | 0 | cxt.sequences = NIL; |
4165 | 0 | cxt.tables = NIL; |
4166 | 0 | cxt.views = NIL; |
4167 | 0 | cxt.indexes = NIL; |
4168 | 0 | cxt.triggers = NIL; |
4169 | 0 | cxt.grants = NIL; |
4170 | 0 | cxt.foreign_keys = NIL; |
4171 | | |
4172 | | /* |
4173 | | * Run through each schema element in the schema element list. Check |
4174 | | * target schema names, separate statements by type, and do preliminary |
4175 | | * analysis. |
4176 | | */ |
4177 | 0 | foreach(elements, schemaElts) |
4178 | 0 | { |
4179 | 0 | Node *element = lfirst(elements); |
4180 | |
|
4181 | 0 | switch (nodeTag(element)) |
4182 | 0 | { |
4183 | 0 | case T_CreateSeqStmt: |
4184 | 0 | { |
4185 | 0 | CreateSeqStmt *elp = (CreateSeqStmt *) element; |
4186 | |
|
4187 | 0 | checkSchemaNameRV(&cxt, elp->sequence); |
4188 | 0 | cxt.sequences = lappend(cxt.sequences, element); |
4189 | 0 | } |
4190 | 0 | break; |
4191 | | |
4192 | 0 | case T_CreateStmt: |
4193 | 0 | { |
4194 | 0 | CreateStmt *elp = (CreateStmt *) element; |
4195 | |
|
4196 | 0 | checkSchemaNameRV(&cxt, elp->relation); |
4197 | | /* Pull out any foreign key clauses, add to fk_elements */ |
4198 | 0 | elp = transformCreateSchemaCreateTable(pstate, |
4199 | 0 | elp, |
4200 | 0 | &cxt.foreign_keys); |
4201 | | |
4202 | | /* |
4203 | | * XXX todo: deal with other constraints |
4204 | | */ |
4205 | 0 | cxt.tables = lappend(cxt.tables, elp); |
4206 | 0 | } |
4207 | 0 | break; |
4208 | | |
4209 | 0 | case T_ViewStmt: |
4210 | 0 | { |
4211 | 0 | ViewStmt *elp = (ViewStmt *) element; |
4212 | |
|
4213 | 0 | checkSchemaNameRV(&cxt, elp->view); |
4214 | | |
4215 | | /* |
4216 | | * XXX todo: deal with references between views |
4217 | | */ |
4218 | 0 | cxt.views = lappend(cxt.views, element); |
4219 | 0 | } |
4220 | 0 | break; |
4221 | | |
4222 | 0 | case T_IndexStmt: |
4223 | 0 | { |
4224 | 0 | IndexStmt *elp = (IndexStmt *) element; |
4225 | |
|
4226 | 0 | checkSchemaNameRV(&cxt, elp->relation); |
4227 | 0 | cxt.indexes = lappend(cxt.indexes, element); |
4228 | 0 | } |
4229 | 0 | break; |
4230 | | |
4231 | 0 | case T_CreateTrigStmt: |
4232 | 0 | { |
4233 | 0 | CreateTrigStmt *elp = (CreateTrigStmt *) element; |
4234 | |
|
4235 | 0 | checkSchemaNameRV(&cxt, elp->relation); |
4236 | 0 | cxt.triggers = lappend(cxt.triggers, element); |
4237 | 0 | } |
4238 | 0 | break; |
4239 | | |
4240 | 0 | case T_GrantStmt: |
4241 | 0 | cxt.grants = lappend(cxt.grants, element); |
4242 | 0 | break; |
4243 | | |
4244 | 0 | default: |
4245 | 0 | elog(ERROR, "unrecognized node type: %d", |
4246 | 0 | (int) nodeTag(element)); |
4247 | 0 | } |
4248 | 0 | } |
4249 | | |
4250 | 0 | result = NIL; |
4251 | 0 | result = list_concat(result, cxt.sequences); |
4252 | 0 | result = list_concat(result, cxt.tables); |
4253 | 0 | result = list_concat(result, cxt.views); |
4254 | 0 | result = list_concat(result, cxt.indexes); |
4255 | 0 | result = list_concat(result, cxt.triggers); |
4256 | 0 | result = list_concat(result, cxt.grants); |
4257 | 0 | result = list_concat(result, cxt.foreign_keys); |
4258 | |
|
4259 | 0 | return result; |
4260 | 0 | } |
4261 | | |
4262 | | /* |
4263 | | * checkSchemaNameRV |
4264 | | * Check schema name in an element of a CREATE SCHEMA command, |
4265 | | * where the element's name is given by a RangeVar |
4266 | | * |
4267 | | * It's okay if the command doesn't specify a target schema name, because |
4268 | | * CreateSchemaCommand will set up the default creation schema to be the |
4269 | | * new schema. But if a target schema name is given, it had better match. |
4270 | | * We also have to check that the command doesn't say CREATE TEMP, since |
4271 | | * that would likewise put the object into the wrong schema. |
4272 | | */ |
4273 | | static void |
4274 | | checkSchemaNameRV(CreateSchemaStmtContext *cxt, RangeVar *relation) |
4275 | 0 | { |
4276 | 0 | if (relation->schemaname != NULL && |
4277 | 0 | strcmp(cxt->schemaname, relation->schemaname) != 0) |
4278 | 0 | ereport(ERROR, |
4279 | 0 | (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION), |
4280 | 0 | errmsg("CREATE specifies a schema (%s) " |
4281 | 0 | "different from the one being created (%s)", |
4282 | 0 | relation->schemaname, cxt->schemaname), |
4283 | 0 | parser_errposition(cxt->pstate, relation->location))); |
4284 | | |
4285 | 0 | if (relation->relpersistence == RELPERSISTENCE_TEMP) |
4286 | 0 | { |
4287 | | /* spell this error the same as in RangeVarAdjustRelationPersistence */ |
4288 | 0 | ereport(ERROR, |
4289 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4290 | 0 | errmsg("cannot create temporary relation in non-temporary schema"), |
4291 | 0 | parser_errposition(cxt->pstate, relation->location))); |
4292 | 0 | } |
4293 | 0 | } |
4294 | | |
4295 | | /* |
4296 | | * transformCreateSchemaCreateTable |
4297 | | * Process one CreateStmt for transformCreateSchemaStmtElements. |
4298 | | * |
4299 | | * We remove any foreign-key clauses in the statement and convert them into |
4300 | | * ALTER TABLE commands, which we append to *fk_elements. |
4301 | | */ |
4302 | | static CreateStmt * |
4303 | | transformCreateSchemaCreateTable(ParseState *pstate, |
4304 | | CreateStmt *stmt, |
4305 | | List **fk_elements) |
4306 | 0 | { |
4307 | 0 | CreateStmt *newstmt; |
4308 | 0 | List *newElts = NIL; |
4309 | 0 | ListCell *lc; |
4310 | | |
4311 | | /* |
4312 | | * Flat-copy the CreateStmt node, allowing us to replace its tableElts |
4313 | | * list without damaging the input data structure. Most sub-nodes will be |
4314 | | * shared with the input, though. |
4315 | | */ |
4316 | 0 | newstmt = makeNode(CreateStmt); |
4317 | 0 | memcpy(newstmt, stmt, sizeof(CreateStmt)); |
4318 | | |
4319 | | /* Scan for foreign-key constraints */ |
4320 | 0 | foreach(lc, stmt->tableElts) |
4321 | 0 | { |
4322 | 0 | Node *element = lfirst(lc); |
4323 | 0 | AlterTableStmt *alterstmt; |
4324 | 0 | AlterTableCmd *altercmd; |
4325 | |
|
4326 | 0 | if (IsA(element, Constraint)) |
4327 | 0 | { |
4328 | 0 | Constraint *constr = (Constraint *) element; |
4329 | |
|
4330 | 0 | if (constr->contype != CONSTR_FOREIGN) |
4331 | 0 | { |
4332 | | /* Other constraint types pass through unchanged */ |
4333 | 0 | newElts = lappend(newElts, constr); |
4334 | 0 | continue; |
4335 | 0 | } |
4336 | | |
4337 | | /* Make it into an ALTER TABLE ADD CONSTRAINT command */ |
4338 | 0 | altercmd = makeNode(AlterTableCmd); |
4339 | 0 | altercmd->subtype = AT_AddConstraint; |
4340 | 0 | altercmd->name = NULL; |
4341 | 0 | altercmd->def = (Node *) copyObject(constr); |
4342 | |
|
4343 | 0 | alterstmt = makeNode(AlterTableStmt); |
4344 | 0 | alterstmt->relation = copyObject(stmt->relation); |
4345 | 0 | alterstmt->cmds = list_make1(altercmd); |
4346 | 0 | alterstmt->objtype = OBJECT_TABLE; |
4347 | |
|
4348 | 0 | *fk_elements = lappend(*fk_elements, alterstmt); |
4349 | 0 | } |
4350 | 0 | else if (IsA(element, ColumnDef)) |
4351 | 0 | { |
4352 | 0 | ColumnDef *entry = (ColumnDef *) element; |
4353 | 0 | ColumnDef *newentry; |
4354 | 0 | List *entryconstraints; |
4355 | 0 | bool afterFK = false; |
4356 | | |
4357 | | /* |
4358 | | * We must preprocess the list of column constraints to attach |
4359 | | * attributes such as DEFERRED to the appropriate constraint node. |
4360 | | * Do this on a copy. (But execution of the CreateStmt will run |
4361 | | * transformConstraintAttrs on the copy, so we are nonetheless |
4362 | | * relying on transformConstraintAttrs to be idempotent.) |
4363 | | */ |
4364 | 0 | entryconstraints = copyObject(entry->constraints); |
4365 | 0 | transformConstraintAttrs(pstate, entryconstraints); |
4366 | | |
4367 | | /* Scan the column constraints ... */ |
4368 | 0 | foreach_node(Constraint, colconstr, entryconstraints) |
4369 | 0 | { |
4370 | 0 | switch (colconstr->contype) |
4371 | 0 | { |
4372 | 0 | case CONSTR_FOREIGN: |
4373 | | /* colconstr is already a copy, OK to modify */ |
4374 | 0 | colconstr->fk_attrs = list_make1(makeString(entry->colname)); |
4375 | | |
4376 | | /* Make it into an ALTER TABLE ADD CONSTRAINT command */ |
4377 | 0 | altercmd = makeNode(AlterTableCmd); |
4378 | 0 | altercmd->subtype = AT_AddConstraint; |
4379 | 0 | altercmd->name = NULL; |
4380 | 0 | altercmd->def = (Node *) colconstr; |
4381 | |
|
4382 | 0 | alterstmt = makeNode(AlterTableStmt); |
4383 | 0 | alterstmt->relation = copyObject(stmt->relation); |
4384 | 0 | alterstmt->cmds = list_make1(altercmd); |
4385 | 0 | alterstmt->objtype = OBJECT_TABLE; |
4386 | |
|
4387 | 0 | *fk_elements = lappend(*fk_elements, alterstmt); |
4388 | | |
4389 | | /* Remove the Constraint node from entryconstraints */ |
4390 | 0 | entryconstraints = |
4391 | 0 | foreach_delete_current(entryconstraints, colconstr); |
4392 | | |
4393 | | /* |
4394 | | * Immediately-following attribute constraints should |
4395 | | * be dropped, too. |
4396 | | */ |
4397 | 0 | afterFK = true; |
4398 | 0 | break; |
4399 | | |
4400 | | /* |
4401 | | * Column constraint lists separate a Constraint node |
4402 | | * from its attributes (e.g. NOT ENFORCED); so a |
4403 | | * column-level foreign key constraint may be |
4404 | | * represented by multiple Constraint nodes. After |
4405 | | * transformConstraintAttrs, the foreign key |
4406 | | * Constraint node contains all required information, |
4407 | | * making it okay to put into *fk_elements as a |
4408 | | * stand-alone Constraint. But since we removed the |
4409 | | * foreign key Constraint node from entryconstraints, |
4410 | | * we must remove any dependent attribute nodes too, |
4411 | | * else the later re-execution of |
4412 | | * transformConstraintAttrs will misbehave. |
4413 | | */ |
4414 | 0 | case CONSTR_ATTR_DEFERRABLE: |
4415 | 0 | case CONSTR_ATTR_NOT_DEFERRABLE: |
4416 | 0 | case CONSTR_ATTR_DEFERRED: |
4417 | 0 | case CONSTR_ATTR_IMMEDIATE: |
4418 | 0 | case CONSTR_ATTR_ENFORCED: |
4419 | 0 | case CONSTR_ATTR_NOT_ENFORCED: |
4420 | 0 | if (afterFK) |
4421 | 0 | entryconstraints = |
4422 | 0 | foreach_delete_current(entryconstraints, |
4423 | 0 | colconstr); |
4424 | 0 | break; |
4425 | | |
4426 | 0 | default: |
4427 | | /* Any following constraint attributes are unrelated */ |
4428 | 0 | afterFK = false; |
4429 | 0 | break; |
4430 | 0 | } |
4431 | 0 | } |
4432 | | |
4433 | | /* Now make a modified ColumnDef to put into newElts */ |
4434 | 0 | newentry = makeNode(ColumnDef); |
4435 | 0 | memcpy(newentry, entry, sizeof(ColumnDef)); |
4436 | 0 | newentry->constraints = entryconstraints; |
4437 | 0 | newElts = lappend(newElts, newentry); |
4438 | 0 | } |
4439 | 0 | else |
4440 | 0 | { |
4441 | | /* Other node types pass through unchanged */ |
4442 | 0 | newElts = lappend(newElts, element); |
4443 | 0 | } |
4444 | 0 | } |
4445 | | |
4446 | 0 | newstmt->tableElts = newElts; |
4447 | 0 | return newstmt; |
4448 | 0 | } |
4449 | | |
4450 | | /* |
4451 | | * transformPartitionCmd |
4452 | | * Analyze the ATTACH/DETACH PARTITION command |
4453 | | * |
4454 | | * In case of the ATTACH PARTITION command, cxt->partbound is set to the |
4455 | | * transformed value of cmd->bound. |
4456 | | */ |
4457 | | static void |
4458 | | transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) |
4459 | 0 | { |
4460 | 0 | Relation parentRel = cxt->rel; |
4461 | |
|
4462 | 0 | switch (parentRel->rd_rel->relkind) |
4463 | 0 | { |
4464 | 0 | case RELKIND_PARTITIONED_TABLE: |
4465 | | /* transform the partition bound, if any */ |
4466 | 0 | Assert(RelationGetPartitionKey(parentRel) != NULL); |
4467 | 0 | if (cmd->bound != NULL) |
4468 | 0 | cxt->partbound = transformPartitionBound(cxt->pstate, parentRel, |
4469 | 0 | cmd->bound); |
4470 | 0 | break; |
4471 | 0 | case RELKIND_PARTITIONED_INDEX: |
4472 | | |
4473 | | /* |
4474 | | * A partitioned index cannot have a partition bound set. ALTER |
4475 | | * INDEX prevents that with its grammar, but not ALTER TABLE. |
4476 | | */ |
4477 | 0 | if (cmd->bound != NULL) |
4478 | 0 | ereport(ERROR, |
4479 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
4480 | 0 | errmsg("\"%s\" is not a partitioned table", |
4481 | 0 | RelationGetRelationName(parentRel)))); |
4482 | 0 | break; |
4483 | 0 | case RELKIND_RELATION: |
4484 | | /* the table must be partitioned */ |
4485 | 0 | ereport(ERROR, |
4486 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
4487 | 0 | errmsg("table \"%s\" is not partitioned", |
4488 | 0 | RelationGetRelationName(parentRel)))); |
4489 | 0 | break; |
4490 | 0 | case RELKIND_INDEX: |
4491 | | /* the index must be partitioned */ |
4492 | 0 | ereport(ERROR, |
4493 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
4494 | 0 | errmsg("index \"%s\" is not partitioned", |
4495 | 0 | RelationGetRelationName(parentRel)))); |
4496 | 0 | break; |
4497 | 0 | default: |
4498 | | /* parser shouldn't let this case through */ |
4499 | 0 | elog(ERROR, "\"%s\" is not a partitioned table or index", |
4500 | 0 | RelationGetRelationName(parentRel)); |
4501 | 0 | break; |
4502 | 0 | } |
4503 | 0 | } |
4504 | | |
4505 | | /* |
4506 | | * transformPartitionBound |
4507 | | * |
4508 | | * Transform a partition bound specification |
4509 | | */ |
4510 | | PartitionBoundSpec * |
4511 | | transformPartitionBound(ParseState *pstate, Relation parent, |
4512 | | PartitionBoundSpec *spec) |
4513 | 0 | { |
4514 | 0 | PartitionBoundSpec *result_spec; |
4515 | 0 | PartitionKey key = RelationGetPartitionKey(parent); |
4516 | 0 | char strategy = get_partition_strategy(key); |
4517 | 0 | int partnatts = get_partition_natts(key); |
4518 | 0 | List *partexprs = get_partition_exprs(key); |
4519 | | |
4520 | | /* Avoid scribbling on input */ |
4521 | 0 | result_spec = copyObject(spec); |
4522 | |
|
4523 | 0 | if (spec->is_default) |
4524 | 0 | { |
4525 | | /* |
4526 | | * Hash partitioning does not support a default partition; there's no |
4527 | | * use case for it (since the set of partitions to create is perfectly |
4528 | | * defined), and if users do get into it accidentally, it's hard to |
4529 | | * back out from it afterwards. |
4530 | | */ |
4531 | 0 | if (strategy == PARTITION_STRATEGY_HASH) |
4532 | 0 | ereport(ERROR, |
4533 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4534 | 0 | errmsg("a hash-partitioned table may not have a default partition"))); |
4535 | | |
4536 | | /* |
4537 | | * In case of the default partition, parser had no way to identify the |
4538 | | * partition strategy. Assign the parent's strategy to the default |
4539 | | * partition bound spec. |
4540 | | */ |
4541 | 0 | result_spec->strategy = strategy; |
4542 | |
|
4543 | 0 | return result_spec; |
4544 | 0 | } |
4545 | | |
4546 | 0 | if (strategy == PARTITION_STRATEGY_HASH) |
4547 | 0 | { |
4548 | 0 | if (spec->strategy != PARTITION_STRATEGY_HASH) |
4549 | 0 | ereport(ERROR, |
4550 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4551 | 0 | errmsg("invalid bound specification for a hash partition"), |
4552 | 0 | parser_errposition(pstate, exprLocation((Node *) spec)))); |
4553 | | |
4554 | 0 | if (spec->modulus <= 0) |
4555 | 0 | ereport(ERROR, |
4556 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4557 | 0 | errmsg("modulus for hash partition must be an integer value greater than zero"))); |
4558 | | |
4559 | 0 | Assert(spec->remainder >= 0); |
4560 | |
|
4561 | 0 | if (spec->remainder >= spec->modulus) |
4562 | 0 | ereport(ERROR, |
4563 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4564 | 0 | errmsg("remainder for hash partition must be less than modulus"))); |
4565 | 0 | } |
4566 | 0 | else if (strategy == PARTITION_STRATEGY_LIST) |
4567 | 0 | { |
4568 | 0 | ListCell *cell; |
4569 | 0 | char *colname; |
4570 | 0 | Oid coltype; |
4571 | 0 | int32 coltypmod; |
4572 | 0 | Oid partcollation; |
4573 | |
|
4574 | 0 | if (spec->strategy != PARTITION_STRATEGY_LIST) |
4575 | 0 | ereport(ERROR, |
4576 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4577 | 0 | errmsg("invalid bound specification for a list partition"), |
4578 | 0 | parser_errposition(pstate, exprLocation((Node *) spec)))); |
4579 | | |
4580 | | /* Get the only column's name in case we need to output an error */ |
4581 | 0 | if (key->partattrs[0] != 0) |
4582 | 0 | colname = get_attname(RelationGetRelid(parent), |
4583 | 0 | key->partattrs[0], false); |
4584 | 0 | else |
4585 | 0 | colname = deparse_expression((Node *) linitial(partexprs), |
4586 | 0 | deparse_context_for(RelationGetRelationName(parent), |
4587 | 0 | RelationGetRelid(parent)), |
4588 | 0 | false, false); |
4589 | | /* Need its type data too */ |
4590 | 0 | coltype = get_partition_col_typid(key, 0); |
4591 | 0 | coltypmod = get_partition_col_typmod(key, 0); |
4592 | 0 | partcollation = get_partition_col_collation(key, 0); |
4593 | |
|
4594 | 0 | result_spec->listdatums = NIL; |
4595 | 0 | foreach(cell, spec->listdatums) |
4596 | 0 | { |
4597 | 0 | Node *expr = lfirst(cell); |
4598 | 0 | Const *value; |
4599 | 0 | ListCell *cell2; |
4600 | 0 | bool duplicate; |
4601 | |
|
4602 | 0 | value = transformPartitionBoundValue(pstate, expr, |
4603 | 0 | colname, coltype, coltypmod, |
4604 | 0 | partcollation); |
4605 | | |
4606 | | /* Don't add to the result if the value is a duplicate */ |
4607 | 0 | duplicate = false; |
4608 | 0 | foreach(cell2, result_spec->listdatums) |
4609 | 0 | { |
4610 | 0 | Const *value2 = lfirst_node(Const, cell2); |
4611 | |
|
4612 | 0 | if (equal(value, value2)) |
4613 | 0 | { |
4614 | 0 | duplicate = true; |
4615 | 0 | break; |
4616 | 0 | } |
4617 | 0 | } |
4618 | 0 | if (duplicate) |
4619 | 0 | continue; |
4620 | | |
4621 | 0 | result_spec->listdatums = lappend(result_spec->listdatums, |
4622 | 0 | value); |
4623 | 0 | } |
4624 | 0 | } |
4625 | 0 | else if (strategy == PARTITION_STRATEGY_RANGE) |
4626 | 0 | { |
4627 | 0 | if (spec->strategy != PARTITION_STRATEGY_RANGE) |
4628 | 0 | ereport(ERROR, |
4629 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4630 | 0 | errmsg("invalid bound specification for a range partition"), |
4631 | 0 | parser_errposition(pstate, exprLocation((Node *) spec)))); |
4632 | | |
4633 | 0 | if (list_length(spec->lowerdatums) != partnatts) |
4634 | 0 | ereport(ERROR, |
4635 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4636 | 0 | errmsg("FROM must specify exactly one value per partitioning column"))); |
4637 | 0 | if (list_length(spec->upperdatums) != partnatts) |
4638 | 0 | ereport(ERROR, |
4639 | 0 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
4640 | 0 | errmsg("TO must specify exactly one value per partitioning column"))); |
4641 | | |
4642 | | /* |
4643 | | * Convert raw parse nodes into PartitionRangeDatum nodes and perform |
4644 | | * any necessary validation. |
4645 | | */ |
4646 | 0 | result_spec->lowerdatums = |
4647 | 0 | transformPartitionRangeBounds(pstate, spec->lowerdatums, |
4648 | 0 | parent); |
4649 | 0 | result_spec->upperdatums = |
4650 | 0 | transformPartitionRangeBounds(pstate, spec->upperdatums, |
4651 | 0 | parent); |
4652 | 0 | } |
4653 | 0 | else |
4654 | 0 | elog(ERROR, "unexpected partition strategy: %d", (int) strategy); |
4655 | | |
4656 | 0 | return result_spec; |
4657 | 0 | } |
4658 | | |
4659 | | /* |
4660 | | * transformPartitionRangeBounds |
4661 | | * This converts the expressions for range partition bounds from the raw |
4662 | | * grammar representation to PartitionRangeDatum structs |
4663 | | */ |
4664 | | static List * |
4665 | | transformPartitionRangeBounds(ParseState *pstate, List *blist, |
4666 | | Relation parent) |
4667 | 0 | { |
4668 | 0 | List *result = NIL; |
4669 | 0 | PartitionKey key = RelationGetPartitionKey(parent); |
4670 | 0 | List *partexprs = get_partition_exprs(key); |
4671 | 0 | ListCell *lc; |
4672 | 0 | int i, |
4673 | 0 | j; |
4674 | |
|
4675 | 0 | j = 0; |
4676 | 0 | foreach(lc, blist) |
4677 | 0 | { |
4678 | 0 | Node *expr = lfirst(lc); |
4679 | 0 | PartitionRangeDatum *prd = NULL; |
4680 | |
|
4681 | 0 | i = foreach_current_index(lc); |
4682 | | |
4683 | | /* |
4684 | | * Infinite range bounds -- "minvalue" and "maxvalue" -- get passed in |
4685 | | * as ColumnRefs. |
4686 | | */ |
4687 | 0 | if (IsA(expr, ColumnRef)) |
4688 | 0 | { |
4689 | 0 | ColumnRef *cref = (ColumnRef *) expr; |
4690 | 0 | char *cname = NULL; |
4691 | | |
4692 | | /* |
4693 | | * There should be a single field named either "minvalue" or |
4694 | | * "maxvalue". |
4695 | | */ |
4696 | 0 | if (list_length(cref->fields) == 1 && |
4697 | 0 | IsA(linitial(cref->fields), String)) |
4698 | 0 | cname = strVal(linitial(cref->fields)); |
4699 | |
|
4700 | 0 | if (cname == NULL) |
4701 | 0 | { |
4702 | | /* |
4703 | | * ColumnRef is not in the desired single-field-name form. For |
4704 | | * consistency between all partition strategies, let the |
4705 | | * expression transformation report any errors rather than |
4706 | | * doing it ourselves. |
4707 | | */ |
4708 | 0 | } |
4709 | 0 | else if (strcmp("minvalue", cname) == 0) |
4710 | 0 | { |
4711 | 0 | prd = makeNode(PartitionRangeDatum); |
4712 | 0 | prd->kind = PARTITION_RANGE_DATUM_MINVALUE; |
4713 | 0 | prd->value = NULL; |
4714 | 0 | } |
4715 | 0 | else if (strcmp("maxvalue", cname) == 0) |
4716 | 0 | { |
4717 | 0 | prd = makeNode(PartitionRangeDatum); |
4718 | 0 | prd->kind = PARTITION_RANGE_DATUM_MAXVALUE; |
4719 | 0 | prd->value = NULL; |
4720 | 0 | } |
4721 | 0 | } |
4722 | |
|
4723 | 0 | if (prd == NULL) |
4724 | 0 | { |
4725 | 0 | char *colname; |
4726 | 0 | Oid coltype; |
4727 | 0 | int32 coltypmod; |
4728 | 0 | Oid partcollation; |
4729 | 0 | Const *value; |
4730 | | |
4731 | | /* Get the column's name in case we need to output an error */ |
4732 | 0 | if (key->partattrs[i] != 0) |
4733 | 0 | colname = get_attname(RelationGetRelid(parent), |
4734 | 0 | key->partattrs[i], false); |
4735 | 0 | else |
4736 | 0 | { |
4737 | 0 | colname = deparse_expression((Node *) list_nth(partexprs, j), |
4738 | 0 | deparse_context_for(RelationGetRelationName(parent), |
4739 | 0 | RelationGetRelid(parent)), |
4740 | 0 | false, false); |
4741 | 0 | ++j; |
4742 | 0 | } |
4743 | | |
4744 | | /* Need its type data too */ |
4745 | 0 | coltype = get_partition_col_typid(key, i); |
4746 | 0 | coltypmod = get_partition_col_typmod(key, i); |
4747 | 0 | partcollation = get_partition_col_collation(key, i); |
4748 | |
|
4749 | 0 | value = transformPartitionBoundValue(pstate, expr, |
4750 | 0 | colname, |
4751 | 0 | coltype, coltypmod, |
4752 | 0 | partcollation); |
4753 | 0 | if (value->constisnull) |
4754 | 0 | ereport(ERROR, |
4755 | 0 | (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), |
4756 | 0 | errmsg("cannot specify NULL in range bound"))); |
4757 | 0 | prd = makeNode(PartitionRangeDatum); |
4758 | 0 | prd->kind = PARTITION_RANGE_DATUM_VALUE; |
4759 | 0 | prd->value = (Node *) value; |
4760 | 0 | } |
4761 | | |
4762 | 0 | prd->location = exprLocation(expr); |
4763 | |
|
4764 | 0 | result = lappend(result, prd); |
4765 | 0 | } |
4766 | | |
4767 | | /* |
4768 | | * Once we see MINVALUE or MAXVALUE for one column, the remaining columns |
4769 | | * must be the same. |
4770 | | */ |
4771 | 0 | validateInfiniteBounds(pstate, result); |
4772 | |
|
4773 | 0 | return result; |
4774 | 0 | } |
4775 | | |
4776 | | /* |
4777 | | * validateInfiniteBounds |
4778 | | * |
4779 | | * Check that a MAXVALUE or MINVALUE specification in a partition bound is |
4780 | | * followed only by more of the same. |
4781 | | */ |
4782 | | static void |
4783 | | validateInfiniteBounds(ParseState *pstate, List *blist) |
4784 | 0 | { |
4785 | 0 | ListCell *lc; |
4786 | 0 | PartitionRangeDatumKind kind = PARTITION_RANGE_DATUM_VALUE; |
4787 | |
|
4788 | 0 | foreach(lc, blist) |
4789 | 0 | { |
4790 | 0 | PartitionRangeDatum *prd = lfirst_node(PartitionRangeDatum, lc); |
4791 | |
|
4792 | 0 | if (kind == prd->kind) |
4793 | 0 | continue; |
4794 | | |
4795 | 0 | switch (kind) |
4796 | 0 | { |
4797 | 0 | case PARTITION_RANGE_DATUM_VALUE: |
4798 | 0 | kind = prd->kind; |
4799 | 0 | break; |
4800 | | |
4801 | 0 | case PARTITION_RANGE_DATUM_MAXVALUE: |
4802 | 0 | ereport(ERROR, |
4803 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
4804 | 0 | errmsg("every bound following MAXVALUE must also be MAXVALUE"), |
4805 | 0 | parser_errposition(pstate, exprLocation((Node *) prd)))); |
4806 | 0 | break; |
4807 | | |
4808 | 0 | case PARTITION_RANGE_DATUM_MINVALUE: |
4809 | 0 | ereport(ERROR, |
4810 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
4811 | 0 | errmsg("every bound following MINVALUE must also be MINVALUE"), |
4812 | 0 | parser_errposition(pstate, exprLocation((Node *) prd)))); |
4813 | 0 | break; |
4814 | 0 | } |
4815 | 0 | } |
4816 | 0 | } |
4817 | | |
4818 | | /* |
4819 | | * Transform one entry in a partition bound spec, producing a constant. |
4820 | | */ |
4821 | | static Const * |
4822 | | transformPartitionBoundValue(ParseState *pstate, Node *val, |
4823 | | const char *colName, Oid colType, int32 colTypmod, |
4824 | | Oid partCollation) |
4825 | 0 | { |
4826 | 0 | Node *value; |
4827 | | |
4828 | | /* Transform raw parsetree */ |
4829 | 0 | value = transformExpr(pstate, val, EXPR_KIND_PARTITION_BOUND); |
4830 | | |
4831 | | /* |
4832 | | * transformExpr() should have already rejected column references, |
4833 | | * subqueries, aggregates, window functions, and SRFs, based on the |
4834 | | * EXPR_KIND_ of a partition bound expression. |
4835 | | */ |
4836 | 0 | Assert(!contain_var_clause(value)); |
4837 | | |
4838 | | /* |
4839 | | * Coerce to the correct type. This might cause an explicit coercion step |
4840 | | * to be added on top of the expression, which must be evaluated before |
4841 | | * returning the result to the caller. |
4842 | | */ |
4843 | 0 | value = coerce_to_target_type(pstate, |
4844 | 0 | value, exprType(value), |
4845 | 0 | colType, |
4846 | 0 | colTypmod, |
4847 | 0 | COERCION_ASSIGNMENT, |
4848 | 0 | COERCE_IMPLICIT_CAST, |
4849 | 0 | -1); |
4850 | |
|
4851 | 0 | if (value == NULL) |
4852 | 0 | ereport(ERROR, |
4853 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
4854 | 0 | errmsg("specified value cannot be cast to type %s for column \"%s\"", |
4855 | 0 | format_type_be(colType), colName), |
4856 | 0 | parser_errposition(pstate, exprLocation(val)))); |
4857 | | |
4858 | | /* |
4859 | | * Evaluate the expression, if needed, assigning the partition key's data |
4860 | | * type and collation to the resulting Const node. |
4861 | | */ |
4862 | 0 | if (!IsA(value, Const)) |
4863 | 0 | { |
4864 | 0 | assign_expr_collations(pstate, value); |
4865 | 0 | value = (Node *) expression_planner((Expr *) value); |
4866 | 0 | value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod, |
4867 | 0 | partCollation); |
4868 | 0 | if (!IsA(value, Const)) |
4869 | 0 | elog(ERROR, "could not evaluate partition bound expression"); |
4870 | 0 | } |
4871 | 0 | else |
4872 | 0 | { |
4873 | | /* |
4874 | | * If the expression is already a Const, as is often the case, we can |
4875 | | * skip the rather expensive steps above. But we still have to insert |
4876 | | * the right collation, since coerce_to_target_type doesn't handle |
4877 | | * that. |
4878 | | */ |
4879 | 0 | ((Const *) value)->constcollid = partCollation; |
4880 | 0 | } |
4881 | | |
4882 | | /* |
4883 | | * Attach original expression's parse location to the Const, so that |
4884 | | * that's what will be reported for any later errors related to this |
4885 | | * partition bound. |
4886 | | */ |
4887 | 0 | ((Const *) value)->location = exprLocation(val); |
4888 | |
|
4889 | 0 | return (Const *) value; |
4890 | 0 | } |