Index: doc/src/sgml/catalogs.sgml
===================================================================
RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/catalogs.sgml,v
retrieving revision 2.129
diff -c -p -r2.129 catalogs.sgml
*** doc/src/sgml/catalogs.sgml 31 Jul 2006 20:08:55 -0000 2.129
--- doc/src/sgml/catalogs.sgml 17 Aug 2006 03:34:45 -0000
***************
*** 3502,3507 ****
--- 3502,3519 ----
+ ev_kind
+ char
+
+
+ l> = with local check option,
+ c> = with cascaded check option,
+ n> = no check option specified,
+ e> = rule was created by user
+
+
+
+
ev_qual
text
Index: doc/src/sgml/information_schema.sgml
===================================================================
RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/information_schema.sgml,v
retrieving revision 1.26
diff -c -p -r1.26 information_schema.sgml
*** doc/src/sgml/information_schema.sgml 2 May 2006 18:07:51 -0000 1.26
--- doc/src/sgml/information_schema.sgml 17 Aug 2006 03:34:45 -0000
*************** ORDER BY c.ordinal_position;
*** 5085,5091 ****
check_option
character_data
! Applies to a feature not available in PostgreSQL>
--- 5085,5095 ----
check_option
character_data
!
! The level of integrity checking in updatable views,
! either LOCAL, CASCADED
! or NONE
!
Index: doc/src/sgml/intro.sgml
===================================================================
RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/intro.sgml,v
retrieving revision 1.31
diff -c -p -r1.31 intro.sgml
*** doc/src/sgml/intro.sgml 10 Mar 2006 19:10:48 -0000 1.31
--- doc/src/sgml/intro.sgml 17 Aug 2006 03:34:45 -0000
***************
*** 110,116 ****
triggers
! views
transactional integrity
--- 110,116 ----
triggers
! updatable views
transactional integrity
Index: doc/src/sgml/ref/create_view.sgml
===================================================================
RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/create_view.sgml,v
retrieving revision 1.31
diff -c -p -r1.31 create_view.sgml
*** doc/src/sgml/ref/create_view.sgml 1 Nov 2005 21:09:50 -0000 1.31
--- doc/src/sgml/ref/create_view.sgml 17 Aug 2006 03:34:45 -0000
*************** PostgreSQL documentation
*** 22,27 ****
--- 22,28 ----
CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW name [ ( column_name [, ...] ) ]
AS query
+ [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
*************** CREATE [ OR REPLACE ] [ TEMP | TEMPORARY
*** 109,114 ****
--- 110,150 ----
+
+
+
+ CHECK OPTION
+
+
+ This option has to do with updatable views. All
+ INSERT> and UPDATE> commands on the view
+ will be checked to ensure data satisfy the view-defining
+ condition (that is, the new data would be visible through the
+ view). If they do not, the update will be rejected.
+
+
+
+
+
+ LOCAL
+
+
+ Check for integrity on this view.
+
+
+
+
+
+ CASCADED
+
+
+ Check for integrity on this view and on any dependent
+ view. CASCADED> is assumed if neither
+ CASCADED> nor LOCAL> is specified.
+
+
+
+
*************** CREATE [ OR REPLACE ] [ TEMP | TEMPORARY
*** 116,126 ****
Notes
! Currently, views are read only: the system will not allow an insert,
! update, or delete on a view. You can get the effect of an updatable
! view by creating rules that rewrite inserts, etc. on the view into
! appropriate actions on other tables. For more information see
! .
--- 152,201 ----
Notes
! Currently, views are updatable following SQL92 specifications, that
! is:
!
!
!
!
!
! Views with just one base table (or another updatable view)
!
!
!
!
! No aggregate functions
!
!
!
!
! No HAVING, DISTINCT nor
! GROUP BY clauses
!
!
!
!
!
! No UNION, INTERSECT,
! EXCEPTclauses
!
!
!
!
!
! Views are insertable only if you provide in the defining
! SELECT statement all columns that are NOT NULL and don't
! have a default value.
!
!
!
! The updatable views implementation is based on the rule system. Because of
! this, you can get the same effect in more complex views by creating your
! own rules that rewrite the INSERT,
! UPDATE and UPDATE actions on the view
! into appropriate actions on other tables. You can also replace
! automatically generated rules with your own rules. For more information,
! refer to .
*************** CREATE VIEW comedies AS
*** 171,225 ****
Compatibility
- The SQL standard specifies some additional capabilities for the
- CREATE VIEW statement:
-
- CREATE VIEW name [ ( column_name [, ...] ) ]
- AS query
- [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
-
-
-
-
- The optional clauses for the full SQL command are:
-
-
-
- CHECK OPTION
-
-
- This option has to do with updatable views. All
- INSERT> and UPDATE> commands on the view
- will be checked to ensure data satisfy the view-defining
- condition (that is, the new data would be visible through the
- view). If they do not, the update will be rejected.
-
-
-
-
-
- LOCAL
-
-
- Check for integrity on this view.
-
-
-
-
-
- CASCADED
-
-
- Check for integrity on this view and on any dependent
- view. CASCADED> is assumed if neither
- CASCADED> nor LOCAL> is specified.
-
-
-
-
-
-
-
CREATE OR REPLACE VIEW is a
PostgreSQL language extension.
So is the concept of a temporary view.
--- 246,251 ----
Index: src/backend/catalog/information_schema.sql
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/information_schema.sql,v
retrieving revision 1.33
diff -c -p -r1.33 information_schema.sql
*** src/backend/catalog/information_schema.sql 2 Apr 2006 17:38:13 -0000 1.33
--- src/backend/catalog/information_schema.sql 17 Aug 2006 03:34:45 -0000
*************** CREATE VIEW views AS
*** 2126,2132 ****
ELSE null END
AS character_data) AS view_definition,
! CAST('NONE' AS character_data) AS check_option,
CAST(
CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 2 AND is_instead)
--- 2126,2143 ----
ELSE null END
AS character_data) AS view_definition,
! CAST(
! CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 2 AND is_instead AND ev_kind = 'l')
! OR EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 3 AND is_instead AND ev_kind = 'l')
! OR EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 4 AND is_instead AND ev_kind = 'l')
! THEN 'LOCAL'
! WHEN EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 2 AND is_instead AND ev_kind = 'c')
! OR EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 3 AND is_instead AND ev_kind = 'c')
! OR EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 4 AND is_instead AND ev_kind = 'c')
! THEN 'CASCADED'
! ELSE 'NONE'
! END
! AS character_data) AS check_option,
CAST(
CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite WHERE ev_class = c.oid AND ev_type = 2 AND is_instead)
Index: src/backend/commands/view.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/view.c,v
retrieving revision 1.96
diff -c -p -r1.96 view.c
*** src/backend/commands/view.c 13 Jul 2006 16:49:14 -0000 1.96
--- src/backend/commands/view.c 20 Aug 2006 19:55:36 -0000
***************
*** 29,41 ****
--- 29,44 ----
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+ #include "rewrite/viewUpdate.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
+ #include "utils/memutils.h"
static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc);
static bool isViewOnTempTable_walker(Node *node, void *context);
+
/*---------------------------------------------------------------------
* isViewOnTempTable
*
*************** FormViewRetrieveRule(const RangeVar *vie
*** 281,330 ****
}
static void
! DefineViewRules(const RangeVar *view, Query *viewParse, bool replace)
{
RuleStmt *retrieve_rule;
- #ifdef NOTYET
- RuleStmt *replace_rule;
- RuleStmt *append_rule;
- RuleStmt *delete_rule;
- #endif
-
retrieve_rule = FormViewRetrieveRule(view, viewParse, replace);
! #ifdef NOTYET
! replace_rule = FormViewReplaceRule(view, viewParse);
! append_rule = FormViewAppendRule(view, viewParse);
! delete_rule = FormViewDeleteRule(view, viewParse);
! #endif
!
! DefineQueryRewrite(retrieve_rule);
!
! #ifdef NOTYET
! DefineQueryRewrite(replace_rule);
! DefineQueryRewrite(append_rule);
! DefineQueryRewrite(delete_rule);
! #endif
}
! /*---------------------------------------------------------------
* UpdateRangeTableOfViewParse
*
! * Update the range table of the given parsetree.
! * 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 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...
*
! * These extra RT entries are not actually used in the query,
! * except for run-time permission checking.
! *---------------------------------------------------------------
*/
static Query *
UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
--- 284,315 ----
}
static void
! DefineViewRules(const RangeVar *view, Query *viewParse, bool replace,
! bool checkOption, bool cascade)
{
RuleStmt *retrieve_rule;
retrieve_rule = FormViewRetrieveRule(view, viewParse, replace);
! DefineQueryRewrite(retrieve_rule,
! makeViewCheckOption(checkOption, cascade));
+ CreateViewUpdateRules(viewParse, view, checkOption, cascade);
}
! /*
* UpdateRangeTableOfViewParse
*
! * Update the range table of the given parsetree. 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 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...
*
! * These extra RT entries are not actually used in the query, except for
! * run-time permission checking.
*/
static Query *
UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
*************** UpdateRangeTableOfViewParse(Oid viewOid,
*** 388,394 ****
*-------------------------------------------------------------------
*/
void
! DefineView(RangeVar *view, Query *viewParse, bool replace)
{
Oid viewOid;
--- 373,380 ----
*-------------------------------------------------------------------
*/
void
! DefineView(RangeVar *view, Query *viewParse, bool replace, bool checkOption,
! bool cascade)
{
Oid viewOid;
*************** DefineView(RangeVar *view, Query *viewPa
*** 429,435 ****
/*
* Now create the rules associated with the view.
*/
! DefineViewRules(view, viewParse, replace);
}
/*
--- 415,421 ----
/*
* Now create the rules associated with the view.
*/
! DefineViewRules(view, viewParse, replace, checkOption, cascade);
}
/*
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.556
diff -c -p -r2.556 gram.y
*** src/backend/parser/gram.y 12 Aug 2006 18:58:54 -0000 2.556
--- src/backend/parser/gram.y 19 Aug 2006 19:48:37 -0000
*************** AlterOwnerStmt: ALTER AGGREGATE func_nam
*** 4473,4479 ****
*****************************************************************************/
RuleStmt: CREATE opt_or_replace RULE name AS
! { QueryIsRule=TRUE; }
ON event TO qualified_name where_clause
DO opt_instead RuleActionList
{
--- 4473,4479 ----
*****************************************************************************/
RuleStmt: CREATE opt_or_replace RULE name AS
! { QueryIsRule = true; }
ON event TO qualified_name where_clause
DO opt_instead RuleActionList
{
*************** RuleStmt: CREATE opt_or_replace RULE nam
*** 4486,4492 ****
n->instead = $13;
n->actions = $14;
$$ = (Node *)n;
! QueryIsRule=FALSE;
}
;
--- 4486,4492 ----
n->instead = $13;
n->actions = $14;
$$ = (Node *)n;
! QueryIsRule = false;
}
;
*************** RuleActionList:
*** 4499,4511 ****
/* the thrashing around here is to discard "empty" statements... */
RuleActionMulti:
RuleActionMulti ';' RuleActionStmtOrEmpty
! { if ($3 != NULL)
$$ = lappend($1, $3);
else
$$ = $1;
}
| RuleActionStmtOrEmpty
! { if ($1 != NULL)
$$ = list_make1($1);
else
$$ = NIL;
--- 4499,4513 ----
/* the thrashing around here is to discard "empty" statements... */
RuleActionMulti:
RuleActionMulti ';' RuleActionStmtOrEmpty
! {
! if ($3 != NULL)
$$ = lappend($1, $3);
else
$$ = $1;
}
| RuleActionStmtOrEmpty
! {
! if ($1 != NULL)
$$ = list_make1($1);
else
$$ = NIL;
*************** ViewStmt: CREATE OptTemp VIEW qualified_
*** 4770,4775 ****
--- 4772,4778 ----
n->view->istemp = $2;
n->aliases = $5;
n->query = (Query *) $7;
+ n->options = $8;
$$ = (Node *) n;
}
| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
*************** ViewStmt: CREATE OptTemp VIEW qualified_
*** 4781,4786 ****
--- 4784,4790 ----
n->view->istemp = $4;
n->aliases = $7;
n->query = (Query *) $9;
+ n->options = $10;
$$ = (Node *) n;
}
;
*************** ViewStmt: CREATE OptTemp VIEW qualified_
*** 4792,4812 ****
opt_check_option:
WITH_CHECK OPTION
{
! ereport(ERROR,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("WITH CHECK OPTION is not implemented")));
}
| WITH_CASCADED CHECK OPTION
{
! ereport(ERROR,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("WITH CHECK OPTION is not implemented")));
}
| WITH_LOCAL CHECK OPTION
{
! ereport(ERROR,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("WITH CHECK OPTION is not implemented")));
}
| /* EMPTY */ { $$ = NIL; }
;
--- 4796,4810 ----
opt_check_option:
WITH_CHECK OPTION
{
! $$ = list_make1((Node *)makeString("cascaded"));
}
| WITH_CASCADED CHECK OPTION
{
! $$ = list_make1((Node *)makeString("cascaded"));
}
| WITH_LOCAL CHECK OPTION
{
! $$ = list_make1((Node *)makeString("local"));
}
| /* EMPTY */ { $$ = NIL; }
;
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.175
diff -c -p -r1.175 keywords.c
*** src/backend/parser/keywords.c 12 Aug 2006 02:52:05 -0000 1.175
--- src/backend/parser/keywords.c 17 Aug 2006 03:34:45 -0000
*************** static const ScanKeyword ScanKeywords[]
*** 67,72 ****
--- 67,73 ----
{"cache", CACHE},
{"called", CALLED},
{"cascade", CASCADE},
+ {"cascaded", CASCADED},
{"case", CASE},
{"cast", CAST},
{"chain", CHAIN},
Index: src/backend/rewrite/Makefile
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/rewrite/Makefile,v
retrieving revision 1.15
diff -c -p -r1.15 Makefile
*** src/backend/rewrite/Makefile 29 Nov 2003 19:51:55 -0000 1.15
--- src/backend/rewrite/Makefile 20 Aug 2006 19:47:51 -0000
*************** top_builddir = ../../..
*** 13,19 ****
include $(top_builddir)/src/Makefile.global
OBJS = rewriteRemove.o rewriteDefine.o \
! rewriteHandler.o rewriteManip.o rewriteSupport.o
all: SUBSYS.o
--- 13,20 ----
include $(top_builddir)/src/Makefile.global
OBJS = rewriteRemove.o rewriteDefine.o \
! rewriteHandler.o rewriteManip.o rewriteSupport.o \
! viewUpdate.o
all: SUBSYS.o
Index: src/backend/rewrite/rewriteDefine.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/rewrite/rewriteDefine.c,v
retrieving revision 1.112
diff -c -p -r1.112 rewriteDefine.c
*** src/backend/rewrite/rewriteDefine.c 12 Aug 2006 20:05:55 -0000 1.112
--- src/backend/rewrite/rewriteDefine.c 17 Aug 2006 03:34:45 -0000
***************
*** 23,28 ****
--- 23,29 ----
#include "parser/parse_expr.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
+ #include "rewrite/rewriteRemove.h"
#include "rewrite/rewriteSupport.h"
#include "storage/smgr.h"
#include "utils/acl.h"
*************** InsertRule(char *rulname,
*** 47,52 ****
--- 48,54 ----
Oid eventrel_oid,
AttrNumber evslot_index,
bool evinstead,
+ char evkind,
Node *event_qual,
List *action,
bool replace)
*************** InsertRule(char *rulname,
*** 78,83 ****
--- 80,86 ----
values[i++] = Int16GetDatum(evslot_index); /* ev_attr */
values[i++] = CharGetDatum(evtype + '0'); /* ev_type */
values[i++] = BoolGetDatum(evinstead); /* is_instead */
+ values[i++] = CharGetDatum(evkind); /* ev_kind */
values[i++] = DirectFunctionCall1(textin, CStringGetDatum(evqual)); /* ev_qual */
values[i++] = DirectFunctionCall1(textin, CStringGetDatum(actiontree)); /* ev_action */
*************** InsertRule(char *rulname,
*** 97,107 ****
if (HeapTupleIsValid(oldtup))
{
if (!replace)
! ereport(ERROR,
! (errcode(ERRCODE_DUPLICATE_OBJECT),
! errmsg("rule \"%s\" for relation \"%s\" already exists",
! rulname, get_rel_name(eventrel_oid))));
/*
* When replacing, we don't need to replace every attribute
*/
--- 100,131 ----
if (HeapTupleIsValid(oldtup))
{
if (!replace)
! {
! /*
! * If REPLACE was not used we still have to check if the
! * rule is implicit: then we have to replace it anyways.
! */
! char old_tup_is_implicit;
! bool isnull;
! Datum dat;
!
! dat = heap_getattr(oldtup,
! Anum_pg_rewrite_ev_kind,
! RelationGetDescr(pg_rewrite_desc),
! &isnull);
! /* should not happen */
! if (!isnull)
! elog(ERROR, "got null field in ev_kind where not null expected");
!
! old_tup_is_implicit = DatumGetChar(dat);
+ if (old_tup_is_implicit == NO_OPTION_EXPLICIT)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("rule \"%s\" for relation \"%s\" already exists",
+ rulname, get_rel_name(eventrel_oid))));
+ }
+
/*
* When replacing, we don't need to replace every attribute
*/
*************** InsertRule(char *rulname,
*** 109,114 ****
--- 133,139 ----
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_kind - 1] = 'r';
replaces[Anum_pg_rewrite_ev_qual - 1] = 'r';
replaces[Anum_pg_rewrite_ev_action - 1] = 'r';
*************** InsertRule(char *rulname,
*** 177,183 ****
}
void
! DefineQueryRewrite(RuleStmt *stmt)
{
RangeVar *event_obj = stmt->relation;
Node *event_qual = stmt->whereClause;
--- 202,208 ----
}
void
! DefineQueryRewrite(RuleStmt *stmt, char ev_kind)
{
RangeVar *event_obj = stmt->relation;
Node *event_qual = stmt->whereClause;
*************** DefineQueryRewrite(RuleStmt *stmt)
*** 444,449 ****
--- 469,484 ----
}
setRuleCheckAsUser_Expr(event_qual, GetUserId());
+ /*
+ * Implicit rules should be dropped automatically when someone
+ * wants to have its *own* rules on the view. is_implicit is set
+ * to NO_OPTION_EXPLICIT in this case so we drop all implicit
+ * rules on the specified event type immediately.
+ */
+
+ if (ev_kind == NO_OPTION_EXPLICIT)
+ deleteImplicitRulesOnEvent(event_relation, event_type);
+
/* discard rule if it's null action and not INSTEAD; it's a no-op */
if (action != NIL || is_instead)
{
*************** DefineQueryRewrite(RuleStmt *stmt)
*** 452,457 ****
--- 487,493 ----
ev_relid,
event_attno,
is_instead,
+ ev_kind,
event_qual,
action,
replace);
*************** DefineQueryRewrite(RuleStmt *stmt)
*** 469,483 ****
}
/*
! * IF the relation is becoming a view, delete the storage files associated
* with it. NB: we had better have AccessExclusiveLock to do this ...
*
* XXX what about getting rid of its TOAST table? For now, we don't.
*/
if (RelisBecomingView)
{
! RelationOpenSmgr(event_relation);
smgrscheduleunlink(event_relation->rd_smgr, event_relation->rd_istemp);
}
/* Close rel, but keep lock till commit... */
--- 505,526 ----
}
/*
! * If the relation is becoming a view, delete the storage files associated
* with it. NB: we had better have AccessExclusiveLock to do this ...
*
* XXX what about getting rid of its TOAST table? For now, we don't.
*/
if (RelisBecomingView)
{
! /*
! * XXX -- Why this change?
! *
! RelationOpenSmgr(event_relation);
! */
! if (event_relation->rd_smgr == NULL)
! event_relation->rd_smgr = smgropen(event_relation->rd_node);
smgrscheduleunlink(event_relation->rd_smgr, event_relation->rd_istemp);
+ event_relation->rd_smgr = NULL;
}
/* Close rel, but keep lock till commit... */
Index: src/backend/rewrite/rewriteHandler.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/rewrite/rewriteHandler.c,v
retrieving revision 1.165
diff -c -p -r1.165 rewriteHandler.c
*** src/backend/rewrite/rewriteHandler.c 2 Aug 2006 01:59:47 -0000 1.165
--- src/backend/rewrite/rewriteHandler.c 20 Aug 2006 01:20:23 -0000
***************
*** 15,20 ****
--- 15,21 ----
#include "access/heapam.h"
#include "catalog/pg_type.h"
+ #include "catalog/pg_rewrite.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "parser/analyze.h"
*************** typedef struct rewrite_event
*** 34,45 ****
CmdType event; /* type of rule being fired */
} rewrite_event;
static bool acquireLocksOnSubLinks(Node *node, void *context);
static Query *rewriteRuleAction(Query *parsetree,
Query *rule_action,
Node *rule_qual,
int rt_index,
! CmdType event);
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
static void rewriteTargetList(Query *parsetree, Relation target_relation,
List **attrno_list);
--- 35,55 ----
CmdType event; /* type of rule being fired */
} rewrite_event;
+ /* Rule rewrite status for updatable views */
+ typedef struct view_update_event
+ {
+ bool viewUpdate;
+ bool needCheckOption;
+ char checkMode;
+ } view_update_event;
+
static bool acquireLocksOnSubLinks(Node *node, void *context);
static Query *rewriteRuleAction(Query *parsetree,
Query *rule_action,
Node *rule_qual,
int rt_index,
! CmdType event,
! view_update_event *view_event);
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
static void rewriteTargetList(Query *parsetree, Relation target_relation,
List **attrno_list);
*************** rewriteRuleAction(Query *parsetree,
*** 257,263 ****
Query *rule_action,
Node *rule_qual,
int rt_index,
! CmdType event)
{
int current_varno,
new_varno;
--- 267,274 ----
Query *rule_action,
Node *rule_qual,
int rt_index,
! CmdType event,
! view_update_event *view_event)
{
int current_varno,
new_varno;
*************** rewriteRuleAction(Query *parsetree,
*** 387,393 ****
* queries one w/rule_qual, one w/NOT rule_qual. Also add user query qual
* onto rule action
*/
! AddQual(sub_action, rule_qual);
AddQual(sub_action, parsetree->jointree->quals);
--- 398,405 ----
* queries one w/rule_qual, one w/NOT rule_qual. Also add user query qual
* onto rule action
*/
! if (view_event->needCheckOption)
! AddQual(sub_action, rule_qual);
AddQual(sub_action, parsetree->jointree->quals);
*************** build_column_default(Relation rel, int a
*** 820,825 ****
--- 832,850 ----
}
}
+ #if 0
+ /*
+ * I will do this only in case of relkind == RELKIND_VIEW.
+ * This is the last attempt to get a value for expr before we
+ * consider that expr must be NULL.
+ */
+ if (expr == NULL && rel->rd_rel->relkind == RELKIND_VIEW)
+ {
+ expr = (Node *)makeNode(SetToDefault);
+ return expr;
+ }
+ #endif
+
if (expr == NULL)
{
/*
*************** CopyAndAddInvertedQual(Query *parsetree,
*** 1362,1367 ****
--- 1387,1394 ----
* Return value:
* list of rule actions adjusted for use with this query
*
+ * view_event -- FIXME -- what is this for?
+ *
* Qualified INSTEAD rules generate their action with the qualification
* condition added. They also generate a modified version of the original
* query with the negated qualification added, so that it will run only for
*************** fireRules(Query *parsetree,
*** 1376,1381 ****
--- 1403,1409 ----
int rt_index,
CmdType event,
List *locks,
+ view_update_event *view_event,
bool *instead_flag,
Query **qual_product)
{
*************** fireRules(Query *parsetree,
*** 1390,1400 ****
QuerySource qsrc;
ListCell *r;
/* Determine correct QuerySource value for actions */
if (rule_lock->isInstead)
{
! if (event_qual != NULL)
! qsrc = QSRC_QUAL_INSTEAD_RULE;
else
{
qsrc = QSRC_INSTEAD_RULE;
--- 1418,1507 ----
QuerySource qsrc;
ListCell *r;
+ /*
+ * First check: look if this rule is an implicit view update rule. If
+ * false, always apply any rule qualification.
+ */
+ if (rule_lock->ev_kind == NO_OPTION_EXPLICIT)
+ {
+ view_event->viewUpdate = false;
+ view_event->needCheckOption = true;
+ view_event->checkMode = rule_lock->ev_kind;
+ }
+
+ /*
+ * Second check: Look if we are already in a view update or not. If
+ * true and the event check mode is set to LOCAL_OPTION_IMPLICIT in
+ * conjunction with viewUpdate = true, we don't need to apply the check
+ * option otherwise.
+ */
+ else if (!view_event->viewUpdate &&
+ ((rule_lock->ev_kind == LOCAL_OPTION_IMPLICIT) ||
+ (rule_lock->ev_kind == CASCADED_OPTION_IMPLICIT)))
+
+ {
+ /* apply check option, but only once */
+ view_event->viewUpdate = true;
+ view_event->needCheckOption = true;
+ view_event->checkMode = rule_lock->ev_kind;
+ }
+
+ /*
+ * Third check: Look if we are in a recursive view update in
+ * conjunction with a CASCADED CHECK OPTION. If true, we always need to
+ * apply any check options found.
+ */
+ else if (view_event->viewUpdate &&
+ (view_event->checkMode == CASCADED_OPTION_IMPLICIT) &&
+ (rule_lock->ev_kind != NO_OPTION_EXPLICIT))
+ {
+ view_event->needCheckOption = true;
+ }
+
+ /*
+ * 4th check: make sure we reset the view update event if we are in a
+ * recursive view update and passed the first view already. If we had a
+ * LOCAL CHECK OPTION, we have to make sure we don't fire any check
+ * options anymore.
+ */
+ else if (view_event->viewUpdate &&
+ (view_event->checkMode == LOCAL_OPTION_IMPLICIT) &&
+ (rule_lock->ev_kind != NO_OPTION_EXPLICIT))
+ {
+ view_event->needCheckOption = false;
+ /* force LOCAL check mode */
+ view_event->checkMode = LOCAL_OPTION_IMPLICIT;
+ }
+
/* Determine correct QuerySource value for actions */
if (rule_lock->isInstead)
{
! if (event_qual != NULL)
! {
! /*
! * Check out wether we need to apply any
! * rule qualification to a view update rule.
! * We only bother on implicit rules that aren't
! * marked with NO_OPTION_EXPLICIT
! */
! if ((rule_lock->ev_kind != NO_OPTION_EXPLICIT)
! && (view_event->viewUpdate))
! {
! if (view_event->needCheckOption)
! {
! qsrc = QSRC_QUAL_INSTEAD_RULE;
! }
! else
! {
! qsrc = QSRC_INSTEAD_RULE;
! *instead_flag = true;
! }
! }
! else
! {
! qsrc = QSRC_QUAL_INSTEAD_RULE;
! }
! }
else
{
qsrc = QSRC_INSTEAD_RULE;
*************** fireRules(Query *parsetree,
*** 1438,1444 ****
continue;
rule_action = rewriteRuleAction(parsetree, rule_action,
! event_qual, rt_index, event);
rule_action->querySource = qsrc;
rule_action->canSetTag = false; /* might change later */
--- 1545,1552 ----
continue;
rule_action = rewriteRuleAction(parsetree, rule_action,
! event_qual, rt_index, event,
! view_event);
rule_action->querySource = qsrc;
rule_action->canSetTag = false; /* might change later */
*************** fireRules(Query *parsetree,
*** 1459,1465 ****
* infinite recursion.
*/
static List *
! RewriteQuery(Query *parsetree, List *rewrite_events)
{
CmdType event = parsetree->commandType;
bool instead = false;
--- 1567,1574 ----
* infinite recursion.
*/
static List *
! RewriteQuery(Query *parsetree, List *rewrite_events,
! view_update_event *view_event)
{
CmdType event = parsetree->commandType;
bool instead = false;
*************** RewriteQuery(Query *parsetree, List *rew
*** 1550,1555 ****
--- 1659,1665 ----
result_relation,
event,
locks,
+ view_event,
&instead,
&qual_product);
*************** RewriteQuery(Query *parsetree, List *rew
*** 1583,1589 ****
Query *pt = (Query *) lfirst(n);
List *newstuff;
! newstuff = RewriteQuery(pt, rewrite_events);
rewritten = list_concat(rewritten, newstuff);
}
--- 1693,1699 ----
Query *pt = (Query *) lfirst(n);
List *newstuff;
! newstuff = RewriteQuery(pt, rewrite_events, view_event);
rewritten = list_concat(rewritten, newstuff);
}
*************** QueryRewrite(Query *parsetree)
*** 1646,1658 ****
CmdType origCmdType;
bool foundOriginalQuery;
Query *lastInstead;
/*
* Step 1
*
* Apply all non-SELECT rules possibly getting 0 or many queries
*/
! querylist = RewriteQuery(parsetree, NIL);
/*
* Step 2
--- 1756,1772 ----
CmdType origCmdType;
bool foundOriginalQuery;
Query *lastInstead;
+ view_update_event v_event;
/*
* Step 1
*
* Apply all non-SELECT rules possibly getting 0 or many queries
*/
! v_event.viewUpdate = false;
! v_event.needCheckOption = false;
! v_event.checkMode = NO_OPTION_EXPLICIT;
! querylist = RewriteQuery(parsetree, NIL, &v_event);
/*
* Step 2
Index: src/backend/rewrite/rewriteRemove.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/rewrite/rewriteRemove.c,v
retrieving revision 1.65
diff -c -p -r1.65 rewriteRemove.c
*** src/backend/rewrite/rewriteRemove.c 16 Jun 2006 20:23:44 -0000 1.65
--- src/backend/rewrite/rewriteRemove.c 17 Aug 2006 03:34:45 -0000
*************** RemoveRewriteRule(Oid owningRel, const c
*** 89,94 ****
--- 89,138 ----
performDeletion(&object, behavior);
}
+ /*
+ * deleteImplicitRulesOnEvent
+ *
+ * This will delete implicit rules, if any exists, on the event in the relation
+ */
+ void
+ deleteImplicitRulesOnEvent(Relation rel, CmdType event_type)
+ {
+ RuleLock *rulelocks = rel->rd_rules;
+ int nlocks;
+ int i;
+
+ /*
+ * Select rules are implicit (are they marked as implicit??)
+ * but we don't want to delete them
+ */
+ if (event_type == CMD_SELECT)
+ return;
+
+ /*
+ * If there are no rules on the relation we waste no more time
+ */
+ if (rulelocks == NULL)
+ return;
+
+ nlocks = rulelocks->numLocks;
+
+ /*
+ * Look at all rules looking for the ones that are on the event and are
+ * implicit
+ */
+ for (i = 0; i < nlocks; i++)
+ {
+ RewriteRule *oneLock = rulelocks->rules[i];
+
+ if (oneLock->event == event_type
+ && (oneLock->ev_kind != NO_OPTION_EXPLICIT))
+ {
+ RemoveRewriteRuleById(oneLock->ruleId);
+ deleteDependencyRecordsFor(RewriteRelationId, oneLock->ruleId);
+ }
+ }
+ }
+
/*
* Guts of rule deletion.
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.265
diff -c -p -r1.265 utility.c
*** src/backend/tcop/utility.c 12 Aug 2006 20:05:56 -0000 1.265
--- src/backend/tcop/utility.c 17 Aug 2006 03:34:45 -0000
***************
*** 20,25 ****
--- 20,26 ----
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h"
+ #include "catalog/pg_rewrite.h"
#include "catalog/toasting.h"
#include "commands/alter.h"
#include "commands/async.h"
*************** ProcessUtility(Node *parsetree,
*** 764,771 ****
case T_ViewStmt: /* CREATE VIEW */
{
ViewStmt *stmt = (ViewStmt *) parsetree;
! DefineView(stmt->view, stmt->query, stmt->replace);
}
break;
--- 765,787 ----
case T_ViewStmt: /* CREATE VIEW */
{
ViewStmt *stmt = (ViewStmt *) parsetree;
+ bool checkOption = false;
+ bool cascade = false;
+ ListCell *cell;
! if (list_length(stmt->options) > 0)
! checkOption = true;
!
! foreach(cell, stmt->options)
! {
! Value *val = (Value *) lfirst(cell);
!
! if (strncmp(strVal(val), "cascade", strlen("cascade")) == 0)
! cascade = true;
! }
!
! DefineView(stmt->view, stmt->query, stmt->replace,
! checkOption, cascade);
}
break;
*************** ProcessUtility(Node *parsetree,
*** 803,809 ****
break;
case T_RuleStmt: /* CREATE RULE */
! DefineQueryRewrite((RuleStmt *) parsetree);
break;
case T_CreateSeqStmt:
--- 819,825 ----
break;
case T_RuleStmt: /* CREATE RULE */
! DefineQueryRewrite((RuleStmt *) parsetree, NO_OPTION_EXPLICIT);
break;
case T_CreateSeqStmt:
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.231
diff -c -p -r1.231 ruleutils.c
*** src/backend/utils/adt/ruleutils.c 12 Aug 2006 02:52:05 -0000 1.231
--- src/backend/utils/adt/ruleutils.c 18 Aug 2006 03:02:15 -0000
***************
*** 18,23 ****
--- 18,24 ----
#include "catalog/pg_depend.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
+ #include "catalog/pg_rewrite.h"
#include "catalog/pg_trigger.h"
#include "executor/spi.h"
#include "funcapi.h"
*************** make_viewdef(StringInfo buf, HeapTuple r
*** 1683,1688 ****
--- 1684,1690 ----
Oid ev_class;
int2 ev_attr;
bool is_instead;
+ char ev_kind;
char *ev_qual;
char *ev_action;
List *actions = NIL;
*************** make_viewdef(StringInfo buf, HeapTuple r
*** 1705,1710 ****
--- 1707,1715 ----
fno = SPI_fnumber(rulettc, "is_instead");
is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ fno = SPI_fnumber(rulettc, "ev_kind");
+ ev_kind = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
fno = SPI_fnumber(rulettc, "ev_qual");
ev_qual = SPI_getvalue(ruletup, rulettc, fno);
*************** make_viewdef(StringInfo buf, HeapTuple r
*** 1732,1737 ****
--- 1737,1761 ----
get_query_def(query, buf, NIL, RelationGetDescr(ev_relation),
prettyFlags, 0);
+
+ /*
+ * Support for updatable views: append the check option if
+ * required. If any pretty print flags is enabled, we need
+ * to do a linebreak before.
+ */
+ if (ev_kind != NO_OPTION_EXPLICIT || ev_kind != NO_OPTION_IMPLICIT)
+ {
+ if (prettyFlags & PRETTYFLAG_INDENT)
+ appendStringInfo(buf, "\n");
+
+ appendStringInfoSpaces(buf, 1);
+
+ if (ev_kind == LOCAL_OPTION_IMPLICIT)
+ appendStringInfo(buf, " WITH LOCAL CHECK OPTION");
+ else if (ev_kind == CASCADED_OPTION_IMPLICIT)
+ appendStringInfo(buf, " WITH CASCADED CHECK OPTION");
+ }
+
appendStringInfo(buf, ";");
heap_close(ev_relation, AccessShareLock);
Index: src/backend/utils/cache/relcache.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/cache/relcache.c,v
retrieving revision 1.247
diff -c -p -r1.247 relcache.c
*** src/backend/utils/cache/relcache.c 31 Jul 2006 20:09:05 -0000 1.247
--- src/backend/utils/cache/relcache.c 17 Aug 2006 03:34:45 -0000
*************** RelationBuildRuleLock(Relation relation)
*** 646,651 ****
--- 646,652 ----
rule->event = rewrite_form->ev_type - '0';
rule->attrno = rewrite_form->ev_attr;
rule->isInstead = rewrite_form->is_instead;
+ rule->ev_kind = rewrite_form->ev_kind;
/*
* Must use heap_getattr to fetch ev_action and ev_qual. Also,
*************** equalRuleLocks(RuleLock *rlock1, RuleLoc
*** 748,753 ****
--- 749,756 ----
return false;
if (!equal(rule1->actions, rule2->actions))
return false;
+ if(rule1->ev_kind != rule2->ev_kind)
+ return false;
}
}
else if (rlock2 != NULL)
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.446
diff -c -p -r1.446 pg_dump.c
*** src/bin/pg_dump/pg_dump.c 4 Aug 2006 18:32:15 -0000 1.446
--- src/bin/pg_dump/pg_dump.c 17 Aug 2006 03:34:45 -0000
*************** int optreset;
*** 51,56 ****
--- 51,57 ----
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
+ #include "catalog/pg_rewrite.h"
#include "commands/sequence.h"
#include "libpq/libpq-fs.h"
#include "mb/pg_wchar.h"
*************** getRules(int *numRules)
*** 3632,3637 ****
--- 3633,3639 ----
int i_ruletable;
int i_ev_type;
int i_is_instead;
+ int i_ev_kind;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
*************** getRules(int *numRules)
*** 3640,3646 ****
{
appendPQExpBuffer(query, "SELECT "
"tableoid, oid, rulename, "
! "ev_class as ruletable, ev_type, is_instead "
"FROM pg_rewrite "
"ORDER BY oid");
}
--- 3642,3649 ----
{
appendPQExpBuffer(query, "SELECT "
"tableoid, oid, rulename, "
! "ev_class as ruletable, ev_type, is_instead, "
! "ev_kind "
"FROM pg_rewrite "
"ORDER BY oid");
}
*************** getRules(int *numRules)
*** 3649,3655 ****
appendPQExpBuffer(query, "SELECT "
"(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, "
"oid, rulename, "
! "ev_class as ruletable, ev_type, is_instead "
"FROM pg_rewrite "
"ORDER BY oid");
}
--- 3652,3659 ----
appendPQExpBuffer(query, "SELECT "
"(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, "
"oid, rulename, "
! "ev_class as ruletable, ev_type, is_instead, "
! "ev_kind "
"FROM pg_rewrite "
"ORDER BY oid");
}
*************** getRules(int *numRules)
*** 3669,3674 ****
--- 3673,3679 ----
i_ruletable = PQfnumber(res, "ruletable");
i_ev_type = PQfnumber(res, "ev_type");
i_is_instead = PQfnumber(res, "is_instead");
+ i_ev_kind = PQfnumber(res, "ev_kind");
for (i = 0; i < ntups; i++)
{
*************** getRules(int *numRules)
*** 3692,3713 ****
ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
if (ruleinfo[i].ruletable)
{
/*
! * If the table is a view, force its ON SELECT rule to be sorted
! * before the view itself --- this ensures that any dependencies
! * for the rule affect the table's positioning. Other rules are
! * forced to appear after their table.
*/
! if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
! ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
! {
! addObjectDependency(&ruleinfo[i].ruletable->dobj,
! ruleinfo[i].dobj.dumpId);
! /* We'll merge the rule into CREATE VIEW, if possible */
! ruleinfo[i].separate = false;
! }
else
{
addObjectDependency(&ruleinfo[i].dobj,
--- 3697,3731 ----
ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
+ ruleinfo[i].ev_kind = *(PQgetvalue(res, i, i_ev_kind));
if (ruleinfo[i].ruletable)
{
/*
! * If the table is a view, force its ON SELECT rule to be
! * sorted before the view itself --- this ensures that any
! * dependencies for the rule affect the table's positioning.
! * Other rules are forced to appear after their table.
! *
! * Do the same for implicit rules (for updateable views support).
*/
! if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW)
! if (ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
! {
! addObjectDependency(&ruleinfo[i].ruletable->dobj,
! ruleinfo[i].dobj.dumpId);
! /* We'll merge the rule into CREATE VIEW, if possible */
! ruleinfo[i].separate = false;
! }
! else
! if (ruleinfo[i].ev_kind != NO_OPTION_EXPLICIT)
! {
! addObjectDependency(&ruleinfo[i].ruletable->dobj,
! ruleinfo[i].dobj.dumpId);
! /* We'll merge the rule into CREATE VIEW, if possible */
! ruleinfo[i].separate = false;
! }
! else
! ruleinfo[i].separate = true;
else
{
addObjectDependency(&ruleinfo[i].dobj,
Index: src/bin/pg_dump/pg_dump.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_dump/pg_dump.h,v
retrieving revision 1.128
diff -c -p -r1.128 pg_dump.h
*** src/bin/pg_dump/pg_dump.h 1 Aug 2006 18:05:04 -0000 1.128
--- src/bin/pg_dump/pg_dump.h 17 Aug 2006 03:34:45 -0000
*************** typedef struct _ruleInfo
*** 261,268 ****
TableInfo *ruletable; /* link to table the rule is for */
char ev_type;
bool is_instead;
bool separate; /* TRUE if must dump as separate item */
! /* separate is always true for non-ON SELECT rules */
} RuleInfo;
typedef struct _triggerInfo
--- 261,272 ----
TableInfo *ruletable; /* link to table the rule is for */
char ev_type;
bool is_instead;
+ char ev_kind;
bool separate; /* TRUE if must dump as separate item */
! /*
! * separate is true for non-ON SELECT rules and
! * for implicit rules (for updateable views support)
! */
} RuleInfo;
typedef struct _triggerInfo
Index: src/bin/pg_dump/pg_dump_sort.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_dump/pg_dump_sort.c,v
retrieving revision 1.15
diff -c -p -r1.15 pg_dump_sort.c
*** src/bin/pg_dump/pg_dump_sort.c 14 Jul 2006 14:52:26 -0000 1.15
--- src/bin/pg_dump/pg_dump_sort.c 17 Aug 2006 03:34:45 -0000
*************** repairDependencyLoop(DumpableObject **lo
*** 816,821 ****
--- 816,862 ----
}
}
+ /* View and its implicit rule */
+ if (nLoop == 2 &&
+ loop[0]->objType == DO_TABLE &&
+ loop[1]->objType == DO_RULE &&
+ ((RuleInfo *) loop[1])->ev_kind != 'e' &&
+ ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
+ {
+ repairViewRuleLoop(loop[0], loop[1]);
+ return;
+ }
+ if (nLoop == 2 &&
+ loop[1]->objType == DO_TABLE &&
+ loop[0]->objType == DO_RULE &&
+ ((RuleInfo *) loop[0])->ev_kind != 'e' &&
+ ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
+ {
+ repairViewRuleLoop(loop[1], loop[0]);
+ return;
+ }
+
+ /* Indirect loop involving view and implicit rule */
+ if (nLoop > 2)
+ {
+ for (i = 0; i < nLoop; i++)
+ {
+ if (loop[i]->objType == DO_TABLE)
+ {
+ for (j = 0; j < nLoop; j++)
+ {
+ if (loop[j]->objType == DO_RULE &&
+ ((RuleInfo *) loop[j])->ev_kind != 'e' &&
+ ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
+ {
+ repairViewRuleMultiLoop(loop[i], loop[j]);
+ return;
+ }
+ }
+ }
+ }
+ }
+
/* Table and CHECK constraint */
if (nLoop == 2 &&
loop[0]->objType == DO_TABLE &&
Index: src/include/catalog/catversion.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/catversion.h,v
retrieving revision 1.349
diff -c -p -r1.349 catversion.h
*** src/include/catalog/catversion.h 12 Aug 2006 02:52:06 -0000 1.349
--- src/include/catalog/catversion.h 17 Aug 2006 03:34:45 -0000
***************
*** 53,58 ****
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 200608101
#endif
--- 53,58 ----
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 200608121
#endif
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.420
diff -c -p -r1.420 pg_proc.h
*** src/include/catalog/pg_proc.h 6 Aug 2006 03:53:44 -0000 1.420
--- src/include/catalog/pg_proc.h 17 Aug 2006 03:34:45 -0000
*************** DATA(insert OID = 2557 ( bool PGNS
*** 3841,3846 ****
--- 3841,3847 ----
DESCR("convert int4 to boolean");
DATA(insert OID = 2558 ( int4 PGNSP PGUID 12 f f t f i 1 23 "16" _null_ _null_ _null_ bool_int4 - _null_ ));
DESCR("convert boolean to int4");
+ /* internal function required for view update rules */
DATA(insert OID = 2559 ( lastval PGNSP PGUID 12 f f t f v 0 20 "" _null_ _null_ _null_ lastval - _null_ ));
DESCR("current value from last used sequence");
*************** DESCR("GiST support");
*** 3894,3899 ****
--- 3895,3904 ----
DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 f f t f i 1 2281 "2281" _null_ _null_ _null_ gist_circle_compress - _null_ ));
DESCR("GiST support");
+ /* functions for view update facility */
+ DATA(insert OID = 2570 ( pg_view_update_error PGNSP PGUID 12 f f f f i 1 16 "16" _null_ _null_ _null_ pg_view_update_error - _null_));
+ DESCR("evaluates boolean input for view update check option");
+
/* GIN */
DATA(insert OID = 2730 ( gingettuple PGNSP PGUID 12 f f t f v 2 16 "2281 2281" _null_ _null_ _null_ gingettuple - _null_ ));
DESCR("gin(internal)");
Index: src/include/catalog/pg_rewrite.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_rewrite.h,v
retrieving revision 1.26
diff -c -p -r1.26 pg_rewrite.h
*** src/include/catalog/pg_rewrite.h 5 Mar 2006 15:58:55 -0000 1.26
--- src/include/catalog/pg_rewrite.h 17 Aug 2006 03:34:45 -0000
*************** CATALOG(pg_rewrite,2618)
*** 43,48 ****
--- 43,49 ----
int2 ev_attr;
char ev_type;
bool is_instead;
+ char ev_kind;
/* NB: remaining fields must be accessed via heap_getattr */
text ev_qual;
*************** typedef FormData_pg_rewrite *Form_pg_rew
*** 60,72 ****
* compiler constants for pg_rewrite
* ----------------
*/
! #define Natts_pg_rewrite 7
#define Anum_pg_rewrite_rulename 1
#define Anum_pg_rewrite_ev_class 2
#define Anum_pg_rewrite_ev_attr 3
#define Anum_pg_rewrite_ev_type 4
#define Anum_pg_rewrite_is_instead 5
! #define Anum_pg_rewrite_ev_qual 6
! #define Anum_pg_rewrite_ev_action 7
#endif /* PG_REWRITE_H */
--- 61,82 ----
* compiler constants for pg_rewrite
* ----------------
*/
! #define Natts_pg_rewrite 8
#define Anum_pg_rewrite_rulename 1
#define Anum_pg_rewrite_ev_class 2
#define Anum_pg_rewrite_ev_attr 3
#define Anum_pg_rewrite_ev_type 4
#define Anum_pg_rewrite_is_instead 5
! #define Anum_pg_rewrite_ev_kind 6
! #define Anum_pg_rewrite_ev_qual 7
! #define Anum_pg_rewrite_ev_action 8
!
! /*
! * Possible values for ev_kind
! */
! #define LOCAL_OPTION_IMPLICIT 'l' /* WITH LOCAL CHECK OPTION */
! #define CASCADED_OPTION_IMPLICIT 'c' /* WITH CASCADED CHECK OPTION */
! #define NO_OPTION_IMPLICIT 'n' /* no check option specified */
! #define NO_OPTION_EXPLICIT 'e' /* rule was created by user */
#endif /* PG_REWRITE_H */
Index: src/include/commands/view.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/commands/view.h,v
retrieving revision 1.23
diff -c -p -r1.23 view.h
*** src/include/commands/view.h 5 Mar 2006 15:58:55 -0000 1.23
--- src/include/commands/view.h 17 Aug 2006 03:34:45 -0000
***************
*** 16,22 ****
#include "nodes/parsenodes.h"
! extern void DefineView(RangeVar *view, Query *view_parse, bool replace);
extern void RemoveView(const RangeVar *view, DropBehavior behavior);
#endif /* VIEW_H */
--- 16,24 ----
#include "nodes/parsenodes.h"
! extern void DefineView(RangeVar *view, Query *view_parse, bool replace,
! bool checkOption, bool cascade);
!
extern void RemoveView(const RangeVar *view, DropBehavior behavior);
#endif /* VIEW_H */
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.323
diff -c -p -r1.323 parsenodes.h
*** src/include/nodes/parsenodes.h 12 Aug 2006 20:05:56 -0000 1.323
--- src/include/nodes/parsenodes.h 19 Aug 2006 19:07:27 -0000
*************** typedef struct ViewStmt
*** 1677,1682 ****
--- 1677,1683 ----
List *aliases; /* target column names */
Query *query; /* the SQL statement */
bool replace; /* replace an existing view? */
+ List *options; /* view check options, NIL means no check */
} ViewStmt;
/* ----------------------
Index: src/include/nodes/relation.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.126
diff -c -p -r1.126 relation.h
*** src/include/nodes/relation.h 1 Jul 2006 18:38:33 -0000 1.126
--- src/include/nodes/relation.h 18 Aug 2006 23:29:13 -0000
*************** typedef struct RelOptInfo
*** 260,266 ****
/* information about a base rel (not set for join rels!) */
Index relid;
! RTEKind rtekind; /* RELATION, SUBQUERY, or FUNCTION */
AttrNumber min_attr; /* smallest attrno of rel (often <0) */
AttrNumber max_attr; /* largest attrno of rel */
Relids *attr_needed; /* array indexed [min_attr .. max_attr] */
--- 260,266 ----
/* information about a base rel (not set for join rels!) */
Index relid;
! RTEKind rtekind; /* see parsenodes.h */
AttrNumber min_attr; /* smallest attrno of rel (often <0) */
AttrNumber max_attr; /* largest attrno of rel */
Relids *attr_needed; /* array indexed [min_attr .. max_attr] */
Index: src/include/rewrite/prs2lock.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/rewrite/prs2lock.h,v
retrieving revision 1.21
diff -c -p -r1.21 prs2lock.h
*** src/include/rewrite/prs2lock.h 5 Mar 2006 15:58:58 -0000 1.21
--- src/include/rewrite/prs2lock.h 17 Aug 2006 03:34:45 -0000
*************** typedef struct RewriteRule
*** 29,34 ****
--- 29,35 ----
Node *qual;
List *actions;
bool isInstead;
+ char ev_kind;
} RewriteRule;
/*
Index: src/include/rewrite/rewriteDefine.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/rewrite/rewriteDefine.h,v
retrieving revision 1.21
diff -c -p -r1.21 rewriteDefine.h
*** src/include/rewrite/rewriteDefine.h 5 Mar 2006 15:58:58 -0000 1.21
--- src/include/rewrite/rewriteDefine.h 17 Aug 2006 03:34:45 -0000
***************
*** 16,22 ****
#include "nodes/parsenodes.h"
! extern void DefineQueryRewrite(RuleStmt *args);
extern void RenameRewriteRule(Oid owningRel, const char *oldName,
const char *newName);
--- 16,23 ----
#include "nodes/parsenodes.h"
! extern void DefineQueryRewrite(RuleStmt *args,
! char ev_kind);
extern void RenameRewriteRule(Oid owningRel, const char *oldName,
const char *newName);
Index: src/include/rewrite/rewriteRemove.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/rewrite/rewriteRemove.h,v
retrieving revision 1.22
diff -c -p -r1.22 rewriteRemove.h
*** src/include/rewrite/rewriteRemove.h 16 Jun 2006 20:23:45 -0000 1.22
--- src/include/rewrite/rewriteRemove.h 17 Aug 2006 03:34:45 -0000
***************
*** 20,24 ****
--- 20,25 ----
extern void RemoveRewriteRule(Oid owningRel, const char *ruleName,
DropBehavior behavior, bool missing_ok);
extern void RemoveRewriteRuleById(Oid ruleOid);
+ extern void deleteImplicitRulesOnEvent(Relation rel, CmdType event_type);
#endif /* REWRITEREMOVE_H */
Index: src/include/rewrite/rewriteSupport.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/rewrite/rewriteSupport.h,v
retrieving revision 1.28
diff -c -p -r1.28 rewriteSupport.h
*** src/include/rewrite/rewriteSupport.h 5 Mar 2006 15:58:58 -0000 1.28
--- src/include/rewrite/rewriteSupport.h 17 Aug 2006 03:34:45 -0000
***************
*** 17,22 ****
--- 17,33 ----
/* The ON SELECT rule of a view is always named this: */
#define ViewSelectRuleName "_RETURN"
+ /*------------------------------------------------------------------------------
+ * some names to be used for implicit view update rules
+ *------------------------------------------------------------------------------
+ */
+ #define INSERTRULENAME "_INSERT"
+ #define DELETERULENAME "_DELETE"
+ #define UPDATERULENAME "_UPDATE"
+ #define NOTHING_INSERTRULENAME "_NOTHING_INSERT"
+ #define NOTHING_UPDATERULENAME "_NOTHING_UPDATE"
+ #define NOTHING_DELETERULENAME "_NOTHING_DELETE"
+
extern bool IsDefinedRewriteRule(Oid owningRel, const char *ruleName);
extern void SetRelationRuleStatus(Oid relationId, bool relHasRules,