00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include "access/heapam.h"
00018 #include "access/xact.h"
00019 #include "catalog/namespace.h"
00020 #include "commands/defrem.h"
00021 #include "commands/tablecmds.h"
00022 #include "commands/view.h"
00023 #include "miscadmin.h"
00024 #include "nodes/makefuncs.h"
00025 #include "nodes/nodeFuncs.h"
00026 #include "parser/analyze.h"
00027 #include "parser/parse_relation.h"
00028 #include "rewrite/rewriteDefine.h"
00029 #include "rewrite/rewriteManip.h"
00030 #include "rewrite/rewriteSupport.h"
00031 #include "utils/acl.h"
00032 #include "utils/builtins.h"
00033 #include "utils/lsyscache.h"
00034 #include "utils/rel.h"
00035 #include "utils/syscache.h"
00036
00037
00038 static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc);
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 static Oid
00050 DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
00051 List *options)
00052 {
00053 Oid viewOid;
00054 LOCKMODE lockmode;
00055 CreateStmt *createStmt = makeNode(CreateStmt);
00056 List *attrList;
00057 ListCell *t;
00058
00059
00060
00061
00062
00063 attrList = NIL;
00064 foreach(t, tlist)
00065 {
00066 TargetEntry *tle = lfirst(t);
00067
00068 if (!tle->resjunk)
00069 {
00070 ColumnDef *def = makeNode(ColumnDef);
00071
00072 def->colname = pstrdup(tle->resname);
00073 def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr),
00074 exprTypmod((Node *) tle->expr));
00075 def->inhcount = 0;
00076 def->is_local = true;
00077 def->is_not_null = false;
00078 def->is_from_type = false;
00079 def->storage = 0;
00080 def->raw_default = NULL;
00081 def->cooked_default = NULL;
00082 def->collClause = NULL;
00083 def->collOid = exprCollation((Node *) tle->expr);
00084
00085
00086
00087
00088
00089 if (type_is_collatable(exprType((Node *) tle->expr)))
00090 {
00091 if (!OidIsValid(def->collOid))
00092 ereport(ERROR,
00093 (errcode(ERRCODE_INDETERMINATE_COLLATION),
00094 errmsg("could not determine which collation to use for view column \"%s\"",
00095 def->colname),
00096 errhint("Use the COLLATE clause to set the collation explicitly.")));
00097 }
00098 else
00099 Assert(!OidIsValid(def->collOid));
00100 def->constraints = NIL;
00101
00102 attrList = lappend(attrList, def);
00103 }
00104 }
00105
00106 if (attrList == NIL)
00107 ereport(ERROR,
00108 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
00109 errmsg("view must have at least one column")));
00110
00111
00112
00113
00114
00115
00116
00117 lockmode = replace ? AccessExclusiveLock : NoLock;
00118 (void) RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid);
00119
00120 if (OidIsValid(viewOid) && replace)
00121 {
00122 Relation rel;
00123 TupleDesc descriptor;
00124 List *atcmds = NIL;
00125 AlterTableCmd *atcmd;
00126
00127
00128 rel = relation_open(viewOid, NoLock);
00129
00130
00131 if (rel->rd_rel->relkind != RELKIND_VIEW)
00132 ereport(ERROR,
00133 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00134 errmsg("\"%s\" is not a view",
00135 RelationGetRelationName(rel))));
00136
00137
00138 CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
00139
00140
00141
00142
00143
00144
00145 Assert(relation->relpersistence == rel->rd_rel->relpersistence);
00146
00147
00148
00149
00150
00151
00152 descriptor = BuildDescForRelation(attrList);
00153 checkViewTupleDesc(descriptor, rel->rd_att);
00154
00155
00156
00157
00158
00159 atcmd = makeNode(AlterTableCmd);
00160 atcmd->subtype = AT_ReplaceRelOptions;
00161 atcmd->def = (Node *) options;
00162 atcmds = lappend(atcmds, atcmd);
00163
00164
00165
00166
00167
00168
00169 if (list_length(attrList) > rel->rd_att->natts)
00170 {
00171 ListCell *c;
00172 int skip = rel->rd_att->natts;
00173
00174 foreach(c, attrList)
00175 {
00176 if (skip > 0)
00177 {
00178 skip--;
00179 continue;
00180 }
00181 atcmd = makeNode(AlterTableCmd);
00182 atcmd->subtype = AT_AddColumnToView;
00183 atcmd->def = (Node *) lfirst(c);
00184 atcmds = lappend(atcmds, atcmd);
00185 }
00186 }
00187
00188
00189 AlterTableInternal(viewOid, atcmds, true);
00190
00191
00192
00193
00194 relation_close(rel, NoLock);
00195
00196 return viewOid;
00197 }
00198 else
00199 {
00200 Oid relid;
00201
00202
00203
00204
00205
00206 createStmt->relation = relation;
00207 createStmt->tableElts = attrList;
00208 createStmt->inhRelations = NIL;
00209 createStmt->constraints = NIL;
00210 createStmt->options = options;
00211 createStmt->oncommit = ONCOMMIT_NOOP;
00212 createStmt->tablespacename = NULL;
00213 createStmt->if_not_exists = false;
00214
00215
00216
00217
00218
00219
00220 relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid);
00221 Assert(relid != InvalidOid);
00222 return relid;
00223 }
00224 }
00225
00226
00227
00228
00229
00230
00231
00232 static void
00233 checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
00234 {
00235 int i;
00236
00237 if (newdesc->natts < olddesc->natts)
00238 ereport(ERROR,
00239 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
00240 errmsg("cannot drop columns from view")));
00241
00242
00243 for (i = 0; i < olddesc->natts; i++)
00244 {
00245 Form_pg_attribute newattr = newdesc->attrs[i];
00246 Form_pg_attribute oldattr = olddesc->attrs[i];
00247
00248
00249 if (newattr->attisdropped != oldattr->attisdropped)
00250 ereport(ERROR,
00251 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
00252 errmsg("cannot drop columns from view")));
00253
00254 if (strcmp(NameStr(newattr->attname), NameStr(oldattr->attname)) != 0)
00255 ereport(ERROR,
00256 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
00257 errmsg("cannot change name of view column \"%s\" to \"%s\"",
00258 NameStr(oldattr->attname),
00259 NameStr(newattr->attname))));
00260
00261 if (newattr->atttypid != oldattr->atttypid ||
00262 newattr->atttypmod != oldattr->atttypmod)
00263 ereport(ERROR,
00264 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
00265 errmsg("cannot change data type of view column \"%s\" from %s to %s",
00266 NameStr(oldattr->attname),
00267 format_type_with_typemod(oldattr->atttypid,
00268 oldattr->atttypmod),
00269 format_type_with_typemod(newattr->atttypid,
00270 newattr->atttypmod))));
00271
00272 }
00273
00274
00275
00276
00277
00278
00279 }
00280
00281 static void
00282 DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
00283 {
00284
00285
00286
00287
00288 DefineQueryRewrite(pstrdup(ViewSelectRuleName),
00289 viewOid,
00290 NULL,
00291 CMD_SELECT,
00292 true,
00293 replace,
00294 list_make1(viewParse));
00295
00296
00297
00298
00299 }
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318 static Query *
00319 UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
00320 {
00321 Relation viewRel;
00322 List *new_rt;
00323 RangeTblEntry *rt_entry1,
00324 *rt_entry2;
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334 viewParse = (Query *) copyObject(viewParse);
00335
00336
00337 viewRel = relation_open(viewOid, AccessShareLock);
00338
00339
00340
00341
00342
00343 rt_entry1 = addRangeTableEntryForRelation(NULL, viewRel,
00344 makeAlias("old", NIL),
00345 false, false);
00346 rt_entry2 = addRangeTableEntryForRelation(NULL, viewRel,
00347 makeAlias("new", NIL),
00348 false, false);
00349
00350 rt_entry1->requiredPerms = 0;
00351 rt_entry2->requiredPerms = 0;
00352
00353 new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
00354
00355 viewParse->rtable = new_rt;
00356
00357
00358
00359
00360 OffsetVarNodes((Node *) viewParse, 2, 0);
00361
00362 relation_close(viewRel, AccessShareLock);
00363
00364 return viewParse;
00365 }
00366
00367
00368
00369
00370
00371 Oid
00372 DefineView(ViewStmt *stmt, const char *queryString)
00373 {
00374 Query *viewParse;
00375 Oid viewOid;
00376 RangeVar *view;
00377
00378
00379
00380
00381
00382
00383
00384
00385 viewParse = parse_analyze((Node *) copyObject(stmt->query),
00386 queryString, NULL, 0);
00387
00388
00389
00390
00391
00392 if (!IsA(viewParse, Query))
00393 elog(ERROR, "unexpected parse analysis result");
00394 if (viewParse->utilityStmt != NULL &&
00395 IsA(viewParse->utilityStmt, CreateTableAsStmt))
00396 ereport(ERROR,
00397 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00398 errmsg("views must not contain SELECT INTO")));
00399 if (viewParse->commandType != CMD_SELECT ||
00400 viewParse->utilityStmt != NULL)
00401 elog(ERROR, "unexpected parse analysis result");
00402
00403
00404
00405
00406
00407
00408 if (viewParse->hasModifyingCTE)
00409 ereport(ERROR,
00410 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00411 errmsg("views must not contain data-modifying statements in WITH")));
00412
00413
00414
00415
00416
00417 if (stmt->aliases != NIL)
00418 {
00419 ListCell *alist_item = list_head(stmt->aliases);
00420 ListCell *targetList;
00421
00422 foreach(targetList, viewParse->targetList)
00423 {
00424 TargetEntry *te = (TargetEntry *) lfirst(targetList);
00425
00426 Assert(IsA(te, TargetEntry));
00427
00428 if (te->resjunk)
00429 continue;
00430 te->resname = pstrdup(strVal(lfirst(alist_item)));
00431 alist_item = lnext(alist_item);
00432 if (alist_item == NULL)
00433 break;
00434 }
00435
00436 if (alist_item != NULL)
00437 ereport(ERROR,
00438 (errcode(ERRCODE_SYNTAX_ERROR),
00439 errmsg("CREATE VIEW specifies more column "
00440 "names than columns")));
00441 }
00442
00443
00444 if (stmt->view->relpersistence == RELPERSISTENCE_UNLOGGED)
00445 ereport(ERROR,
00446 (errcode(ERRCODE_SYNTAX_ERROR),
00447 errmsg("views cannot be unlogged because they do not have storage")));
00448
00449
00450
00451
00452
00453
00454
00455 view = copyObject(stmt->view);
00456 if (view->relpersistence == RELPERSISTENCE_PERMANENT
00457 && isQueryUsingTempRelation(viewParse))
00458 {
00459 view->relpersistence = RELPERSISTENCE_TEMP;
00460 ereport(NOTICE,
00461 (errmsg("view \"%s\" will be a temporary view",
00462 view->relname)));
00463 }
00464
00465
00466
00467
00468
00469
00470
00471 viewOid = DefineVirtualRelation(view, viewParse->targetList,
00472 stmt->replace, stmt->options);
00473
00474
00475
00476
00477
00478
00479 CommandCounterIncrement();
00480
00481 StoreViewQuery(viewOid, viewParse, stmt->replace);
00482
00483 return viewOid;
00484 }
00485
00486
00487
00488
00489 void
00490 StoreViewQuery(Oid viewOid, Query *viewParse, bool replace)
00491 {
00492
00493
00494
00495
00496 viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse);
00497
00498
00499
00500
00501 DefineViewRules(viewOid, viewParse, replace);
00502 }