Header And Logo

PostgreSQL
| The world's most advanced open source database.

view.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * view.c
00004  *    use rewrite rules to construct views
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/commands/view.c
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  * DefineVirtualRelation
00042  *
00043  * Create the "view" relation. `DefineRelation' does all the work,
00044  * we just provide the correct arguments ... at least when we're
00045  * creating a view.  If we're updating an existing view, we have to
00046  * work harder.
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      * create a list of ColumnDef nodes based on the names and types of the
00061      * (non-junk) targetlist items from the view's SELECT list.
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              * It's possible that the column is of a collatable type but the
00087              * collation could not be resolved, so double-check.
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      * Look up, check permissions on, and lock the creation namespace; also
00113      * check for a preexisting view with the same name.  This will also set
00114      * relation->relpersistence to RELPERSISTENCE_TEMP if the selected
00115      * namespace is temporary.
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         /* Relation is already locked, but we must build a relcache entry. */
00128         rel = relation_open(viewOid, NoLock);
00129 
00130         /* Make sure it *is* a view. */
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         /* Also check it's not in use already */
00138         CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
00139 
00140         /*
00141          * Due to the namespace visibility rules for temporary objects, we
00142          * should only end up replacing a temporary view with another
00143          * temporary view, and similarly for permanent views.
00144          */
00145         Assert(relation->relpersistence == rel->rd_rel->relpersistence);
00146 
00147         /*
00148          * Create a tuple descriptor to compare against the existing view, and
00149          * verify that the old column list is an initial prefix of the new
00150          * column list.
00151          */
00152         descriptor = BuildDescForRelation(attrList);
00153         checkViewTupleDesc(descriptor, rel->rd_att);
00154 
00155         /*
00156          * The new options list replaces the existing options list, even if
00157          * it's empty.
00158          */
00159         atcmd = makeNode(AlterTableCmd);
00160         atcmd->subtype = AT_ReplaceRelOptions;
00161         atcmd->def = (Node *) options;
00162         atcmds = lappend(atcmds, atcmd);
00163 
00164         /*
00165          * If new attributes have been added, we must add pg_attribute entries
00166          * for them.  It is convenient (although overkill) to use the ALTER
00167          * TABLE ADD COLUMN infrastructure for this.
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         /* OK, let's do it. */
00189         AlterTableInternal(viewOid, atcmds, true);
00190 
00191         /*
00192          * Seems okay, so return the OID of the pre-existing view.
00193          */
00194         relation_close(rel, NoLock);    /* keep the lock! */
00195 
00196         return viewOid;
00197     }
00198     else
00199     {
00200         Oid         relid;
00201 
00202         /*
00203          * now set the parameters for keys/inheritance etc. All of these are
00204          * uninteresting for views...
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          * finally create the relation (this will error out if there's an
00217          * existing view, so we don't need more code to complain if "replace"
00218          * is false).
00219          */
00220         relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid);
00221         Assert(relid != InvalidOid);
00222         return relid;
00223     }
00224 }
00225 
00226 /*
00227  * Verify that tupledesc associated with proposed new view definition
00228  * matches tupledesc of old view.  This is basically a cut-down version
00229  * of equalTupleDescs(), with code added to generate specific complaints.
00230  * Also, we allow the new tupledesc to have more columns than the old.
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     /* we can ignore tdhasoid */
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         /* XXX msg not right, but we don't support DROP COL on view anyway */
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         /* XXX would it be safe to allow atttypmod to change?  Not sure */
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         /* We can ignore the remaining attributes of an attribute... */
00272     }
00273 
00274     /*
00275      * We ignore the constraint fields.  The new view desc can't have any
00276      * constraints, and the only ones that could be on the old view are
00277      * defaults, which we are happy to leave in place.
00278      */
00279 }
00280 
00281 static void
00282 DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
00283 {
00284     /*
00285      * Set up the ON SELECT rule.  Since the query has already been through
00286      * parse analysis, we use DefineQueryRewrite() directly.
00287      */
00288     DefineQueryRewrite(pstrdup(ViewSelectRuleName),
00289                        viewOid,
00290                        NULL,
00291                        CMD_SELECT,
00292                        true,
00293                        replace,
00294                        list_make1(viewParse));
00295 
00296     /*
00297      * Someday: automatic ON INSERT, etc
00298      */
00299 }
00300 
00301 /*---------------------------------------------------------------
00302  * UpdateRangeTableOfViewParse
00303  *
00304  * Update the range table of the given parsetree.
00305  * This update consists of adding two new entries IN THE BEGINNING
00306  * of the range table (otherwise the rule system will die a slow,
00307  * horrible and painful death, and we do not want that now, do we?)
00308  * one for the OLD relation and one for the NEW one (both of
00309  * them refer in fact to the "view" relation).
00310  *
00311  * Of course we must also increase the 'varnos' of all the Var nodes
00312  * by 2...
00313  *
00314  * These extra RT entries are not actually used in the query,
00315  * except for run-time permission checking.
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      * Make a copy of the given parsetree.  It's not so much that we don't
00328      * want to scribble on our input, it's that the parser has a bad habit of
00329      * outputting multiple links to the same subtree for constructs like
00330      * BETWEEN, and we mustn't have OffsetVarNodes increment the varno of a
00331      * Var node twice.  copyObject will expand any multiply-referenced subtree
00332      * into multiple copies.
00333      */
00334     viewParse = (Query *) copyObject(viewParse);
00335 
00336     /* need to open the rel for addRangeTableEntryForRelation */
00337     viewRel = relation_open(viewOid, AccessShareLock);
00338 
00339     /*
00340      * Create the 2 new range table entries and form the new range table...
00341      * OLD first, then NEW....
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     /* Must override addRangeTableEntry's default access-check flags */
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      * Now offset all var nodes by 2, and jointree RT indexes too.
00359      */
00360     OffsetVarNodes((Node *) viewParse, 2, 0);
00361 
00362     relation_close(viewRel, AccessShareLock);
00363 
00364     return viewParse;
00365 }
00366 
00367 /*
00368  * DefineView
00369  *      Execute a CREATE VIEW command.
00370  */
00371 Oid
00372 DefineView(ViewStmt *stmt, const char *queryString)
00373 {
00374     Query      *viewParse;
00375     Oid         viewOid;
00376     RangeVar   *view;
00377 
00378     /*
00379      * Run parse analysis to convert the raw parse tree to a Query.  Note this
00380      * also acquires sufficient locks on the source table(s).
00381      *
00382      * Since parse analysis scribbles on its input, copy the raw parse tree;
00383      * this ensures we don't corrupt a prepared statement, for example.
00384      */
00385     viewParse = parse_analyze((Node *) copyObject(stmt->query),
00386                               queryString, NULL, 0);
00387 
00388     /*
00389      * The grammar should ensure that the result is a single SELECT Query.
00390      * However, it doesn't forbid SELECT INTO, so we have to check for that.
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      * Check for unsupported cases.  These tests are redundant with ones in
00405      * DefineQueryRewrite(), but that function will complain about a bogus ON
00406      * SELECT rule, and we'd rather the message complain about a view.
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      * If a list of column names was given, run through and insert these into
00415      * the actual query tree. - thomas 2000-03-08
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             /* junk columns don't get aliases */
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;          /* done assigning aliases */
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     /* Unlogged views are not sensible. */
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      * If the user didn't explicitly ask for a temporary view, check whether
00451      * we need one implicitly.  We allow TEMP to be inserted automatically as
00452      * long as the CREATE command is consistent with that --- no explicit
00453      * schema name.
00454      */
00455     view = copyObject(stmt->view);      /* don't corrupt original command */
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      * Create the view relation
00467      *
00468      * NOTE: if it already exists and replace is false, the xact will be
00469      * aborted.
00470      */
00471     viewOid = DefineVirtualRelation(view, viewParse->targetList,
00472                                     stmt->replace, stmt->options);
00473 
00474     /*
00475      * The relation we have just created is not visible to any other commands
00476      * running with the same transaction & command id. So, increment the
00477      * command id counter (but do NOT pfree any memory!!!!)
00478      */
00479     CommandCounterIncrement();
00480 
00481     StoreViewQuery(viewOid, viewParse, stmt->replace);
00482 
00483     return viewOid;
00484 }
00485 
00486 /*
00487  * Use the rules system to store the query for the view.
00488  */
00489 void
00490 StoreViewQuery(Oid viewOid, Query *viewParse, bool replace)
00491 {
00492     /*
00493      * The range table of 'viewParse' does not contain entries for the "OLD"
00494      * and "NEW" relations. So... add them!
00495      */
00496     viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse);
00497 
00498     /*
00499      * Now create the rules associated with the view.
00500      */
00501     DefineViewRules(viewOid, viewParse, replace);
00502 }