diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 4fae50cd32..53d8142b78 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ - + @@ -4154,13 +4154,6 @@ True if the rule is an INSTEAD rule - - is_auto - bool - - True if the rule was automatically generated - - ev_qual text diff --git a/doc/src/sgml/intro.sgml b/doc/src/sgml/intro.sgml index 298ba910a9..ccc3c8d772 100644 --- a/doc/src/sgml/intro.sgml +++ b/doc/src/sgml/intro.sgml @@ -1,4 +1,4 @@ - + Preface @@ -110,7 +110,7 @@ triggers - updatable views + views transactional integrity diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml index daea5a97cb..1a3281dda6 100644 --- a/doc/src/sgml/ref/create_view.sgml +++ b/doc/src/sgml/ref/create_view.sgml @@ -1,5 +1,5 @@ @@ -115,99 +115,11 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW n Notes - Some views are updatable, which means that the - commands INSERT, UPDATE, - and DELETE can be used on the view as if it - were a regular table. A view is updatable if it - does not contain: - - - - - more than one underlying table (joins) or no underlying table at all - - - - - - underlying tables/views that are themselves not updatable, - including table value constructors and table functions - - - - - - subqueries in the FROM list - - - - - - items in the select list that are not direct references to a - column of the underlying table, such as literals or any - nontrivial value expression - - - - - - references to system columns in the select list - - - - - - more than one reference to the same column in the select list - - - - - aggregate function calls - - - - window function calls - - - - - WITH or WITH RECURSIVE clauses - - - - - - DISTINCT, GROUP BY, or - HAVING clauses - - - - - - UNION, INTERSECT, or - EXCEPT clauses - - - - - - LIMIT or OFFSET clauses - (or other equivalent spellings thereof) - - - - - - - The updatable views implementation is based on the rule system. - Because of this, you can also make more complex views updatable or - insertable by creating your own rules that rewrite - the INSERT, - UPDATE, and DELETE actions - on the view into appropriate actions on other tables. You can - also replace the automatically generated rules by your own rules. - For more information on the rule system, refer - to . + 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 + . diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index de1834ab1c..909c6a8865 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.112 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.113 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -27,9 +27,7 @@ #include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" -#include "rewrite/rewriteRemove.h" #include "rewrite/rewriteSupport.h" -#include "rewrite/viewUpdate.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -310,28 +308,13 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) viewOid, NULL, CMD_SELECT, - true, /* is_instead */ - true, /* is_auto */ + true, replace, list_make1(viewParse)); /* - * Delete all implicit rules on replace. CreateViewUpdateRules() - * below will re-create them if appropriate for the new view - * definition. + * Someday: automatic ON INSERT, etc */ - if (replace) - { - Relation rel = heap_open(viewOid, AccessExclusiveLock); - RemoveAutomaticRulesOnEvent(rel, CMD_INSERT); - RemoveAutomaticRulesOnEvent(rel, CMD_DELETE); - RemoveAutomaticRulesOnEvent(rel, CMD_UPDATE); - heap_close(rel, NoLock); - } - - CommandCounterIncrement(); - - CreateViewUpdateRules(viewOid, viewParse); } /*--------------------------------------------------------------- diff --git a/src/backend/rewrite/Makefile b/src/backend/rewrite/Makefile index 196765aa6c..c4c64cb0cc 100644 --- a/src/backend/rewrite/Makefile +++ b/src/backend/rewrite/Makefile @@ -4,7 +4,7 @@ # Makefile for rewrite # # IDENTIFICATION -# $PostgreSQL: pgsql/src/backend/rewrite/Makefile,v 1.18 2009/01/22 17:27:54 petere Exp $ +# $PostgreSQL: pgsql/src/backend/rewrite/Makefile,v 1.19 2009/01/27 12:40:15 petere Exp $ # #------------------------------------------------------------------------- @@ -13,7 +13,6 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = rewriteRemove.o rewriteDefine.o \ - rewriteHandler.o rewriteManip.o rewriteSupport.o \ - viewUpdate.o + rewriteHandler.o rewriteManip.o rewriteSupport.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index fe81a9ff13..03c5c5ae38 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,14 +8,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.135 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.136 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" -#include "access/xact.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" @@ -26,7 +25,6 @@ #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" -#include "rewrite/rewriteRemove.h" #include "rewrite/rewriteSupport.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -41,7 +39,6 @@ static void checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect); static bool setRuleCheckAsUser_walker(Node *node, Oid *context); static void setRuleCheckAsUser_Query(Query *qry, Oid userid); -static const char *rule_event_string(CmdType evtype); /* @@ -55,7 +52,6 @@ InsertRule(char *rulname, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead, - bool is_auto, Node *event_qual, List *action, bool replace) @@ -88,7 +84,6 @@ InsertRule(char *rulname, values[i++] = CharGetDatum(evtype + '0'); /* ev_type */ values[i++] = CharGetDatum(RULE_FIRES_ON_ORIGIN); /* ev_enabled */ values[i++] = BoolGetDatum(evinstead); /* is_instead */ - values[i++] = BoolGetDatum(is_auto); /* is_auto */ values[i++] = CStringGetTextDatum(evqual); /* ev_qual */ values[i++] = CStringGetTextDatum(actiontree); /* ev_action */ @@ -107,11 +102,7 @@ InsertRule(char *rulname, if (HeapTupleIsValid(oldtup)) { - /* - * If REPLACE was not used we still check if the old rule is - * automatic: Then we replace it anyway. - */ - if (!replace && !((Form_pg_rewrite) GETSTRUCT(oldtup))->is_auto) + if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", @@ -124,7 +115,6 @@ InsertRule(char *rulname, replaces[Anum_pg_rewrite_ev_attr - 1] = true; replaces[Anum_pg_rewrite_ev_type - 1] = true; replaces[Anum_pg_rewrite_is_instead - 1] = true; - replaces[Anum_pg_rewrite_is_auto - 1] = true; replaces[Anum_pg_rewrite_ev_qual - 1] = true; replaces[Anum_pg_rewrite_ev_action - 1] = true; @@ -215,7 +205,6 @@ DefineRule(RuleStmt *stmt, const char *queryString) whereClause, stmt->event, stmt->instead, - false, /* not is_auto */ stmt->replace, actions); } @@ -234,7 +223,6 @@ DefineQueryRewrite(char *rulename, Node *event_qual, CmdType event_type, bool is_instead, - bool is_auto, bool replace, List *action) { @@ -458,42 +446,6 @@ DefineQueryRewrite(char *rulename, RelationGetDescr(event_relation), false); } - - /* - * If defining a non-automatic DO INSTEAD rule, drop all - * automatic rules on the same event. - */ - if (!is_auto && is_instead) - { - RemoveAutomaticRulesOnEvent(event_relation, event_type); - CommandCounterIncrement(); - } - - /* - * If defining an automatic rule and there is a manual rule on - * the same event, warn and don't do it. - */ - if (is_auto && event_relation->rd_rules != NULL) - { - int i; - - for (i = 0; i < event_relation->rd_rules->numLocks; i++) - { - RewriteRule *rule = event_relation->rd_rules->rules[i]; - - if (rule->event == event_type && !rule->is_auto && rule->isInstead == is_instead) - { - ereport(WARNING, - (errmsg("automatic %s rule not created because manually created %s rule exists", - rule_event_string(event_type), rule_event_string(event_type)), - errhint("If you prefer to have the automatic rule, drop the manually created rule and run CREATE OR REPLACE VIEW again."))); - - heap_close(event_relation, NoLock); - return; - } - } - } - } /* @@ -509,7 +461,6 @@ DefineQueryRewrite(char *rulename, event_relid, event_attno, is_instead, - is_auto, event_qual, action, replace); @@ -803,16 +754,3 @@ RenameRewriteRule(Oid owningRel, const char *oldName, } #endif - - -static const char * -rule_event_string(CmdType type) -{ - if (type == CMD_INSERT) - return "INSERT"; - if (type == CMD_UPDATE) - return "UPDATE"; - if (type == CMD_DELETE) - return "DELETE"; - return "???"; -} diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 7cfeacb730..336ae829ce 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.184 2009/01/22 20:16:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.185 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -1899,20 +1899,20 @@ QueryRewrite(Query *parsetree) { case CMD_INSERT: ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("view is not updatable"), + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot insert into a view"), errhint("You need an unconditional ON INSERT DO INSTEAD rule."))); break; case CMD_UPDATE: ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("view is not updatable"), + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot update a view"), errhint("You need an unconditional ON UPDATE DO INSTEAD rule."))); break; case CMD_DELETE: ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("view is not updatable"), + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot delete from a view"), errhint("You need an unconditional ON DELETE DO INSTEAD rule."))); break; default: diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index 90e99bd8ac..a7b4413e3e 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteRemove.c,v 1.76 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteRemove.c,v 1.77 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -90,39 +90,6 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, performDeletion(&object, behavior); } -/* - * RemoveAutomaticRulesOnEvent - * - * This will delete automatic rules, if any exist, on the event in the - * relation. - */ -void -RemoveAutomaticRulesOnEvent(Relation rel, CmdType event_type) -{ - RuleLock *rulelocks = rel->rd_rules; - int i; - - /* If there are no rules on the relation, waste no more time. */ - if (rulelocks == NULL) - return; - - /* - * Look at all rules looking for the ones that are on the event - * and are automatic. - */ - for (i = 0; i < rulelocks->numLocks; i++) - { - RewriteRule *oneLock = rulelocks->rules[i]; - - if (oneLock->event == event_type && oneLock->is_auto) - { - RemoveRewriteRuleById(oneLock->ruleId); - elog(DEBUG1, "removing automatic rule with OID %u\n", - oneLock->ruleId); - deleteDependencyRecordsFor(RewriteRelationId, oneLock->ruleId); - } - } -} /* * Guts of rule deletion. diff --git a/src/backend/rewrite/viewUpdate.c b/src/backend/rewrite/viewUpdate.c deleted file mode 100644 index 3687f19429..0000000000 --- a/src/backend/rewrite/viewUpdate.c +++ /dev/null @@ -1,1401 +0,0 @@ -/*------------------------------------------------------------------------- - * - * viewUpdate.c - * routines for translating a view definition into - * INSERT/UPDATE/DELETE rules (i.e. updatable views). - * - * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * ORIGINAL AUTHORS - * Bernd Helmle, Jaime Casanova - * - * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/viewUpdate.c,v 1.1 2009/01/22 17:27:54 petere Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "access/xact.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_rewrite.h" -#include "nodes/nodeFuncs.h" -#include "parser/parse_oper.h" -#include "parser/parsetree.h" -#include "rewrite/rewriteDefine.h" -#include "rewrite/viewUpdate.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" -#include "utils/syscache.h" -#include "utils/rel.h" - -typedef TargetEntry** ViewDefColumnList; - -typedef struct ViewBaseRelation -{ - List *defs; /* List of all base relations (root starts - * with only one relation because we - * implement only simple updatability) */ - Oid parentRelation; /* Oid of parent relation, 0 indicates root */ -} ViewBaseRelation; - -typedef struct ViewBaseRelationItem -{ - Relation rel; /* the Relation itself */ - Query *rule; /* _RETURN rule of a view relation */ - TargetEntry **tentries; /* saves order of column target list */ -} ViewBaseRelationItem; - -typedef struct ViewExprContext -{ - Index newRTE; - Index oldRTE; - Index baseRTE; - Index subQueryLevel; - ViewDefColumnList tentries; -} ViewExprContext; - -static const char *get_auto_rule_name(CmdType type); -static Query *get_return_rule(Relation rel); -static void read_rearranged_cols(ViewBaseRelation *tree); -static bool is_select_query_updatable(const Query *query); -static Oid get_reloid_from_select(const Query *select, - int *rti, RangeTblEntry **rel_entry); -static void create_update_rule(Oid viewOid, - const Query *select, - const Relation baserel, - TargetEntry **tentries, - CmdType ruletype); -static void get_base_base_relations(const Query *view, Oid baserelid, List **list); -static void copyReversedTargetEntryPtr(List *targetList, - ViewDefColumnList targets); -static bool check_reltree(ViewBaseRelation *node); -static Query *form_update_query(const Query *select, ViewDefColumnList tentries, CmdType type); -static RangeTblEntry *get_relation_RTE(const Query *select, - unsigned int *offset); -static Index get_rtindex_for_rel(List *rte_list, - const char *relname); -static bool replace_tlist_varno_walker(Node *node, - ViewExprContext *ctxt); -static OpExpr *create_opexpr(Var *var_left, Var *var_right); -static void form_where_for_updrule(const Query *select, FromExpr **from, - const Relation baserel, Index baserti, - Index oldrti); -static void build_update_target_list(const Query *update, const Query *select, - const Relation baserel); - -/*------------------------------------------------------------------------------ - * Private functions - * ----------------------------------------------------------------------------- - */ - -static const char * -get_auto_rule_name(CmdType type) -{ - if (type == CMD_INSERT) - return "_INSERT"; - if (type == CMD_UPDATE) - return "_UPDATE"; - if (type == CMD_DELETE) - return "_DELETE"; - return NULL; -} - -/* - * Returns the range table index for the specified relname. - * - * XXX This seems pretty grotty ... can't we do this in some other way? - */ -static Index -get_rtindex_for_rel(List *rte_list, const char *relname) -{ - ListCell *cell; - int index = 0; - - AssertArg(relname); - - foreach(cell, rte_list) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(cell); - - index++; - - if (rte && strncmp(rte->eref->aliasname, relname, NAMEDATALEN) == 0) - break; - } - - Assert(index > 0); - - return (Index) index; -} - -/* - * Returns the RangeTblEntry starting at the specified offset. The - * function can be used to iterate over the rtable list of the - * specified select query tree. Returns NULL if nothing is found. - * - * NOTE: The function only returns those RangeTblEntry that do not - * match a *NEW* or *OLD* RangeTblEntry. - * - * The offset is incremented as a side effect. - */ -static RangeTblEntry * -get_relation_RTE(const Query *select, unsigned int *offset) -{ - AssertArg(offset); - AssertArg(select); - - while (*offset <= list_length(select->rtable)) - { - RangeTblEntry *rte = rt_fetch(*offset, select->rtable); - (*offset)++; - - /* skip non-table RTEs */ - if (rte->rtekind != RTE_RELATION) - continue; - - /* - * Skip RTEs named *NEW* and *OLD*. - * - * XXX It would be nice to be able to use something else than just - * the names here ... However, rtekind does not work as expected :-( - */ - if (strncmp(rte->eref->aliasname, "*NEW*", 6) == 0 - || strncmp(rte->eref->aliasname, "*OLD*", 6) == 0) - continue; - - return rte; - } - - return NULL; -} - -/* - * Rewrite varno's and varattno for the specified Var node if it is in - * a reversed order regarding to the underlying relation. The lookup - * table tentries holds all TargetEntries which are on a different - * location in the view definition. If var isn't on a different - * position in the current view than on its original relation, nothing - * is done. - * - * Note: This function assumes that the caller has already checked all - * parameters for NULL. - */ -static void -adjustVarnoIfReversedCol(Var *var, - Index newRTE, - ViewDefColumnList tentries) -{ - TargetEntry *entry = tentries[var->varattno - 1]; - - /* - * tentries holds NULL if given var isn't on a different location - * in the view Only replace if column order is reversed. - */ - if (entry && entry->resno != var->varattno) - { - var->varattno = entry->resno; - var->varoattno = entry->resno; - } - - /* Finally, make varno point to the *NEW* range table entry. */ - var->varno = newRTE; - var->varnoold = newRTE; -} - -/* - * Creates an equal operator expression for the specified Vars. They - * are assumed to be of the same type. - */ -static OpExpr * -create_opexpr(Var *var_left, Var *var_right) -{ - OpExpr *result; - HeapTuple tuple; - Form_pg_operator operator; - Oid eqOid; - - AssertArg(var_left); - AssertArg(var_right); - Assert(var_left->vartype == var_right->vartype); - - get_sort_group_operators(var_left->vartype, false, true, false, - NULL, &eqOid, NULL); - - tuple = SearchSysCache(OPEROID, ObjectIdGetDatum(eqOid), 0, 0, 0); - - operator = (Form_pg_operator) GETSTRUCT(tuple); - result = makeNode(OpExpr); - - result->opno = HeapTupleGetOid(tuple); - result->opfuncid = operator->oprcode; - result->opresulttype = operator->oprresult; - result->opretset = false; - - result->args = lappend(result->args, var_left); - result->args = lappend(result->args, var_right); - - ReleaseSysCache(tuple); - - return result; -} - -/* - * Creates an expression tree for a WHERE clause. - * - * If from is not NULL, assigns the root node to the specified - * FromExpr of the target query tree. - * - * Note that the function appends the specified opExpr op to the - * specified anchor (if anchor != NULL) and returns that immediately. - * That way this function could be used to add operator nodes to an - * existing BoolExpr tree or (if from is given), to create a new Query - * qualification list. - */ -static Node * -build_expression_tree(FromExpr *from, Node **anchor, BoolExpr *expr, OpExpr *op) -{ - /* Already some nodes there? */ - if (*anchor) - { - expr->args = lappend(expr->args, op); - ((BoolExpr *)(*anchor))->args = lappend(((BoolExpr *)(*anchor))->args, - expr); - *anchor = (Node *)expr; - } - else - { - /* Currently no nodes ... */ - BoolExpr *boolexpr = makeNode(BoolExpr); - expr->args = lappend(expr->args, op); - boolexpr->args = lappend(boolexpr->args, expr); - - *anchor = (Node *) boolexpr; - - if (from) - from->quals = *anchor; - } - - return *anchor; -} - -/* - * Forms the WHERE clause for DELETE/UPDATE rules targeted to the - * specified view. - */ -static void -form_where_for_updrule(const Query *select, /* View retrieve rule */ - FromExpr **from, /* FromExpr for stmt */ - const Relation baserel, /* base relation of view */ - Index baserti, /* Index of base relation RTE */ - Index oldrti) /* Index of *OLD* RTE */ -{ - BoolExpr *expr = NULL; - Node *anchor = NULL; - Form_pg_attribute *attrs; - ListCell *cell; - - AssertArg(baserti > 0); - AssertArg(oldrti > 0); - AssertArg(*from); - AssertArg(baserel); - - attrs = baserel->rd_att->attrs; - - foreach(cell, select->targetList) - { - TargetEntry *te = (TargetEntry *) lfirst(cell); - Var *var1; - Var *var2; - OpExpr *op; - BoolExpr *null_condition; - NullTest *nulltest1; - NullTest *nulltest2; - - /* If te->expr holds no Var pointer, continue. */ - if (!IsA(te->expr, Var)) - continue; - - null_condition = makeNode(BoolExpr); - nulltest1 = makeNode(NullTest); - nulltest2 = makeNode(NullTest); - - /* - * These are the new operands we had to check for equality. - * - * For DELETE/UPDATE rules, var1 points to the *OLD* RTE, var2 - * references the base relation. - */ - var1 = copyObject((Var *) (te->expr)); - - /* - * Look at varoattno to determine whether this attribute has a different - * location in the underlying base table. If that case, retrieve the - * attribute from the base table and assign it to var2; otherwise - * simply copy it to var1. - */ - if (var1->varoattno > 0) - { - var2 = makeNode(Var); - - var2->varno = baserti; - var2->varnoold = baserti; - var2->varattno = attrs[var1->varoattno - 1]->attnum; - var2->vartype = attrs[var1->varoattno - 1]->atttypid; - var2->vartypmod = attrs[var1->varoattno - 1]->atttypmod; - var2->varlevelsup = var1->varlevelsup; - var2->varnoold = var2->varno; - var2->varoattno = var2->varattno; - } - else - { - var2 = copyObject(var1); - var2->varno = baserti; - var2->varnoold = baserti; - } - - var1->varno = oldrti; - var1->varnoold = oldrti; - - /* - * rewrite varattno of var2 to point to the right column in relation - * *OLD* or *NEW* - */ - var2->varattno = te->resorigcol; - var2->varoattno = te->resorigcol; - - /* - * rewrite varattno of var1 to point to the right column in base - * relation - */ - var1->varattno = te->resno; - var1->varoattno = te->resno; - - op = create_opexpr(var1, var2); - expr = makeNode(BoolExpr); - expr->boolop = OR_EXPR; - null_condition->boolop = AND_EXPR; - - nulltest1->arg = (Expr *)var1; - nulltest1->nulltesttype = IS_NULL; - - nulltest2->arg = (Expr *)var2; - nulltest2->nulltesttype = IS_NULL; - - null_condition->args = lappend(null_condition->args, nulltest1); - null_condition->args = lappend(null_condition->args, nulltest2); - expr->args = lappend(expr->args, null_condition); - - anchor = build_expression_tree(*from, (Node **) &anchor, expr, op); - } -} - -/* - * Replaces the varnos for the specified targetlist to rtIndex - */ -static bool -replace_tlist_varno_walker(Node *node, - ViewExprContext *ctxt) -{ - AssertArg(ctxt); - - if (!node) - return false; - - switch(node->type) - { - case T_Var: - elog(DEBUG1, "adjusting varno old %d to new %d", - ((Var *)(node))->varno, - ctxt->newRTE); - - ((Var *)(node))->varno = ctxt->newRTE; - adjustVarnoIfReversedCol((Var *)node, - ctxt->newRTE, - ctxt->tentries); - /* nothing more to do */ - break; - - case T_ArrayRef: - { - ArrayRef *array = (ArrayRef *) node; - - /* - * Things are getting complicated here. We have found an - * array subscripting operation. It's necessary to - * examine all varno's found in this operation to make - * sure, we're getting right. This covers cases where a - * view selects a single index or complete array from a - * base table or view. - */ - - /* - * Look at expressions that evaluate upper array - * indexes. Make sure all varno's are modified. This is - * done by walking the expression tree recursively. - */ - expression_tree_walker((Node *) array->refupperindexpr, - replace_tlist_varno_walker, - (void *)ctxt); - - expression_tree_walker((Node *) array->reflowerindexpr, - replace_tlist_varno_walker, - (void *)ctxt); - - expression_tree_walker((Node *) array->refexpr, - replace_tlist_varno_walker, - (void *)ctxt); - - expression_tree_walker((Node *) array->refassgnexpr, - replace_tlist_varno_walker, - (void *)ctxt); - } - - default: - break; - } - - return expression_tree_walker(node, replace_tlist_varno_walker, ctxt); -} - -/* - * Adds RTEs to form a query tree. - * - * select has to be a valid initialized view definition query tree - * (the function assumes that this query has passed the - * is_select_query_updatable() function). - */ -static Query * -form_update_query(const Query *select, ViewDefColumnList tentries, CmdType type) -{ - RangeTblEntry *rte; - Oid reloid; - Query *newquery; - - AssertArg(select); - AssertArg(tentries); - - newquery = makeNode(Query); - newquery->commandType = type; - - /* copy the range table entries */ - newquery->rtable = copyObject(select->rtable); - - /* prepare other stuff */ - newquery->canSetTag = true; - newquery->jointree = makeNode(FromExpr); - - /* - * Set result relation to the base relation. - * - * Since we currently only support updatable views with one - * underlying table, we simply extract the one relation which - * isn't labeled as *OLD* or *NEW*. - */ - reloid = get_reloid_from_select(select, &(newquery->resultRelation), &rte); - if (!OidIsValid(reloid)) - elog(ERROR, "could not retrieve base relation OID"); - - Assert(newquery->resultRelation > 0); - - /* adjust inFromCl of result range table entry */ - rte->inFromCl = false; - - /* We don't need a target list for DELETE. */ - if (type != CMD_DELETE) - { - ViewExprContext ctxt; - ListCell *cell; - - /* Copy all target entries. */ - newquery->targetList = copyObject(select->targetList); - - /* - * Replace all varnos to point to the *NEW* node in all targetentry - * expressions. - */ - - ctxt.newRTE = PRS2_NEW_VARNO; - ctxt.tentries = tentries; - - foreach(cell, newquery->targetList) - { - Node *node = (Node *) lfirst(cell); - expression_tree_walker(node, - replace_tlist_varno_walker, - (void *) &ctxt); - } - } - - return newquery; -} - -/* - * Rewrite a TargetEntry, based on the given arguments to match - * the new Query tree of the new DELETE/UPDATE/INSERT rule and/or - * its underlying base relation. - * - * form_te_for_update() needs to carefully reassign Varno's of - * all Var expressions assigned to the given TargetEntry and to - * adjust all type info values and attribute index locations so - * that the rewritten TargetEntry corresponds to the correct - * column in the underlying base relation. - * - * Someone should consider that columns could be in reversed - * order in a view definition, so we need to take care to - * "restore" the correct order of all columns in the target list - * of the new view update rules. - * - * There's also some additional overhead if we have an array field - * involved. In this case we have to loop recursively through the - * array expressions to get all target entries right. - */ -static void -form_te_for_update(int2 attnum, Form_pg_attribute attrs, Oid baserelid, - Expr *expr, TargetEntry *te_update) -{ - /* - * First, try if this is an array subscripting operation. If true, dive - * recursively into the subscripting tree examining all varnos. - */ - - if (IsA(expr, ArrayRef)) - { - ArrayRef *array = (ArrayRef *) expr; - - if (array->refassgnexpr != NULL) - form_te_for_update(attnum, attrs, baserelid, array->refassgnexpr, - te_update); - - if (array->refupperindexpr != NIL) - { - ListCell *cell; - - foreach(cell, array->refupperindexpr) - form_te_for_update(attnum, attrs, baserelid, (Expr *) lfirst(cell), te_update); - } - - if (array->reflowerindexpr != NIL) - { - ListCell *cell; - - foreach(cell, array->reflowerindexpr) - form_te_for_update(attnum, attrs, baserelid, (Expr *) lfirst(cell), te_update); - } - - if (array->refexpr != NULL) - form_te_for_update(attnum, attrs, baserelid, array->refexpr, - te_update); - } - else if (IsA(expr, Var)) - { - /* - * Base case of recursion: actually build the TargetEntry. - */ - Var *upd_var = (Var *) (te_update->expr); - - upd_var->varattno = te_update->resno; - upd_var->varoattno = te_update->resno; - - upd_var->vartype = attrs->atttypid; - upd_var->vartypmod = attrs->atttypmod; - - upd_var->varnoold = upd_var->varno; - - te_update->resno = attnum; - te_update->resname = pstrdup(get_attname(baserelid, attnum)); - te_update->ressortgroupref = 0; - te_update->resorigcol = 0; - te_update->resorigtbl = 0; - te_update->resjunk = false; - } -} - -/* - * Create the returning list for the given query tree. This allows - * using RETURING in view update actions. Note that the function - * creates the returning list from the target list of the given query - * tree if src is set to NULL. This requires to call - * build_update_target_list() on that query tree before. If src != - * NULL, the target list is created from this query tree instead. - */ -static void -create_rule_returning_list(Query *query, const Query *src, Index newRTE, - ViewDefColumnList tentries) -{ - ViewExprContext ctxt; - ListCell *cell; - - ctxt.newRTE = newRTE; - ctxt.tentries = tentries; - - /* determine target list source */ - if (src) - query->returningList = copyObject(src->targetList); - else - query->returningList = copyObject(query->targetList); - - foreach(cell, query->returningList) - expression_tree_walker((Node *) lfirst(cell), - replace_tlist_varno_walker, - (void *) &ctxt); -} - -/* - * Build the target list for a view UPDATE rule. - * - * Note: The function assumes a Query tree specified by update, which - * was copied by form_update_query(). We need the original Query tree - * to adjust the properties of each member of the TargetList of the - * new query tree. - */ -static void -build_update_target_list(const Query *update, const Query *select, - Relation baserel) -{ - ListCell *cell1; - ListCell *cell2; - - /* - * This Assert() is appropriate, since we rely on a query tree - * created by from_query(), which copies the target list from the - * original query tree specified by the argument select, which - * holds the current view definition. So both target lists have - * to be equal in length. - */ - Assert(list_length(update->targetList) == list_length(select->targetList)); - - /* - * Copy the target list of the view definition to the - * returningList. This is required to support RETURNING clauses - * in view update actions. - */ - forboth(cell1, select->targetList, cell2, update->targetList) - { - TargetEntry *entry = (TargetEntry *) lfirst(cell1); - TargetEntry *upd_entry = (TargetEntry *) lfirst(cell2); - int attindex; - Form_pg_attribute attr; - - if (entry->resorigcol > 0) - /* - * This column seems to have a different order than in the base - * table. We get the attribute from the base relation referenced - * by rel and create a new resdom. This new result domain is then - * assigned instead of the old one. - */ - attindex = entry->resorigcol; - else - attindex = entry->resno; - - attr = baserel->rd_att->attrs[attindex - 1]; - - form_te_for_update(attindex, attr, baserel->rd_id, upd_entry->expr, - upd_entry); - } -} - -/* - * Examines the columns by the current view and initializes the lookup - * table for all rearranged columns in base relations. The function - * requires a relation tree initialized by get_base_base_relations(). - */ -static void -read_rearranged_cols(ViewBaseRelation *tree) -{ - AssertArg(tree); - - if (tree->defs) - { - int num_items = list_length(tree->defs); - int i; - - /* - * Traverse the relation tree and look on all base relations - * for reversed column order in their target lists. We have - * to perform a look-ahead-read on the tree, because we need - * to know how much columns the next base relation has, to - * allocate enough memory in tentries. - * - * Note that if we only have one base relation (a "real" - * table, not a view) exists, we have nothing to do, because - * this base relation cannot have a reversed column order - * caused by a view definition query. - */ - for (i = num_items - 1; i > 0; i--) - { - ViewBaseRelationItem *item_current; - ViewBaseRelationItem *item_next; - ViewBaseRelation *current; - ViewBaseRelation *next; - - current = (ViewBaseRelation *) list_nth(tree->defs, i); - - /* - * We look ahead for the next base relation. We can do - * this here safely, because the loop condition terminates - * before reaching the list head. - */ - next = (ViewBaseRelation *) list_nth(tree->defs, i - 1); - - /* - * Note that the code currently requires a simply updatable - * relation tree. This means we handle one base relation - * per loop, only. - */ - Assert(next); - Assert(current); - Assert(list_length(next->defs) == 1); - Assert(list_length(current->defs) == 1); - - item_current = (ViewBaseRelationItem *) list_nth(current->defs, 0); - item_next = (ViewBaseRelationItem *) list_nth(next->defs, 0); - - /* allocate tentries buffer */ - item_current->tentries = (ViewDefColumnList) palloc(sizeof(TargetEntry *) * RelationGetNumberOfAttributes(item_next->rel)); - - copyReversedTargetEntryPtr(item_current->rule->targetList, - item_current->tentries); - } - } -} - -/*------------------------------------------------------------------------------ - * Retrieves all relations from the view that can be considered a "base - * relation". The function returns a list that holds lists of all relation - * OIDs found for the view. The list is filled top down, that means the head of - * the list holds the relations for the "highest" view in the tree. - * - * Consider this view definition tree where each node is a relation the above - * node is based on: - * - * 1 - * / \ - * 2 3 - * / \ \ - * 4 5 6 - * / - * 7 - * - * The function will then return a list with the following layout: - * - * Listindex Node(s) - * -------------------------- - * 1 7 - * 2 4 5 6 - * 3 2 3 - * - * As you can see in the table, all relations that are "children" of the - * given root relation (the view relation itself) are saved in the - * tree, except the root node itself. - *------------------------------------------------------------------------------ - */ -static void -get_base_base_relations(const Query *view, Oid baserelid, List **list) -{ - RangeTblEntry *entry; - unsigned int offset = 1; - ViewBaseRelation *childRel; - - if (!view) - return; - - childRel = (ViewBaseRelation *) palloc(sizeof(ViewBaseRelation)); - childRel->defs = NIL; - childRel->parentRelation = baserelid; - - /* Get all OIDs from the RTE list of view. */ - while ((entry = get_relation_RTE(view, &offset)) != NULL) - { - Relation rel; - ViewBaseRelationItem *item; - - /* - * Is this really a view or relation? - */ - rel = relation_open(entry->relid, AccessShareLock); - - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_VIEW) - { - /* don't need this one */ - relation_close(rel, AccessShareLock); - continue; - } - - item = (ViewBaseRelationItem *) palloc0(sizeof(ViewBaseRelationItem)); - item->rel = rel; - item->rule = NULL; - - if (rel->rd_rel->relkind == RELKIND_VIEW) - /* - * Get the rule _RETURN expression tree for the specified - * relation OID. We need this to recurse into the view - * base relation tree. - */ - item->rule = get_return_rule(rel); - - elog(DEBUG1, "extracted relation %s for relation tree", - RelationGetRelationName(rel)); - childRel->defs = lappend(childRel->defs, item); - - /* recurse to any other child relations */ - if (item->rule) - get_base_base_relations(item->rule, RelationGetRelid(rel), list); - - } - - if (childRel->defs) - *list = lappend(*list, childRel); -} - -static void -copyReversedTargetEntryPtr(List *targetList, ViewDefColumnList targets) -{ - ListCell *cell; - - AssertArg(targets); - AssertArg(targetList); - - /* NOTE: We only reassign pointers. */ - foreach(cell, targetList) - { - Node *node = (Node *) lfirst(cell); - - if (IsA(node, TargetEntry)) - { - /* - * Look at the resdom's resorigcol to determine whether - * this is a reversed column (meaning, it has a different - * column number than the underlying base table). - */ - TargetEntry *entry = (TargetEntry *) node; - - if (!IsA(entry->expr, Var)) - /* nothing to do here */ - continue; - - if (entry->resorigcol > 0 && entry->resno != entry->resorigcol) - { - /* - * Save this TargetEntry to the appropiate place in - * the lookup table. Do it only if not already - * occupied (this could happen if the column is - * specified more than one time in the view - * definition). - */ - if (targets[entry->resorigcol - 1] == NULL) - targets[entry->resorigcol - 1] = entry; - } - } - } -} - -/* - * Transforms the specified view definition into an INSERT, UPDATE, or - * DELETE rule. - * - * Note: The function assumes that the specified query tree has passed the - * is_select_query_updatable() function. - */ -static void -create_update_rule(Oid viewOid, const Query *select, const Relation baserel, - ViewDefColumnList tentries, - CmdType ruletype) -{ - Query *newquery; - Oid baserelid; - Index baserti; - RangeTblEntry *rte; - - AssertArg(tentries); - AssertArg(baserel); - AssertArg(select); - AssertArg(ruletype == CMD_INSERT || ruletype == CMD_UPDATE || ruletype == CMD_DELETE); - - newquery = form_update_query(select, tentries, ruletype); - - /* - * form_update_query() has prepared the jointree of the new UPDATE/DELETE rule. - * - * Now, our UPDATE rule needs range table references for the *NEW* - * and base relation RTEs. A DELETE rule needs range table - * references for the *OLD* and base relation RTEs. - */ - - baserelid = get_reloid_from_select(select, NULL, &rte); - if (!OidIsValid(baserelid)) - elog(ERROR, "could not get the base relation from the view definition"); - - baserti = get_rtindex_for_rel(newquery->rtable, - rte->eref->aliasname); - Assert(baserti > 0); - - rte = rt_fetch(baserti, newquery->rtable); - - if (ruletype != CMD_INSERT) - { - RangeTblRef *oldref; - RangeTblRef *baseref; - - oldref = makeNode(RangeTblRef); - oldref->rtindex = PRS2_OLD_VARNO; - - baseref = makeNode(RangeTblRef); - baseref->rtindex = baserti; - - newquery->jointree->fromlist = list_make2(baseref, oldref); - - /* Create the WHERE condition qualification for the rule action. */ - form_where_for_updrule(select, &(newquery->jointree), - baserel, baserti, PRS2_OLD_VARNO); - } - - if (ruletype != CMD_DELETE) - /* - * We must reorder the columns in the targetlist to match the - * underlying table. We do this after calling - * form_where_for_updrule() because build_update_target_list() - * relies on the original resdoms in the update tree. - */ - build_update_target_list(newquery, select, baserel); - - /* - * Create the returning list now that build_update_target_list() - * has done the leg work. - */ - if (ruletype == CMD_DELETE) - create_rule_returning_list(newquery, select, PRS2_OLD_VARNO, tentries); - else - create_rule_returning_list(newquery, NULL, PRS2_NEW_VARNO, tentries); - - /* Set ACL bit */ - if (ruletype == CMD_INSERT) - rte->requiredPerms |= ACL_INSERT; - else if (ruletype == CMD_UPDATE) - rte->requiredPerms |= ACL_UPDATE; - else if (ruletype == CMD_DELETE) - rte->requiredPerms |= ACL_DELETE; - - /* Create system rule */ - DefineQueryRewrite(pstrdup(get_auto_rule_name(ruletype)), - viewOid, /* event_relid */ - NULL, /* WHERE clause */ - ruletype, - true, /* is_instead */ - true, /* is_auto */ - false, /* replace */ - list_make1(newquery) /* action */); -} - -/* - * Checks the specified Query for updatability. Currently, "simply - * updatable" rules are implemented. - */ -static bool -is_select_query_updatable(const Query *query) -{ - ListCell *cell; - List *seen_attnos; - - AssertArg(query); - AssertArg(query->commandType == CMD_SELECT); - - /* - * check for unsupported clauses in the view definition - */ - - if (query->hasAggs) - { - elog(DEBUG1, "view is not updatable because it uses an aggregate function"); - return false; - } - - if (query->hasWindowFuncs) - { - elog(DEBUG1, "view is not updatable because it uses a window function"); - return false; - } - - if (query->hasRecursive) - { - elog(DEBUG1, "view is not updatable because it contains a WITH RECURSIVE clause"); - return false; - } - - if (query->cteList) - { - elog(DEBUG1, "view is not updatable because it contains a WITH clause"); - return false; - } - - if (list_length(query->groupClause) >= 1) - { - elog(DEBUG1, "view is not updatable because it contains a GROUP BY clause"); - return false; - } - - if (query->havingQual) - { - elog(DEBUG1, "view is not updatable because it contains a HAVING clause"); - return false; - } - - if (list_length(query->distinctClause) >= 1) - { - elog(DEBUG1, "view is not updatable because it contains a DISTINCT clause"); - return false; - } - - if (query->limitOffset) - { - elog(DEBUG1, "view is not updatable because it contains an OFFSET clause"); - return false; - } - - if (query->limitCount) - { - elog(DEBUG1, "view is not updatable because it contains a LIMIT clause"); - return false; - } - - if (query->setOperations) - { - elog(DEBUG1, "view is not updatable because it contains UNION or INTERSECT or EXCEPT"); - return false; - } - - /* - * Test for number of involved relations. Since we assume to - * operate on a view definition SELECT query tree, we must count 3 - * rtable entries. Otherwise this seems not to be a view based on - * a single relation. - */ - if (list_length(query->rtable) > 3) - { - elog(DEBUG1, "view is not updatable because it has more than one underlying table"); - return false; - } - - /* Any rtable entries involved? */ - if (list_length(query->rtable) < 3) - { - elog(DEBUG1, "view is not updatable because it has no underlying tables"); - return false; - } - - /* - * Walk down the target list and look for nodes that aren't Vars. - * "Simply updatable" doesn't allow functions, host variables, or - * constant expressions in the target list. - * - * Also, check if any of the target list entries are indexed array - * expressions, which aren't supported. - */ - seen_attnos = NIL; - - foreach(cell, query->targetList) - { - Node *node = (Node *) lfirst(cell); - - if (IsA(node, TargetEntry)) - { - TargetEntry *te = (TargetEntry *) node; - - /* - * TODO -- it would be nice to support Const nodes here as well - * (but apparently it isn't in the standard) - */ - if (!IsA(te->expr, Var) && !IsA(te->expr, ArrayRef)) - { - elog(DEBUG1, "view is not updatable because select list contains a derived column"); - return false; - } - - /* This is currently only partially implemented, but can be fixed. */ - if (IsA(te->expr, ArrayRef)) - { - elog(DEBUG1, "view is not updatable because select list contains an array element reference"); - return false; - } - - if (IsA(te->expr, Var)) - { - Var *var = (Var *) te->expr; - - /* System columns aren't updatable. */ - if (var->varattno < 0) - { - elog(DEBUG1, "view is not updatable because select list references a system column"); - return false; - } - - if (list_member_int(seen_attnos, var->varattno)) - { - elog(DEBUG1, "view is not updatable because select list references the same column more than once"); - return false; - } - else - seen_attnos = lappend_int(seen_attnos, var->varattno); - } - } - } - - /* - * Finally, check that all RTEs are acceptable. This rejects - * table functions, which cannot ever be updatable, and also WITH - * clauses. - */ - foreach(cell, query->rtable) - { - RangeTblEntry *entry = (RangeTblEntry *) lfirst(cell); - - if (entry->rtekind != RTE_RELATION) - { - elog(DEBUG1, "view is not updatable because correlation \"%s\" is not a table", - entry->eref->aliasname); - return false; - } - } - - return true; -} - -/* - * Traverse the specified relation tree. The function stops at the - * base relations at the leafs of the tree. If any of the relations - * has more than one base relation, it is considered a not simply - * updatable view and false is returned. - */ -static bool -check_reltree(ViewBaseRelation *node) -{ - ListCell *cell; - - AssertArg(node); - - foreach(cell, node->defs) - { - /* Walk down the tree */ - ViewBaseRelation *relations = (ViewBaseRelation *) lfirst(cell); - - if (list_length(relations->defs) > 1) - { - elog(DEBUG1, "possible JOIN/UNION in view definition: %d", list_length(relations->defs)); - return false; - } - else if (list_length(relations->defs) == 1) { - ViewBaseRelationItem *item = (ViewBaseRelationItem *) linitial(relations->defs); - - /* if the relation found is a view, check its updatability */ - if (item->rel->rd_rel->relkind == RELKIND_VIEW && !is_select_query_updatable(item->rule)) - { - elog(DEBUG1, "base view \"%s\" is not updatable", - RelationGetRelationName(item->rel)); - return false; - } - } - } - - return true; -} - -/* - * Given a SELECT query tree, return the OID of the first RTE_RELATION range - * table entry found that is not *NEW* nor *OLD*. - * - * Also sets the RangeTblEntry pointer into rel_entry, and the range - * table index into rti, unless they are NULL. - * - * This function assumes that the specified query tree was checked by a - * previous call to the is_select_query_updatable() function. - */ -static Oid -get_reloid_from_select(const Query *select, int *rti, RangeTblEntry **rel_entry) -{ - ListCell *cell; - Oid result = InvalidOid; - int index; - - /* Check specified query tree. Return immediately on error. */ - if (select == NULL || select->commandType != CMD_SELECT) - return InvalidOid; - - /* - * We loop through the RTEs to get information about all involved - * relations. We return the first OID we find in the list that is not - * *NEW* nor *OLD*. - */ - index = 0; - foreach(cell, select->rtable) - { - RangeTblEntry *entry = (RangeTblEntry *) lfirst(cell); - - index++; - - if (entry == NULL) - elog(ERROR, "null RTE pointer in get_reloid_from_select"); - - elog(DEBUG1, "extracted range table entry for %u", entry->relid); - - /* Return the first RELATION rte we find */ - if (entry->rtekind == RTE_RELATION) - { - /* - * XXX This is ugly. The parser prepends two RTEs with rtekind - * RTE_RELATION named *NEW* and *OLD*. We have to exclude them by - * name! It would be much better if it used RTE_SPECIAL - * instead, but other parts of the system stop working if one - * just changes it naively. - */ - if (strncmp(entry->eref->aliasname, "*NEW*", 6) == 0 - || strncmp(entry->eref->aliasname, "*OLD*", 6) == 0) - continue; - - result = entry->relid; - if (rti != NULL) - *rti = index; - if (rel_entry != NULL) - *rel_entry = entry; - break; - } - } - - return result; -} - -/* - * get_return_rule: returns the _RETURN rule of a view as a Query node. - */ -static Query * -get_return_rule(Relation rel) -{ - Query *query = NULL; - int i; - - AssertArg(rel->rd_rel->relkind == RELKIND_VIEW); - - for (i = 0; i < rel->rd_rules->numLocks; i++) - { - RewriteRule *rule = rel->rd_rules->rules[i]; - - if (rule->event == CMD_SELECT) - { - /* A _RETURN rule should have only one action */ - if (list_length(rule->actions) != 1) - elog(ERROR, "invalid _RETURN rule action specification"); - - query = linitial(rule->actions); - break; - } - } - - return query; -} - -/*------------------------------------------------------------------------------ - * Public functions - *------------------------------------------------------------------------------ - */ - -/* - * CreateViewUpdateRules - * - * This is the main entry point to creating an updatable view's rules. Given a - * rule definition, examine it, and create the rules if appropiate, or return - * doing nothing if not. - */ -void -CreateViewUpdateRules(Oid viewOid, const Query *viewDef) -{ - Relation baserel; - Form_pg_attribute *attrs; - ViewDefColumnList tentries; - Oid baserelid; - MemoryContext cxt; - MemoryContext oldcxt; - ViewBaseRelation *tree; - ListCell *cell; - - /* - * The routines in this file leak memory like crazy, so make sure we - * allocate it all in an appropiate context. - */ - cxt = AllocSetContextCreate(TopTransactionContext, - "UpdateRulesContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldcxt = MemoryContextSwitchTo(cxt); - - /* - * Create the lookup table for the view definition target columns. We save - * the RESDOMS in that manner to look quickly for reversed column orders. - */ - - baserelid = get_reloid_from_select(viewDef, NULL, NULL); - - /* Get relation tree */ - tree = (ViewBaseRelation *) palloc(sizeof(ViewBaseRelation)); - - tree->parentRelation = InvalidOid; - tree->defs = NIL; - get_base_base_relations(viewDef, baserelid, &(tree->defs)); - - /* Check the query tree for updatability */ - if (!check_reltree(tree) || !is_select_query_updatable(viewDef)) - { - elog(DEBUG1, "view is not updatable"); - goto finish; - } - - baserel = heap_open(baserelid, AccessShareLock); - attrs = baserel->rd_att->attrs; - - /* - * Copy TargetEntries to match the slot numbers in the target list with - * their original column attribute number. Note that only pointers are - * copied and they are valid only as long as the specified SELECT query - * stays valid! - */ - tentries = (ViewDefColumnList) - palloc0(baserel->rd_rel->relnatts * sizeof(TargetEntry *)); - - copyReversedTargetEntryPtr(viewDef->targetList, tentries); - - /* - * Now do the same for the base relation tree. read_rearranged_cols - * traverses the relation tree and performs a copyReversedTargetEntry() - * call to each base relation. - */ - read_rearranged_cols(tree); - - create_update_rule(viewOid, viewDef, baserel, tentries, CMD_INSERT); - create_update_rule(viewOid, viewDef, baserel, tentries, CMD_DELETE); - create_update_rule(viewOid, viewDef, baserel, tentries, CMD_UPDATE); - - ereport(NOTICE, (errmsg("CREATE VIEW has created automatic view update rules"))); - - /* free remaining stuff */ - heap_close(baserel, NoLock); - -finish: - /* get_base_base_relations leaves some open relations */ - foreach(cell, tree->defs) - { - ListCell *cell2; - ViewBaseRelation *vbr = (ViewBaseRelation *) lfirst(cell); - - foreach(cell2, vbr->defs) - { - ViewBaseRelationItem *vbri = (ViewBaseRelationItem *) lfirst(cell2); - - relation_close(vbri->rel, NoLock); - } - } - - MemoryContextSwitchTo(oldcxt); - MemoryContextDelete(cxt); -} diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 1cac276516..196ca0155f 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.283 2009/01/26 19:41:06 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.284 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -639,7 +639,6 @@ RelationBuildRuleLock(Relation relation) rule->attrno = rewrite_form->ev_attr; rule->enabled = rewrite_form->ev_enabled; rule->isInstead = rewrite_form->is_instead; - rule->is_auto = rewrite_form->is_auto; /* * Must use heap_getattr to fetch ev_action and ev_qual. Also, the @@ -763,8 +762,6 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2) return false; if (!equal(rule1->actions, rule2->actions)) return false; - if(rule1->is_auto != rule2->is_auto) - return false; } } else if (rlock2 != NULL) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index f47333c605..cb6ae92a38 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.516 2009/01/22 20:16:07 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.517 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -4003,17 +4003,7 @@ getRules(int *numRules) /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); - if (g_fout->remoteVersion >= 80400) - { - appendPQExpBuffer(query, "SELECT " - "tableoid, oid, rulename, " - "ev_class as ruletable, ev_type, is_instead, " - "ev_enabled " - "FROM pg_rewrite " - "WHERE NOT is_auto " - "ORDER BY oid"); - } - else if (g_fout->remoteVersion >= 80300) + if (g_fout->remoteVersion >= 80300) { appendPQExpBuffer(query, "SELECT " "tableoid, oid, rulename, " diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 35b6b844d9..2f70c70428 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.148 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.149 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -304,6 +304,7 @@ typedef struct _ruleInfo bool is_instead; char ev_enabled; bool separate; /* TRUE if must dump as separate item */ + /* separate is always true for non-ON SELECT rules */ } RuleInfo; typedef struct _triggerInfo diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 11349023d9..b83c948964 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.519 2009/01/22 20:16:08 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.520 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200901221 +#define CATALOG_VERSION_NO 200901271 #endif diff --git a/src/include/catalog/pg_rewrite.h b/src/include/catalog/pg_rewrite.h index 2dc621e5fe..4c43898873 100644 --- a/src/include/catalog/pg_rewrite.h +++ b/src/include/catalog/pg_rewrite.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_rewrite.h,v 1.32 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_rewrite.h,v 1.33 2009/01/27 12:40:15 petere Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -40,15 +40,6 @@ CATALOG(pg_rewrite,2618) char ev_enabled; bool is_instead; - /* - * is_auto: True if the rule was automatically generated (update - * rules). This is tracked separately from the dependency system, - * because we want to allow overwriting the automatic update - * rules. So both automatically and manually generated rules have - * dependency type AUTO. - */ - bool is_auto; - /* NB: remaining fields must be accessed via heap_getattr */ text ev_qual; text ev_action; @@ -65,15 +56,14 @@ typedef FormData_pg_rewrite *Form_pg_rewrite; * compiler constants for pg_rewrite * ---------------- */ -#define Natts_pg_rewrite 9 +#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_ev_enabled 5 #define Anum_pg_rewrite_is_instead 6 -#define Anum_pg_rewrite_is_auto 7 -#define Anum_pg_rewrite_ev_qual 8 -#define Anum_pg_rewrite_ev_action 9 +#define Anum_pg_rewrite_ev_qual 7 +#define Anum_pg_rewrite_ev_action 8 #endif /* PG_REWRITE_H */ diff --git a/src/include/rewrite/prs2lock.h b/src/include/rewrite/prs2lock.h index 5d039c21c0..70dbae514d 100644 --- a/src/include/rewrite/prs2lock.h +++ b/src/include/rewrite/prs2lock.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/rewrite/prs2lock.h,v 1.26 2009/01/22 17:27:55 petere Exp $ + * $PostgreSQL: pgsql/src/include/rewrite/prs2lock.h,v 1.27 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,6 @@ typedef struct RewriteRule List *actions; char enabled; bool isInstead; - bool is_auto; } RewriteRule; /* diff --git a/src/include/rewrite/rewriteDefine.h b/src/include/rewrite/rewriteDefine.h index 10936fab84..757957cf7f 100644 --- a/src/include/rewrite/rewriteDefine.h +++ b/src/include/rewrite/rewriteDefine.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/rewrite/rewriteDefine.h,v 1.32 2009/01/22 17:27:55 petere Exp $ + * $PostgreSQL: pgsql/src/include/rewrite/rewriteDefine.h,v 1.33 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,6 @@ extern void DefineQueryRewrite(char *rulename, Node *event_qual, CmdType event_type, bool is_instead, - bool is_auto, bool replace, List *action); diff --git a/src/include/rewrite/rewriteRemove.h b/src/include/rewrite/rewriteRemove.h index f7d3c35465..7f007d1ca6 100644 --- a/src/include/rewrite/rewriteRemove.h +++ b/src/include/rewrite/rewriteRemove.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/rewrite/rewriteRemove.h,v 1.26 2009/01/22 17:27:55 petere Exp $ + * $PostgreSQL: pgsql/src/include/rewrite/rewriteRemove.h,v 1.27 2009/01/27 12:40:15 petere Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,5 @@ extern void RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, bool missing_ok); extern void RemoveRewriteRuleById(Oid ruleOid); -extern void RemoveAutomaticRulesOnEvent(Relation rel, CmdType event_type); #endif /* REWRITEREMOVE_H */ diff --git a/src/include/rewrite/viewUpdate.h b/src/include/rewrite/viewUpdate.h deleted file mode 100644 index 1423870846..0000000000 --- a/src/include/rewrite/viewUpdate.h +++ /dev/null @@ -1,21 +0,0 @@ - -/*------------------------------------------------------------------------- - * - * viewUpdate.h - * - * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $PostgreSQL: pgsql/src/include/rewrite/viewUpdate.h,v 1.1 2009/01/22 17:27:55 petere Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef VIEW_UPDATE_H -#define VIEW_UPDATE_H - -#include "nodes/parsenodes.h" - -extern void -CreateViewUpdateRules(Oid viewOid, const Query *viewDef); - -#endif /* VIEW_UPDATE_H */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 5ac24e5b3d..4768c8b186 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -132,7 +132,6 @@ ALTER INDEX onek_unique1 RENAME TO tmp_onek_unique1; ALTER INDEX tmp_onek_unique1 RENAME TO onek_unique1; -- renaming views CREATE VIEW tmp_view (unique1) AS SELECT unique1 FROM tenk1; -NOTICE: CREATE VIEW has created automatic view update rules ALTER TABLE tmp_view RENAME TO tmp_view_new; -- hack to ensure we get an indexscan here ANALYZE tenk1; @@ -593,7 +592,6 @@ alter table atacc1 alter oid drop not null; ERROR: cannot alter system column "oid" -- try creating a view and altering that, should fail create view myview as select * from atacc1; -NOTICE: CREATE VIEW has created automatic view update rules alter table myview alter column test drop not null; ERROR: "myview" is not a table alter table myview alter column test set not null; @@ -661,7 +659,6 @@ ERROR: column "c3" of relation "def_test" does not exist -- to allow insertions into it, and then alter the view to add -- a default create view def_view_test as select * from def_test; -NOTICE: CREATE VIEW has created automatic view update rules create rule def_view_test_ins as on insert to def_view_test do instead insert into def_test select new.*; @@ -845,7 +842,6 @@ alter table atacc1 drop xmin; ERROR: cannot drop system column "xmin" -- try creating a view and altering that, should fail create view myview as select * from atacc1; -NOTICE: CREATE VIEW has created automatic view update rules select * from myview; b | c | d ---+---+--- @@ -1440,7 +1436,6 @@ create table alter1.t1(f1 serial primary key, f2 int check (f2 > 0)); NOTICE: CREATE TABLE will create implicit sequence "t1_f1_seq" for serial column "t1.f1" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1" create view alter1.v1 as select * from alter1.t1; -NOTICE: CREATE VIEW has created automatic view update rules create function alter1.plus1(int) returns int as 'select $1+1' language sql; create domain alter1.posint integer check (value > 0); create type alter1.ctype as (f1 int, f2 text); diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 232bacfd6d..04383e43d2 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -27,10 +27,8 @@ CREATE TABLE viewtest_tbl (a int, b int); COPY viewtest_tbl FROM stdin; CREATE OR REPLACE VIEW viewtest AS SELECT * FROM viewtest_tbl; -NOTICE: CREATE VIEW has created automatic view update rules CREATE OR REPLACE VIEW viewtest AS SELECT * FROM viewtest_tbl WHERE a > 10; -NOTICE: CREATE VIEW has created automatic view update rules SELECT * FROM viewtest; a | b ----+---- @@ -40,7 +38,6 @@ SELECT * FROM viewtest; CREATE OR REPLACE VIEW viewtest AS SELECT a, b FROM viewtest_tbl WHERE a > 5 ORDER BY b DESC; -NOTICE: CREATE VIEW has created automatic view update rules SELECT * FROM viewtest; a | b ----+---- @@ -74,17 +71,13 @@ SET search_path TO temp_view_test, public; CREATE TEMPORARY TABLE temp_table (a int, id int); -- should be created in temp_view_test schema CREATE VIEW v1 AS SELECT * FROM base_table; -NOTICE: CREATE VIEW has created automatic view update rules -- should be created in temp object schema CREATE VIEW v1_temp AS SELECT * FROM temp_table; NOTICE: view "v1_temp" will be a temporary view -NOTICE: CREATE VIEW has created automatic view update rules -- should be created in temp object schema CREATE TEMP VIEW v2_temp AS SELECT * FROM base_table; -NOTICE: CREATE VIEW has created automatic view update rules -- should be created in temp_views schema CREATE VIEW temp_view_test.v2 AS SELECT * FROM base_table; -NOTICE: CREATE VIEW has created automatic view update rules -- should fail CREATE VIEW temp_view_test.v3_temp AS SELECT * FROM temp_table; NOTICE: view "v3_temp" will be a temporary view @@ -114,25 +107,18 @@ CREATE VIEW v5_temp AS NOTICE: view "v5_temp" will be a temporary view -- subqueries CREATE VIEW v4 AS SELECT * FROM base_table WHERE id IN (SELECT id FROM base_table2); -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v5 AS SELECT t1.id, t2.a FROM base_table t1, (SELECT * FROM base_table2) t2; CREATE VIEW v6 AS SELECT * FROM base_table WHERE EXISTS (SELECT 1 FROM base_table2); -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v7 AS SELECT * FROM base_table WHERE NOT EXISTS (SELECT 1 FROM base_table2); -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v8 AS SELECT * FROM base_table WHERE EXISTS (SELECT 1); -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v6_temp AS SELECT * FROM base_table WHERE id IN (SELECT id FROM temp_table); NOTICE: view "v6_temp" will be a temporary view -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v7_temp AS SELECT t1.id, t2.a FROM base_table t1, (SELECT * FROM temp_table) t2; NOTICE: view "v7_temp" will be a temporary view CREATE VIEW v8_temp AS SELECT * FROM base_table WHERE EXISTS (SELECT 1 FROM temp_table); NOTICE: view "v8_temp" will be a temporary view -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v9_temp AS SELECT * FROM base_table WHERE NOT EXISTS (SELECT 1 FROM temp_table); NOTICE: view "v9_temp" will be a temporary view -NOTICE: CREATE VIEW has created automatic view update rules -- a view should also be temporary if it references a temporary view CREATE VIEW v10_temp AS SELECT * FROM v7_temp; NOTICE: view "v10_temp" will be a temporary view @@ -144,10 +130,8 @@ NOTICE: view "v12_temp" will be a temporary view CREATE SEQUENCE seq1; CREATE TEMPORARY SEQUENCE seq1_temp; CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1; -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp; NOTICE: view "v13_temp" will be a temporary view -NOTICE: CREATE VIEW has created automatic view update rules SELECT relname FROM pg_class WHERE relname LIKE 'v_' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'temp_view_test') @@ -235,7 +219,6 @@ CREATE TEMP TABLE tmptbl (i int, j int); CREATE VIEW pubview AS SELECT * FROM tbl1 WHERE tbl1.a BETWEEN (SELECT d FROM tbl2 WHERE c = 1) AND (SELECT e FROM tbl3 WHERE f = 2) AND EXISTS (SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f); -NOTICE: CREATE VIEW has created automatic view update rules SELECT count(*) FROM pg_class where relname = 'pubview' AND relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname = 'testviewschm2'); count @@ -249,7 +232,6 @@ BETWEEN (SELECT d FROM tbl2 WHERE c = 1) AND (SELECT e FROM tbl3 WHERE f = 2) AND EXISTS (SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f) AND NOT EXISTS (SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j); NOTICE: view "mytempview" will be a temporary view -NOTICE: CREATE VIEW has created automatic view update rules SELECT count(*) FROM pg_class where relname LIKE 'mytempview' And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%'); count diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out index 90f1bb86e1..092c90403a 100644 --- a/src/test/regress/expected/drop_if_exists.out +++ b/src/test/regress/expected/drop_if_exists.out @@ -13,7 +13,6 @@ ERROR: view "test_view_exists" does not exist DROP VIEW IF EXISTS test_view_exists; NOTICE: view "test_view_exists" does not exist, skipping CREATE VIEW test_view_exists AS select * from test_exists; -NOTICE: CREATE VIEW has created automatic view update rules DROP VIEW IF EXISTS test_view_exists; DROP VIEW test_view_exists; ERROR: view "test_view_exists" does not exist diff --git a/src/test/regress/expected/plancache.out b/src/test/regress/expected/plancache.out index 853ae94fdb..c0681d26e3 100644 --- a/src/test/regress/expected/plancache.out +++ b/src/test/regress/expected/plancache.out @@ -79,7 +79,6 @@ EXECUTE prepstmt2(123); -- but should trigger invalidation anyway CREATE TEMP VIEW pcacheview AS SELECT * FROM pcachetest; -NOTICE: CREATE VIEW has created automatic view update rules PREPARE vprep AS SELECT * FROM pcacheview; EXECUTE vprep; q1 | q2 @@ -237,9 +236,6 @@ select cachebug(); NOTICE: table "temptable" does not exist, skipping CONTEXT: SQL statement "drop table if exists temptable cascade" PL/pgSQL function "cachebug" line 3 at SQL statement -NOTICE: CREATE VIEW has created automatic view update rules -CONTEXT: SQL statement "create temp view vv as select * from temptable" -PL/pgSQL function "cachebug" line 5 at SQL statement NOTICE: 1 NOTICE: 2 NOTICE: 3 @@ -252,9 +248,6 @@ select cachebug(); NOTICE: drop cascades to view vv CONTEXT: SQL statement "drop table if exists temptable cascade" PL/pgSQL function "cachebug" line 3 at SQL statement -NOTICE: CREATE VIEW has created automatic view update rules -CONTEXT: SQL statement "create temp view vv as select * from temptable" -PL/pgSQL function "cachebug" line 5 at SQL statement NOTICE: 1 NOTICE: 2 NOTICE: 3 diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index be5c476548..95dcea5a1d 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1229,7 +1229,6 @@ ROLLBACK; -- WHERE CURRENT OF may someday work with views, but today is not that day. -- For now, just make sure it errors out cleanly. CREATE TEMP VIEW ucview AS SELECT * FROM uctest; -NOTICE: CREATE VIEW has created automatic view update rules CREATE RULE ucrule AS ON DELETE TO ucview DO INSTEAD DELETE FROM uctest WHERE f1 = OLD.f1; BEGIN; diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 88d1ab3b78..7d3b44b856 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -189,12 +189,9 @@ DELETE FROM atest3; -- ok -- views SET SESSION AUTHORIZATION regressuser3; CREATE VIEW atestv1 AS SELECT * FROM atest1; -- ok -NOTICE: CREATE VIEW has created automatic view update rules /* The next *should* fail, but it's not implemented that way yet. */ CREATE VIEW atestv2 AS SELECT * FROM atest2; -NOTICE: CREATE VIEW has created automatic view update rules CREATE VIEW atestv3 AS SELECT * FROM atest3; -- ok -NOTICE: CREATE VIEW has created automatic view update rules SELECT * FROM atestv1; -- ok a | b ---+----- @@ -222,7 +219,6 @@ SELECT * FROM atestv3; -- ok (0 rows) CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view -NOTICE: CREATE VIEW has created automatic view update rules SELECT * FROM atestv4; -- ok one | two | three -----+-----+------- diff --git a/src/test/regress/expected/returning.out b/src/test/regress/expected/returning.out index 05afef0f19..b04f6f1a0e 100644 --- a/src/test/regress/expected/returning.out +++ b/src/test/regress/expected/returning.out @@ -195,7 +195,6 @@ SELECT * FROM foochild; DROP TABLE foochild; -- Rules and views CREATE TEMP VIEW voo AS SELECT f1, f2 FROM foo; -NOTICE: CREATE VIEW has created automatic view update rules CREATE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES(new.*, 57); INSERT INTO voo VALUES(11,'zit'); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index e0618abd67..2daa79d732 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -10,7 +10,6 @@ create table rtest_t1 (a int4, b int4); create table rtest_t2 (a int4, b int4); create table rtest_t3 (a int4, b int4); create view rtest_v1 as select * from rtest_t1; -NOTICE: CREATE VIEW has created automatic view update rules create rule rtest_v1_ins as on insert to rtest_v1 do instead insert into rtest_t1 values (new.a, new.b); create rule rtest_v1_upd as on update to rtest_v1 do instead @@ -756,12 +755,9 @@ create table rtest_view3 (a int4, b text); create table rtest_view4 (a int4, b text, c int4); create view rtest_vview1 as select a, b from rtest_view1 X where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a); -NOTICE: CREATE VIEW has created automatic view update rules create view rtest_vview2 as select a, b from rtest_view1 where v; -NOTICE: CREATE VIEW has created automatic view update rules create view rtest_vview3 as select a, b from rtest_vview2 X where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a); -NOTICE: CREATE VIEW has created automatic view update rules create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount from rtest_view1 X, rtest_view2 Y where X.a = Y.a @@ -1337,8 +1333,8 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; - tablename | rulename | definition ----------------+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + tablename | rulename | definition +---------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- pg_settings | pg_settings_n | CREATE RULE pg_settings_n AS ON UPDATE TO pg_settings DO INSTEAD NOTHING; pg_settings | pg_settings_u | CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, false) AS set_config; rtest_emp | rtest_emp_del | CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) VALUES (old.ename, "current_user"(), 'fired'::bpchar, '$0.00'::money, old.salary); @@ -1363,21 +1359,12 @@ SELECT tablename, rulename, definition FROM pg_rules rtest_v1 | rtest_v1_del | CREATE RULE rtest_v1_del AS ON DELETE TO rtest_v1 DO INSTEAD DELETE FROM rtest_t1 WHERE (rtest_t1.a = old.a); rtest_v1 | rtest_v1_ins | CREATE RULE rtest_v1_ins AS ON INSERT TO rtest_v1 DO INSTEAD INSERT INTO rtest_t1 (a, b) VALUES (new.a, new.b); rtest_v1 | rtest_v1_upd | CREATE RULE rtest_v1_upd AS ON UPDATE TO rtest_v1 DO INSTEAD UPDATE rtest_t1 SET a = new.a, b = new.b WHERE (rtest_t1.a = old.a); - rtest_vview1 | _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO rtest_vview1 DO INSTEAD DELETE FROM rtest_view1 x WHERE ((((old.a IS NULL) AND (x.a IS NULL)) OR (old.a = x.a)) AND (((old.b IS NULL) AND (x.b IS NULL)) OR (old.b = x.b))) RETURNING old.a, old.b; - rtest_vview1 | _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO rtest_vview1 DO INSTEAD INSERT INTO rtest_view1 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - rtest_vview1 | _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO rtest_vview1 DO INSTEAD UPDATE rtest_view1 x SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (x.a IS NULL)) OR (old.a = x.a)) AND (((old.b IS NULL) AND (x.b IS NULL)) OR (old.b = x.b))) RETURNING new.a, new.b; - rtest_vview2 | _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO rtest_vview2 DO INSTEAD DELETE FROM rtest_view1 WHERE ((((old.a IS NULL) AND (rtest_view1.a IS NULL)) OR (old.a = rtest_view1.a)) AND (((old.b IS NULL) AND (rtest_view1.b IS NULL)) OR (old.b = rtest_view1.b))) RETURNING old.a, old.b; - rtest_vview2 | _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO rtest_vview2 DO INSTEAD INSERT INTO rtest_view1 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - rtest_vview2 | _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO rtest_vview2 DO INSTEAD UPDATE rtest_view1 SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (rtest_view1.a IS NULL)) OR (old.a = rtest_view1.a)) AND (((old.b IS NULL) AND (rtest_view1.b IS NULL)) OR (old.b = rtest_view1.b))) RETURNING new.a, new.b; - rtest_vview3 | _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO rtest_vview3 DO INSTEAD DELETE FROM rtest_vview2 x WHERE ((((old.a IS NULL) AND (x.a IS NULL)) OR (old.a = x.a)) AND (((old.b IS NULL) AND (x.b IS NULL)) OR (old.b = x.b))) RETURNING old.a, old.b; - rtest_vview3 | _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO rtest_vview3 DO INSTEAD INSERT INTO rtest_vview2 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - rtest_vview3 | _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO rtest_vview3 DO INSTEAD UPDATE rtest_vview2 x SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (x.a IS NULL)) OR (old.a = x.a)) AND (((old.b IS NULL) AND (x.b IS NULL)) OR (old.b = x.b))) RETURNING new.a, new.b; shoelace | shoelace_del | CREATE RULE shoelace_del AS ON DELETE TO shoelace DO INSTEAD DELETE FROM shoelace_data WHERE (shoelace_data.sl_name = old.sl_name); shoelace | shoelace_ins | CREATE RULE shoelace_ins AS ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data (sl_name, sl_avail, sl_color, sl_len, sl_unit) VALUES (new.sl_name, new.sl_avail, new.sl_color, new.sl_len, new.sl_unit); shoelace | shoelace_upd | CREATE RULE shoelace_upd AS ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = new.sl_name, sl_avail = new.sl_avail, sl_color = new.sl_color, sl_len = new.sl_len, sl_unit = new.sl_unit WHERE (shoelace_data.sl_name = old.sl_name); shoelace_data | log_shoelace | CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data WHERE (new.sl_avail <> old.sl_avail) DO INSERT INTO shoelace_log (sl_name, sl_avail, log_who, log_when) VALUES (new.sl_name, new.sl_avail, 'Al Bundy'::name, 'Thu Jan 01 00:00:00 1970'::timestamp without time zone); shoelace_ok | shoelace_ok_ins | CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = (shoelace.sl_avail + new.ok_quant) WHERE (shoelace.sl_name = new.ok_name); -(38 rows) +(29 rows) -- -- CREATE OR REPLACE RULE @@ -1479,7 +1466,6 @@ insert into test_2 (name) values ('Test 4'); insert into test_3 (name) values ('Test 5'); insert into test_3 (name) values ('Test 6'); create view id_ordered as select * from id order by id; -NOTICE: CREATE VIEW has created automatic view update rules create rule update_id_ordered as on update to id_ordered do instead update id set name = new.name where id = old.id; select * from id_ordered; diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index b30fdcbb80..f6dbc0212c 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -349,7 +349,6 @@ create temp table shipped ( ); create temp view shipped_view as select * from shipped where ttype = 'wt'; -NOTICE: CREATE VIEW has created automatic view update rules create rule shipped_view_insert as on insert to shipped_view do instead insert into shipped values('wt', new.ordnum, new.partnum, new.value); insert into parts (partnum, cost) values (1, 1234.56); diff --git a/src/test/regress/expected/view_update.out b/src/test/regress/expected/view_update.out deleted file mode 100644 index 09c2d4f6f5..0000000000 --- a/src/test/regress/expected/view_update.out +++ /dev/null @@ -1,370 +0,0 @@ -CREATE TABLE vutest1 (a integer, b text); -INSERT INTO vutest1 VALUES (1, 'one'); -INSERT INTO vutest1 VALUES (2, 'two'); --- simple view updatability conditions -CREATE VIEW vutestv1 AS SELECT a, b FROM vutest1; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv2 AS SELECT * FROM vutest1; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv3 AS SELECT b, a FROM vutest1; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv4 AS SELECT a, b FROM vutest1 WHERE a < 5; -NOTICE: CREATE VIEW has created automatic view update rules --- not updatable tests: -CREATE VIEW vutestv5 AS SELECT sum(a) FROM vutest1; -- aggregate function -CREATE VIEW vutestv6 AS SELECT b FROM vutest1 GROUP BY b; -- GROUP BY -CREATE VIEW vutestv7 AS SELECT l.b AS x, r.b AS y FROM vutest1 l, vutest1 r WHERE r.a = l.a; -- JOIN -CREATE VIEW vutestv8 AS SELECT 42; -- no table -CREATE VIEW vutestv9 AS SELECT a * 2 AS x, b || b AS y FROM vutest1; -- derived columns -CREATE VIEW vutestv10 AS SELECT a AS x, a AS y FROM vutest1; -- column referenced more than once -CREATE VIEW vutestv11 AS SELECT * FROM generate_series(1, 5); -- table function -CREATE VIEW vutestv12 AS SELECT xmin, xmax, a, b FROM vutest1; -- system columns -CREATE VIEW vutestv13 AS SELECT DISTINCT a, b FROM vutest1; -- DISTINCT -CREATE VIEW vutestv14 AS SELECT a, b FROM vutest1 WHERE a > (SELECT avg(a) FROM vutest1); -- *is* updatable, but SQL standard disallows this -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv15 AS SELECT a, b FROM vutest1 UNION ALL SELECT a, b FROM vutest1; -- UNION -CREATE VIEW vutestv16 AS SELECT x, y FROM (SELECT * FROM vutest1) AS foo (x, y); -- subquery ("derived table"); SQL standard allows this -CREATE VIEW vutestv17 AS SELECT a, 5, b FROM vutest1; -- constant -CREATE VIEW vutestv18 AS SELECT a, b FROM vutest1 LIMIT 1; -- LIMIT -CREATE VIEW vutestv19 AS SELECT a, b FROM vutest1 OFFSET 1; -- OFFSET -CREATE VIEW vutestv101 AS SELECT a, rank() OVER (PARTITION BY a ORDER BY b DESC) FROM vutest1; -- window function -CREATE VIEW vutestv102 AS WITH foo AS (SELECT a, b FROM vutest1) SELECT * FROM foo; -- SQL standard allows this -CREATE VIEW vutestv103 AS WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM t) SELECT a FROM vutest1; -- recursive -INSERT INTO vutestv1 VALUES (3, 'three'); -INSERT INTO vutestv2 VALUES (4, 'four'); -INSERT INTO vutestv3 VALUES (5, 'five'); -- fail -ERROR: invalid input syntax for integer: "five" -LINE 1: INSERT INTO vutestv3 VALUES (5, 'five'); - ^ -INSERT INTO vutestv3 VALUES ('five', 5); -INSERT INTO vutestv3 (a, b) VALUES (6, 'six'); -INSERT INTO vutestv4 VALUES (7, 'seven'); -- ok, but would be check option issue -INSERT INTO vutestv5 VALUES (8); -- fail -ERROR: view is not updatable -HINT: You need an unconditional ON INSERT DO INSTEAD rule. -SELECT * FROM vutest1; - a | b ----+------- - 1 | one - 2 | two - 3 | three - 4 | four - 5 | five - 6 | six - 7 | seven -(7 rows) - -SELECT * FROM vutestv1; - a | b ----+------- - 1 | one - 2 | two - 3 | three - 4 | four - 5 | five - 6 | six - 7 | seven -(7 rows) - -SELECT * FROM vutestv2; - a | b ----+------- - 1 | one - 2 | two - 3 | three - 4 | four - 5 | five - 6 | six - 7 | seven -(7 rows) - -SELECT * FROM vutestv3; - b | a --------+--- - one | 1 - two | 2 - three | 3 - four | 4 - five | 5 - six | 6 - seven | 7 -(7 rows) - -SELECT * FROM vutestv4; - a | b ----+------- - 1 | one - 2 | two - 3 | three - 4 | four -(4 rows) - -SELECT * FROM vutestv5; - sum ------ - 28 -(1 row) - -UPDATE vutestv1 SET b = 'a lot' WHERE a = 7; -DELETE FROM vutestv2 WHERE a = 1; -UPDATE vutestv4 SET b = b || '!' WHERE a > 1; -DELETE FROM vutestv4 WHERE a > 3; -UPDATE vutestv6 SET b = 37; -- fail -ERROR: view is not updatable -HINT: You need an unconditional ON UPDATE DO INSTEAD rule. -DELETE FROM vutestv5; -- fail -ERROR: view is not updatable -HINT: You need an unconditional ON DELETE DO INSTEAD rule. -SELECT * FROM vutest1 ORDER BY a, b; - a | b ----+-------- - 2 | two! - 3 | three! - 5 | five - 6 | six - 7 | a lot -(5 rows) - -SELECT * FROM vutestv1 ORDER BY a, b; - a | b ----+-------- - 2 | two! - 3 | three! - 5 | five - 6 | six - 7 | a lot -(5 rows) - -SELECT * FROM vutestv2 ORDER BY a, b; - a | b ----+-------- - 2 | two! - 3 | three! - 5 | five - 6 | six - 7 | a lot -(5 rows) - -SELECT * FROM vutestv4 ORDER BY a, b; - a | b ----+-------- - 2 | two! - 3 | three! -(2 rows) - -TRUNCATE TABLE vutest1; --- views on views -CREATE VIEW vutestv20 AS SELECT a AS x, b AS y FROM vutestv1; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv21 AS SELECT x AS a FROM vutestv20 WHERE x % 2 = 0; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv22 AS SELECT sum(a) FROM vutestv21; -- not updatable -CREATE VIEW vutestv23 AS SELECT * FROM vutestv12; -- not updatable -INSERT INTO vutestv20 (x, y) VALUES (1, 'one'); -INSERT INTO vutestv20 (x, y) VALUES (3, 'three'); -INSERT INTO vutestv21 VALUES (2); -SELECT * FROM vutest1; - a | b ----+------- - 1 | one - 3 | three - 2 | -(3 rows) - -SELECT * FROM vutestv20; - x | y ----+------- - 1 | one - 3 | three - 2 | -(3 rows) - -SELECT * FROM vutestv21; - a ---- - 2 -(1 row) - -UPDATE vutestv20 SET y = 'eins' WHERE x = 1; -UPDATE vutestv21 SET a = 222; -SELECT * FROM vutest1; - a | b ------+------- - 3 | three - 1 | eins - 222 | -(3 rows) - -SELECT * FROM vutestv20; - x | y ------+------- - 3 | three - 1 | eins - 222 | -(3 rows) - -SELECT * FROM vutestv21; - a ------ - 222 -(1 row) - -DELETE FROM vutestv20 WHERE x = 3; -SELECT * FROM vutest1; - a | b ------+------ - 1 | eins - 222 | -(2 rows) - -SELECT * FROM vutestv20; - x | y ------+------ - 1 | eins - 222 | -(2 rows) - -SELECT * FROM vutestv21; - a ------ - 222 -(1 row) - --- insert tests -CREATE TABLE vutest2 (a int PRIMARY KEY, b text NOT NULL, c text NOT NULL DEFAULT 'foo'); -NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "vutest2_pkey" for table "vutest2" -CREATE VIEW vutestv30 AS SELECT a, b, c FROM vutest2; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv31 AS SELECT a, b FROM vutest2; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv32 AS SELECT a, c FROM vutest2; -NOTICE: CREATE VIEW has created automatic view update rules -INSERT INTO vutestv30 VALUES (1, 'one', 'eins'); -INSERT INTO vutestv31 VALUES (2, 'two'); -INSERT INTO vutestv32 VALUES (3, 'drei'); -- fail -ERROR: null value in column "b" violates not-null constraint -UPDATE vutestv31 SET a = 22 WHERE a = 2; -UPDATE vutestv32 SET c = 'drei!' WHERE a = 3; -SELECT rulename, definition FROM pg_rules WHERE tablename LIKE 'vutestv%' ORDER BY tablename, rulename; - rulename | definition -----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv1 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv1 DO INSTEAD INSERT INTO vutest1 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv1 DO INSTEAD UPDATE vutest1 SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING new.a, new.b; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv14 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv14 DO INSTEAD INSERT INTO vutest1 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv14 DO INSTEAD UPDATE vutest1 SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING new.a, new.b; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv2 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv2 DO INSTEAD INSERT INTO vutest1 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv2 DO INSTEAD UPDATE vutest1 SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING new.a, new.b; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv20 DO INSTEAD DELETE FROM vutestv1 WHERE ((((old.x IS NULL) AND (vutestv1.a IS NULL)) OR (old.x = vutestv1.a)) AND (((old.y IS NULL) AND (vutestv1.b IS NULL)) OR (old.y = vutestv1.b))) RETURNING old.x, old.y; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv20 DO INSTEAD INSERT INTO vutestv1 (a, b) VALUES (new.x, new.y) RETURNING new.x AS a, new.y AS b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv20 DO INSTEAD UPDATE vutestv1 SET a = new.x, b = new.y WHERE ((((old.x IS NULL) AND (vutestv1.a IS NULL)) OR (old.x = vutestv1.a)) AND (((old.y IS NULL) AND (vutestv1.b IS NULL)) OR (old.y = vutestv1.b))) RETURNING new.x AS a, new.y AS b; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv21 DO INSTEAD DELETE FROM vutestv20 WHERE ((((old.a IS NULL) AND (vutestv20.x IS NULL)) OR (old.a = vutestv20.x))) RETURNING old.a; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv21 DO INSTEAD INSERT INTO vutestv20 (x) VALUES (new.a) RETURNING new.a AS x; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv21 DO INSTEAD UPDATE vutestv20 SET x = new.a WHERE ((((old.a IS NULL) AND (vutestv20.x IS NULL)) OR (old.a = vutestv20.x))) RETURNING new.a AS x; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv3 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b)) AND (((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a))) RETURNING old.b, old.a; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv3 DO INSTEAD INSERT INTO vutest1 (b, a) VALUES (new.b, new.a) RETURNING new.a AS b, new.b AS a; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv3 DO INSTEAD UPDATE vutest1 SET b = new.b, a = new.a WHERE ((((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b)) AND (((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a))) RETURNING new.a AS b, new.b AS a; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv30 DO INSTEAD DELETE FROM vutest2 WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.b IS NULL) AND (vutest2.b IS NULL)) OR (old.b = vutest2.b) OR (((old.c IS NULL) AND (vutest2.c IS NULL)) OR (old.c = vutest2.c)))) RETURNING old.a, old.b, old.c; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv30 DO INSTEAD INSERT INTO vutest2 (a, b, c) VALUES (new.a, new.b, new.c) RETURNING new.a, new.b, new.c; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv30 DO INSTEAD UPDATE vutest2 SET a = new.a, b = new.b, c = new.c WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.b IS NULL) AND (vutest2.b IS NULL)) OR (old.b = vutest2.b) OR (((old.c IS NULL) AND (vutest2.c IS NULL)) OR (old.c = vutest2.c)))) RETURNING new.a, new.b, new.c; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv31 DO INSTEAD DELETE FROM vutest2 WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.b IS NULL) AND (vutest2.b IS NULL)) OR (old.b = vutest2.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv31 DO INSTEAD INSERT INTO vutest2 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv31 DO INSTEAD UPDATE vutest2 SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.b IS NULL) AND (vutest2.b IS NULL)) OR (old.b = vutest2.b))) RETURNING new.a, new.b; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv32 DO INSTEAD DELETE FROM vutest2 WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.c IS NULL) AND (vutest2.c IS NULL)) OR (old.c = vutest2.c))) RETURNING old.a, old.c; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv32 DO INSTEAD INSERT INTO vutest2 (a, c) VALUES (new.a, new.c) RETURNING new.a, new.c; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv32 DO INSTEAD UPDATE vutest2 SET a = new.a, c = new.c WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.c IS NULL) AND (vutest2.c IS NULL)) OR (old.c = vutest2.c))) RETURNING new.a, new.c; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv4 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv4 DO INSTEAD INSERT INTO vutest1 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv4 DO INSTEAD UPDATE vutest1 SET a = new.a, b = new.b WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING new.a, new.b; -(30 rows) - --- interaction of manual and automatic rules, view replacement -CREATE VIEW vutestv40 AS SELECT a, b FROM vutest1; -NOTICE: CREATE VIEW has created automatic view update rules -CREATE RULE zmy_update AS ON UPDATE TO vutestv40 DO INSTEAD DELETE FROM vutest1; -- drops automatic _UPDATE rule -CREATE RULE "_INSERT" AS ON INSERT TO vutestv40 DO INSTEAD DELETE FROM vutest1; -- replaces automatic _INSERT rule -CREATE RULE zmy_delete AS ON DELETE TO vutestv40 DO ALSO DELETE FROM vutest1; -- leaves automatic _DELETE rule (because of ALSO) -CREATE VIEW vutestv41 AS SELECT a + 1 AS aa, b FROM vutest1; -- not updatable -CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv41 DO INSTEAD UPDATE vutest1 SET a = new.aa - 1, b = new.b WHERE a = old.aa - 1 AND b = old.b; -CREATE OR REPLACE VIEW vutestv41 AS SELECT a AS aa, b FROM vutest1; -- *now* updatable, manual _UPDATE rule stays -WARNING: automatic UPDATE rule not created because manually created UPDATE rule exists -HINT: If you prefer to have the automatic rule, drop the manually created rule and run CREATE OR REPLACE VIEW again. -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv42 AS SELECT a + 1 AS aa, b FROM vutest1; -- not updatable -CREATE RULE zmy_update AS ON UPDATE TO vutestv42 DO INSTEAD UPDATE vutest1 SET a = new.aa - 1, b = new.b WHERE a = old.aa - 1 AND b = old.b; -CREATE OR REPLACE VIEW vutestv42 AS SELECT a AS aa, b FROM vutest1; -- *now* updatable, zmy_update stays, no _UPDATE created -WARNING: automatic UPDATE rule not created because manually created UPDATE rule exists -HINT: If you prefer to have the automatic rule, drop the manually created rule and run CREATE OR REPLACE VIEW again. -NOTICE: CREATE VIEW has created automatic view update rules -CREATE VIEW vutestv43 AS SELECT a AS aa, b FROM vutest1; -- updatable -NOTICE: CREATE VIEW has created automatic view update rules -CREATE RULE zmy_update AS ON UPDATE TO vutestv43 DO INSTEAD DELETE FROM vutest1; -- drops automatic _UPDATE rule -CREATE OR REPLACE VIEW vutestv43 AS SELECT a + 1 AS aa, b FROM vutest1; -- no longer updatable, automatic rules are deleted, manual rules kept -CREATE VIEW vutestv44 AS SELECT a, b FROM vutest1; -- updatable -NOTICE: CREATE VIEW has created automatic view update rules -CREATE RULE zmy_update AS ON UPDATE TO vutestv44 DO INSTEAD DELETE FROM vutest1; -- drops automatic _UPDATE rule -CREATE OR REPLACE VIEW vutestv44 AS SELECT a, b FROM vutest2; -- automatic update rules are updated, manual rules kept -WARNING: automatic UPDATE rule not created because manually created UPDATE rule exists -HINT: If you prefer to have the automatic rule, drop the manually created rule and run CREATE OR REPLACE VIEW again. -NOTICE: CREATE VIEW has created automatic view update rules -SELECT rulename, definition FROM pg_rules WHERE tablename LIKE 'vutestv4_' ORDER BY tablename, rulename; - rulename | definition -------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv40 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.a IS NULL) AND (vutest1.a IS NULL)) OR (old.a = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv40 DO INSTEAD DELETE FROM vutest1; - zmy_delete | CREATE RULE zmy_delete AS ON DELETE TO vutestv40 DO DELETE FROM vutest1; - zmy_update | CREATE RULE zmy_update AS ON UPDATE TO vutestv40 DO INSTEAD DELETE FROM vutest1; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv41 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.aa IS NULL) AND (vutest1.a IS NULL)) OR (old.aa = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.aa, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv41 DO INSTEAD INSERT INTO vutest1 (a, b) VALUES (new.aa, new.b) RETURNING new.aa AS a, new.b; - _UPDATE | CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv41 DO INSTEAD UPDATE vutest1 SET a = (new.aa - 1), b = new.b WHERE ((vutest1.a = (old.aa - 1)) AND (vutest1.b = old.b)); - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv42 DO INSTEAD DELETE FROM vutest1 WHERE ((((old.aa IS NULL) AND (vutest1.a IS NULL)) OR (old.aa = vutest1.a)) AND (((old.b IS NULL) AND (vutest1.b IS NULL)) OR (old.b = vutest1.b))) RETURNING old.aa, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv42 DO INSTEAD INSERT INTO vutest1 (a, b) VALUES (new.aa, new.b) RETURNING new.aa AS a, new.b; - zmy_update | CREATE RULE zmy_update AS ON UPDATE TO vutestv42 DO INSTEAD UPDATE vutest1 SET a = (new.aa - 1), b = new.b WHERE ((vutest1.a = (old.aa - 1)) AND (vutest1.b = old.b)); - zmy_update | CREATE RULE zmy_update AS ON UPDATE TO vutestv43 DO INSTEAD DELETE FROM vutest1; - _DELETE | CREATE RULE "_DELETE" AS ON DELETE TO vutestv44 DO INSTEAD DELETE FROM vutest2 WHERE ((((old.a IS NULL) AND (vutest2.a IS NULL)) OR (old.a = vutest2.a)) AND (((old.b IS NULL) AND (vutest2.b IS NULL)) OR (old.b = vutest2.b))) RETURNING old.a, old.b; - _INSERT | CREATE RULE "_INSERT" AS ON INSERT TO vutestv44 DO INSTEAD INSERT INTO vutest2 (a, b) VALUES (new.a, new.b) RETURNING new.a, new.b; - zmy_update | CREATE RULE zmy_update AS ON UPDATE TO vutestv44 DO INSTEAD DELETE FROM vutest1; -(14 rows) - --- ACL -CREATE USER regressuser1; -CREATE USER regressuser2; -GRANT SELECT, INSERT, UPDATE ON vutest1 TO regressuser1; -SET ROLE regressuser1; -CREATE VIEW vutestv50 AS SELECT a, b FROM vutest1; -NOTICE: CREATE VIEW has created automatic view update rules -GRANT SELECT, UPDATE, DELETE ON vutestv50 TO regressuser2; -SELECT * FROM vutestv50; - a | b ------+------ - 1 | eins - 222 | -(2 rows) - -INSERT INTO vutestv50 VALUES (0, 'zero'); -UPDATE vutestv50 SET a = 1; -UPDATE vutestv50 SET a = 2 WHERE a = 1; -DELETE FROM vutestv50; -- ERROR -ERROR: permission denied for relation vutest1 -RESET ROLE; -SET ROLE regressuser2; -SELECT * FROM vutestv50; - a | b ----+------ - 2 | eins - 2 | - 2 | zero -(3 rows) - -INSERT INTO vutestv50 VALUES (0, 'zero'); -- ERROR -ERROR: permission denied for relation vutestv50 -UPDATE vutestv50 SET a = 1; -UPDATE vutestv50 SET a = 2 WHERE a = 1; -DELETE FROM vutestv50; -- ERROR on vutest1 -ERROR: permission denied for relation vutest1 -RESET ROLE; -DROP VIEW vutestv50; -REVOKE ALL PRIVILEGES ON vutest1 FROM regressuser1; -DROP USER regressuser1, regressuser2; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 300a182cee..7b4425b940 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -1,5 +1,5 @@ # ---------- -# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.54 2009/01/22 17:27:55 petere Exp $ +# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.55 2009/01/27 12:40:15 petere Exp $ # # By convention, we put no more than twenty tests in any one parallel group; # this limits the number of connections needed to run the tests. @@ -79,8 +79,6 @@ test: misc # ---------- test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window -test: view_update - # ---------- # Another group of parallel tests # NB: temp.sql does a reconnect which transiently uses 2 connections, diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 2773cf891f..ba93ea8520 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.51 2009/01/22 17:27:55 petere Exp $ +# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.52 2009/01/27 12:40:15 petere Exp $ # This should probably be in an order similar to parallel_schedule. test: boolean test: char @@ -100,7 +100,6 @@ test: tsearch test: tsdicts test: foreign_data test: window -test: view_update test: plancache test: limit test: plpgsql diff --git a/src/test/regress/sql/view_update.sql b/src/test/regress/sql/view_update.sql deleted file mode 100644 index d6ece26b15..0000000000 --- a/src/test/regress/sql/view_update.sql +++ /dev/null @@ -1,168 +0,0 @@ -CREATE TABLE vutest1 (a integer, b text); -INSERT INTO vutest1 VALUES (1, 'one'); -INSERT INTO vutest1 VALUES (2, 'two'); - - --- simple view updatability conditions - -CREATE VIEW vutestv1 AS SELECT a, b FROM vutest1; -CREATE VIEW vutestv2 AS SELECT * FROM vutest1; -CREATE VIEW vutestv3 AS SELECT b, a FROM vutest1; -CREATE VIEW vutestv4 AS SELECT a, b FROM vutest1 WHERE a < 5; - --- not updatable tests: -CREATE VIEW vutestv5 AS SELECT sum(a) FROM vutest1; -- aggregate function -CREATE VIEW vutestv6 AS SELECT b FROM vutest1 GROUP BY b; -- GROUP BY -CREATE VIEW vutestv7 AS SELECT l.b AS x, r.b AS y FROM vutest1 l, vutest1 r WHERE r.a = l.a; -- JOIN -CREATE VIEW vutestv8 AS SELECT 42; -- no table -CREATE VIEW vutestv9 AS SELECT a * 2 AS x, b || b AS y FROM vutest1; -- derived columns -CREATE VIEW vutestv10 AS SELECT a AS x, a AS y FROM vutest1; -- column referenced more than once -CREATE VIEW vutestv11 AS SELECT * FROM generate_series(1, 5); -- table function -CREATE VIEW vutestv12 AS SELECT xmin, xmax, a, b FROM vutest1; -- system columns -CREATE VIEW vutestv13 AS SELECT DISTINCT a, b FROM vutest1; -- DISTINCT -CREATE VIEW vutestv14 AS SELECT a, b FROM vutest1 WHERE a > (SELECT avg(a) FROM vutest1); -- *is* updatable, but SQL standard disallows this -CREATE VIEW vutestv15 AS SELECT a, b FROM vutest1 UNION ALL SELECT a, b FROM vutest1; -- UNION -CREATE VIEW vutestv16 AS SELECT x, y FROM (SELECT * FROM vutest1) AS foo (x, y); -- subquery ("derived table"); SQL standard allows this -CREATE VIEW vutestv17 AS SELECT a, 5, b FROM vutest1; -- constant -CREATE VIEW vutestv18 AS SELECT a, b FROM vutest1 LIMIT 1; -- LIMIT -CREATE VIEW vutestv19 AS SELECT a, b FROM vutest1 OFFSET 1; -- OFFSET -CREATE VIEW vutestv101 AS SELECT a, rank() OVER (PARTITION BY a ORDER BY b DESC) FROM vutest1; -- window function -CREATE VIEW vutestv102 AS WITH foo AS (SELECT a, b FROM vutest1) SELECT * FROM foo; -- SQL standard allows this -CREATE VIEW vutestv103 AS WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM t) SELECT a FROM vutest1; -- recursive - -INSERT INTO vutestv1 VALUES (3, 'three'); -INSERT INTO vutestv2 VALUES (4, 'four'); -INSERT INTO vutestv3 VALUES (5, 'five'); -- fail -INSERT INTO vutestv3 VALUES ('five', 5); -INSERT INTO vutestv3 (a, b) VALUES (6, 'six'); -INSERT INTO vutestv4 VALUES (7, 'seven'); -- ok, but would be check option issue -INSERT INTO vutestv5 VALUES (8); -- fail - -SELECT * FROM vutest1; -SELECT * FROM vutestv1; -SELECT * FROM vutestv2; -SELECT * FROM vutestv3; -SELECT * FROM vutestv4; -SELECT * FROM vutestv5; - -UPDATE vutestv1 SET b = 'a lot' WHERE a = 7; -DELETE FROM vutestv2 WHERE a = 1; -UPDATE vutestv4 SET b = b || '!' WHERE a > 1; -DELETE FROM vutestv4 WHERE a > 3; -UPDATE vutestv6 SET b = 37; -- fail -DELETE FROM vutestv5; -- fail - -SELECT * FROM vutest1 ORDER BY a, b; -SELECT * FROM vutestv1 ORDER BY a, b; -SELECT * FROM vutestv2 ORDER BY a, b; -SELECT * FROM vutestv4 ORDER BY a, b; - -TRUNCATE TABLE vutest1; - - --- views on views - -CREATE VIEW vutestv20 AS SELECT a AS x, b AS y FROM vutestv1; -CREATE VIEW vutestv21 AS SELECT x AS a FROM vutestv20 WHERE x % 2 = 0; -CREATE VIEW vutestv22 AS SELECT sum(a) FROM vutestv21; -- not updatable -CREATE VIEW vutestv23 AS SELECT * FROM vutestv12; -- not updatable - -INSERT INTO vutestv20 (x, y) VALUES (1, 'one'); -INSERT INTO vutestv20 (x, y) VALUES (3, 'three'); -INSERT INTO vutestv21 VALUES (2); - -SELECT * FROM vutest1; -SELECT * FROM vutestv20; -SELECT * FROM vutestv21; - -UPDATE vutestv20 SET y = 'eins' WHERE x = 1; -UPDATE vutestv21 SET a = 222; - -SELECT * FROM vutest1; -SELECT * FROM vutestv20; -SELECT * FROM vutestv21; - -DELETE FROM vutestv20 WHERE x = 3; - -SELECT * FROM vutest1; -SELECT * FROM vutestv20; -SELECT * FROM vutestv21; - - --- insert tests - -CREATE TABLE vutest2 (a int PRIMARY KEY, b text NOT NULL, c text NOT NULL DEFAULT 'foo'); - -CREATE VIEW vutestv30 AS SELECT a, b, c FROM vutest2; -CREATE VIEW vutestv31 AS SELECT a, b FROM vutest2; -CREATE VIEW vutestv32 AS SELECT a, c FROM vutest2; - -INSERT INTO vutestv30 VALUES (1, 'one', 'eins'); -INSERT INTO vutestv31 VALUES (2, 'two'); -INSERT INTO vutestv32 VALUES (3, 'drei'); -- fail - -UPDATE vutestv31 SET a = 22 WHERE a = 2; -UPDATE vutestv32 SET c = 'drei!' WHERE a = 3; - - -SELECT rulename, definition FROM pg_rules WHERE tablename LIKE 'vutestv%' ORDER BY tablename, rulename; - - --- interaction of manual and automatic rules, view replacement - -CREATE VIEW vutestv40 AS SELECT a, b FROM vutest1; -CREATE RULE zmy_update AS ON UPDATE TO vutestv40 DO INSTEAD DELETE FROM vutest1; -- drops automatic _UPDATE rule -CREATE RULE "_INSERT" AS ON INSERT TO vutestv40 DO INSTEAD DELETE FROM vutest1; -- replaces automatic _INSERT rule -CREATE RULE zmy_delete AS ON DELETE TO vutestv40 DO ALSO DELETE FROM vutest1; -- leaves automatic _DELETE rule (because of ALSO) - -CREATE VIEW vutestv41 AS SELECT a + 1 AS aa, b FROM vutest1; -- not updatable -CREATE RULE "_UPDATE" AS ON UPDATE TO vutestv41 DO INSTEAD UPDATE vutest1 SET a = new.aa - 1, b = new.b WHERE a = old.aa - 1 AND b = old.b; -CREATE OR REPLACE VIEW vutestv41 AS SELECT a AS aa, b FROM vutest1; -- *now* updatable, manual _UPDATE rule stays - -CREATE VIEW vutestv42 AS SELECT a + 1 AS aa, b FROM vutest1; -- not updatable -CREATE RULE zmy_update AS ON UPDATE TO vutestv42 DO INSTEAD UPDATE vutest1 SET a = new.aa - 1, b = new.b WHERE a = old.aa - 1 AND b = old.b; -CREATE OR REPLACE VIEW vutestv42 AS SELECT a AS aa, b FROM vutest1; -- *now* updatable, zmy_update stays, no _UPDATE created - -CREATE VIEW vutestv43 AS SELECT a AS aa, b FROM vutest1; -- updatable -CREATE RULE zmy_update AS ON UPDATE TO vutestv43 DO INSTEAD DELETE FROM vutest1; -- drops automatic _UPDATE rule -CREATE OR REPLACE VIEW vutestv43 AS SELECT a + 1 AS aa, b FROM vutest1; -- no longer updatable, automatic rules are deleted, manual rules kept - -CREATE VIEW vutestv44 AS SELECT a, b FROM vutest1; -- updatable -CREATE RULE zmy_update AS ON UPDATE TO vutestv44 DO INSTEAD DELETE FROM vutest1; -- drops automatic _UPDATE rule -CREATE OR REPLACE VIEW vutestv44 AS SELECT a, b FROM vutest2; -- automatic update rules are updated, manual rules kept - - -SELECT rulename, definition FROM pg_rules WHERE tablename LIKE 'vutestv4_' ORDER BY tablename, rulename; - - --- ACL - -CREATE USER regressuser1; -CREATE USER regressuser2; - -GRANT SELECT, INSERT, UPDATE ON vutest1 TO regressuser1; - -SET ROLE regressuser1; -CREATE VIEW vutestv50 AS SELECT a, b FROM vutest1; - -GRANT SELECT, UPDATE, DELETE ON vutestv50 TO regressuser2; - -SELECT * FROM vutestv50; -INSERT INTO vutestv50 VALUES (0, 'zero'); -UPDATE vutestv50 SET a = 1; -UPDATE vutestv50 SET a = 2 WHERE a = 1; -DELETE FROM vutestv50; -- ERROR -RESET ROLE; - -SET ROLE regressuser2; -SELECT * FROM vutestv50; -INSERT INTO vutestv50 VALUES (0, 'zero'); -- ERROR -UPDATE vutestv50 SET a = 1; -UPDATE vutestv50 SET a = 2 WHERE a = 1; -DELETE FROM vutestv50; -- ERROR on vutest1 -RESET ROLE; - -DROP VIEW vutestv50; - -REVOKE ALL PRIVILEGES ON vutest1 FROM regressuser1; -DROP USER regressuser1, regressuser2;