/Users/deen/code/yugabyte-db/src/postgres/src/backend/commands/view.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * view.c |
4 | | * use rewrite rules to construct views |
5 | | * |
6 | | * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/commands/view.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include "access/heapam.h" |
18 | | #include "access/xact.h" |
19 | | #include "catalog/namespace.h" |
20 | | #include "commands/defrem.h" |
21 | | #include "commands/tablecmds.h" |
22 | | #include "commands/view.h" |
23 | | #include "miscadmin.h" |
24 | | #include "nodes/makefuncs.h" |
25 | | #include "nodes/nodeFuncs.h" |
26 | | #include "parser/analyze.h" |
27 | | #include "parser/parse_relation.h" |
28 | | #include "rewrite/rewriteDefine.h" |
29 | | #include "rewrite/rewriteManip.h" |
30 | | #include "rewrite/rewriteHandler.h" |
31 | | #include "rewrite/rewriteSupport.h" |
32 | | #include "utils/acl.h" |
33 | | #include "utils/builtins.h" |
34 | | #include "utils/lsyscache.h" |
35 | | #include "utils/rel.h" |
36 | | #include "utils/syscache.h" |
37 | | |
38 | | /* YB includes. */ |
39 | | #include "catalog/catalog.h" |
40 | | #include "pg_yb_utils.h" |
41 | | |
42 | | |
43 | | static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc); |
44 | | |
45 | | /*--------------------------------------------------------------------- |
46 | | * Validator for "check_option" reloption on views. The allowed values |
47 | | * are "local" and "cascaded". |
48 | | */ |
49 | | void |
50 | | validateWithCheckOption(const char *value) |
51 | 0 | { |
52 | 0 | if (value == NULL || |
53 | 0 | (strcmp(value, "local") != 0 && |
54 | 0 | strcmp(value, "cascaded") != 0)) |
55 | 0 | { |
56 | 0 | ereport(ERROR, |
57 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
58 | 0 | errmsg("invalid value for \"check_option\" option"), |
59 | 0 | errdetail("Valid values are \"local\" and \"cascaded\"."))); |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | | /*--------------------------------------------------------------------- |
64 | | * DefineVirtualRelation |
65 | | * |
66 | | * Create a view relation and use the rules system to store the query |
67 | | * for the view. |
68 | | * |
69 | | * EventTriggerAlterTableStart must have been called already. |
70 | | *--------------------------------------------------------------------- |
71 | | */ |
72 | | static ObjectAddress |
73 | | DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, |
74 | | List *options, Query *viewParse) |
75 | 475 | { |
76 | 475 | Oid viewOid; |
77 | 475 | LOCKMODE lockmode; |
78 | 475 | CreateStmt *createStmt = makeNode(CreateStmt); |
79 | 0 | List *attrList; |
80 | 475 | ListCell *t; |
81 | | |
82 | | /* |
83 | | * create a list of ColumnDef nodes based on the names and types of the |
84 | | * (non-junk) targetlist items from the view's SELECT list. |
85 | | */ |
86 | 475 | attrList = NIL; |
87 | 475 | foreach(t, tlist) |
88 | 4.19k | { |
89 | 4.19k | TargetEntry *tle = (TargetEntry *) lfirst(t); |
90 | | |
91 | 4.19k | if (!tle->resjunk) |
92 | 4.17k | { |
93 | 4.17k | ColumnDef *def = makeColumnDef(tle->resname, |
94 | 4.17k | exprType((Node *) tle->expr), |
95 | 4.17k | exprTypmod((Node *) tle->expr), |
96 | 4.17k | exprCollation((Node *) tle->expr)); |
97 | | |
98 | | /* |
99 | | * It's possible that the column is of a collatable type but the |
100 | | * collation could not be resolved, so double-check. |
101 | | */ |
102 | 4.17k | if (type_is_collatable(exprType((Node *) tle->expr))) |
103 | 1.74k | { |
104 | 1.74k | if (!OidIsValid(def->collOid)) |
105 | 1.74k | ereport(ERROR, |
106 | 1.74k | (errcode(ERRCODE_INDETERMINATE_COLLATION), |
107 | 1.74k | errmsg("could not determine which collation to use for view column \"%s\"", |
108 | 1.74k | def->colname), |
109 | 1.74k | errhint("Use the COLLATE clause to set the collation explicitly."))); |
110 | 1.74k | } |
111 | 2.42k | else |
112 | 4.17k | Assert(!OidIsValid(def->collOid)); |
113 | | |
114 | 4.17k | attrList = lappend(attrList, def); |
115 | 4.17k | } |
116 | 4.19k | } |
117 | | |
118 | 475 | if (attrList == NIL) |
119 | 475 | ereport(ERROR, |
120 | 475 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
121 | 475 | errmsg("view must have at least one column"))); |
122 | | |
123 | | /* |
124 | | * Look up, check permissions on, and lock the creation namespace; also |
125 | | * check for a preexisting view with the same name. This will also set |
126 | | * relation->relpersistence to RELPERSISTENCE_TEMP if the selected |
127 | | * namespace is temporary. |
128 | | */ |
129 | 475 | lockmode = replace ? AccessExclusiveLock11 : NoLock464 ; |
130 | 475 | (void) RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid); |
131 | | |
132 | | /* |
133 | | * In YB, system views can only be created during initdb and YSQL upgrade. |
134 | | */ |
135 | 475 | if (IsYugaByteEnabled() && |
136 | 475 | !IsYsqlUpgrade && |
137 | 475 | !462 IsBootstrapProcessingMode462 () && |
138 | 475 | !YBIsPreparingTemplates()462 && |
139 | 475 | YbIsSystemNamespaceByName(relation->schemaname)222 ) |
140 | 0 | { |
141 | 0 | ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
142 | 0 | errmsg("system views cannot be created outside of YSQL upgrade"))); |
143 | 0 | } |
144 | | |
145 | 475 | if (OidIsValid(viewOid) && replace2 ) |
146 | 2 | { |
147 | 2 | Relation rel; |
148 | 2 | TupleDesc descriptor; |
149 | 2 | List *atcmds = NIL; |
150 | 2 | AlterTableCmd *atcmd; |
151 | 2 | ObjectAddress address; |
152 | | |
153 | | /* Relation is already locked, but we must build a relcache entry. */ |
154 | 2 | rel = relation_open(viewOid, NoLock); |
155 | | |
156 | | /* Make sure it *is* a view. */ |
157 | 2 | if (rel->rd_rel->relkind != RELKIND_VIEW) |
158 | 2 | ereport(ERROR, |
159 | 2 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
160 | 2 | errmsg("\"%s\" is not a view", |
161 | 2 | RelationGetRelationName(rel)))); |
162 | | |
163 | | /* Also check it's not in use already */ |
164 | 2 | CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); |
165 | | |
166 | | /* |
167 | | * Due to the namespace visibility rules for temporary objects, we |
168 | | * should only end up replacing a temporary view with another |
169 | | * temporary view, and similarly for permanent views. |
170 | | */ |
171 | 2 | Assert(relation->relpersistence == rel->rd_rel->relpersistence); |
172 | | |
173 | | /* |
174 | | * Create a tuple descriptor to compare against the existing view, and |
175 | | * verify that the old column list is an initial prefix of the new |
176 | | * column list. |
177 | | */ |
178 | 2 | descriptor = BuildDescForRelation(attrList); |
179 | 2 | checkViewTupleDesc(descriptor, rel->rd_att); |
180 | | |
181 | | /* |
182 | | * If new attributes have been added, we must add pg_attribute entries |
183 | | * for them. It is convenient (although overkill) to use the ALTER |
184 | | * TABLE ADD COLUMN infrastructure for this. |
185 | | * |
186 | | * Note that we must do this before updating the query for the view, |
187 | | * since the rules system requires that the correct view columns be in |
188 | | * place when defining the new rules. |
189 | | */ |
190 | 2 | if (list_length(attrList) > rel->rd_att->natts) |
191 | 1 | { |
192 | 1 | ListCell *c; |
193 | 1 | int skip = rel->rd_att->natts; |
194 | | |
195 | 1 | foreach(c, attrList) |
196 | 3 | { |
197 | 3 | if (skip > 0) |
198 | 1 | { |
199 | 1 | skip--; |
200 | 1 | continue; |
201 | 1 | } |
202 | 2 | atcmd = makeNode(AlterTableCmd); |
203 | 0 | atcmd->subtype = AT_AddColumnToView; |
204 | 2 | atcmd->def = (Node *) lfirst(c); |
205 | 2 | atcmds = lappend(atcmds, atcmd); |
206 | 2 | } |
207 | | |
208 | | /* EventTriggerAlterTableStart called by ProcessUtilitySlow */ |
209 | 1 | AlterTableInternal(viewOid, atcmds, true); |
210 | | |
211 | | /* Make the new view columns visible */ |
212 | 1 | CommandCounterIncrement(); |
213 | 1 | } |
214 | | |
215 | | /* |
216 | | * Update the query for the view. |
217 | | * |
218 | | * Note that we must do this before updating the view options, because |
219 | | * the new options may not be compatible with the old view query (for |
220 | | * example if we attempt to add the WITH CHECK OPTION, we require that |
221 | | * the new view be automatically updatable, but the old view may not |
222 | | * have been). |
223 | | */ |
224 | 2 | StoreViewQuery(viewOid, viewParse, replace); |
225 | | |
226 | | /* Make the new view query visible */ |
227 | 2 | CommandCounterIncrement(); |
228 | | |
229 | | /* |
230 | | * Finally update the view options. |
231 | | * |
232 | | * The new options list replaces the existing options list, even if |
233 | | * it's empty. |
234 | | */ |
235 | 2 | atcmd = makeNode(AlterTableCmd); |
236 | 0 | atcmd->subtype = AT_ReplaceRelOptions; |
237 | 2 | atcmd->def = (Node *) options; |
238 | 2 | atcmds = list_make1(atcmd); |
239 | | |
240 | | /* EventTriggerAlterTableStart called by ProcessUtilitySlow */ |
241 | 2 | AlterTableInternal(viewOid, atcmds, true); |
242 | | |
243 | 2 | ObjectAddressSet(address, RelationRelationId, viewOid); |
244 | | |
245 | | /* |
246 | | * Seems okay, so return the OID of the pre-existing view. |
247 | | */ |
248 | 2 | relation_close(rel, NoLock); /* keep the lock! */ |
249 | | |
250 | 2 | return address; |
251 | 2 | } |
252 | 473 | else |
253 | 473 | { |
254 | 473 | ObjectAddress address; |
255 | | |
256 | | /* |
257 | | * Set the parameters for keys/inheritance etc. All of these are |
258 | | * uninteresting for views... |
259 | | */ |
260 | 473 | createStmt->relation = relation; |
261 | 473 | createStmt->tableElts = attrList; |
262 | 473 | createStmt->inhRelations = NIL; |
263 | 473 | createStmt->constraints = NIL; |
264 | 473 | createStmt->options = options; |
265 | 473 | createStmt->oncommit = ONCOMMIT_NOOP; |
266 | 473 | createStmt->tablespacename = NULL; |
267 | 473 | createStmt->if_not_exists = false; |
268 | | |
269 | | /* |
270 | | * Create the relation (this will error out if there's an existing |
271 | | * view, so we don't need more code to complain if "replace" is |
272 | | * false). |
273 | | */ |
274 | 473 | address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL, |
275 | 473 | NULL); |
276 | 473 | Assert(address.objectId != InvalidOid); |
277 | | |
278 | | /* Make the new view relation visible */ |
279 | 473 | CommandCounterIncrement(); |
280 | | |
281 | | /* Store the query for the view */ |
282 | 473 | StoreViewQuery(address.objectId, viewParse, replace); |
283 | | |
284 | 473 | return address; |
285 | 473 | } |
286 | 475 | } |
287 | | |
288 | | /* |
289 | | * Verify that tupledesc associated with proposed new view definition |
290 | | * matches tupledesc of old view. This is basically a cut-down version |
291 | | * of equalTupleDescs(), with code added to generate specific complaints. |
292 | | * Also, we allow the new tupledesc to have more columns than the old. |
293 | | */ |
294 | | static void |
295 | | checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc) |
296 | 2 | { |
297 | 2 | int i; |
298 | | |
299 | 2 | if (newdesc->natts < olddesc->natts) |
300 | 2 | ereport(ERROR, |
301 | 2 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
302 | 2 | errmsg("cannot drop columns from view"))); |
303 | | /* we can ignore tdhasoid */ |
304 | | |
305 | 4 | for (i = 0; 2 i < olddesc->natts; i++2 ) |
306 | 2 | { |
307 | 2 | Form_pg_attribute newattr = TupleDescAttr(newdesc, i); |
308 | 2 | Form_pg_attribute oldattr = TupleDescAttr(olddesc, i); |
309 | | |
310 | | /* XXX msg not right, but we don't support DROP COL on view anyway */ |
311 | 2 | if (newattr->attisdropped != oldattr->attisdropped) |
312 | 2 | ereport(ERROR, |
313 | 2 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
314 | 2 | errmsg("cannot drop columns from view"))); |
315 | | |
316 | 2 | if (strcmp(NameStr(newattr->attname), NameStr(oldattr->attname)) != 0) |
317 | 2 | ereport(ERROR, |
318 | 2 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
319 | 2 | errmsg("cannot change name of view column \"%s\" to \"%s\"", |
320 | 2 | NameStr(oldattr->attname), |
321 | 2 | NameStr(newattr->attname)))); |
322 | | /* XXX would it be safe to allow atttypmod to change? Not sure */ |
323 | 2 | if (newattr->atttypid != oldattr->atttypid || |
324 | 2 | newattr->atttypmod != oldattr->atttypmod) |
325 | 2 | ereport(ERROR, |
326 | 2 | (errcode(ERRCODE_INVALID_TABLE_DEFINITION), |
327 | 2 | errmsg("cannot change data type of view column \"%s\" from %s to %s", |
328 | 2 | NameStr(oldattr->attname), |
329 | 2 | format_type_with_typemod(oldattr->atttypid, |
330 | 2 | oldattr->atttypmod), |
331 | 2 | format_type_with_typemod(newattr->atttypid, |
332 | 2 | newattr->atttypmod)))); |
333 | | /* We can ignore the remaining attributes of an attribute... */ |
334 | 2 | } |
335 | | |
336 | | /* |
337 | | * We ignore the constraint fields. The new view desc can't have any |
338 | | * constraints, and the only ones that could be on the old view are |
339 | | * defaults, which we are happy to leave in place. |
340 | | */ |
341 | 2 | } |
342 | | |
343 | | static void |
344 | | DefineViewRules(Oid viewOid, Query *viewParse, bool replace) |
345 | 511 | { |
346 | | /* |
347 | | * Set up the ON SELECT rule. Since the query has already been through |
348 | | * parse analysis, we use DefineQueryRewrite() directly. |
349 | | */ |
350 | 511 | DefineQueryRewrite(pstrdup(ViewSelectRuleName), |
351 | 511 | viewOid, |
352 | 511 | NULL, |
353 | 511 | CMD_SELECT, |
354 | 511 | true, |
355 | 511 | replace, |
356 | 511 | list_make1(viewParse)); |
357 | | |
358 | | /* |
359 | | * Someday: automatic ON INSERT, etc |
360 | | */ |
361 | 511 | } |
362 | | |
363 | | /*--------------------------------------------------------------- |
364 | | * UpdateRangeTableOfViewParse |
365 | | * |
366 | | * Update the range table of the given parsetree. |
367 | | * This update consists of adding two new entries IN THE BEGINNING |
368 | | * of the range table (otherwise the rule system will die a slow, |
369 | | * horrible and painful death, and we do not want that now, do we?) |
370 | | * one for the OLD relation and one for the NEW one (both of |
371 | | * them refer in fact to the "view" relation). |
372 | | * |
373 | | * Of course we must also increase the 'varnos' of all the Var nodes |
374 | | * by 2... |
375 | | * |
376 | | * These extra RT entries are not actually used in the query, |
377 | | * except for run-time permission checking. |
378 | | *--------------------------------------------------------------- |
379 | | */ |
380 | | static Query * |
381 | | UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) |
382 | 511 | { |
383 | 511 | Relation viewRel; |
384 | 511 | List *new_rt; |
385 | 511 | RangeTblEntry *rt_entry1, |
386 | 511 | *rt_entry2; |
387 | 511 | ParseState *pstate; |
388 | | |
389 | | /* |
390 | | * Make a copy of the given parsetree. It's not so much that we don't |
391 | | * want to scribble on our input, it's that the parser has a bad habit of |
392 | | * outputting multiple links to the same subtree for constructs like |
393 | | * BETWEEN, and we mustn't have OffsetVarNodes increment the varno of a |
394 | | * Var node twice. copyObject will expand any multiply-referenced subtree |
395 | | * into multiple copies. |
396 | | */ |
397 | 511 | viewParse = copyObject(viewParse); |
398 | | |
399 | | /* Create a dummy ParseState for addRangeTableEntryForRelation */ |
400 | 511 | pstate = make_parsestate(NULL); |
401 | | |
402 | | /* need to open the rel for addRangeTableEntryForRelation */ |
403 | 511 | viewRel = relation_open(viewOid, AccessShareLock); |
404 | | |
405 | | /* |
406 | | * Create the 2 new range table entries and form the new range table... |
407 | | * OLD first, then NEW.... |
408 | | */ |
409 | 511 | rt_entry1 = addRangeTableEntryForRelation(pstate, viewRel, |
410 | 511 | makeAlias("old", NIL), |
411 | 511 | false, false); |
412 | 511 | rt_entry2 = addRangeTableEntryForRelation(pstate, viewRel, |
413 | 511 | makeAlias("new", NIL), |
414 | 511 | false, false); |
415 | | /* Must override addRangeTableEntry's default access-check flags */ |
416 | 511 | rt_entry1->requiredPerms = 0; |
417 | 511 | rt_entry2->requiredPerms = 0; |
418 | | |
419 | 511 | new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); |
420 | | |
421 | 511 | viewParse->rtable = new_rt; |
422 | | |
423 | | /* |
424 | | * Now offset all var nodes by 2, and jointree RT indexes too. |
425 | | */ |
426 | 511 | OffsetVarNodes((Node *) viewParse, 2, 0); |
427 | | |
428 | 511 | relation_close(viewRel, AccessShareLock); |
429 | | |
430 | 511 | return viewParse; |
431 | 511 | } |
432 | | |
433 | | /* |
434 | | * DefineView |
435 | | * Execute a CREATE VIEW command. |
436 | | */ |
437 | | ObjectAddress |
438 | | DefineView(ViewStmt *stmt, const char *queryString, |
439 | | int stmt_location, int stmt_len) |
440 | 475 | { |
441 | 475 | RawStmt *rawstmt; |
442 | 475 | Query *viewParse; |
443 | 475 | RangeVar *view; |
444 | 475 | ListCell *cell; |
445 | 475 | bool check_option; |
446 | 475 | ObjectAddress address; |
447 | | |
448 | | /* |
449 | | * Run parse analysis to convert the raw parse tree to a Query. Note this |
450 | | * also acquires sufficient locks on the source table(s). |
451 | | * |
452 | | * Since parse analysis scribbles on its input, copy the raw parse tree; |
453 | | * this ensures we don't corrupt a prepared statement, for example. |
454 | | */ |
455 | 475 | rawstmt = makeNode(RawStmt); |
456 | 475 | rawstmt->stmt = (Node *) copyObject(stmt->query); |
457 | 475 | rawstmt->stmt_location = stmt_location; |
458 | 475 | rawstmt->stmt_len = stmt_len; |
459 | | |
460 | 475 | viewParse = parse_analyze(rawstmt, queryString, NULL, 0, NULL); |
461 | | |
462 | | /* |
463 | | * The grammar should ensure that the result is a single SELECT Query. |
464 | | * However, it doesn't forbid SELECT INTO, so we have to check for that. |
465 | | */ |
466 | 475 | if (!IsA(viewParse, Query)) |
467 | 0 | elog(ERROR, "unexpected parse analysis result"); |
468 | 475 | if (viewParse->utilityStmt != NULL && |
469 | 475 | IsA0 (viewParse->utilityStmt, CreateTableAsStmt)) |
470 | 475 | ereport(ERROR, |
471 | 475 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
472 | 475 | errmsg("views must not contain SELECT INTO"))); |
473 | 475 | if (viewParse->commandType != CMD_SELECT) |
474 | 0 | elog(ERROR, "unexpected parse analysis result"); |
475 | | |
476 | | /* |
477 | | * Check for unsupported cases. These tests are redundant with ones in |
478 | | * DefineQueryRewrite(), but that function will complain about a bogus ON |
479 | | * SELECT rule, and we'd rather the message complain about a view. |
480 | | */ |
481 | 475 | if (viewParse->hasModifyingCTE) |
482 | 475 | ereport(ERROR, |
483 | 475 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
484 | 475 | errmsg("views must not contain data-modifying statements in WITH"))); |
485 | | |
486 | | /* |
487 | | * If the user specified the WITH CHECK OPTION, add it to the list of |
488 | | * reloptions. |
489 | | */ |
490 | 475 | if (stmt->withCheckOption == LOCAL_CHECK_OPTION) |
491 | 0 | stmt->options = lappend(stmt->options, |
492 | 0 | makeDefElem("check_option", |
493 | 0 | (Node *) makeString("local"), -1)); |
494 | 475 | else if (stmt->withCheckOption == CASCADED_CHECK_OPTION) |
495 | 0 | stmt->options = lappend(stmt->options, |
496 | 0 | makeDefElem("check_option", |
497 | 0 | (Node *) makeString("cascaded"), -1)); |
498 | | |
499 | | /* |
500 | | * Check that the view is auto-updatable if WITH CHECK OPTION was |
501 | | * specified. |
502 | | */ |
503 | 475 | check_option = false; |
504 | | |
505 | 475 | foreach(cell, stmt->options) |
506 | 8 | { |
507 | 8 | DefElem *defel = (DefElem *) lfirst(cell); |
508 | | |
509 | 8 | if (strcmp(defel->defname, "check_option") == 0) |
510 | 0 | check_option = true; |
511 | 8 | } |
512 | | |
513 | | /* |
514 | | * If the check option is specified, look to see if the view is actually |
515 | | * auto-updatable or not. |
516 | | */ |
517 | 475 | if (check_option) |
518 | 0 | { |
519 | 0 | const char *view_updatable_error = |
520 | 0 | view_query_is_auto_updatable(viewParse, true); |
521 | |
|
522 | 0 | if (view_updatable_error) |
523 | 0 | ereport(ERROR, |
524 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
525 | 0 | errmsg("WITH CHECK OPTION is supported only on automatically updatable views"), |
526 | 0 | errhint("%s", _(view_updatable_error)))); |
527 | 0 | } |
528 | | |
529 | | /* |
530 | | * If a list of column names was given, run through and insert these into |
531 | | * the actual query tree. - thomas 2000-03-08 |
532 | | */ |
533 | 475 | if (stmt->aliases != NIL) |
534 | 0 | { |
535 | 0 | ListCell *alist_item = list_head(stmt->aliases); |
536 | 0 | ListCell *targetList; |
537 | |
|
538 | 0 | foreach(targetList, viewParse->targetList) |
539 | 0 | { |
540 | 0 | TargetEntry *te = lfirst_node(TargetEntry, targetList); |
541 | | |
542 | | /* junk columns don't get aliases */ |
543 | 0 | if (te->resjunk) |
544 | 0 | continue; |
545 | 0 | te->resname = pstrdup(strVal(lfirst(alist_item))); |
546 | 0 | alist_item = lnext(alist_item); |
547 | 0 | if (alist_item == NULL) |
548 | 0 | break; /* done assigning aliases */ |
549 | 0 | } |
550 | |
|
551 | 0 | if (alist_item != NULL) |
552 | 0 | ereport(ERROR, |
553 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
554 | 0 | errmsg("CREATE VIEW specifies more column " |
555 | 0 | "names than columns"))); |
556 | 0 | } |
557 | | |
558 | | /* Unlogged views are not sensible. */ |
559 | 475 | if (stmt->view->relpersistence == RELPERSISTENCE_UNLOGGED) |
560 | 475 | ereport(ERROR, |
561 | 475 | (errcode(ERRCODE_SYNTAX_ERROR), |
562 | 475 | errmsg("views cannot be unlogged because they do not have storage"))); |
563 | | |
564 | | /* |
565 | | * If the user didn't explicitly ask for a temporary view, check whether |
566 | | * we need one implicitly. We allow TEMP to be inserted automatically as |
567 | | * long as the CREATE command is consistent with that --- no explicit |
568 | | * schema name. |
569 | | */ |
570 | 475 | view = copyObject(stmt->view); /* don't corrupt original command */ |
571 | 475 | if (view->relpersistence == RELPERSISTENCE_PERMANENT |
572 | 475 | && isQueryUsingTempRelation(viewParse)463 ) |
573 | 1 | { |
574 | 1 | view->relpersistence = RELPERSISTENCE_TEMP; |
575 | 1 | ereport(NOTICE, |
576 | 1 | (errmsg("view \"%s\" will be a temporary view", |
577 | 1 | view->relname))); |
578 | 1 | } |
579 | | |
580 | | /* |
581 | | * Create the view relation |
582 | | * |
583 | | * NOTE: if it already exists and replace is false, the xact will be |
584 | | * aborted. |
585 | | */ |
586 | 475 | address = DefineVirtualRelation(view, viewParse->targetList, |
587 | 475 | stmt->replace, stmt->options, viewParse); |
588 | | |
589 | 475 | return address; |
590 | 475 | } |
591 | | |
592 | | /* |
593 | | * Use the rules system to store the query for the view. |
594 | | */ |
595 | | void |
596 | | StoreViewQuery(Oid viewOid, Query *viewParse, bool replace) |
597 | 511 | { |
598 | | /* |
599 | | * The range table of 'viewParse' does not contain entries for the "OLD" |
600 | | * and "NEW" relations. So... add them! |
601 | | */ |
602 | 511 | viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse); |
603 | | |
604 | | /* |
605 | | * Now create the rules associated with the view. |
606 | | */ |
607 | 511 | DefineViewRules(viewOid, viewParse, replace); |
608 | 511 | } |