OSDN Git Service

Fix bugs in relpersistence handling during table creation.
[pg-rex/syncrep.git] / src / backend / commands / view.c
index 069d922..b238199 100644 (file)
  * view.c
  *       use rewrite rules to construct views
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: view.c,v 1.36 1999/07/16 04:58:42 momjian Exp $
+ *
+ * IDENTIFICATION
+ *       src/backend/commands/view.c
  *
  *-------------------------------------------------------------------------
  */
-#include <stdio.h>
-#include <string.h>
-
 #include "postgres.h"
 
+#include "access/heapam.h"
 #include "access/xact.h"
-#include "catalog/heap.h"
-#include "commands/creatinh.h"
+#include "catalog/namespace.h"
+#include "commands/defrem.h"
+#include "commands/tablecmds.h"
 #include "commands/view.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/analyze.h"
 #include "parser/parse_relation.h"
-#include "parser/parse_type.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteManip.h"
-#include "rewrite/rewriteRemove.h"
+#include "rewrite/rewriteSupport.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+
+
+static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc);
+static bool isViewOnTempTable_walker(Node *node, void *context);
 
 /*---------------------------------------------------------------------
- * DefineVirtualRelation
+ * isViewOnTempTable
  *
- * Create the "view" relation.
- * `DefineRelation' does all the work, we just provide the correct
- * arguments!
+ * Returns true iff any of the relations underlying this view are
+ * temporary tables.
+ *---------------------------------------------------------------------
+ */
+static bool
+isViewOnTempTable(Query *viewParse)
+{
+       return isViewOnTempTable_walker((Node *) viewParse, NULL);
+}
+
+static bool
+isViewOnTempTable_walker(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+
+       if (IsA(node, Query))
+       {
+               Query      *query = (Query *) node;
+               ListCell   *rtable;
+
+               foreach(rtable, query->rtable)
+               {
+                       RangeTblEntry *rte = lfirst(rtable);
+
+                       if (rte->rtekind == RTE_RELATION)
+                       {
+                               Relation        rel = heap_open(rte->relid, AccessShareLock);
+                               char            relpersistence = rel->rd_rel->relpersistence;
+
+                               heap_close(rel, AccessShareLock);
+                               if (relpersistence == RELPERSISTENCE_TEMP)
+                                       return true;
+                       }
+               }
+
+               return query_tree_walker(query,
+                                                                isViewOnTempTable_walker,
+                                                                context,
+                                                                QTW_IGNORE_JOINALIASES);
+       }
+
+       return expression_tree_walker(node,
+                                                                 isViewOnTempTable_walker,
+                                                                 context);
+}
+
+/*---------------------------------------------------------------------
+ * DefineVirtualRelation
  *
- * If the relation already exists, then 'DefineRelation' will abort
- * the xact...
+ * Create the "view" relation. `DefineRelation' does all the work,
+ * we just provide the correct arguments ... at least when we're
+ * creating a view.  If we're updating an existing view, we have to
+ * work harder.
  *---------------------------------------------------------------------
  */
-static void
-DefineVirtualRelation(char *relname, List *tlist)
+static Oid
+DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
+                                         Oid namespaceId)
 {
-       CreateStmt      createStmt;
-       List       *attrList,
-                          *t;
-       TargetEntry *entry;
-       Resdom     *res;
-       char       *resname;
-       char       *restypename;
+       Oid                     viewOid;
+       CreateStmt *createStmt = makeNode(CreateStmt);
+       List       *attrList;
+       ListCell   *t;
 
        /*
-        * create a list with one entry per attribute of this relation. Each
-        * entry is a two element list. The first element is the name of the
-        * attribute (a string) and the second the name of the type (NOTE: a
-        * string, not a type id!).
+        * create a list of ColumnDef nodes based on the names and types of the
+        * (non-junk) targetlist items from the view's SELECT list.
         */
        attrList = NIL;
-       if (tlist != NIL)
+       foreach(t, tlist)
        {
-               foreach(t, tlist)
+               TargetEntry *tle = lfirst(t);
+
+               if (!tle->resjunk)
                {
                        ColumnDef  *def = makeNode(ColumnDef);
-                       TypeName   *typename;
+
+                       def->colname = pstrdup(tle->resname);
+                       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);
 
                        /*
-                        * find the names of the attribute & its type
+                        * It's possible that the column is of a collatable type but the
+                        * collation could not be resolved, so double-check.
                         */
-                       entry = lfirst(t);
-                       res = entry->resdom;
-                       resname = res->resname;
-                       restypename = typeidTypeName(res->restype);
-
-                       typename = makeNode(TypeName);
-
-                       typename->name = pstrdup(restypename);
-                       typename->typmod = res->restypmod;
-
-                       def->colname = pstrdup(resname);
-
-                       def->typename = typename;
-
-                       def->is_not_null = false;
-                       def->defval = (char *) NULL;
+                       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);
                }
        }
-       else
-               elog(ERROR, "attempted to define virtual relation with no attrs");
 
-       /*
-        * now create the parametesr for keys/inheritance etc. All of them are
-        * nil...
-        */
-       createStmt.relname = relname;
-       createStmt.istemp = false;
-       createStmt.tableElts = attrList;
-/*       createStmt.tableType = NULL;*/
-       createStmt.inhRelnames = NIL;
-       createStmt.constraints = NIL;
+       if (attrList == NIL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                errmsg("view must have at least one column")));
 
        /*
-        * finally create the relation...
+        * Check to see if we want to replace an existing view.
         */
-       DefineRelation(&createStmt, RELKIND_RELATION);
-}
+       viewOid = get_relname_relid(relation->relname, namespaceId);
 
-/*------------------------------------------------------------------
- * makeViewRetrieveRuleName
- *
- * Given a view name, returns the name for the 'on retrieve to "view"'
- * rule.
- * This routine is called when defining/removing a view.
- *------------------------------------------------------------------
- */
-char *
-MakeRetrieveViewRuleName(char *viewName)
-{
-       char       *buf;
+       if (OidIsValid(viewOid) && replace)
+       {
+               Relation        rel;
+               TupleDesc       descriptor;
+
+               /*
+                * Yes.  Get exclusive lock on the existing view ...
+                */
+               rel = relation_open(viewOid, AccessExclusiveLock);
+
+               /*
+                * Make sure it *is* a view, and do permissions checks.
+                */
+               if (rel->rd_rel->relkind != RELKIND_VIEW)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("\"%s\" is not a view",
+                                                       RelationGetRelationName(rel))));
+
+               if (!pg_class_ownercheck(viewOid, GetUserId()))
+                       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+                                                  RelationGetRelationName(rel));
+
+               /* Also check it's not in use already */
+               CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
+
+               /*
+                * Due to the namespace visibility rules for temporary objects, we
+                * should only end up replacing a temporary view with another
+                * temporary view, and similarly for permanent views.
+                */
+               Assert(relation->relpersistence == rel->rd_rel->relpersistence);
+
+               /*
+                * Create a tuple descriptor to compare against the existing view, and
+                * verify that the old column list is an initial prefix of the new
+                * column list.
+                */
+               descriptor = BuildDescForRelation(attrList);
+               checkViewTupleDesc(descriptor, rel->rd_att);
+
+               /*
+                * 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;
+                       int                     skip = rel->rd_att->natts;
+
+                       foreach(c, attrList)
+                       {
+                               AlterTableCmd *atcmd;
+
+                               if (skip > 0)
+                               {
+                                       skip--;
+                                       continue;
+                               }
+                               atcmd = makeNode(AlterTableCmd);
+                               atcmd->subtype = AT_AddColumnToView;
+                               atcmd->def = (Node *) lfirst(c);
+                               atcmds = lappend(atcmds, atcmd);
+                       }
+                       AlterTableInternal(viewOid, atcmds, true);
+               }
 
-       buf = palloc(strlen(viewName) + 5);
-       snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName);
+               /*
+                * Seems okay, so return the OID of the pre-existing view.
+                */
+               relation_close(rel, NoLock);    /* keep the lock! */
 
-       return buf;
+               return viewOid;
+       }
+       else
+       {
+               Oid                     relid;
+
+               /*
+                * now set the parameters for keys/inheritance etc. All of these are
+                * uninteresting for views...
+                */
+               createStmt->relation = (RangeVar *) relation;
+               createStmt->tableElts = attrList;
+               createStmt->inhRelations = NIL;
+               createStmt->constraints = NIL;
+               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).
+                */
+               relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid);
+               Assert(relid != InvalidOid);
+               return relid;
+       }
 }
 
-static RuleStmt *
-FormViewRetrieveRule(char *viewName, Query *viewParse)
+/*
+ * Verify that tupledesc associated with proposed new view definition
+ * matches tupledesc of old view.  This is basically a cut-down version
+ * of equalTupleDescs(), with code added to generate specific complaints.
+ * Also, we allow the new tupledesc to have more columns than the old.
+ */
+static void
+checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
 {
-       RuleStmt   *rule;
-       char       *rname;
-       Attr       *attr;
+       int                     i;
+
+       if (newdesc->natts < olddesc->natts)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                errmsg("cannot drop columns from view")));
+       /* we can ignore tdhasoid */
+
+       for (i = 0; i < olddesc->natts; i++)
+       {
+               Form_pg_attribute newattr = newdesc->attrs[i];
+               Form_pg_attribute oldattr = olddesc->attrs[i];
+
+               /* XXX msg not right, but we don't support DROP COL on view anyway */
+               if (newattr->attisdropped != oldattr->attisdropped)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                        errmsg("cannot drop columns from view")));
+
+               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))));
+               /* XXX would it be safe to allow atttypmod to change?  Not sure */
+               if (newattr->atttypid != oldattr->atttypid ||
+                       newattr->atttypmod != oldattr->atttypmod)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                        errmsg("cannot change data type of view column \"%s\" from %s to %s",
+                                                       NameStr(oldattr->attname),
+                                                       format_type_with_typemod(oldattr->atttypid,
+                                                                                                        oldattr->atttypmod),
+                                                       format_type_with_typemod(newattr->atttypid,
+                                                                                                        newattr->atttypmod))));
+               /* We can ignore the remaining attributes of an attribute... */
+       }
 
        /*
-        * Create a RuleStmt that corresponds to the suitable rewrite rule
-        * args for DefineQueryRewrite();
+        * We ignore the constraint fields.  The new view desc can't have any
+        * constraints, and the only ones that could be on the old view are
+        * defaults, which we are happy to leave in place.
         */
-       rule = makeNode(RuleStmt);
-       rname = MakeRetrieveViewRuleName(viewName);
-
-       attr = makeNode(Attr);
-       attr->relname = pstrdup(viewName);
-/*       attr->refname = pstrdup(viewName);*/
-       rule->rulename = pstrdup(rname);
-       rule->whereClause = NULL;
-       rule->event = CMD_SELECT;
-       rule->object = attr;
-       rule->instead = true;
-       rule->actions = lcons(viewParse, NIL);
-
-       return rule;
 }
 
 static void
-DefineViewRules(char *viewName, Query *viewParse)
+DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
 {
-       RuleStmt   *retrieve_rule = NULL;
-
-#ifdef NOTYET
-       RuleStmt   *replace_rule = NULL;
-       RuleStmt   *append_rule = NULL;
-       RuleStmt   *delete_rule = NULL;
-
-#endif
-
-       retrieve_rule = FormViewRetrieveRule(viewName, viewParse);
-
-#ifdef NOTYET
-
-       replace_rule = FormViewReplaceRule(viewName, viewParse);
-       append_rule = FormViewAppendRule(viewName, viewParse);
-       delete_rule = FormViewDeleteRule(viewName, viewParse);
-
-#endif
-
-       DefineQueryRewrite(retrieve_rule);
-
-#ifdef NOTYET
-       DefineQueryRewrite(replace_rule);
-       DefineQueryRewrite(append_rule);
-       DefineQueryRewrite(delete_rule);
-#endif
+       /*
+        * Set up the ON SELECT rule.  Since the query has already been through
+        * parse analysis, we use DefineQueryRewrite() directly.
+        */
+       DefineQueryRewrite(pstrdup(ViewSelectRuleName),
+                                          viewOid,
+                                          NULL,
+                                          CMD_SELECT,
+                                          true,
+                                          replace,
+                                          list_make1(viewParse));
 
+       /*
+        * Someday: automatic ON INSERT, etc
+        */
 }
 
 /*---------------------------------------------------------------
@@ -188,124 +348,188 @@ DefineViewRules(char *viewName, Query *viewParse)
  * This update consists of adding two new entries IN THE BEGINNING
  * of the range table (otherwise the rule system will die a slow,
  * horrible and painful death, and we do not want that now, do we?)
- * one for the CURRENT relation and one for the NEW one (both of
+ * one for the OLD relation and one for the NEW one (both of
  * them refer in fact to the "view" relation).
  *
  * Of course we must also increase the 'varnos' of all the Var nodes
  * by 2...
  *
- * NOTE: these are destructive changes. It would be difficult to
- * make a complete copy of the parse tree and make the changes
- * in the copy.
+ * These extra RT entries are not actually used in the query,
+ * except for run-time permission checking.
  *---------------------------------------------------------------
  */
-static void
-UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
+static Query *
+UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
 {
-       List       *old_rt;
+       Relation        viewRel;
        List       *new_rt;
        RangeTblEntry *rt_entry1,
                           *rt_entry2;
 
        /*
-        * first offset all var nodes by 2
+        * Make a copy of the given parsetree.  It's not so much that we don't
+        * want to scribble on our input, it's that the parser has a bad habit of
+        * outputting multiple links to the same subtree for constructs like
+        * BETWEEN, and we mustn't have OffsetVarNodes increment the varno of a
+        * Var node twice.      copyObject will expand any multiply-referenced subtree
+        * into multiple copies.
         */
-       OffsetVarNodes((Node *) viewParse->targetList, 2, 0);
-       OffsetVarNodes(viewParse->qual, 2, 0);
-
-       OffsetVarNodes(viewParse->havingQual, 2, 0);
+       viewParse = (Query *) copyObject(viewParse);
 
+       /* need to open the rel for addRangeTableEntryForRelation */
+       viewRel = relation_open(viewOid, AccessShareLock);
 
        /*
-        * find the old range table...
+        * Create the 2 new range table entries and form the new range table...
+        * OLD first, then NEW....
         */
-       old_rt = viewParse->rtable;
+       rt_entry1 = addRangeTableEntryForRelation(NULL, viewRel,
+                                                                                         makeAlias("old", NIL),
+                                                                                         false, false);
+       rt_entry2 = addRangeTableEntryForRelation(NULL, viewRel,
+                                                                                         makeAlias("new", NIL),
+                                                                                         false, false);
+       /* Must override addRangeTableEntry's default access-check flags */
+       rt_entry1->requiredPerms = 0;
+       rt_entry2->requiredPerms = 0;
+
+       new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
-       /*
-        * create the 2 new range table entries and form the new range
-        * table... CURRENT first, then NEW....
-        */
-       rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*",
-                                                                  FALSE, FALSE);
-       rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, "*NEW*",
-                                                                  FALSE, FALSE);
-       new_rt = lcons(rt_entry2, old_rt);
-       new_rt = lcons(rt_entry1, new_rt);
+       viewParse->rtable = new_rt;
 
        /*
-        * Now the tricky part.... Update the range table in place... Be
-        * careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
+        * Now offset all var nodes by 2, and jointree RT indexes too.
         */
-       viewParse->rtable = new_rt;
+       OffsetVarNodes((Node *) viewParse, 2, 0);
+
+       relation_close(viewRel, AccessShareLock);
+
+       return viewParse;
 }
 
-/*-------------------------------------------------------------------
+/*
  * DefineView
- *
- *             - takes a "viewname", "parsetree" pair and then
- *             1)              construct the "virtual" relation
- *             2)              commit the command but NOT the transaction,
- *                             so that the relation exists
- *                             before the rules are defined.
- *             2)              define the "n" rules specified in the PRS2 paper
- *                             over the "virtual" relation
- *-------------------------------------------------------------------
+ *             Execute a CREATE VIEW command.
  */
 void
-DefineView(char *viewName, Query *viewParse)
+DefineView(ViewStmt *stmt, const char *queryString)
 {
-       List       *viewTlist;
+       Query      *viewParse;
+       Oid                     viewOid;
+       Oid                     namespaceId;
+       RangeVar   *view;
 
-       viewTlist = viewParse->targetList;
+       /*
+        * Run parse analysis to convert the raw parse tree to a Query.  Note this
+        * also acquires sufficient locks on the source table(s).
+        *
+        * Since parse analysis scribbles on its input, copy the raw parse tree;
+        * this ensures we don't corrupt a prepared statement, for example.
+        */
+       viewParse = parse_analyze((Node *) copyObject(stmt->query),
+                                                         queryString, NULL, 0);
 
        /*
-        * Create the "view" relation NOTE: if it already exists, the xaxt
-        * will be aborted.
+        * The grammar should ensure that the result is a single SELECT Query.
         */
-       DefineVirtualRelation(viewName, viewTlist);
+       if (!IsA(viewParse, Query) ||
+               viewParse->commandType != CMD_SELECT)
+               elog(ERROR, "unexpected parse analysis result");
 
        /*
-        * The relation we have just created is not visible to any other
-        * commands running with the same transaction & command id. So,
-        * increment the command id counter (but do NOT pfree any memory!!!!)
+        * 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.
         */
-       CommandCounterIncrement();
+       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")));
 
        /*
-        * The range table of 'viewParse' does not contain entries for the
-        * "CURRENT" and "NEW" relations. So... add them! NOTE: we make the
-        * update in place! After this call 'viewParse' will never be what it
-        * used to be...
+        * If a list of column names was given, run through and insert these into
+        * the actual query tree. - thomas 2000-03-08
         */
-       UpdateRangeTableOfViewParse(viewName, viewParse);
-       DefineViewRules(viewName, viewParse);
-}
+       if (stmt->aliases != NIL)
+       {
+               ListCell   *alist_item = list_head(stmt->aliases);
+               ListCell   *targetList;
 
-/*------------------------------------------------------------------
- * RemoveView
- *
- * Remove a view given its name
- *------------------------------------------------------------------
- */
-void
-RemoveView(char *viewName)
-{
-       char       *rname;
+               foreach(targetList, viewParse->targetList)
+               {
+                       TargetEntry *te = (TargetEntry *) lfirst(targetList);
+
+                       Assert(IsA(te, TargetEntry));
+                       /* junk columns don't get aliases */
+                       if (te->resjunk)
+                               continue;
+                       te->resname = pstrdup(strVal(lfirst(alist_item)));
+                       alist_item = lnext(alist_item);
+                       if (alist_item == NULL)
+                               break;                  /* done assigning aliases */
+               }
+
+               if (alist_item != NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("CREATE VIEW specifies more column "
+                                                       "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 = copyObject(stmt->view);  /* don't corrupt original command */
+       if (view->relpersistence == RELPERSISTENCE_PERMANENT
+               && isViewOnTempTable(viewParse))
+       {
+               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);
 
        /*
-        * first remove all the "view" rules... Currently we only have one!
+        * Create the view relation
+        *
+        * NOTE: if it already exists and replace is false, the xact will be
+        * aborted.
         */
-       rname = MakeRetrieveViewRuleName(viewName);
-       RemoveRewriteRule(rname);
+       viewOid = DefineVirtualRelation(view, viewParse->targetList,
+                                                                       stmt->replace, namespaceId);
 
        /*
-        * we don't really need that, but just in case...
+        * The relation we have just created is not visible to any other commands
+        * running with the same transaction & command id. So, increment the
+        * command id counter (but do NOT pfree any memory!!!!)
         */
        CommandCounterIncrement();
 
        /*
-        * now remove the relation.
+        * The range table of 'viewParse' does not contain entries for the "OLD"
+        * and "NEW" relations. So... add them!
+        */
+       viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse);
+
+       /*
+        * Now create the rules associated with the view.
         */
-       heap_destroy_with_catalog(viewName);
-       pfree(rname);
+       DefineViewRules(viewOid, viewParse, stmt->replace);
 }