diff options
author | Bruce Momjian <bruce@momjian.us> | 1998-10-02 16:28:04 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 1998-10-02 16:28:04 +0000 |
commit | f93b6974f91491a895e875d37b474de48d4b9d8e (patch) | |
tree | c9aa857e0e241d6aa1290b2e49498b4733a8beb9 /src/backend/rewrite/rewriteHandler.c | |
parent | 9b21a18cee705fa972e5b8f8ab106145015bafe7 (diff) | |
download | postgresql-f93b6974f91491a895e875d37b474de48d4b9d8e.tar.gz postgresql-f93b6974f91491a895e875d37b474de48d4b9d8e.zip |
Here's a combination of all the patches I'm currently waiting
for against a just updated CVS tree. It contains
Partial new rewrite system that handles subselects, view
aggregate columns, insert into select from view, updates
with set col = view-value and select rules restriction to
view definition.
Updates for rule/view backparsing utility functions to
handle subselects correct.
New system views pg_tables and pg_indexes (where you can
see the complete index definition in the latter one).
Enabling array references on query parameters.
Bugfix for functional index.
Little changes to system views pg_rules and pg_views.
The rule system isn't a release-stopper any longer.
But another stopper is that I don't know if the latest
changes to PL/pgSQL (not already in CVS) made it compile on
AIX. Still wait for some response from Dave.
Jan
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 2623 |
1 files changed, 2137 insertions, 486 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 7a4637e91e5..0bbeeb0c51b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.21 1998/09/01 04:31:33 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.22 1998/10/02 16:27:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "utils/rel.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" +#include "nodes/relation.h" #include "parser/parsetree.h" /* for parsetree manipulation */ #include "parser/parse_relation.h" @@ -31,20 +32,44 @@ #include "commands/creatinh.h" #include "access/heapam.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/acl.h" #include "catalog/pg_shadow.h" +#include "catalog/pg_type.h" + + + +static RewriteInfo *gatherRewriteMeta(Query *parsetree, + Query *rule_action, + Node *rule_qual, + int rt_index, + CmdType event, + bool *instead_flag); +static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up); +static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up); +static void offset_varnodes(Node *node, int offset, int sublevels_up); +static void change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up); +static void modifyAggregUplevel(Node *node); +static void modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up); +static void modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr); +static SubLink *modifyAggregMakeSublink(Expr *origexp, Query *parsetree); +static void modifyAggregQual(Node **nodePtr, Query *parsetree); + + + + + + + + + + + + + +static Query *fireRIRrules(Query *parsetree); -static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, - int rt_index, int relation_level, - Relation relation, int *modified); -static List *fireRules(Query *parsetree, int rt_index, CmdType event, - bool *instead_flag, List *locks, List **qual_products); -static void QueryRewriteSubLink(Node *node); -static List *QueryRewriteOne(Query *parsetree); -static List *deepRewriteQuery(Query *parsetree); -static void RewritePreprocessQuery(Query *parsetree); -static Query *RewritePostprocessNonSelect(Query *parsetree); /* * gatherRewriteMeta - @@ -118,146 +143,1753 @@ gatherRewriteMeta(Query *parsetree, return info; } -static List * -OptimizeRIRRules(List *locks) + +/* + * rangeTableEntry_used - + * we need to process a RTE for RIR rules only if it is + * referenced somewhere in var nodes of the query. + */ +static bool +rangeTableEntry_used(Node *node, int rt_index, int sublevels_up) { - List *attr_level = NIL, - *i; - List *relation_level = NIL; + if (node == NULL) + return FALSE; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + return rangeTableEntry_used( + (Node *)(tle->expr), + rt_index, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + return rangeTableEntry_used( + (Node *)(agg->target), + rt_index, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + return rangeTableEntry_used( + (Node *)(grp->entry), + rt_index, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + return rangeTableEntry_used( + (Node *)(exp->args), + rt_index, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + return rangeTableEntry_used( + (Node *)(iter->iterexpr), + rt_index, + sublevels_up); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + if (rangeTableEntry_used( + (Node *)(ref->refupperindexpr), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(ref->reflowerindexpr), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(ref->refexpr), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(ref->refassgnexpr), + rt_index, + sublevels_up)) + return TRUE; + + return FALSE; + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + if (var->varlevelsup == sublevels_up) + return var->varno == rt_index; + else + return FALSE; + } + break; + + case T_Param: + return FALSE; + + case T_Const: + return FALSE; + + case T_List: + { + List *l; + + foreach (l, (List *)node) { + if (rangeTableEntry_used( + (Node *)lfirst(l), + rt_index, + sublevels_up)) + return TRUE; + } + return FALSE; + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + if (rangeTableEntry_used( + (Node *)(sub->lefthand), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(sub->subselect), + rt_index, + sublevels_up + 1)) + return TRUE; + + return FALSE; + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + if (rangeTableEntry_used( + (Node *)(qry->targetList), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(qry->qual), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(qry->havingQual), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(qry->groupClause), + rt_index, + sublevels_up)) + return TRUE; + + return FALSE; + } + break; + + default: + elog(NOTICE, "unknown node tag %d in rangeTableEntry_used()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; - foreach(i, locks) - { - RewriteRule *rule_lock = lfirst(i); - if (rule_lock->attrno == -1) - relation_level = lappend(relation_level, rule_lock); - else - attr_level = lappend(attr_level, rule_lock); } - return nconc(relation_level, attr_level); + + return FALSE; } + /* - * idea is to fire regular rules first, then qualified instead - * rules and unqualified instead rules last. Any lemming is counted for. + * attribute_used - + * Check if a specific attribute number of a RTE is used + * somewhere in the query */ -static List * -orderRules(List *locks) +static bool +attribute_used(Node *node, int rt_index, int attno, int sublevels_up) { - List *regular = NIL; - List *instead_rules = NIL; - List *instead_qualified = NIL; - List *i; + if (node == NULL) + return FALSE; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + return attribute_used( + (Node *)(tle->expr), + rt_index, + attno, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + return attribute_used( + (Node *)(agg->target), + rt_index, + attno, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + return attribute_used( + (Node *)(grp->entry), + rt_index, + attno, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + return attribute_used( + (Node *)(exp->args), + rt_index, + attno, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + return attribute_used( + (Node *)(iter->iterexpr), + rt_index, + attno, + sublevels_up); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + if (attribute_used( + (Node *)(ref->refupperindexpr), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(ref->reflowerindexpr), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(ref->refexpr), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(ref->refassgnexpr), + rt_index, + attno, + sublevels_up)) + return TRUE; + + return FALSE; + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + if (var->varlevelsup == sublevels_up) + return var->varno == rt_index; + else + return FALSE; + } + break; + + case T_Param: + return FALSE; + + case T_Const: + return FALSE; + + case T_List: + { + List *l; + + foreach (l, (List *)node) { + if (attribute_used( + (Node *)lfirst(l), + rt_index, + attno, + sublevels_up)) + return TRUE; + } + return FALSE; + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + if (attribute_used( + (Node *)(sub->lefthand), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(sub->subselect), + rt_index, + attno, + sublevels_up + 1)) + return TRUE; + + return FALSE; + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + if (attribute_used( + (Node *)(qry->targetList), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(qry->qual), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(qry->havingQual), + rt_index, + attno, + sublevels_up)) + return TRUE; + + if (attribute_used( + (Node *)(qry->groupClause), + rt_index, + attno, + sublevels_up)) + return TRUE; + + return FALSE; + } + break; + + default: + elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; - foreach(i, locks) - { - RewriteRule *rule_lock = (RewriteRule *) lfirst(i); - if (rule_lock->isInstead) - { - if (rule_lock->qual == NULL) - instead_rules = lappend(instead_rules, rule_lock); - else - instead_qualified = lappend(instead_qualified, rule_lock); - } - else - regular = lappend(regular, rule_lock); } - regular = nconc(regular, instead_qualified); - return nconc(regular, instead_rules); + + return FALSE; } -static int -AllRetrieve(List *actions) + +/* + * offset_varnodes - + * We need another version of OffsetVarNodes() when processing + * RIR rules + */ +static void +offset_varnodes(Node *node, int offset, int sublevels_up) { - List *n; + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + offset_varnodes( + (Node *)(tle->expr), + offset, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + offset_varnodes( + (Node *)(agg->target), + offset, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + offset_varnodes( + (Node *)(grp->entry), + offset, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + offset_varnodes( + (Node *)(exp->args), + offset, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + offset_varnodes( + (Node *)(iter->iterexpr), + offset, + sublevels_up); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + offset_varnodes( + (Node *)(ref->refupperindexpr), + offset, + sublevels_up); + offset_varnodes( + (Node *)(ref->reflowerindexpr), + offset, + sublevels_up); + offset_varnodes( + (Node *)(ref->refexpr), + offset, + sublevels_up); + offset_varnodes( + (Node *)(ref->refassgnexpr), + offset, + sublevels_up); + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + if (var->varlevelsup == sublevels_up) { + var->varno += offset; + var->varnoold += offset; + } + } + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + offset_varnodes( + (Node *)lfirst(l), + offset, + sublevels_up); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + offset_varnodes( + (Node *)(sub->lefthand), + offset, + sublevels_up); + + offset_varnodes( + (Node *)(sub->subselect), + offset, + sublevels_up + 1); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + offset_varnodes( + (Node *)(qry->targetList), + offset, + sublevels_up); + + offset_varnodes( + (Node *)(qry->qual), + offset, + sublevels_up); + + offset_varnodes( + (Node *)(qry->havingQual), + offset, + sublevels_up); + + offset_varnodes( + (Node *)(qry->groupClause), + offset, + sublevels_up); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in offset_varnodes()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; - foreach(n, actions) - { - Query *pt = lfirst(n); - /* - * in the old postgres code, we check whether command_type is a - * consp of '('*'.commandType). but we've never supported - * transitive closures. Hence removed - ay 10/94. - */ - if (pt->commandType != CMD_SELECT) - return false; } - return true; } -static List * -FireRetrieveRulesAtQuery(Query *parsetree, - int rt_index, - Relation relation, - bool *instead_flag, - int rule_flag) + +/* + * change_varnodes - + * and another ChangeVarNodes() too + */ +static void +change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up) { - List *i, - *locks; - RuleLock *rt_entry_locks = NULL; - List *work = NIL; + if (node == NULL) + return; - if ((rt_entry_locks = relation->rd_rules) == NULL) - return NIL; + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; - locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree); + change_varnodes( + (Node *)(tle->expr), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + change_varnodes( + (Node *)(agg->target), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + change_varnodes( + (Node *)(grp->entry), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + change_varnodes( + (Node *)(exp->args), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + change_varnodes( + (Node *)(iter->iterexpr), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + change_varnodes( + (Node *)(ref->refupperindexpr), + rt_index, + new_index, + sublevels_up); + change_varnodes( + (Node *)(ref->reflowerindexpr), + rt_index, + new_index, + sublevels_up); + change_varnodes( + (Node *)(ref->refexpr), + rt_index, + new_index, + sublevels_up); + change_varnodes( + (Node *)(ref->refassgnexpr), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + if (var->varlevelsup == sublevels_up && + var->varno == rt_index) { + var->varno = new_index; + var->varnoold = new_index; + } + } + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + change_varnodes( + (Node *)lfirst(l), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + change_varnodes( + (Node *)(sub->lefthand), + rt_index, + new_index, + sublevels_up); + + change_varnodes( + (Node *)(sub->subselect), + rt_index, + new_index, + sublevels_up + 1); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + change_varnodes( + (Node *)(qry->targetList), + rt_index, + new_index, + sublevels_up); + + change_varnodes( + (Node *)(qry->qual), + rt_index, + new_index, + sublevels_up); + + change_varnodes( + (Node *)(qry->havingQual), + rt_index, + new_index, + sublevels_up); + + change_varnodes( + (Node *)(qry->groupClause), + rt_index, + new_index, + sublevels_up); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in change_varnodes()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; - /* find all retrieve instead */ - foreach(i, locks) - { - RewriteRule *rule_lock = (RewriteRule *) lfirst(i); - if (!rule_lock->isInstead) - continue; - work = lappend(work, rule_lock); } - if (work != NIL) - { - work = OptimizeRIRRules(locks); - foreach(i, work) - { - RewriteRule *rule_lock = lfirst(i); - int relation_level; - int modified = FALSE; +} + + +/* + * modifyAggregUplevel - + * In the newly created sublink for an aggregate column used in + * the qualification, we must adjust the varlevelsup in all the + * var nodes. + */ +static void +modifyAggregUplevel(Node *node) +{ + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + modifyAggregUplevel( + (Node *)(tle->expr)); + } + break; - relation_level = (rule_lock->attrno == -1); - if (rule_lock->actions == NIL) + case T_Aggreg: { - *instead_flag = TRUE; - return NIL; + Aggreg *agg = (Aggreg *)node; + + modifyAggregUplevel( + (Node *)(agg->target)); } - if (!rule_flag && - length(rule_lock->actions) >= 2 && - AllRetrieve(rule_lock->actions)) + break; + + case T_Expr: { - *instead_flag = TRUE; - return rule_lock->actions; + Expr *exp = (Expr *)node; + + modifyAggregUplevel( + (Node *)(exp->args)); } - ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation, - &modified); - if (modified) + break; + + case T_Iter: { - *instead_flag = TRUE; - FixResdomTypes(parsetree->targetList); - return lcons(parsetree, NIL); + Iter *iter = (Iter *)node; + + modifyAggregUplevel( + (Node *)(iter->iterexpr)); } - } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + modifyAggregUplevel( + (Node *)(ref->refupperindexpr)); + modifyAggregUplevel( + (Node *)(ref->reflowerindexpr)); + modifyAggregUplevel( + (Node *)(ref->refexpr)); + modifyAggregUplevel( + (Node *)(ref->refassgnexpr)); + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + var->varlevelsup++; + } + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + modifyAggregUplevel( + (Node *)lfirst(l)); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + modifyAggregUplevel( + (Node *)(sub->lefthand)); + + modifyAggregUplevel( + (Node *)(sub->oper)); + + modifyAggregUplevel( + (Node *)(sub->subselect)); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + modifyAggregUplevel( + (Node *)(qry->targetList)); + + modifyAggregUplevel( + (Node *)(qry->qual)); + + modifyAggregUplevel( + (Node *)(qry->havingQual)); + + modifyAggregUplevel( + (Node *)(qry->groupClause)); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in modifyAggregUplevel()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; + + } - return NIL; } -/* Idea is like this: - * - * retrieve-instead-retrieve rules have different semantics than update nodes - * Separate RIR rules from others. Pass others to FireRules. - * Order RIR rules and process. - * - * side effect: parsetree's rtable field might be changed +/* + * modifyAggregChangeVarnodes - + * Change the var nodes in a sublink created for an aggregate column + * used in the qualification that is subject of the aggregate + * function to point to the correct local RTE. + */ +static void +modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(tle->expr)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(agg->target)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(grp->entry)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(exp->args)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(iter->iterexpr)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(ref->refupperindexpr)), + rt_index, + new_index, + sublevels_up); + modifyAggregChangeVarnodes( + (Node **)(&(ref->reflowerindexpr)), + rt_index, + new_index, + sublevels_up); + modifyAggregChangeVarnodes( + (Node **)(&(ref->refexpr)), + rt_index, + new_index, + sublevels_up); + modifyAggregChangeVarnodes( + (Node **)(&(ref->refassgnexpr)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + if (var->varlevelsup == sublevels_up && + var->varno == rt_index) { + var = copyObject(var); + var->varno = new_index; + var->varnoold = new_index; + var->varlevelsup = 0; + + *nodePtr = (Node *)var; + } + } + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + modifyAggregChangeVarnodes( + (Node **)(&lfirst(l)), + rt_index, + new_index, + sublevels_up); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(sub->lefthand)), + rt_index, + new_index, + sublevels_up); + + modifyAggregChangeVarnodes( + (Node **)(&(sub->oper)), + rt_index, + new_index, + sublevels_up); + + modifyAggregChangeVarnodes( + (Node **)(&(sub->subselect)), + rt_index, + new_index, + sublevels_up + 1); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + modifyAggregChangeVarnodes( + (Node **)(&(qry->targetList)), + rt_index, + new_index, + sublevels_up); + + modifyAggregChangeVarnodes( + (Node **)(&(qry->qual)), + rt_index, + new_index, + sublevels_up); + + modifyAggregChangeVarnodes( + (Node **)(&(qry->havingQual)), + rt_index, + new_index, + sublevels_up); + + modifyAggregChangeVarnodes( + (Node **)(&(qry->groupClause)), + rt_index, + new_index, + sublevels_up); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in modifyAggregChangeVarnodes()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; + + + } +} + + +/* + * modifyAggregDropQual - + * remove the pure aggreg clase from a qualification + */ +static void +modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_Var: + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + Aggreg *oagg = (Aggreg *)orignode; + + modifyAggregDropQual( + (Node **)(&(agg->target)), + (Node *)(oagg->target), + expr); + } + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_GroupClause: + break; + + case T_Expr: + { + Expr *this_expr = (Expr *)node; + Expr *orig_expr = (Expr *)orignode; + + if (orig_expr == expr) { + Const *ctrue; + + if (expr->typeOid != BOOLOID) + elog(ERROR, + "aggregate expression in qualification isn't of type bool"); + ctrue = makeNode(Const); + ctrue->consttype = BOOLOID; + ctrue->constlen = 1; + ctrue->constisnull = FALSE; + ctrue->constvalue = (Datum)TRUE; + ctrue->constbyval = TRUE; + + *nodePtr = (Node *)ctrue; + } + else + modifyAggregDropQual( + (Node **)(&(this_expr->args)), + (Node *)(orig_expr->args), + expr); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + Iter *oiter = (Iter *)orignode; + + modifyAggregDropQual( + (Node **)(&(iter->iterexpr)), + (Node *)(oiter->iterexpr), + expr); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + ArrayRef *oref = (ArrayRef *)orignode; + + modifyAggregDropQual( + (Node **)(&(ref->refupperindexpr)), + (Node *)(oref->refupperindexpr), + expr); + modifyAggregDropQual( + (Node **)(&(ref->reflowerindexpr)), + (Node *)(oref->reflowerindexpr), + expr); + modifyAggregDropQual( + (Node **)(&(ref->refexpr)), + (Node *)(oref->refexpr), + expr); + modifyAggregDropQual( + (Node **)(&(ref->refassgnexpr)), + (Node *)(oref->refassgnexpr), + expr); + } + break; + + case T_List: + { + List *l; + List *ol = (List *)orignode; + int li = 0; + + foreach (l, (List *)node) { + modifyAggregDropQual( + (Node **)(&(lfirst(l))), + (Node *)nth(li, ol), + expr); + li++; + } + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + SubLink *osub = (SubLink *)orignode; + + modifyAggregDropQual( + (Node **)(&(sub->subselect)), + (Node *)(osub->subselect), + expr); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + Query *oqry = (Query *)orignode; + + modifyAggregDropQual( + (Node **)(&(qry->qual)), + (Node *)(oqry->qual), + expr); + + modifyAggregDropQual( + (Node **)(&(qry->havingQual)), + (Node *)(oqry->havingQual), + expr); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in modifyAggregDropQual()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; + + + } +} + + +/* + * modifyAggregMakeSublink - + * Create a sublink node for a qualification expression that + * uses an aggregate column of a view + */ +static SubLink * +modifyAggregMakeSublink(Expr *origexp, Query *parsetree) +{ + SubLink *sublink; + Query *subquery; + Node *subqual; + RangeTblEntry *rte; + Aggreg *aggreg; + Var *target; + TargetEntry *tle; + Resdom *resdom; + Expr *exp = copyObject(origexp); + + if (nodeTag(nth(0, exp->args)) == T_Aggreg) + if (nodeTag(nth(1, exp->args)) == T_Aggreg) + elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported"); + else + elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual"); + + aggreg = (Aggreg *)nth(1, exp->args); + target = (Var *)(aggreg->target); + rte = (RangeTblEntry *)nth(target->varno - 1, parsetree->rtable); + tle = makeNode(TargetEntry); + resdom = makeNode(Resdom); + + aggreg->usenulls = TRUE; + + resdom->resno = 1; + resdom->restype = ((Oper *)(exp->oper))->opresulttype; + resdom->restypmod = -1; + resdom->resname = pstrdup("<noname>"); + resdom->reskey = 0; + resdom->reskeyop = 0; + resdom->resjunk = 0; + + tle->resdom = resdom; + tle->expr = (Node *)aggreg; + + subqual = copyObject(parsetree->qual); + modifyAggregDropQual((Node **)&subqual, (Node *)parsetree->qual, origexp); + + sublink = makeNode(SubLink); + sublink->subLinkType = EXPR_SUBLINK; + sublink->useor = FALSE; + sublink->lefthand = lappend(NIL, copyObject(lfirst(exp->args))); + sublink->oper = lappend(NIL, copyObject(exp)); + sublink->subselect = NULL; + + subquery = makeNode(Query); + sublink->subselect = (Node *)subquery; + + subquery->commandType = CMD_SELECT; + subquery->utilityStmt = NULL; + subquery->resultRelation = 0; + subquery->into = NULL; + subquery->isPortal = FALSE; + subquery->isBinary = FALSE; + subquery->unionall = FALSE; + subquery->uniqueFlag = NULL; + subquery->sortClause = NULL; + subquery->rtable = lappend(NIL, rte); + subquery->targetList = lappend(NIL, tle); + subquery->qual = subqual; + subquery->groupClause = NIL; + subquery->havingQual = NULL; + subquery->hasAggs = TRUE; + subquery->hasSubLinks = FALSE; + subquery->unionClause = NULL; + + + modifyAggregUplevel((Node *)sublink); + + modifyAggregChangeVarnodes((Node **)&(sublink->lefthand), target->varno, + 1, target->varlevelsup); + modifyAggregChangeVarnodes((Node **)&(sublink->oper), target->varno, + 1, target->varlevelsup); + modifyAggregChangeVarnodes((Node **)&(sublink->subselect), target->varno, + 1, target->varlevelsup); + + return sublink; +} + + +/* + * modifyAggregQual - + * Search for qualification expressions that contain aggregate + * functions and substiture them by sublinks. These expressions + * originally come from qualifications that use aggregate columns + * of a view. */ static void +modifyAggregQual(Node **nodePtr, Query *parsetree) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_Var: + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + modifyAggregQual( + (Node **)(&(grp->entry)), + parsetree); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + SubLink *sub; + + + if (length(exp->args) != 2) { + modifyAggregQual( + (Node **)(&(exp->args)), + parsetree); + break; + } + + if (nodeTag(nth(0, exp->args)) != T_Aggreg && + nodeTag(nth(1, exp->args)) != T_Aggreg) { + + modifyAggregQual( + (Node **)(&(exp->args)), + parsetree); + break; + } + + sub = modifyAggregMakeSublink(exp, + parsetree); + + *nodePtr = (Node *)sub; + parsetree->hasSubLinks = TRUE; + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + modifyAggregQual( + (Node **)(&(iter->iterexpr)), + parsetree); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + modifyAggregQual( + (Node **)(&(ref->refupperindexpr)), + parsetree); + modifyAggregQual( + (Node **)(&(ref->reflowerindexpr)), + parsetree); + modifyAggregQual( + (Node **)(&(ref->refexpr)), + parsetree); + modifyAggregQual( + (Node **)(&(ref->refassgnexpr)), + parsetree); + } + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + modifyAggregQual( + (Node **)(&(lfirst(l))), + parsetree); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + modifyAggregQual( + (Node **)(&(sub->subselect)), + (Query *)(sub->subselect)); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + modifyAggregQual( + (Node **)(&(qry->qual)), + parsetree); + + modifyAggregQual( + (Node **)(&(qry->havingQual)), + parsetree); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in modifyAggregQual()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; + + + } +} + + +static Node * +FindMatchingTLEntry(List *tlist, char *e_attname) +{ + List *i; + + foreach(i, tlist) + { + TargetEntry *tle = lfirst(i); + char *resname; + + resname = tle->resdom->resname; + if (!strcmp(e_attname, resname)) + return (tle->expr); + } + return NULL; +} + + +static Node * +make_null(Oid type) +{ + Const *c = makeNode(Const); + + c->consttype = type; + c->constlen = get_typlen(type); + c->constvalue = PointerGetDatum(NULL); + c->constisnull = true; + c->constbyval = get_typbyval(type); + return (Node *) c; +} + + +static void +apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + apply_RIR_view( + (Node **)(&(tle->expr)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + apply_RIR_view( + (Node **)(&(agg->target)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + apply_RIR_view( + (Node **)(&(grp->entry)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + apply_RIR_view( + (Node **)(&(exp->args)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + apply_RIR_view( + (Node **)(&(iter->iterexpr)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + apply_RIR_view( + (Node **)(&(ref->refupperindexpr)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + apply_RIR_view( + (Node **)(&(ref->reflowerindexpr)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + apply_RIR_view( + (Node **)(&(ref->refexpr)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + apply_RIR_view( + (Node **)(&(ref->refassgnexpr)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_Var: + { + Var *var = (Var *)node; + + if (var->varlevelsup == sublevels_up && + var->varno == rt_index) { + Node *exp; + + exp = FindMatchingTLEntry( + tlist, + get_attname(rte->relid, + var->varattno)); + + if (exp == NULL) { + *nodePtr = make_null(var->vartype); + return; + } + + if (var->varlevelsup > 0 && + nodeTag(exp) == T_Var) { + exp = copyObject(exp); + ((Var *)exp)->varlevelsup = var->varlevelsup; + } + *nodePtr = exp; + *modified = TRUE; + } + } + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + apply_RIR_view( + (Node **)(&(lfirst(l))), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + + apply_RIR_view( + (Node **)(&(sub->lefthand)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + + apply_RIR_view( + (Node **)(&(sub->subselect)), + rt_index, + rte, + tlist, + modified, + sublevels_up + 1); + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + apply_RIR_view( + (Node **)(&(qry->targetList)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + + apply_RIR_view( + (Node **)(&(qry->qual)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + + apply_RIR_view( + (Node **)(&(qry->havingQual)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + + apply_RIR_view( + (Node **)(&(qry->groupClause)), + rt_index, + rte, + tlist, + modified, + sublevels_up); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in apply_RIR_view()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; + + + } +} + + +static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, int rt_index, @@ -280,12 +1912,6 @@ ApplyRetrieveRule(Query *parsetree, * rules with more than one * action? -ay */ - /* - * WARNING!!! If we sometimes handle rules with more than one - * action, the view acl checks might get broken. - * viewAclOverride should only become true (below) if this is - * a relation_level, instead, select query - Jan - */ return; rule_action = copyObject(lfirst(rule->actions)); nothing = FALSE; @@ -310,124 +1936,290 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = rtable; rule_action->rtable = rtable; - OffsetVarNodes(rule_action->qual, rt_length); - OffsetVarNodes((Node *) rule_action->targetList, rt_length); - OffsetVarNodes(rule_qual, rt_length); - - OffsetVarNodes((Node *) rule_action->groupClause, rt_length); - OffsetVarNodes((Node *) rule_action->havingQual, rt_length); - - ChangeVarNodes(rule_action->qual, - PRS2_CURRENT_VARNO + rt_length, rt_index, 0); - ChangeVarNodes((Node *) rule_action->targetList, - PRS2_CURRENT_VARNO + rt_length, rt_index, 0); - ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0); + offset_varnodes((Node *) rule_qual, rt_length, 0); + offset_varnodes((Node *) rule_action, rt_length, 0); - ChangeVarNodes((Node *) rule_action->groupClause, + change_varnodes((Node *) rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0); - ChangeVarNodes((Node *) rule_action->havingQual, + change_varnodes((Node *) rule_action, PRS2_CURRENT_VARNO + rt_length, rt_index, 0); if (relation_level) { - HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index, - modified); + apply_RIR_view((Node **) &parsetree, rt_index, + (RangeTblEntry *)nth(rt_index - 1, rtable), + rule_action->targetList, modified, 0); + apply_RIR_view((Node **) &rule_action, rt_index, + (RangeTblEntry *)nth(rt_index - 1, rtable), + rule_action->targetList, modified, 0); } else { - HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList, - rt_index, rule->attrno, modified, &badsql); + HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList, + rt_index, rule->attrno, modified, &badsql); } - if (*modified && !badsql) - { - AddQual(parsetree, rule_action->qual); + if (*modified && !badsql) { + AddQual(parsetree, rule_action->qual); + /* This will only work if the query made to the view defined by the following + * groupClause groups by the same attributes or does not use group at all! */ + if (parsetree->groupClause == NULL) + parsetree->groupClause=rule_action->groupClause; + AddHavingQual(parsetree, rule_action->havingQual); + parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs); + parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks); + } +} + + +static void +fireRIRonSubselect(Node *node) +{ + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + fireRIRonSubselect( + (Node *)(tle->expr)); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + fireRIRonSubselect( + (Node *)(agg->target)); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + fireRIRonSubselect( + (Node *)(grp->entry)); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + fireRIRonSubselect( + (Node *)(exp->args)); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + fireRIRonSubselect( + (Node *)(iter->iterexpr)); + } + break; + + case T_ArrayRef: + { + ArrayRef *ref = (ArrayRef *)node; + + fireRIRonSubselect( + (Node *)(ref->refupperindexpr)); + fireRIRonSubselect( + (Node *)(ref->reflowerindexpr)); + fireRIRonSubselect( + (Node *)(ref->refexpr)); + fireRIRonSubselect( + (Node *)(ref->refassgnexpr)); + } + break; + + case T_Var: + break; + + case T_Param: + break; + + case T_Const: + break; + + case T_List: + { + List *l; + + foreach (l, (List *)node) + fireRIRonSubselect( + (Node *)(lfirst(l))); + } + break; + + case T_SubLink: + { + SubLink *sub = (SubLink *)node; + Query *qry; + + fireRIRonSubselect( + (Node *)(sub->lefthand)); + + qry = fireRIRrules((Query *)(sub->subselect)); + + fireRIRonSubselect( + (Node *)qry); + + sub->subselect = (Node *) qry; + } + break; + + case T_Query: + { + Query *qry = (Query *)node; + + fireRIRonSubselect( + (Node *)(qry->targetList)); + + fireRIRonSubselect( + (Node *)(qry->qual)); + + fireRIRonSubselect( + (Node *)(qry->havingQual)); + + fireRIRonSubselect( + (Node *)(qry->groupClause)); + } + break; + + default: + elog(NOTICE, "unknown node tag %d in fireRIRonSubselect()", nodeTag(node)); + elog(NOTICE, "Node is: %s", nodeToString(node)); + break; + - /* - * This will only work if the query made to the view defined by - * the following groupClause groups by the same attributes or does - * not use group at all! - */ - if (parsetree->groupClause == NULL) - parsetree->groupClause = rule_action->groupClause; - AddHavingQual(parsetree, rule_action->havingQual); - parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs); - parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks); } } -static List * -ProcessRetrieveQuery(Query *parsetree, - List *rtable, - bool *instead_flag, - bool rule) + +/* + * fireRIRrules - + * Apply all RIR rules on each rangetable entry in a query + */ +static Query * +fireRIRrules(Query *parsetree) { - List *rt; - List *product_queries = NIL; - int rt_index = 0; + int rt_index; + RangeTblEntry *rte; + Relation rel; + List *locks; + RuleLock *rules; + RewriteRule *rule; + RewriteRule RIRonly; + int modified; + int i; + List *l; + + rt_index = 0; + while(rt_index < length(parsetree->rtable)) { + ++rt_index; + if (!rangeTableEntry_used((Node *)parsetree, rt_index, 0)) + continue; + + rte = nth(rt_index - 1, parsetree->rtable); + rel = heap_openr(rte->relname); + if (rel->rd_rules == NULL) { + heap_close(rel); + continue; + } - foreach(rt, rtable) - { - RangeTblEntry *rt_entry = lfirst(rt); - Relation rt_entry_relation = NULL; - List *result = NIL; + rules = rel->rd_rules; + locks = NIL; - rt_index++; - rt_entry_relation = heap_openr(rt_entry->relname); + /* + * Collect the RIR rules that we must apply + */ + for (i = 0; i < rules->numLocks; i++) { + rule = rules->rules[i]; + if (rule->event != CMD_SELECT) + continue; + + if (rule->attrno > 0 && + !attribute_used((Node *)parsetree, + rt_index, + rule->attrno, 0)) + continue; + locks = lappend(locks, rule); + } + /* + * Check permissions + */ + checkLockPerms(locks, parsetree, rt_index); - if (rt_entry_relation->rd_rules != NULL) - { - result = - FireRetrieveRulesAtQuery(parsetree, - rt_index, - rt_entry_relation, - instead_flag, - rule); + /* + * Now apply them + */ + foreach (l, locks) { + rule = lfirst(l); + + RIRonly.event = rule->event; + RIRonly.attrno = rule->attrno; + RIRonly.qual = rule->qual; + RIRonly.actions = rule->actions; + + ApplyRetrieveRule(parsetree, + &RIRonly, + rt_index, + RIRonly.attrno == -1, + rel, + &modified); } - heap_close(rt_entry_relation); - if (*instead_flag) - return result; + + heap_close(rel); } - if (rule) - return NIL; - rt_index = 0; + fireRIRonSubselect((Node *) parsetree); + modifyAggregQual((Node **) &(parsetree->qual), parsetree); + + return parsetree; +} - foreach(rt, rtable) - { - RangeTblEntry *rt_entry = lfirst(rt); - Relation rt_entry_relation = NULL; - RuleLock *rt_entry_locks = NULL; - List *result = NIL; - List *locks = NIL; - List *dummy_products; - rt_index++; - rt_entry_relation = heap_openr(rt_entry->relname); - rt_entry_locks = rt_entry_relation->rd_rules; - heap_close(rt_entry_relation); +/* + * idea is to fire regular rules first, then qualified instead + * rules and unqualified instead rules last. Any lemming is counted for. + */ +static List * +orderRules(List *locks) +{ + List *regular = NIL; + List *instead_rules = NIL; + List *instead_qualified = NIL; + List *i; + foreach(i, locks) + { + RewriteRule *rule_lock = (RewriteRule *) lfirst(i); - if (rt_entry_locks) - { - locks = - matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree); - } - if (locks != NIL) + if (rule_lock->isInstead) { - result = fireRules(parsetree, rt_index, CMD_SELECT, - instead_flag, locks, &dummy_products); - if (*instead_flag) - return lappend(NIL, result); - if (result != NIL) - product_queries = nconc(product_queries, result); + if (rule_lock->qual == NULL) + instead_rules = lappend(instead_rules, rule_lock); + else + instead_qualified = lappend(instead_qualified, rule_lock); } + else + regular = lappend(regular, rule_lock); } - return product_queries; + regular = nconc(regular, instead_qualified); + return nconc(regular, instead_rules); } + + static Query * CopyAndAddQual(Query *parsetree, List *actions, @@ -462,6 +2254,7 @@ CopyAndAddQual(Query *parsetree, } + /* * fireRules - * Iterate through rule locks applying rules. @@ -488,13 +2281,7 @@ fireRules(Query *parsetree, /* choose rule to fire from list of rules */ if (locks == NIL) { - ProcessRetrieveQuery(parsetree, - parsetree->rtable, - instead_flag, TRUE); - if (*instead_flag) - return lappend(NIL, parsetree); - else - return NIL; + return NIL; } locks = orderRules(locks); /* real instead rules last */ @@ -505,7 +2292,6 @@ fireRules(Query *parsetree, *event_qual; List *actions; List *r; - bool orig_instead_flag = *instead_flag; /* * Instead rules change the resultRelation of the query. So the @@ -645,8 +2431,10 @@ fireRules(Query *parsetree, *-------------------------------------------------- */ info->rule_action->rtable = info->rt; + /* ProcessRetrieveQuery(info->rule_action, info->rt, &orig_instead_flag, TRUE); + */ /*-------------------------------------------------- * Step 4 @@ -670,128 +2458,32 @@ fireRules(Query *parsetree, return results; } -/* ---------- - * RewritePreprocessQuery - - * adjust details in the parsetree, the rule system - * depends on - * ---------- - */ -static void -RewritePreprocessQuery(Query *parsetree) -{ - /* ---------- - * if the query has a resultRelation, reassign the - * result domain numbers to the attribute numbers in the - * target relation. FixNew() depends on it when replacing - * *new* references in a rule action by the expressions - * from the rewritten query. - * ---------- - */ - if (parsetree->resultRelation > 0) - { - RangeTblEntry *rte; - Relation rd; - List *tl; - TargetEntry *tle; - int resdomno; - - rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1, - parsetree->rtable); - rd = heap_openr(rte->relname); - - foreach(tl, parsetree->targetList) - { - tle = (TargetEntry *) lfirst(tl); - resdomno = attnameAttNum(rd, tle->resdom->resname); - tle->resdom->resno = resdomno; - } - - heap_close(rd); - } -} - - -/* ---------- - * RewritePostprocessNonSelect - - * apply instead select rules on a query fired in by - * the rewrite system - * ---------- - */ -static Query * -RewritePostprocessNonSelect(Query *parsetree) -{ - List *rt; - int rt_index = 0; - Query *newtree = copyObject(parsetree); - - foreach(rt, parsetree->rtable) - { - RangeTblEntry *rt_entry = lfirst(rt); - Relation rt_entry_relation = NULL; - RuleLock *rt_entry_locks = NULL; - List *locks = NIL; - List *instead_locks = NIL; - List *lock; - RewriteRule *rule; - - rt_index++; - rt_entry_relation = heap_openr(rt_entry->relname); - rt_entry_locks = rt_entry_relation->rd_rules; - - if (rt_entry_locks) - { - int origcmdtype = newtree->commandType; - - newtree->commandType = CMD_SELECT; - locks = - matchLocks(CMD_SELECT, rt_entry_locks, rt_index, newtree); - newtree->commandType = origcmdtype; - } - if (locks != NIL) - { - foreach(lock, locks) - { - rule = (RewriteRule *) lfirst(lock); - if (rule->isInstead) - instead_locks = nconc(instead_locks, lock); - } - } - if (instead_locks != NIL) - { - foreach(lock, instead_locks) - { - int relation_level; - int modified = 0; - - rule = (RewriteRule *) lfirst(lock); - relation_level = (rule->attrno == -1); - - ApplyRetrieveRule(newtree, - rule, - rt_index, - relation_level, - rt_entry_relation, - &modified); - } - } - - heap_close(rt_entry_relation); - } - return newtree; -} static List * RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products) { CmdType event; - List *product_queries = NIL; - int result_relation = 0; + List *product_queries = NIL; + int result_relation = 0; + RangeTblEntry *rt_entry; + Relation rt_entry_relation = NULL; + RuleLock *rt_entry_locks = NULL; Assert(parsetree != NULL); event = parsetree->commandType; + /* + * SELECT rules are handled later when we have all the + * queries that should get executed + */ + if (event == CMD_SELECT) + return NIL; + + /* + * Utilities aren't rewritten at all - why is this here? + */ if (event == CMD_UTILITY) return NIL; @@ -803,79 +2495,34 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products) result_relation = parsetree->resultRelation; - if (event != CMD_SELECT) - { - - /* - * the statement is an update, insert or delete - */ - RangeTblEntry *rt_entry; - Relation rt_entry_relation = NULL; - RuleLock *rt_entry_locks = NULL; - - rt_entry = rt_fetch(result_relation, parsetree->rtable); - rt_entry_relation = heap_openr(rt_entry->relname); - rt_entry_locks = rt_entry_relation->rd_rules; - heap_close(rt_entry_relation); - - if (rt_entry_locks != NULL) - { - List *locks = - matchLocks(event, rt_entry_locks, result_relation, parsetree); - - product_queries = - fireRules(parsetree, - result_relation, - event, - instead_flag, - locks, - qual_products); - } - - /* ---------- - * deepRewriteQuery does not handle the situation - * where a query fired by a rule uses relations that - * have instead select rules defined (views and the like). - * So we care for them here. - * ---------- - */ - if (product_queries != NIL) - { - List *pq; - Query *tmp; - List *new_products = NIL; - - foreach(pq, product_queries) - { - tmp = (Query *) lfirst(pq); - tmp = RewritePostprocessNonSelect(tmp); - new_products = lappend(new_products, tmp); - } - product_queries = new_products; - } + /* + * the statement is an update, insert or delete - fire rules + * on it. + */ + rt_entry = rt_fetch(result_relation, parsetree->rtable); + rt_entry_relation = heap_openr(rt_entry->relname); + rt_entry_locks = rt_entry_relation->rd_rules; + heap_close(rt_entry_relation); - return product_queries; - } - else + if (rt_entry_locks != NULL) { + List *locks = + matchLocks(event, rt_entry_locks, result_relation, parsetree); + + product_queries = + fireRules(parsetree, + result_relation, + event, + instead_flag, + locks, + qual_products); + } - /* - * the statement is a select - */ - Query *other; - - /* - * ApplyRetrieveRule changes the range table XXX Unions are copied - * again. - */ - other = copyObject(parsetree); + return product_queries; - return - ProcessRetrieveQuery(other, parsetree->rtable, - instead_flag, FALSE); - } } + /* * to avoid infinite recursion, we restrict the number of times a query * can be rewritten. Detecting cycles is left for the reader as an excercise. @@ -887,103 +2534,6 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products) static int numQueryRewriteInvoked = 0; /* - * QueryRewrite - - * rewrite one query via QueryRewrite system, possibly returning 0, or many - * queries - */ -List * -QueryRewrite(Query *parsetree) -{ - RewritePreprocessQuery(parsetree); - - QueryRewriteSubLink(parsetree->qual); - QueryRewriteSubLink(parsetree->havingQual); - - return QueryRewriteOne(parsetree); -} - -/* - * QueryRewriteSubLink - * - * This rewrites the SubLink subqueries first, doing the lowest ones first. - * We already have code in the main rewrite loops to process correlated - * variables from upper queries that exist in subqueries. - */ -static void -QueryRewriteSubLink(Node *node) -{ - if (node == NULL) - return; - - switch (nodeTag(node)) - { - case T_TargetEntry: - break; - case T_Aggreg: - break; - case T_Expr: - { - Expr *expr = (Expr *) node; - - QueryRewriteSubLink((Node *) expr->args); - } - break; - case T_Var: - break; - case T_List: - { - List *l; - - foreach(l, (List *) node) - QueryRewriteSubLink(lfirst(l)); - } - break; - case T_SubLink: - { - SubLink *sublink = (SubLink *) node; - Query *query = (Query *) sublink->subselect; - List *ret; - - /* - * Nest down first. We do this so if a rewrite adds a - * SubLink we don't process it as part of this loop. - */ - QueryRewriteSubLink((Node *) query->qual); - - QueryRewriteSubLink((Node *) query->havingQual); - - ret = QueryRewriteOne(query); - if (!ret) - sublink->subselect = NULL; - else if (lnext(ret) == NIL) - sublink->subselect = lfirst(ret); - else - elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries."); - } - break; - default: - /* ignore the others */ - break; - } - return; -} - -/* - * QueryOneRewrite - - * rewrite one query - */ -static List * -QueryRewriteOne(Query *parsetree) -{ - numQueryRewriteInvoked = 0; - - /* - * take a deep breath and apply all the rewrite rules - ay - */ - return deepRewriteQuery(parsetree); -} - -/* * deepRewriteQuery - * rewrites the query and apply the rules again on the queries rewritten */ @@ -1040,3 +2590,104 @@ deepRewriteQuery(Query *parsetree) return rewritten; } + + +/* + * QueryOneRewrite - + * rewrite one query + */ +static List * +QueryRewriteOne(Query *parsetree) +{ + numQueryRewriteInvoked = 0; + + /* + * take a deep breath and apply all the rewrite rules - ay + */ + return deepRewriteQuery(parsetree); +} + + +/* ---------- + * RewritePreprocessQuery - + * adjust details in the parsetree, the rule system + * depends on + * ---------- + */ +static void +RewritePreprocessQuery(Query *parsetree) +{ + /* ---------- + * if the query has a resultRelation, reassign the + * result domain numbers to the attribute numbers in the + * target relation. FixNew() depends on it when replacing + * *new* references in a rule action by the expressions + * from the rewritten query. + * ---------- + */ + if (parsetree->resultRelation > 0) + { + RangeTblEntry *rte; + Relation rd; + List *tl; + TargetEntry *tle; + int resdomno; + + rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1, + parsetree->rtable); + rd = heap_openr(rte->relname); + + foreach(tl, parsetree->targetList) + { + tle = (TargetEntry *) lfirst(tl); + resdomno = attnameAttNum(rd, tle->resdom->resname); + tle->resdom->resno = resdomno; + } + + heap_close(rd); + } +} + + +/* + * QueryRewrite - + * rewrite one query via query rewrite system, possibly returning 0 + * or many queries + */ +List * +QueryRewrite(Query *parsetree) +{ + List *querylist; + List *results = NIL; + List *l; + Query *query; + + /* + * Step 1 + * + * There still seems something broken with the resdom numbers + * so we reassign them first. + */ + RewritePreprocessQuery(parsetree); + + /* + * Step 2 + * + * Apply all non-SELECT rules possibly getting 0 or many queries + */ + querylist = QueryRewriteOne(parsetree); + + /* + * Step 3 + * + * Apply all the RIR rules on each query + */ + foreach (l, querylist) { + query = (Query *)lfirst(l); + results = lappend(results, fireRIRrules(query)); + } + + return results; +} + + |