Commit 248c67d7 authored by Tom Lane's avatar Tom Lane

CREATE OR REPLACE VIEW, CREATE OR REPLACE RULE.

Gavin Sherry, Neil Conway, and Tom Lane all got their hands dirty
on this one ...
parent c7a165ad
......@@ -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);
}
/*
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
;
......
......@@ -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
......
......@@ -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;
......
......@@ -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 */
......@@ -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;
/* ----------------------
......
......@@ -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;
......@@ -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)
......@@ -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;
......@@ -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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment