OSDN Git Service

Fix bugs in relpersistence handling during table creation.
[pg-rex/syncrep.git] / src / backend / commands / view.c
index c29e88a..b238199 100644 (file)
@@ -3,12 +3,12 @@
  * view.c
  *       use rewrite rules to construct views
  *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.111 2009/01/01 17:23:40 momjian Exp $
+ *       src/backend/commands/view.c
  *
  *-------------------------------------------------------------------------
  */
@@ -68,10 +68,10 @@ isViewOnTempTable_walker(Node *node, void *context)
                        if (rte->rtekind == RTE_RELATION)
                        {
                                Relation        rel = heap_open(rte->relid, AccessShareLock);
-                               bool            istemp = rel->rd_istemp;
+                               char            relpersistence = rel->rd_rel->relpersistence;
 
                                heap_close(rel, AccessShareLock);
-                               if (istemp)
+                               if (relpersistence == RELPERSISTENCE_TEMP)
                                        return true;
                        }
                }
@@ -97,10 +97,10 @@ isViewOnTempTable_walker(Node *node, void *context)
  *---------------------------------------------------------------------
  */
 static Oid
-DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
+DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
+                                         Oid namespaceId)
 {
-       Oid                     viewOid,
-                               namespaceId;
+       Oid                     viewOid;
        CreateStmt *createStmt = makeNode(CreateStmt);
        List       *attrList;
        ListCell   *t;
@@ -119,13 +119,33 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
                        ColumnDef  *def = makeNode(ColumnDef);
 
                        def->colname = pstrdup(tle->resname);
-                       def->typename = makeTypeNameFromOid(exprType((Node *) tle->expr),
+                       def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr),
                                                                                         exprTypmod((Node *) tle->expr));
                        def->inhcount = 0;
                        def->is_local = true;
                        def->is_not_null = false;
+                       def->is_from_type = false;
+                       def->storage = 0;
                        def->raw_default = NULL;
                        def->cooked_default = NULL;
+                       def->collClause = NULL;
+                       def->collOid = exprCollation((Node *) tle->expr);
+
+                       /*
+                        * It's possible that the column is of a collatable type but the
+                        * collation could not be resolved, so double-check.
+                        */
+                       if (type_is_collatable(exprType((Node *) tle->expr)))
+                       {
+                               if (!OidIsValid(def->collOid))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INDETERMINATE_COLLATION),
+                                                        errmsg("could not determine which collation to use for view column \"%s\"",
+                                                                       def->colname),
+                                                        errhint("Use the COLLATE clause to set the collation explicitly.")));
+                       }
+                       else
+                               Assert(!OidIsValid(def->collOid));
                        def->constraints = NIL;
 
                        attrList = lappend(attrList, def);
@@ -140,7 +160,6 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
        /*
         * Check to see if we want to replace an existing view.
         */
-       namespaceId = RangeVarGetCreationNamespace(relation);
        viewOid = get_relname_relid(relation->relname, namespaceId);
 
        if (OidIsValid(viewOid) && replace)
@@ -172,9 +191,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
                /*
                 * Due to the namespace visibility rules for temporary objects, we
                 * should only end up replacing a temporary view with another
-                * temporary view, and vice versa.
+                * temporary view, and similarly for permanent views.
                 */
-               Assert(relation->istemp == rel->rd_istemp);
+               Assert(relation->relpersistence == rel->rd_rel->relpersistence);
 
                /*
                 * Create a tuple descriptor to compare against the existing view, and
@@ -185,14 +204,14 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
                checkViewTupleDesc(descriptor, rel->rd_att);
 
                /*
-                * If new attributes have been added, we must add pg_attribute entries
+                * If new attributes have been added, we must add pg_attribute entries
                 * for them.  It is convenient (although overkill) to use the ALTER
                 * TABLE ADD COLUMN infrastructure for this.
                 */
                if (list_length(attrList) > rel->rd_att->natts)
                {
-                       List            *atcmds = NIL;
-                       ListCell        *c;
+                       List       *atcmds = NIL;
+                       ListCell   *c;
                        int                     skip = rel->rd_att->natts;
 
                        foreach(c, attrList)
@@ -221,6 +240,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
        }
        else
        {
+               Oid                     relid;
+
                /*
                 * now set the parameters for keys/inheritance etc. All of these are
                 * uninteresting for views...
@@ -232,13 +253,16 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
                createStmt->options = list_make1(defWithOids(false));
                createStmt->oncommit = ONCOMMIT_NOOP;
                createStmt->tablespacename = NULL;
+               createStmt->if_not_exists = false;
 
                /*
                 * finally create the relation (this will error out if there's an
                 * existing view, so we don't need more code to complain if "replace"
                 * is false).
                 */
-               return DefineRelation(createStmt, RELKIND_VIEW);
+               relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid);
+               Assert(relid != InvalidOid);
+               return relid;
        }
 }
 
@@ -273,9 +297,9 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
                if (strcmp(NameStr(newattr->attname), NameStr(oldattr->attname)) != 0)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                        errmsg("cannot change name of view column \"%s\" to \"%s\"",
-                                                       NameStr(oldattr->attname),
-                                                       NameStr(newattr->attname))));
+                                errmsg("cannot change name of view column \"%s\" to \"%s\"",
+                                               NameStr(oldattr->attname),
+                                               NameStr(newattr->attname))));
                /* XXX would it be safe to allow atttypmod to change?  Not sure */
                if (newattr->atttypid != oldattr->atttypid ||
                        newattr->atttypmod != oldattr->atttypmod)
@@ -360,10 +384,10 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
         * OLD first, then NEW....
         */
        rt_entry1 = addRangeTableEntryForRelation(NULL, viewRel,
-                                                                                         makeAlias("*OLD*", NIL),
+                                                                                         makeAlias("old", NIL),
                                                                                          false, false);
        rt_entry2 = addRangeTableEntryForRelation(NULL, viewRel,
-                                                                                         makeAlias("*NEW*", NIL),
+                                                                                         makeAlias("new", NIL),
                                                                                          false, false);
        /* Must override addRangeTableEntry's default access-check flags */
        rt_entry1->requiredPerms = 0;
@@ -392,6 +416,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
 {
        Query      *viewParse;
        Oid                     viewOid;
+       Oid                     namespaceId;
        RangeVar   *view;
 
        /*
@@ -412,6 +437,20 @@ DefineView(ViewStmt *stmt, const char *queryString)
                elog(ERROR, "unexpected parse analysis result");
 
        /*
+        * Check for unsupported cases.  These tests are redundant with ones in
+        * DefineQueryRewrite(), but that function will complain about a bogus ON
+        * SELECT rule, and we'd rather the message complain about a view.
+        */
+       if (viewParse->intoClause != NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("views must not contain SELECT INTO")));
+       if (viewParse->hasModifyingCTE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+               errmsg("views must not contain data-modifying statements in WITH")));
+
+       /*
         * If a list of column names was given, run through and insert these into
         * the actual query tree. - thomas 2000-03-08
         */
@@ -441,22 +480,32 @@ DefineView(ViewStmt *stmt, const char *queryString)
                                                        "names than columns")));
        }
 
+       /* Unlogged views are not sensible. */
+       if (stmt->view->relpersistence == RELPERSISTENCE_UNLOGGED)
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+               errmsg("views cannot be unlogged because they do not have storage")));
+
        /*
         * If the user didn't explicitly ask for a temporary view, check whether
         * we need one implicitly.      We allow TEMP to be inserted automatically as
         * long as the CREATE command is consistent with that --- no explicit
         * schema name.
         */
-       view = stmt->view;
-       if (!view->istemp && isViewOnTempTable(viewParse))
+       view = copyObject(stmt->view);  /* don't corrupt original command */
+       if (view->relpersistence == RELPERSISTENCE_PERMANENT
+               && isViewOnTempTable(viewParse))
        {
-               view = copyObject(view);        /* don't corrupt original command */
-               view->istemp = true;
+               view->relpersistence = RELPERSISTENCE_TEMP;
                ereport(NOTICE,
                                (errmsg("view \"%s\" will be a temporary view",
                                                view->relname)));
        }
 
+       /* Might also need to make it temporary if placed in temp schema. */
+       namespaceId = RangeVarGetCreationNamespace(view);
+       RangeVarAdjustRelationPersistence(view, namespaceId);
+
        /*
         * Create the view relation
         *
@@ -464,7 +513,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
         * aborted.
         */
        viewOid = DefineVirtualRelation(view, viewParse->targetList,
-                                                                       stmt->replace);
+                                                                       stmt->replace, namespaceId);
 
        /*
         * The relation we have just created is not visible to any other commands