CREATE OR REPLACE VIEW, CREATE OR REPLACE RULE.
authorTom Lane
Mon, 2 Sep 2002 02:13:02 +0000 (02:13 +0000)
committerTom Lane
Mon, 2 Sep 2002 02:13:02 +0000 (02:13 +0000)
Gavin Sherry, Neil Conway, and Tom Lane all got their hands dirty
on this one ...

12 files changed:
src/backend/commands/view.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/utility.c
src/include/commands/view.h
src/include/nodes/parsenodes.h
src/test/regress/expected/create_view.out
src/test/regress/expected/rules.out
src/test/regress/sql/create_view.sql
src/test/regress/sql/rules.sql

index c635a28571de18091d0f86d1e4f94682e86a360f..46cdf0aca0ba26717008e3b81163e7754b0b52bc 100644 (file)
@@ -6,43 +6,43 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: view.c,v 1.68 2002/08/30 19:23:19 tgl Exp $
+ *
+ * IDENTIFICATION
+ *   $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.69 2002/09/02 02:13:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "access/xact.h"
+#include "access/heapam.h"
 #include "catalog/dependency.h"
-#include "catalog/heap.h"
 #include "catalog/namespace.h"
 #include "commands/tablecmds.h"
 #include "commands/view.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.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/syscache.h"
+#include "utils/acl.h"
+#include "utils/lsyscache.h"
 
 
 /*---------------------------------------------------------------------
  * DefineVirtualRelation
  *
- * Create the "view" relation.
- * `DefineRelation' does all the work, we just provide the correct
- * arguments!
- *
- * 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 Oid
-DefineVirtualRelation(const RangeVar *relation, List *tlist)
+DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
 {
+   Oid         viewOid,
+               namespaceId;
    CreateStmt *createStmt = makeNode(CreateStmt);
    List       *attrList,
               *t;
@@ -52,7 +52,7 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist)
     * the (non-junk) targetlist items from the view's SELECT list.
     */
    attrList = NIL;
-   foreach(t, tlist)
+   foreach (t, tlist)
    {
        TargetEntry *entry = lfirst(t);
        Resdom     *res = entry->resdom;
@@ -83,23 +83,74 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist)
        elog(ERROR, "attempted to define virtual relation with no attrs");
 
    /*
-    * now create the parameters for keys/inheritance etc. All of them are
-    * nil...
+    * Check to see if we want to replace an existing view.
     */
-   createStmt->relation = (RangeVar *) relation;
-   createStmt->tableElts = attrList;
-   createStmt->inhRelations = NIL;
-   createStmt->constraints = NIL;
-   createStmt->hasoids = false;
+   namespaceId = RangeVarGetCreationNamespace(relation);
+   viewOid = get_relname_relid(relation->relname, namespaceId);
 
-   /*
-    * finally create the relation...
-    */
-   return DefineRelation(createStmt, RELKIND_VIEW);
+   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)
+           elog(ERROR, "%s is not a view",
+                RelationGetRelationName(rel));
+
+       if (!pg_class_ownercheck(viewOid, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
+
+       /*
+        * Create a tuple descriptor to compare against the existing view,
+        * and verify it matches.
+        *
+        * XXX the error message is a bit cheesy here: would be useful to
+        * give a more specific complaint about the difference in the
+        * descriptors.  No time for it at the moment though.
+        */
+       descriptor = BuildDescForRelation(attrList);
+       if (!equalTupleDescs(descriptor, rel->rd_att))
+           elog(ERROR, "Cannot change column set of existing view %s",
+                RelationGetRelationName(rel));
+
+       /*
+        * Seems okay, so return the OID of the pre-existing view.
+        */
+       relation_close(rel, NoLock); /* keep the lock! */
+
+       return viewOid;
+   }
+   else
+   {
+       /*
+        * now create the parameters for keys/inheritance etc. All of them are
+        * nil...
+        */
+       createStmt->relation = (RangeVar *) relation;
+       createStmt->tableElts = attrList;
+       createStmt->inhRelations = NIL;
+       createStmt->constraints = NIL;
+       createStmt->hasoids = 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);
+   }
 }
 
 static RuleStmt *
-FormViewRetrieveRule(const RangeVar *view, Query *viewParse)
+FormViewRetrieveRule(const RangeVar *view, Query *viewParse, bool replace)
 {
    RuleStmt   *rule;
 
@@ -114,12 +165,13 @@ FormViewRetrieveRule(const RangeVar *view, Query *viewParse)
    rule->event = CMD_SELECT;
    rule->instead = true;
    rule->actions = makeList1(viewParse);
+   rule->replace = replace;
 
    return rule;
 }
 
 static void
-DefineViewRules(const RangeVar *view, Query *viewParse)
+DefineViewRules(const RangeVar *view, Query *viewParse, bool replace)
 {
    RuleStmt   *retrieve_rule;
 
@@ -129,10 +181,9 @@ DefineViewRules(const RangeVar *view, Query *viewParse)
    RuleStmt   *delete_rule;
 #endif
 
-   retrieve_rule = FormViewRetrieveRule(view, viewParse);
+   retrieve_rule = FormViewRetrieveRule(view, viewParse, replace);
 
 #ifdef NOTYET
-
    replace_rule = FormViewReplaceRule(view, viewParse);
    append_rule = FormViewAppendRule(view, viewParse);
    delete_rule = FormViewDeleteRule(view, viewParse);
@@ -221,16 +272,18 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
  *-------------------------------------------------------------------
  */
 void
-DefineView(const RangeVar *view, Query *viewParse)
+DefineView(const RangeVar *view, Query *viewParse, bool replace)
 {
    Oid         viewOid;
 
    /*
     * Create the view relation
     *
-    * NOTE: if it already exists, the xact will be aborted.
+    * NOTE: if it already exists and replace is false, the xact will 
+    * be aborted.
     */
-   viewOid = DefineVirtualRelation(view, viewParse->targetList);
+
+   viewOid = DefineVirtualRelation(view, viewParse->targetList, replace);
 
    /*
     * The relation we have just created is not visible to any other
@@ -248,7 +301,7 @@ DefineView(const RangeVar *view, Query *viewParse)
    /*
     * Now create the rules associated with the view.
     */
-   DefineViewRules(view, viewParse);
+   DefineViewRules(view, viewParse, replace);
 }
 
 /*
index 1938e7f4738ef7e0b9066eaa6d261b43ab3c2a53..8a69af3de572763840ad3d6523013e032f88ee20 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.209 2002/08/31 22:10:43 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.210 2002/09/02 02:13:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2173,6 +2173,7 @@ _copyRuleStmt(RuleStmt *from)
    Node_Copy(from, newnode, whereClause);
    newnode->event = from->event;
    newnode->instead = from->instead;
+   newnode->replace = from->replace;
    Node_Copy(from, newnode, actions);
 
    return newnode;
@@ -2238,6 +2239,7 @@ _copyViewStmt(ViewStmt *from)
    Node_Copy(from, newnode, view);
    Node_Copy(from, newnode, aliases);
    Node_Copy(from, newnode, query);
+   newnode->replace = from->replace;
 
    return newnode;
 }
index 10b8e79933d7847a4dc2a220efdd4e0e4b30b54d..7f677c0837c09359fe50fcb09bda91917a46737f 100644 (file)
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.157 2002/08/31 22:10:43 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.158 2002/09/02 02:13:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1003,6 +1003,8 @@ _equalRuleStmt(RuleStmt *a, RuleStmt *b)
        return false;
    if (a->instead != b->instead)
        return false;
+   if (a->replace != b->replace)
+       return false;
    if (!equal(a->actions, b->actions))
        return false;
 
@@ -1067,6 +1069,8 @@ _equalViewStmt(ViewStmt *a, ViewStmt *b)
        return false;
    if (!equal(a->query, b->query))
        return false;
+   if (a->replace != b->replace)
+       return false;
 
    return true;
 }
index 463a8d5a4e538ee5f00d8eee47ed0a2eff8574a3..674a6d0d920007b32a25435b097ce2d3f8abaf59 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.364 2002/08/29 00:17:04 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.365 2002/09/02 02:13:01 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -3346,18 +3346,19 @@ opt_column: COLUMN                                  { $$ = COLUMN; }
  *
  *****************************************************************************/
 
-RuleStmt:  CREATE RULE name AS
+RuleStmt:  CREATE opt_or_replace RULE name AS
            { QueryIsRule=TRUE; }
            ON event TO qualified_name where_clause
            DO opt_instead RuleActionList
                {
                    RuleStmt *n = makeNode(RuleStmt);
-                   n->relation = $9;
-                   n->rulename = $3;
-                   n->whereClause = $10;
-                   n->event = $7;
-                   n->instead = $12;
-                   n->actions = $13;
+                   n->replace = $2;
+                   n->relation = $10;
+                   n->rulename = $4;
+                   n->whereClause = $11;
+                   n->event = $8;
+                   n->instead = $13;
+                   n->actions = $14;
                    $$ = (Node *)n;
                    QueryIsRule=FALSE;
                }
@@ -3537,12 +3538,14 @@ opt_trans:  WORK                                    {}
  *
  *****************************************************************************/
 
-ViewStmt:  CREATE VIEW qualified_name opt_column_list AS SelectStmt
+ViewStmt:  CREATE opt_or_replace VIEW qualified_name opt_column_list 
+               AS SelectStmt
                {
                    ViewStmt *n = makeNode(ViewStmt);
-                   n->view = $3;
-                   n->aliases = $4;
-                   n->query = (Query *) $6;
+                   n->replace = $2;
+                   n->view = $4;
+                   n->aliases = $5;
+                   n->query = (Query *) $7;
                    $$ = (Node *)n;
                }
        ;
index 80952d7ddc403d56a33b844a3cc4e88fff853505..f2761894260576e2f9e09b75c84aae0110d3e05c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.77 2002/08/05 03:29:17 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.78 2002/09/02 02:13:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,24 +48,23 @@ InsertRule(char *rulname,
           AttrNumber evslot_index,
           bool evinstead,
           Node *event_qual,
-          List *action)
+          List *action,
+          bool replace)
 {
    char       *evqual = nodeToString(event_qual);
    char       *actiontree = nodeToString((Node *) action);
    int         i;
    Datum       values[Natts_pg_rewrite];
    char        nulls[Natts_pg_rewrite];
+   char        replaces[Natts_pg_rewrite];
    NameData    rname;
    Relation    pg_rewrite_desc;
-   TupleDesc   tupDesc;
-   HeapTuple   tup;
+   HeapTuple   tup,
+               oldtup;
    Oid         rewriteObjectId;
    ObjectAddress   myself,
                    referenced;
-
-   if (IsDefinedRewriteRule(eventrel_oid, rulname))
-       elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
-            rulname);
+   bool        is_update = false;
 
    /*
     * Set up *nulls and *values arrays
@@ -83,22 +82,61 @@ InsertRule(char *rulname,
    values[i++] = DirectFunctionCall1(textin, CStringGetDatum(actiontree)); /* ev_action */
 
    /*
-    * create a new pg_rewrite tuple
+    * Ready to store new pg_rewrite tuple
     */
    pg_rewrite_desc = heap_openr(RewriteRelationName, RowExclusiveLock);
 
-   tupDesc = pg_rewrite_desc->rd_att;
+   /*
+    * Check to see if we are replacing an existing tuple
+    */
+   oldtup = SearchSysCache(RULERELNAME,
+                           ObjectIdGetDatum(eventrel_oid),
+                           PointerGetDatum(rulname),
+                           0, 0);
+
+   if (HeapTupleIsValid(oldtup))
+   {
+       if (!replace)
+           elog(ERROR,"Attempt to insert rule \"%s\" failed: already exists",
+                rulname);
+
+       /*
+        * When replacing, we don't need to replace every attribute
+        */
+       MemSet(replaces, ' ', sizeof(replaces));
+       replaces[Anum_pg_rewrite_ev_attr - 1] = 'r';
+       replaces[Anum_pg_rewrite_ev_type - 1] = 'r';
+       replaces[Anum_pg_rewrite_is_instead - 1] = 'r';
+       replaces[Anum_pg_rewrite_ev_qual - 1] = 'r';
+       replaces[Anum_pg_rewrite_ev_action - 1] = 'r';
+
+       tup = heap_modifytuple(oldtup, pg_rewrite_desc,
+                              values, nulls, replaces);
+
+       simple_heap_update(pg_rewrite_desc, &tup->t_self, tup);
 
-   tup = heap_formtuple(tupDesc,
-                        values,
-                        nulls);
+       ReleaseSysCache(oldtup);
+
+       rewriteObjectId = HeapTupleGetOid(tup);
+       is_update = true;
+   }
+   else
+   {
+       tup = heap_formtuple(pg_rewrite_desc->rd_att, values, nulls);
 
-   rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup);
+       rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup);
+   }
 
+   /* Need to update indexes in either case */
    CatalogUpdateIndexes(pg_rewrite_desc, tup);
 
    heap_freetuple(tup);
 
+   /* If replacing, get rid of old dependencies and make new ones */
+   if (is_update)
+       deleteDependencyRecordsFor(RelationGetRelid(pg_rewrite_desc),
+                                  rewriteObjectId);
+
    /*
     * Install dependency on rule's relation to ensure it will go away
     * on relation deletion.  If the rule is ON SELECT, make the dependency
@@ -114,13 +152,14 @@ InsertRule(char *rulname,
    referenced.objectSubId = 0;
 
    recordDependencyOn(&myself, &referenced,
-       (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
+                      (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
    /*
     * Also install dependencies on objects referenced in action and qual.
     */
    recordDependencyOnExpr(&myself, (Node *) action, NIL,
                           DEPENDENCY_NORMAL);
+
    if (event_qual != NULL)
    {
        /* Find query containing OLD/NEW rtable entries */
@@ -143,6 +182,7 @@ DefineQueryRewrite(RuleStmt *stmt)
    Node       *event_qual = stmt->whereClause;
    CmdType     event_type = stmt->event;
    bool        is_instead = stmt->instead;
+   bool        replace = stmt->replace;
    List       *action = stmt->actions;
    Relation    event_relation;
    Oid         ev_relid;
@@ -232,7 +272,7 @@ DefineQueryRewrite(RuleStmt *stmt)
         * event relation, ...
         */
        i = 0;
-       foreach(tllist, query->targetList)
+       foreach (tllist, query->targetList)
        {
            TargetEntry *tle = (TargetEntry *) lfirst(tllist);
            Resdom     *resdom = tle->resdom;
@@ -282,7 +322,7 @@ DefineQueryRewrite(RuleStmt *stmt)
        /*
         * ... there must not be another ON SELECT rule already ...
         */
-       if (event_relation->rd_rules != NULL)
+       if (!replace && event_relation->rd_rules != NULL)
        {
            for (i = 0; i < event_relation->rd_rules->numLocks; i++)
            {
@@ -364,7 +404,8 @@ DefineQueryRewrite(RuleStmt *stmt)
                            event_attno,
                            is_instead,
                            event_qual,
-                           action);
+                           action,
+                           replace);
 
        /*
         * Set pg_class 'relhasrules' field TRUE for event relation. If
index 17a2533b775da8e74eed4331ca4cead3dc27e7ef..40dec9d68b8265cdf3769008863cc4436ac4a75a 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.175 2002/08/30 19:23:20 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.176 2002/09/02 02:13:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -595,7 +595,7 @@ ProcessUtility(Node *parsetree,
            {
                ViewStmt   *stmt = (ViewStmt *) parsetree;
 
-               DefineView(stmt->view, stmt->query);
+               DefineView(stmt->view, stmt->query, stmt->replace);
            }
            break;
 
index 3603f2a4fab260310436cde4be158550763c1799..e476d8224e224c2a7931ecb15e4e78620686fcb0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: view.h,v 1.16 2002/07/01 15:27:56 tgl Exp $
+ * $Id: view.h,v 1.17 2002/09/02 02:13:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,7 @@
 
 #include "nodes/parsenodes.h"
 
-extern void DefineView(const RangeVar *view, Query *view_parse);
+extern void DefineView(const RangeVar *view, Query *view_parse, bool replace);
 extern void RemoveView(const RangeVar *view, DropBehavior behavior);
 
 #endif   /* VIEW_H */
index a426aeba020fd0f373b4908a7c875b03b6b47271..be8b7fe7d1aa3fd95ce326ece721894af6f56b12 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.204 2002/08/31 22:10:47 tgl Exp $
+ * $Id: parsenodes.h,v 1.205 2002/09/02 02:13:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1371,6 +1371,7 @@ typedef struct RuleStmt
    CmdType     event;          /* SELECT, INSERT, etc */
    bool        instead;        /* is a 'do instead'? */
    List       *actions;        /* the action statements */
+   bool        replace;        /* OR REPLACE */
 } RuleStmt;
 
 /* ----------------------
@@ -1436,6 +1437,7 @@ typedef struct ViewStmt
    RangeVar   *view;           /* the view to be created */
    List       *aliases;        /* target column names */
    Query      *query;          /* the SQL statement */
+   bool        replace;        /* replace an existing view? */
 } ViewStmt;
 
 /* ----------------------
index e398440bce832b6706dce6c5191854fbf9720c03..ade98e6c311ca0f532c0189306550c9273350e74 100644 (file)
@@ -15,3 +15,43 @@ CREATE VIEW iexit AS
 CREATE VIEW toyemp AS
    SELECT name, age, location, 12*salary AS annualsal
    FROM emp;
+--
+-- CREATE OR REPLACE VIEW
+--
+CREATE TABLE viewtest_tbl (a int, b int);
+COPY viewtest_tbl FROM stdin;
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT * FROM viewtest_tbl;
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT * FROM viewtest_tbl WHERE a > 10;
+SELECT * FROM viewtest;
+ a  | b  
+----+----
+ 15 | 20
+ 20 | 25
+(2 rows)
+
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT a, b FROM viewtest_tbl WHERE a > 5 ORDER BY b DESC;
+SELECT * FROM viewtest;
+ a  | b  
+----+----
+ 20 | 25
+ 15 | 20
+ 10 | 15
+(3 rows)
+
+-- should fail
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT a FROM viewtest_tbl WHERE a <> 20;
+ERROR:  Cannot change column set of existing view viewtest
+-- should fail
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT 1, * FROM viewtest_tbl;
+ERROR:  Cannot change column set of existing view viewtest
+-- should fail
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT a, b::numeric FROM viewtest_tbl;
+ERROR:  Cannot change column set of existing view viewtest
+DROP VIEW viewtest;
+DROP TABLE viewtest_tbl;
index 0cfd3f103d46c0c027417149204f772f6b7a743b..bcce2332c4a1367508691eb4a13726755ce5d409 100644 (file)
@@ -1343,3 +1343,21 @@ SELECT tablename, rulename, definition FROM pg_rules
  shoelace_ok   | shoelace_ok_ins | CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = (shoelace.sl_avail + new.ok_quant) WHERE (shoelace.sl_name = new.ok_name);
 (29 rows)
 
+--
+-- CREATE OR REPLACE RULE
+--
+CREATE TABLE ruletest_tbl (a int, b int);
+CREATE TABLE ruletest_tbl2 (a int, b int);
+CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl
+   DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (10, 10);
+INSERT INTO ruletest_tbl VALUES (99, 99);
+CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl
+   DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (1000, 1000);
+INSERT INTO ruletest_tbl VALUES (99, 99);
+SELECT * FROM ruletest_tbl2;
+  a   |  b   
+------+------
+   10 |   10
+ 1000 | 1000
+(2 rows)
+
index 858c8ce960d17f26184b5cd628f83e97384168e4..8c15fc12417f299dc73d3a197f1f5f1acd9286ab 100644 (file)
@@ -19,3 +19,42 @@ CREATE VIEW toyemp AS
    SELECT name, age, location, 12*salary AS annualsal
    FROM emp;
 
+--
+-- CREATE OR REPLACE VIEW
+--
+
+CREATE TABLE viewtest_tbl (a int, b int);
+COPY viewtest_tbl FROM stdin;
+5  10
+10 15
+15 20
+20 25
+\.
+
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT * FROM viewtest_tbl;
+
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT * FROM viewtest_tbl WHERE a > 10;
+
+SELECT * FROM viewtest;
+
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT a, b FROM viewtest_tbl WHERE a > 5 ORDER BY b DESC;
+
+SELECT * FROM viewtest;
+
+-- should fail
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT a FROM viewtest_tbl WHERE a <> 20;
+
+-- should fail
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT 1, * FROM viewtest_tbl;
+
+-- should fail
+CREATE OR REPLACE VIEW viewtest AS
+   SELECT a, b::numeric FROM viewtest_tbl;
+
+DROP VIEW viewtest;
+DROP TABLE viewtest_tbl;
index 6ee6f2a531783bba6ed647115cad0b625a39aa2b..20afc9f0edae650512a3b21847360401767505e6 100644 (file)
@@ -765,3 +765,21 @@ SELECT viewname, definition FROM pg_views ORDER BY viewname;
 SELECT tablename, rulename, definition FROM pg_rules 
    ORDER BY tablename, rulename;
 
+--
+-- CREATE OR REPLACE RULE
+--
+
+CREATE TABLE ruletest_tbl (a int, b int);
+CREATE TABLE ruletest_tbl2 (a int, b int);
+
+CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl
+   DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (10, 10);
+
+INSERT INTO ruletest_tbl VALUES (99, 99);
+
+CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl
+   DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (1000, 1000);
+
+INSERT INTO ruletest_tbl VALUES (99, 99);
+
+SELECT * FROM ruletest_tbl2;