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,