Here's a combination of all the patches I'm currently waiting
authorBruce Momjian
Fri, 2 Oct 1998 16:28:04 +0000 (16:28 +0000)
committerBruce Momjian
Fri, 2 Oct 1998 16:28:04 +0000 (16:28 +0000)
    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

src/backend/access/index/indexam.c
src/backend/rewrite/locks.c
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/utils/adt/ruleutils.c
src/bin/initdb/initdb.sh
src/include/catalog/pg_proc.h
src/include/rewrite/locks.h
src/test/regress/expected/rules.out
src/test/regress/sql/rules.sql

index f695d71107e3b8061af60dbd7b7b26c069a986b1..aa2d9446504ba74ec6e118c75f7175144e546708 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.27 1998/09/07 05:35:30 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.28 1998/10/02 16:27:43 momjian Exp $
  *
  * INTERFACE ROUTINES
  *     index_open      - open an index relation by relationId
@@ -362,7 +362,7 @@ GetIndexValue(HeapTuple tuple,
              bool *attNull)
 {
    Datum       returnVal;
-   bool        isNull;
+   bool        isNull = FALSE;
 
    if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
    {
@@ -375,13 +375,15 @@ GetIndexValue(HeapTuple tuple,
                                      attrNums[i],
                                      hTupDesc,
                                      attNull);
+           if (*attNull)
+               isNull = TRUE;
        }
        returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo),
                                            FIgetnArgs(fInfo),
                                            (char **) attData,
                                            &isNull);
        pfree(attData);
-       *attNull = FALSE;
+       *attNull = isNull;
    }
    else
        returnVal = heap_getattr(tuple, attrNums[attOff], hTupDesc, attNull);
index f57a436420757b6feb213e76bb19bf58a25f8ccb..5c9b0887a1a5e9fea4f20ac335a4f8dd3a822ad7 100644 (file)
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.13 1998/09/01 04:31:30 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.14 1998/10/02 16:27:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,6 @@
 #include "utils/builtins.h"
 #include "catalog/pg_shadow.h"
 
-static void checkLockPerms(List *locks, Query *parsetree, int rt_index);
 
 /*
  * ThisLockWasTriggered
@@ -170,7 +169,7 @@ matchLocks(CmdType event,
 }
 
 
-static void
+void
 checkLockPerms(List *locks, Query *parsetree, int rt_index)
 {
    Relation    ev_rel;
index 20eff2fbb03c002901824cd7d5a6cafb181d2b73..64d46efa408dc0ac960ec3d0ddf12d9c56094b04 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.21 1998/09/01 04:31:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.22 1998/10/02 16:27:46 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -199,11 +199,8 @@ DefineQueryRewrite(RuleStmt *stmt)
    /* ----------
     * The current rewrite handler is known to work on relation level
     * rules only. And for SELECT events, it expects one non-nothing
-    * action that is instead. Since we now hand out views and rules
-    * to regular users, we must deny anything else.
-    *
-    * I know that I must write a new rewrite handler from scratch
-    * for 6.5 so we can remove these checks and allow all the rules.
+    * action that is instead and returns exactly a tuple of the
+    * rewritten relation. This restricts SELECT rules to views.
     *
     *     Jan
     * ----------
@@ -217,6 +214,9 @@ DefineQueryRewrite(RuleStmt *stmt)
    else
        eslot_string = NULL;
 
+   /*
+    * No rule actions that modify OLD or NEW
+    */
    if (action != NIL)
        foreach(l, action)
    {
@@ -233,23 +233,86 @@ DefineQueryRewrite(RuleStmt *stmt)
        }
    }
 
+   /*
+    * Rules ON SELECT are restricted to view definitions
+    */
    if (event_type == CMD_SELECT)
    {
+       TargetEntry     *tle;
+       Resdom          *resdom;
+       Form_pg_attribute   attr;
+       char            *attname;
+       int         i;
+
+       /*
+        * So there cannot be INSTEAD NOTHING, ...
+        */
        if (length(action) == 0)
        {
            elog(NOTICE, "instead nothing rules on select currently not supported");
            elog(ERROR, " use views instead");
        }
+
+       /*
+        * ... there cannot be multiple actions, ...
+        */
        if (length(action) > 1)
            elog(ERROR, "multiple action rules on select currently not supported");
+       /*
+        * ... the one action must be a SELECT, ...
+        */
        query = (Query *) lfirst(action);
        if (!is_instead || query->commandType != CMD_SELECT)
            elog(ERROR, "only instead-select rules currently supported on select");
+       if (event_qual != NULL)
+           elog(ERROR, "event qualifications not supported for rules on select");
+
+       /*
+        * ... the targetlist of the SELECT action must
+        * exactly match the event relation ...
+        */
+       event_relation = heap_openr(event_obj->relname);
+       if (event_relation == NULL)
+           elog(ERROR, "virtual relations not supported yet");
+
+       if (event_relation->rd_att->natts != length(query->targetList))
+           elog(ERROR, "select rules target list must match event relations structure");
+
+       for (i = 1; i <= event_relation->rd_att->natts; i++) {
+           tle = (TargetEntry *)nth(i - 1, query->targetList);
+           resdom = tle->resdom;
+           attr = event_relation->rd_att->attrs[i - 1];
+           attname = nameout(&(attr->attname));
+
+           if (strcmp(resdom->resname, attname) != 0)
+               elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
+
+           if (attr->atttypid != resdom->restype)
+               elog(ERROR, "select rules target entry %d has different type from attribute %s", i,  attname);
+
+           if (attr->atttypmod != resdom->restypmod)
+               elog(ERROR, "select rules target entry %d has different size from attribute %s", i,  attname);
+       }
+
+       /*
+        * ... and final there must not be another ON SELECT
+        * rule already.
+        */
+       if (event_relation->rd_rules != NULL) {
+           for (i = 0; i < event_relation->rd_rules->numLocks; i++) {
+               RewriteRule *rule;
+
+               rule = event_relation->rd_rules->rules[i];
+               if (rule->event == CMD_SELECT)
+                   elog(ERROR, "%s is already a view", nameout(&(event_relation->rd_rel->relname)));
+           }
+       }
+
+       heap_close(event_relation);
    }
 
    /*
-    * This rule is currently allowed - too restricted I know - but women
-    * and children first Jan
+    * This rule is allowed - install it.
     */
 
    event_relation = heap_openr(event_obj->relname);
index 7a4637e91e502ffd7d7986c33ab60d8a2ae9f878..0bbeeb0c51b2334eea7cbe1be79443b69402a2f7 100644 (file)
@@ -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"
 #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,316 +143,2083 @@ gatherRewriteMeta(Query *parsetree,
    return info;
 }
 
-static List *
-OptimizeRIRRules(List *locks)
-{
-   List       *attr_level = NIL,
-              *i;
-   List       *relation_level = NIL;
-
-   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);
-}
 
 /*
- * idea is to fire regular rules first, then qualified instead
- * rules and unqualified instead rules last. Any lemming is counted for.
+ * rangeTableEntry_used -
+ * we need to process a RTE for RIR rules only if it is
+ * referenced somewhere in var nodes of the query.
  */
-static List *
-orderRules(List *locks)
+static bool
+rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
 {
-   List       *regular = NIL;
-   List       *instead_rules = NIL;
-   List       *instead_qualified = NIL;
-   List       *i;
+   if (node == NULL)
+       return FALSE;
 
-   foreach(i, locks)
-   {
-       RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
+   switch(nodeTag(node)) {
+       case T_TargetEntry:
+           {
+               TargetEntry *tle = (TargetEntry *)node;
 
-       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 rangeTableEntry_used(
+                       (Node *)(tle->expr),
+                       rt_index,
+                       sublevels_up);
+           }
+           break;
 
-static int
-AllRetrieve(List *actions)
-{
-   List       *n;
+       case T_Aggreg:
+           {
+               Aggreg  *agg = (Aggreg *)node;
 
-   foreach(n, actions)
-   {
-       Query      *pt = lfirst(n);
+               return rangeTableEntry_used(
+                       (Node *)(agg->target),
+                       rt_index,
+                       sublevels_up);
+           }
+           break;
 
-       /*
-        * 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;
-}
+       case T_GroupClause:
+           {
+               GroupClause *grp = (GroupClause *)node;
 
-static List *
-FireRetrieveRulesAtQuery(Query *parsetree,
-                        int rt_index,
-                        Relation relation,
-                        bool *instead_flag,
-                        int rule_flag)
-{
-   List       *i,
-              *locks;
-   RuleLock   *rt_entry_locks = NULL;
-   List       *work = NIL;
+               return rangeTableEntry_used(
+                       (Node *)(grp->entry),
+                       rt_index,
+                       sublevels_up);
+           }
+           break;
 
-   if ((rt_entry_locks = relation->rd_rules) == NULL)
-       return NIL;
+       case T_Expr:
+           {
+               Expr    *exp = (Expr *)node;
+
+               return rangeTableEntry_used(
+                       (Node *)(exp->args),
+                       rt_index,
+                       sublevels_up);
+           }
+           break;
 
-   locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
+       case T_Iter:
+           {
+               Iter    *iter = (Iter *)node;
 
-   /* find all retrieve instead */
-   foreach(i, locks)
-   {
-       RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
+               return rangeTableEntry_used(
+                       (Node *)(iter->iterexpr),
+                       rt_index,
+                       sublevels_up);
+           }
+           break;
 
-       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;
+       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;
 
-           relation_level = (rule_lock->attrno == -1);
-           if (rule_lock->actions == NIL)
+       case T_Const:
+           return FALSE;
+
+       case T_List:
            {
-               *instead_flag = TRUE;
-               return NIL;
+               List    *l;
+
+               foreach (l, (List *)node) {
+                   if (rangeTableEntry_used(
+                           (Node *)lfirst(l),
+                           rt_index,
+                           sublevels_up))
+                       return TRUE;
+               }
+               return FALSE;
            }
-           if (!rule_flag &&
-               length(rule_lock->actions) >= 2 &&
-               AllRetrieve(rule_lock->actions))
+           break;
+
+       case T_SubLink:
            {
-               *instead_flag = TRUE;
-               return rule_lock->actions;
+               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;
            }
-           ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
-                             &modified);
-           if (modified)
+           break;
+
+       case T_Query:
            {
-               *instead_flag = TRUE;
-               FixResdomTypes(parsetree->targetList);
-               return lcons(parsetree, NIL);
+               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;
+
+
    }
-   return NIL;
+
+   return FALSE;
 }
 
 
-/* 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
+/*
+ * attribute_used -
+ * Check if a specific attribute number of a RTE is used
+ * somewhere in the query
  */
-static void
-ApplyRetrieveRule(Query *parsetree,
-                 RewriteRule *rule,
-                 int rt_index,
-                 int relation_level,
-                 Relation relation,
-                 int *modified)
+static bool
+attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
 {
-   Query      *rule_action = NULL;
-   Node       *rule_qual;
-   List       *rtable,
-              *rt;
-   int         nothing,
-               rt_length;
-   int         badsql = FALSE;
+   if (node == NULL)
+       return FALSE;
 
-   rule_qual = rule->qual;
-   if (rule->actions)
-   {
-       if (length(rule->actions) > 1)  /* ??? because we don't handle
-                                        * rules with more than one
-                                        * action? -ay */
+   switch(nodeTag(node)) {
+       case T_TargetEntry:
+           {
+               TargetEntry *tle = (TargetEntry *)node;
 
-           /*
-            * 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;
-   }
-   else
-       nothing = TRUE;
+               return attribute_used(
+                       (Node *)(tle->expr),
+                       rt_index,
+                       attno,
+                       sublevels_up);
+           }
+           break;
 
-   rtable = copyObject(parsetree->rtable);
-   foreach(rt, rtable)
-   {
-       RangeTblEntry *rte = lfirst(rt);
+       case T_Aggreg:
+           {
+               Aggreg  *agg = (Aggreg *)node;
 
-       /*
-        * this is to prevent add_missing_vars_to_base_rels() from adding
-        * a bogus entry to the new target list.
-        */
-       rte->inFromCl = false;
-   }
-   rt_length = length(rtable);
+               return attribute_used(
+                       (Node *)(agg->target),
+                       rt_index,
+                       attno,
+                       sublevels_up);
+           }
+           break;
 
-   rtable = nconc(rtable, copyObject(rule_action->rtable));
-   parsetree->rtable = rtable;
+       case T_GroupClause:
+           {
+               GroupClause *grp = (GroupClause *)node;
 
-   rule_action->rtable = rtable;
-   OffsetVarNodes(rule_action->qual, rt_length);
-   OffsetVarNodes((Node *) rule_action->targetList, rt_length);
-   OffsetVarNodes(rule_qual, rt_length);
+               return attribute_used(
+                       (Node *)(grp->entry),
+                       rt_index,
+                       attno,
+                       sublevels_up);
+           }
+           break;
 
-   OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
-   OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
+       case T_Expr:
+           {
+               Expr    *exp = (Expr *)node;
 
-   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);
+               return attribute_used(
+                       (Node *)(exp->args),
+                       rt_index,
+                       attno,
+                       sublevels_up);
+           }
+           break;
 
-   ChangeVarNodes((Node *) rule_action->groupClause,
-                  PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
-   ChangeVarNodes((Node *) rule_action->havingQual,
-                  PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
+       case T_Iter:
+           {
+               Iter    *iter = (Iter *)node;
 
-   if (relation_level)
-   {
-       HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
-                      modified);
-   }
-   else
-   {
-       HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
-                              rt_index, rule->attrno, modified, &badsql);
-   }
-   if (*modified && !badsql)
-   {
-       AddQual(parsetree, rule_action->qual);
+               return attribute_used(
+                       (Node *)(iter->iterexpr),
+                       rt_index,
+                       attno,
+                       sublevels_up);
+           }
+           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);
-   }
-}
+       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;
 
-static List *
-ProcessRetrieveQuery(Query *parsetree,
-                    List *rtable,
-                    bool *instead_flag,
-                    bool rule)
-{
-   List       *rt;
-   List       *product_queries = NIL;
-   int         rt_index = 0;
+       case T_Var:
+           {
+               Var *var = (Var *)node;
 
+               if (var->varlevelsup == sublevels_up)
+                   return var->varno == rt_index;
+               else
+                   return FALSE;
+           }
+           break;
 
-   foreach(rt, rtable)
-   {
-       RangeTblEntry *rt_entry = lfirst(rt);
-       Relation    rt_entry_relation = NULL;
-       List       *result = NIL;
+       case T_Param:
+           return FALSE;
 
-       rt_index++;
-       rt_entry_relation = heap_openr(rt_entry->relname);
+       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;
 
 
-       if (rt_entry_relation->rd_rules != NULL)
-       {
-           result =
-               FireRetrieveRulesAtQuery(parsetree,
-                                        rt_index,
-                                        rt_entry_relation,
-                                        instead_flag,
-                                        rule);
-       }
-       heap_close(rt_entry_relation);
-       if (*instead_flag)
-           return result;
    }
-   if (rule)
-       return NIL;
 
-   rt_index = 0;
+   return FALSE;
+}
 
-   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);
+/*
+ * offset_varnodes -
+ * We need another version of OffsetVarNodes() when processing
+ * RIR rules
+ */
+static void
+offset_varnodes(Node *node, int offset, int sublevels_up)
+{
+   if (node == NULL)
+       return;
+
+   switch(nodeTag(node)) {
+       case T_TargetEntry:
+           {
+               TargetEntry *tle = (TargetEntry *)node;
 
+               offset_varnodes(
+                       (Node *)(tle->expr),
+                       offset,
+                       sublevels_up);
+           }
+           break;
 
-       if (rt_entry_locks)
-       {
-           locks =
-               matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
-       }
-       if (locks != NIL)
-       {
-           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);
+       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;
+
+
+   }
+}
+
+
+/*
+ * change_varnodes -
+ * and another ChangeVarNodes() too
+ */
+static void
+change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up)
+{
+   if (node == NULL)
+       return;
+
+   switch(nodeTag(node)) {
+       case T_TargetEntry:
+           {
+               TargetEntry *tle = (TargetEntry *)node;
+
+               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;
+
+
+   }
+}
+
+
+/*
+ * 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;
+
+       case T_Aggreg:
+           {
+               Aggreg  *agg = (Aggreg *)node;
+
+               modifyAggregUplevel(
+                       (Node *)(agg->target));
+           }
+           break;
+
+       case T_Expr:
+           {
+               Expr    *exp = (Expr *)node;
+
+               modifyAggregUplevel(
+                       (Node *)(exp->args));
+           }
+           break;
+
+       case T_Iter:
+           {
+               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;
+
+
+   }
+}
+
+
+/*
+ * 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("");
+   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,
+                 int relation_level,
+                 Relation relation,
+                 int *modified)
+{
+   Query      *rule_action = NULL;
+   Node       *rule_qual;
+   List       *rtable,
+              *rt;
+   int         nothing,
+               rt_length;
+   int         badsql = FALSE;
+
+   rule_qual = rule->qual;
+   if (rule->actions)
+   {
+       if (length(rule->actions) > 1)  /* ??? because we don't handle
+                                        * rules with more than one
+                                        * action? -ay */
+
+           return;
+       rule_action = copyObject(lfirst(rule->actions));
+       nothing = FALSE;
+   }
+   else
+       nothing = TRUE;
+
+   rtable = copyObject(parsetree->rtable);
+   foreach(rt, rtable)
+   {
+       RangeTblEntry *rte = lfirst(rt);
+
+       /*
+        * this is to prevent add_missing_vars_to_base_rels() from adding
+        * a bogus entry to the new target list.
+        */
+       rte->inFromCl = false;
+   }
+   rt_length = length(rtable);
+
+   rtable = nconc(rtable, copyObject(rule_action->rtable));
+   parsetree->rtable = rtable;
+
+   rule_action->rtable = rtable;
+   offset_varnodes((Node *) rule_qual,   rt_length, 0);
+   offset_varnodes((Node *) rule_action, rt_length, 0);
+
+   change_varnodes((Node *) rule_qual, 
+                  PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
+   change_varnodes((Node *) rule_action,
+                  PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
+
+   if (relation_level)
+   {
+     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);
+   }
+   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;
+
+
+   }
+}
+
+
+/*
+ * fireRIRrules -
+ * Apply all RIR rules on each rangetable entry in a query
+ */
+static Query *
+fireRIRrules(Query *parsetree)
+{
+   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;
+       }
+
+       rules = rel->rd_rules;
+       locks = NIL;
+
+       /*
+        * 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);
+
+       /*
+        * 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(rel);
+   }
+
+   fireRIRonSubselect((Node *) parsetree);
+   modifyAggregQual((Node **) &(parsetree->qual), parsetree);
+
+   return parsetree;
+}
+
+
+/*
+ * 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 (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);
    }
-   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.
@@ -886,103 +2533,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;
+}
+
+
index 15c68064a6a4c2d44ea3368dd658647f061b42c3..87786d9cdd078c654a455e58f996c867890dbb00 100644 (file)
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.18 1998/09/11 16:39:59 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.19 1998/10/02 16:27:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,6 +73,23 @@ OffsetVarNodes(Node *node, int offset)
                OffsetVarNodes((Node *) expr->args, offset);
            }
            break;
+       case T_Iter:
+           {
+               Iter       *iter = (Iter *) node;
+
+               OffsetVarNodes((Node *) iter->iterexpr, offset);
+           }
+           break;
+       case T_ArrayRef:
+           {
+               ArrayRef       *ref = (ArrayRef *) node;
+
+               OffsetVarNodes((Node *) ref->refupperindexpr, offset);
+               OffsetVarNodes((Node *) ref->reflowerindexpr, offset);
+               OffsetVarNodes((Node *) ref->refexpr, offset);
+               OffsetVarNodes((Node *) ref->refassgnexpr, offset);
+           }
+           break;
        case T_Var:
            {
                Var        *var = (Var *) node;
@@ -157,6 +174,23 @@ ChangeVarNodes(Node *node, int old_varno, int new_varno, int sublevels_up)
                ChangeVarNodes((Node *) expr->args, old_varno, new_varno, sublevels_up);
            }
            break;
+       case T_Iter:
+           {
+               Iter       *iter = (Iter *) node;
+
+               ChangeVarNodes((Node *) iter->iterexpr, old_varno, new_varno, sublevels_up);
+           }
+           break;
+       case T_ArrayRef:
+           {
+               ArrayRef       *ref = (ArrayRef *) node;
+
+               ChangeVarNodes((Node *) ref->refupperindexpr, old_varno, new_varno, sublevels_up);
+               ChangeVarNodes((Node *) ref->reflowerindexpr, old_varno, new_varno, sublevels_up);
+               ChangeVarNodes((Node *) ref->refexpr, old_varno, new_varno, sublevels_up);
+               ChangeVarNodes((Node *) ref->refassgnexpr, old_varno, new_varno, sublevels_up);
+           }
+           break;
        case T_Var:
            {
                Var        *var = (Var *) node;
@@ -353,6 +387,20 @@ ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
            ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)),
                       sublevels_up);
            break;
+       case T_Iter:
+           ResolveNew(info, targetlist, (Node **) (&(((Iter *) node)->iterexpr)),
+                      sublevels_up);
+           break;
+       case T_ArrayRef:
+           ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refupperindexpr)),
+                      sublevels_up);
+           ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->reflowerindexpr)),
+                      sublevels_up);
+           ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refexpr)),
+                      sublevels_up);
+           ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refassgnexpr)),
+                      sublevels_up);
+           break;
        case T_Var:
            {
                int         this_varno = (int) ((Var *) node)->varno;
@@ -454,6 +502,38 @@ nodeHandleRIRAttributeRule(Node **nodePtr,
                                           sublevels_up);
            }
            break;
+       case T_Iter:
+           {
+               Iter       *iter = (Iter *) node;
+
+               nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable,
+                                          targetlist, rt_index, attr_num,
+                                          modified, badsql,
+                                          sublevels_up);
+           }
+           break;
+       case T_ArrayRef:
+           {
+               ArrayRef       *ref = (ArrayRef *) node;
+
+               nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable,
+                                          targetlist, rt_index, attr_num,
+                                          modified, badsql,
+                                          sublevels_up);
+               nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable,
+                                          targetlist, rt_index, attr_num,
+                                          modified, badsql,
+                                          sublevels_up);
+               nodeHandleRIRAttributeRule((Node **) (&(ref->refexpr)), rtable,
+                                          targetlist, rt_index, attr_num,
+                                          modified, badsql,
+                                          sublevels_up);
+               nodeHandleRIRAttributeRule((Node **) (&(ref->refassgnexpr)), rtable,
+                                          targetlist, rt_index, attr_num,
+                                          modified, badsql,
+                                          sublevels_up);
+           }
+           break;
        case T_Var:
            {
                int         this_varno = ((Var *) node)->varno;
@@ -598,6 +678,33 @@ nodeHandleViewRule(Node **nodePtr,
                                   rt_index, modified, sublevels_up);
            }
            break;
+       case T_Iter:
+           {
+               Iter       *iter = (Iter *) node;
+
+               nodeHandleViewRule((Node **) (&(iter->iterexpr)),
+                                  rtable, targetlist,
+                                  rt_index, modified, sublevels_up);
+           }
+           break;
+       case T_ArrayRef:
+           {
+               ArrayRef       *ref = (ArrayRef *) node;
+
+               nodeHandleViewRule((Node **) (&(ref->refupperindexpr)),
+                                  rtable, targetlist,
+                                  rt_index, modified, sublevels_up);
+               nodeHandleViewRule((Node **) (&(ref->reflowerindexpr)),
+                                  rtable, targetlist,
+                                  rt_index, modified, sublevels_up);
+               nodeHandleViewRule((Node **) (&(ref->refexpr)),
+                                  rtable, targetlist,
+                                  rt_index, modified, sublevels_up);
+               nodeHandleViewRule((Node **) (&(ref->refassgnexpr)),
+                                  rtable, targetlist,
+                                  rt_index, modified, sublevels_up);
+           }
+           break;
        case T_Var:
            {
                Var        *var = (Var *) node;
index f9774bf62f545c5f90f3c8e1ed19fe60cf2acebf..b3a1db8c069010f503755033b5ddafe2d651d708 100644 (file)
@@ -3,7 +3,7 @@
  *           out of it's tuple
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.3 1998/09/01 04:32:49 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.4 1998/10/02 16:27:51 momjian Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
 #include "utils/lsyscache.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_shadow.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_opclass.h"
 #include "fmgr.h"
 
 
+/* ----------
+ * Local data types
+ * ----------
+ */
+typedef struct QryHier {
+   struct QryHier      *parent;
+   Query           *query;
+} QryHier;
+
+
 /* ----------
  * Global data
  * ----------
@@ -64,6 +77,10 @@ static void *plan_getrule = NULL;
 static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
 static void *plan_getview = NULL;
 static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or rulename = $2";
+static void *plan_getam = NULL;
+static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
+static void *plan_getopclass = NULL;
+static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
 
 
 /* ----------
@@ -72,6 +89,8 @@ static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or ru
  */
 text      *pg_get_ruledef(NameData *rname);
 text      *pg_get_viewdef(NameData *rname);
+text       *pg_get_indexdef(Oid indexrelid);
+NameData   *pg_get_userbyid(int4 uid);
 
 
 /* ----------
@@ -80,15 +99,16 @@ text       *pg_get_viewdef(NameData *rname);
  */
 static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc);
 static char *make_viewdef(HeapTuple ruletup, TupleDesc rulettc);
-static char *get_query_def(Query *query);
-static char *get_select_query_def(Query *query);
-static char *get_insert_query_def(Query *query);
-static char *get_update_query_def(Query *query);
-static char *get_delete_query_def(Query *query);
-static char *get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix);
-static char *get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix);
-static char *get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix);
+static char *get_query_def(Query *query, QryHier *parentqh);
+static char *get_select_query_def(Query *query, QryHier *qh);
+static char *get_insert_query_def(Query *query, QryHier *qh);
+static char *get_update_query_def(Query *query, QryHier *qh);
+static char *get_delete_query_def(Query *query, QryHier *qh);
+static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
+static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix);
+static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix);
 static char *get_const_expr(Const *constval);
+static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
 static char *get_relation_name(Oid relid);
 static char *get_attribute_name(Oid relid, int2 attnum);
 static bool check_if_rte_used(int rt_index, Node *node, int sup);
@@ -288,6 +308,272 @@ pg_get_viewdef(NameData *rname)
 }
 
 
+/* ----------
+ * get_viewdef         - Mainly the same thing, but we
+ *               only return the SELECT part of a view
+ * ----------
+ */
+text       *
+pg_get_indexdef(Oid indexrelid)
+{
+   text        *indexdef;
+   HeapTuple   ht_idx;
+   HeapTuple   ht_idxrel;
+   HeapTuple   ht_indrel;
+   HeapTuple   spi_tup;
+   TupleDesc   spi_ttc;
+   int     spi_fno;
+   Form_pg_index   idxrec;
+   Form_pg_class   idxrelrec;
+   Form_pg_class   indrelrec;
+   Datum       spi_args[1];
+   char        spi_nulls[2];
+   int     spirc;
+   int     len;
+   int     keyno;
+   char        buf[8192];
+   char        keybuf[8192];
+   char        *sep;
+
+   /* ----------
+    * Connect to SPI manager
+    * ----------
+    */
+   if (SPI_connect() != SPI_OK_CONNECT)
+       elog(ERROR, "get_indexdef: cannot connect to SPI manager");
+
+   /* ----------
+    * On the first call prepare the plans to lookup pg_am
+    * and pg_opclass.
+    * ----------
+    */
+   if (plan_getam == NULL)
+   {
+       Oid         argtypes[1];
+       void       *plan;
+
+       argtypes[0] = OIDOID;
+       plan = SPI_prepare(query_getam, 1, argtypes);
+       if (plan == NULL)
+           elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
+       plan_getam = SPI_saveplan(plan);
+
+       argtypes[0] = OIDOID;
+       plan = SPI_prepare(query_getopclass, 1, argtypes);
+       if (plan == NULL)
+           elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
+       plan_getopclass = SPI_saveplan(plan);
+   }
+
+   /* ----------
+    * Fetch the pg_index tuple by the Oid of the index
+    * ----------
+    */
+   ht_idx = SearchSysCacheTuple(INDEXRELID,
+           ObjectIdGetDatum(indexrelid), 0, 0, 0);
+   if (!HeapTupleIsValid(ht_idx))
+       elog(ERROR, "syscache lookup for index %d failed", indexrelid);
+   idxrec = (Form_pg_index)GETSTRUCT(ht_idx);
+
+   /* ----------
+    * Fetch the pg_class tuple of the index relation
+    * ----------
+    */
+   ht_idxrel = SearchSysCacheTuple(RELOID,
+           ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0);
+   if (!HeapTupleIsValid(ht_idxrel))
+       elog(ERROR, "syscache lookup for relid %d failed", idxrec->indexrelid);
+   idxrelrec = (Form_pg_class)GETSTRUCT(ht_idxrel);
+
+   /* ----------
+    * Fetch the pg_class tuple of the indexed relation
+    * ----------
+    */
+   ht_indrel = SearchSysCacheTuple(RELOID,
+           ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0);
+   if (!HeapTupleIsValid(ht_indrel))
+       elog(ERROR, "syscache lookup for relid %d failed", idxrec->indrelid);
+   indrelrec = (Form_pg_class)GETSTRUCT(ht_indrel);
+
+   /* ----------
+    * Get the am name for the index relation
+    * ----------
+    */
+   spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
+   spi_nulls[0] = ' ';
+   spi_nulls[1] = '\0';
+   spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
+   if (spirc != SPI_OK_SELECT)
+       elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
+   if (SPI_processed != 1)
+       elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
+   spi_tup = SPI_tuptable->vals[0];
+   spi_ttc = SPI_tuptable->tupdesc;
+   spi_fno = SPI_fnumber(spi_ttc, "amname");
+
+   /* ----------
+    * Start the index definition
+    * ----------
+    */
+   sprintf(buf, "CREATE %sINDEX %s ON %s USING %s (",
+       idxrec->indisunique ? "UNIQUE " : "",
+       nameout(&(idxrelrec->relname)),
+       nameout(&(indrelrec->relname)),
+       SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+           
+   /* ----------
+    * Collect the indexed attributes
+    * ----------
+    */
+   sep = "";
+   keybuf[0] = '\0';
+   for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
+   {
+       if (idxrec->indkey[keyno] == InvalidAttrNumber)
+           break;
+
+       strcat(keybuf, sep);
+       sep = ", ";
+
+       /* ----------
+        * Add the indexed field name
+        * ----------
+        */
+       if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
+           strcat(keybuf, "oid");
+       else
+           strcat(keybuf, get_attribute_name(idxrec->indrelid,
+                       idxrec->indkey[keyno]));
+
+       /* ----------
+        * If not a functional index, add the operator class name
+        * ----------
+        */
+       if (idxrec->indproc == InvalidOid)
+       {
+           spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
+           spi_nulls[0] = ' ';
+           spi_nulls[1] = '\0';
+           spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
+           if (spirc != SPI_OK_SELECT)
+               elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[keyno]);
+           if (SPI_processed != 1)
+               elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[keyno]);
+           spi_tup = SPI_tuptable->vals[0];
+           spi_ttc = SPI_tuptable->tupdesc;
+           spi_fno = SPI_fnumber(spi_ttc, "opcname");
+           strcat(keybuf, " ");
+           strcat(keybuf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+       }
+   }
+
+   /* ----------
+    * For functional index say 'func (attrs) opclass'
+    * ----------
+    */
+   if (idxrec->indproc != InvalidOid)
+   {
+       HeapTuple   proctup;
+       Form_pg_proc    procStruct;
+
+       proctup = SearchSysCacheTuple(PROOID,
+                               ObjectIdGetDatum(idxrec->indproc), 0, 0, 0);
+       if (!HeapTupleIsValid(proctup))
+           elog(ERROR, "cache lookup for proc %d failed", idxrec->indproc);
+
+       procStruct = (Form_pg_proc) GETSTRUCT(proctup);
+       strcat(buf, nameout(&(procStruct->proname)));
+       strcat(buf, " (");
+       strcat(buf, keybuf);
+       strcat(buf, ") ");
+
+       spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
+       spi_nulls[0] = ' ';
+       spi_nulls[1] = '\0';
+       spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
+       if (spirc != SPI_OK_SELECT)
+           elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[0]);
+       if (SPI_processed != 1)
+           elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[0]);
+       spi_tup = SPI_tuptable->vals[0];
+       spi_ttc = SPI_tuptable->tupdesc;
+       spi_fno = SPI_fnumber(spi_ttc, "opcname");
+       strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+   }
+   else
+   /* ----------
+    * For the others say 'attr opclass [, ...]'
+    * ----------
+    */
+   {
+       strcat(buf, keybuf);
+   }
+
+   /* ----------
+    * Finish
+    * ----------
+    */
+   strcat(buf, ")");
+
+   /* ----------
+    * Create the result in upper executor memory
+    * ----------
+    */
+   len = strlen(buf) + VARHDRSZ;
+   indexdef = SPI_palloc(len);
+   VARSIZE(indexdef) = len;
+   memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
+
+   /* ----------
+    * Disconnect from SPI manager
+    * ----------
+    */
+   if (SPI_finish() != SPI_OK_FINISH)
+       elog(ERROR, "get_viewdef: SPI_finish() failed");
+
+   return indexdef;
+}
+
+
+/* ----------
+ * get_userbyid            - Get a user name by usesysid and
+ *               fallback to 'unknown (UID=n)'
+ * ----------
+ */
+NameData   *
+pg_get_userbyid(int4 uid)
+{
+   HeapTuple   usertup;
+   Form_pg_shadow  user_rec;
+   NameData    *result;
+
+   /* ----------
+    * Allocate space for the result
+    * ----------
+    */
+   result = (NameData *) palloc(NAMEDATALEN);
+   memset(result->data, 0, NAMEDATALEN);
+
+   /* ----------
+    * Get the pg_shadow entry and print the result
+    * ----------
+    */
+   usertup = SearchSysCacheTuple(USESYSID,
+           ObjectIdGetDatum(uid), 0, 0, 0);
+   if (HeapTupleIsValid(usertup))
+   {
+       user_rec = (Form_pg_shadow)GETSTRUCT(usertup);
+       StrNCpy(result->data, (&(user_rec->usename))->data, NAMEDATALEN);
+   }
+   else
+   {
+       sprintf((char *)result, "unknown (UID=%d)", uid);
+   }
+
+   return result;
+}
+
+
 /* ----------
  * make_ruledef            - reconstruct the CREATE RULE command
  *               for a given pg_rewrite tuple
@@ -331,16 +617,13 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
 
    fno = SPI_fnumber(rulettc, "ev_qual");
    ev_qual = SPI_getvalue(ruletup, rulettc, fno);
-   if (isnull)
-       ev_qual = NULL;
 
    fno = SPI_fnumber(rulettc, "ev_action");
    ev_action = SPI_getvalue(ruletup, rulettc, fno);
-   if (isnull)
-       ev_action = NULL;
    if (ev_action != NULL)
        actions = (List *) stringToNode(ev_action);
 
+
    /* ----------
     * Build the rules definition text
     * ----------
@@ -391,12 +674,15 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
    {
        Node       *qual;
        Query      *query;
+       QryHier    qh;
 
        qual = stringToNode(ev_qual);
        query = (Query *) lfirst(actions);
+       qh.parent = NULL;
+       qh.query  = query;
 
        strcat(buf, " WHERE ");
-       strcat(buf, get_rule_expr(query->rtable, 0, qual, TRUE));
+       strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
    }
 
    strcat(buf, " DO ");
@@ -415,7 +701,7 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
        foreach(action, actions)
        {
            query = (Query *) lfirst(action);
-           strcat(buf, get_query_def(query));
+           strcat(buf, get_query_def(query, NULL));
            strcat(buf, "; ");
        }
        strcat(buf, ");");
@@ -431,7 +717,7 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
            Query      *query;
 
            query = (Query *) lfirst(actions);
-           strcat(buf, get_query_def(query));
+           strcat(buf, get_query_def(query, NULL));
            strcat(buf, ";");
        }
    }
@@ -482,13 +768,9 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
 
    fno = SPI_fnumber(rulettc, "ev_qual");
    ev_qual = SPI_getvalue(ruletup, rulettc, fno);
-   if (isnull)
-       ev_qual = "";
 
    fno = SPI_fnumber(rulettc, "ev_action");
    ev_action = SPI_getvalue(ruletup, rulettc, fno);
-   if (isnull)
-       ev_action = NULL;
    if (ev_action != NULL)
        actions = (List *) stringToNode(ev_action);
 
@@ -500,7 +782,7 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
    if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, ""))
        return "Not a view";
 
-   strcpy(buf, get_select_query_def(query));
+   strcpy(buf, get_query_def(query, NULL));
    strcat(buf, ";");
 
    /* ----------
@@ -518,24 +800,29 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
  * ----------
  */
 static char *
-get_query_def(Query *query)
+get_query_def(Query *query, QryHier *parentqh)
 {
+   QryHier     qh;
+
+   qh.parent = parentqh;
+   qh.query  = query;
+
    switch (query->commandType)
    {
            case CMD_SELECT:
-           return get_select_query_def(query);
+           return get_select_query_def(query, &qh);
            break;
 
        case CMD_UPDATE:
-           return get_update_query_def(query);
+           return get_update_query_def(query, &qh);
            break;
 
        case CMD_INSERT:
-           return get_insert_query_def(query);
+           return get_insert_query_def(query, &qh);
            break;
 
        case CMD_DELETE:
-           return get_delete_query_def(query);
+           return get_delete_query_def(query, &qh);
            break;
 
        case CMD_NOTHING:
@@ -557,7 +844,7 @@ get_query_def(Query *query)
  * ----------
  */
 static char *
-get_select_query_def(Query *query)
+get_select_query_def(Query *query, QryHier *qh)
 {
    char        buf[8192];
    char       *sep;
@@ -635,7 +922,7 @@ get_select_query_def(Query *query)
        strcat(buf, sep);
        sep = ", ";
 
-       strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
+       strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
 
        /* Check if we must say AS ... */
        if (nodeTag(tle->expr) != T_Var)
@@ -681,7 +968,7 @@ get_select_query_def(Query *query)
                strcat(buf, sep);
                sep = ", ";
                strcat(buf, rte->relname);
-               if (rt_numused > 1)
+               if (strcmp(rte->relname, rte->refname) != 0)
                {
                    strcat(buf, " ");
                    strcat(buf, rte->refname);
@@ -694,7 +981,7 @@ get_select_query_def(Query *query)
    if (query->qual != NULL)
    {
        strcat(buf, " WHERE ");
-       strcat(buf, get_rule_expr(query->rtable, 0, query->qual, (rt_numused > 1)));
+       strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
    }
 
    /* Add the GROUP BY CLAUSE */
@@ -706,7 +993,7 @@ get_select_query_def(Query *query)
        {
            strcat(buf, sep);
            sep = ", ";
-           strcat(buf, get_rule_expr(query->rtable, 0, lfirst(l), (rt_numused > 1)));
+           strcat(buf, get_rule_expr(qh, 0, lfirst(l), (rt_numused > 1)));
        }
    }
 
@@ -723,7 +1010,7 @@ get_select_query_def(Query *query)
  * ----------
  */
 static char *
-get_insert_query_def(Query *query)
+get_insert_query_def(Query *query, QryHier *qh)
 {
    char        buf[8192];
    char       *sep;
@@ -810,12 +1097,12 @@ get_insert_query_def(Query *query)
 
            strcat(buf, sep);
            sep = ", ";
-           strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
+           strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
        }
        strcat(buf, ")");
    }
    else
-       strcat(buf, get_select_query_def(query));
+       strcat(buf, get_query_def(query, qh));
 
    /* ----------
     * Copy the query string into allocated space and return it
@@ -830,7 +1117,7 @@ get_insert_query_def(Query *query)
  * ----------
  */
 static char *
-get_update_query_def(Query *query)
+get_update_query_def(Query *query, QryHier *qh)
 {
    char        buf[8192];
    char       *sep;
@@ -857,7 +1144,7 @@ get_update_query_def(Query *query)
        sep = ", ";
        strcat(buf, tle->resdom->resname);
        strcat(buf, " = ");
-       strcat(buf, get_tle_expr(query->rtable, query->resultRelation,
+       strcat(buf, get_tle_expr(qh, query->resultRelation,
                                 tle, TRUE));
    }
 
@@ -865,7 +1152,7 @@ get_update_query_def(Query *query)
    if (query->qual != NULL)
    {
        strcat(buf, " WHERE ");
-       strcat(buf, get_rule_expr(query->rtable, query->resultRelation,
+       strcat(buf, get_rule_expr(qh, query->resultRelation,
                                  query->qual, TRUE));
    }
 
@@ -882,7 +1169,7 @@ get_update_query_def(Query *query)
  * ----------
  */
 static char *
-get_delete_query_def(Query *query)
+get_delete_query_def(Query *query, QryHier *qh)
 {
    char        buf[8192];
    RangeTblEntry *rte;
@@ -899,7 +1186,7 @@ get_delete_query_def(Query *query)
    if (query->qual != NULL)
    {
        strcat(buf, " WHERE ");
-       strcat(buf, get_rule_expr(query->rtable, 0, query->qual, FALSE));
+       strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
    }
 
    /* ----------
@@ -915,7 +1202,7 @@ get_delete_query_def(Query *query)
  * ----------
  */
 static char *
-get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
+get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
 {
    char        buf[8192];
 
@@ -936,7 +1223,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
            {
                TargetEntry *tle = (TargetEntry *) node;
 
-               return get_rule_expr(rtable, rt_index,
+               return get_rule_expr(qh, rt_index,
                                     (Node *) (tle->expr), varprefix);
            }
            break;
@@ -947,7 +1234,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
 
                strcat(buf, agg->aggname);
                strcat(buf, "(");
-               strcat(buf, get_rule_expr(rtable, rt_index,
+               strcat(buf, get_rule_expr(qh, rt_index,
                                     (Node *) (agg->target), varprefix));
                strcat(buf, ")");
                return pstrdup(buf);
@@ -958,7 +1245,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
            {
                GroupClause *grp = (GroupClause *) node;
 
-               return get_rule_expr(rtable, rt_index,
+               return get_rule_expr(qh, rt_index,
                                     (Node *) (grp->entry), varprefix);
            }
            break;
@@ -974,13 +1261,13 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
                switch (expr->opType)
                {
                    case OP_EXPR:
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                               (Node *) get_leftop(expr),
                                                  varprefix));
                        strcat(buf, " ");
                        strcat(buf, get_opname(((Oper *) expr->oper)->opno));
                        strcat(buf, " ");
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                              (Node *) get_rightop(expr),
                                                  varprefix));
                        return pstrdup(buf);
@@ -988,11 +1275,11 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
 
                    case OR_EXPR:
                        strcat(buf, "(");
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                               (Node *) get_leftop(expr),
                                                  varprefix));
                        strcat(buf, ") OR (");
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                              (Node *) get_rightop(expr),
                                                  varprefix));
                        strcat(buf, ")");
@@ -1001,11 +1288,11 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
 
                    case AND_EXPR:
                        strcat(buf, "(");
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                               (Node *) get_leftop(expr),
                                                  varprefix));
                        strcat(buf, ") AND (");
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                              (Node *) get_rightop(expr),
                                                  varprefix));
                        strcat(buf, ")");
@@ -1014,7 +1301,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
 
                    case NOT_EXPR:
                        strcat(buf, "NOT (");
-                       strcat(buf, get_rule_expr(rtable, rt_index,
+                       strcat(buf, get_rule_expr(qh, rt_index,
                                               (Node *) get_leftop(expr),
                                                  varprefix));
                        strcat(buf, ")");
@@ -1022,7 +1309,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
                        break;
 
                    case FUNC_EXPR:
-                       return get_func_expr(rtable, rt_index,
+                       return get_func_expr(qh, rt_index,
                                             (Expr *) node,
                                             varprefix);
                        break;
@@ -1037,7 +1324,14 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
        case T_Var:
            {
                Var        *var = (Var *) node;
-               RangeTblEntry *rte = (RangeTblEntry *) nth(var->varno - 1, rtable);
+               RangeTblEntry *rte;
+               int sup = var->varlevelsup;
+
+               while(sup-- > 0) qh = qh->parent;
+               rte = (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable);
+
+               if (qh->parent == NULL && var->varlevelsup > 0)
+                   rte = (RangeTblEntry *) nth(var->varno + 1, qh->query->rtable);
 
                if (!strcmp(rte->refname, "*NEW*"))
                    strcat(buf, "new.");
@@ -1047,7 +1341,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
                        strcat(buf, "current.");
                    else
                    {
-                       if (varprefix && var->varno != rt_index)
+                       if (strcmp(rte->relname, rte->refname) != 0)
                        {
                            strcat(buf, rte->refname);
                            strcat(buf, ".");
@@ -1069,30 +1363,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
 
        case T_SubLink:
            {
-               SubLink    *sublink = (SubLink *) node;
-               Query      *query = (Query *) (sublink->subselect);
-               List       *l;
-               char       *sep;
-
-               if (sublink->lefthand != NULL)
-               {
-                   strcat(buf, "(");
-                   sep = "";
-                   foreach(l, sublink->lefthand)
-                   {
-                       strcat(buf, sep);
-                       sep = ", ";
-                       strcat(buf, get_rule_expr(rtable, rt_index,
-                                                 lfirst(l), varprefix));
-                   }
-                   strcat(buf, ") IN ");
-               }
-
-               strcat(buf, "(");
-               strcat(buf, get_query_def(query));
-               strcat(buf, ")");
-
-               return pstrdup(buf);
+               return get_sublink_expr(qh, rt_index, node, varprefix);
            }
            break;
 
@@ -1116,7 +1387,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
  * ----------
  */
 static char *
-get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
+get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
 {
    char        buf[8192];
    HeapTuple   proctup;
@@ -1143,7 +1414,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
        if (!strcmp(proname, "nullvalue"))
        {
            strcpy(buf, "(");
-           strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
+           strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
                                      varprefix));
            strcat(buf, ") ISNULL");
            return pstrdup(buf);
@@ -1151,7 +1422,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
        if (!strcmp(proname, "nonnullvalue"))
        {
            strcpy(buf, "(");
-           strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
+           strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
                                      varprefix));
            strcat(buf, ") NOTNULL");
            return pstrdup(buf);
@@ -1169,7 +1440,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
    {
        strcat(buf, sep);
        sep = ", ";
-       strcat(buf, get_rule_expr(rtable, rt_index, lfirst(l), varprefix));
+       strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
    }
    strcat(buf, ")");
 
@@ -1194,7 +1465,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
  * ----------
  */
 static char *
-get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
+get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
 {
    HeapTuple   proctup;
    Form_pg_proc procStruct;
@@ -1208,12 +1479,12 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
     * ----------
     */
    if (tle->resdom->restypmod < 0)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
    if (nodeTag(tle->expr) != T_Expr)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
    expr = (Expr *) (tle->expr);
    if (expr->opType != FUNC_EXPR)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
 
    func = (Func *) (expr->oper);
 
@@ -1235,11 +1506,11 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
     * ----------
     */
    if (procStruct->pronargs != 2)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
    if (procStruct->prorettype != procStruct->proargtypes[0])
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
    if (procStruct->proargtypes[1] != INT4OID)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
 
    /* ----------
     * Finally (to be totally safe) the second argument must be a
@@ -1248,15 +1519,15 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
     */
    second_arg = (Const *) nth(1, expr->args);
    if (nodeTag((Node *) second_arg) != T_Const)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
    if ((int4) (second_arg->constvalue) != tle->resdom->restypmod)
-       return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+       return get_rule_expr(qh, rt_index, tle->expr, varprefix);
 
    /* ----------
     * Whow - got it. Now get rid of the padding function
     * ----------
     */
-   return get_rule_expr(rtable, rt_index, lfirst(expr->args), varprefix);
+   return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
 }
 
 
@@ -1274,6 +1545,7 @@ get_const_expr(Const *constval)
    char       *extval;
    bool        isnull = FALSE;
    char        buf[8192];
+   char        namebuf[64];
 
    if (constval->constisnull)
        return "NULL";
@@ -1289,7 +1561,83 @@ get_const_expr(Const *constval)
    extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
                                                    &isnull, -1);
 
-   sprintf(buf, "'%s'::%s", extval, nameout(&(typeStruct->typname)));
+   sprintf(namebuf, "::%s", nameout(&(typeStruct->typname)));
+   if (strcmp(namebuf, "::unknown") == 0)
+       namebuf[0] = '\0';
+   sprintf(buf, "'%s'%s", extval, namebuf);
+   return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_sublink_expr            - Parse back a sublink
+ * ----------
+ */
+static char *
+get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
+{
+   SubLink    *sublink = (SubLink *) node;
+   Query      *query = (Query *) (sublink->subselect);
+   Expr       *expr;
+   List       *l;
+   char       *sep;
+   char       buf[8192];
+
+   buf[0] = '\0';
+
+   if (sublink->lefthand != NULL)
+   {
+       if (length(sublink->lefthand) > 1)
+           strcat(buf, "(");
+
+       sep = "";
+       foreach(l, sublink->lefthand)
+       {
+           strcat(buf, sep);
+           sep = ", ";
+           strcat(buf, get_rule_expr(qh, rt_index,
+                                     lfirst(l), varprefix));
+       }
+
+       if (length(sublink->lefthand) > 1)
+           strcat(buf, ") ");
+       else
+           strcat(buf, " ");
+   }
+
+   switch (sublink->subLinkType) {
+       case EXISTS_SUBLINK:
+           strcat(buf, "EXISTS ");
+           break;
+
+       case ANY_SUBLINK:
+           expr = (Expr *)lfirst(sublink->oper);
+           strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
+           strcat(buf, " ANY ");
+           break;
+
+       case ALL_SUBLINK:
+           expr = (Expr *)lfirst(sublink->oper);
+           strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
+           strcat(buf, " ALL ");
+           break;
+
+       case EXPR_SUBLINK:
+           expr = (Expr *)lfirst(sublink->oper);
+           strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
+           strcat(buf, " ");
+           break;
+
+       default:
+           elog(ERROR, "unupported sublink type %d",
+                   sublink->subLinkType);
+           break;
+   }
+
+   strcat(buf, "(");
+   strcat(buf, get_query_def(query, qh));
+   strcat(buf, ")");
+
    return pstrdup(buf);
 }
 
index 0117885d251dc1e51dced59edab6436e013d339e..1ca47f40e2f899ce485c276ff824e4c155b89102 100644 (file)
@@ -26,7 +26,7 @@
 #
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.55 1998/09/09 18:16:36 momjian Exp $
+#    $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.56 1998/10/02 16:27:53 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -435,6 +435,7 @@ echo "REVOKE ALL on pg_shadow FROM public" | \
 
 echo "Creating view pg_rules"
 echo "CREATE TABLE xpg_rules (     \
+       tablename   name,       \
        rulename    name,       \
        definition  text);" | postgres $PGSQL_OPT template1 > /dev/null
 #move it into pg_rules
@@ -445,12 +446,18 @@ echo "UPDATE pg_type SET typname = 'pg_rules' WHERE typname = 'xpg_rules';" |\
 mv $PGDATA/base/template1/xpg_rules $PGDATA/base/template1/pg_rules
 
 echo "CREATE RULE \"_RETpg_rules\" AS ON SELECT TO pg_rules DO INSTEAD \
-       SELECT rulename, pg_get_ruledef(rulename) AS definition \
-         FROM pg_rewrite;" | postgres $PGSQL_OPT template1 > /dev/null
+       SELECT C.relname AS tablename,              \
+              R.rulename AS rulename,              \
+          pg_get_ruledef(R.rulename) AS definition     \
+         FROM pg_rewrite R, pg_class C                 \
+              WHERE R.rulename !~ '^_RET'              \
+          AND C.oid = R.ev_class;" | \
+   postgres $PGSQL_OPT template1 > /dev/null
 
 echo "Creating view pg_views"
 echo "CREATE TABLE xpg_views (     \
        viewname    name,       \
+       viewowner   name,       \
        definition  text);" | postgres $PGSQL_OPT template1 > /dev/null
 #move it into pg_views
 echo "UPDATE pg_class SET relname = 'pg_views' WHERE relname = 'xpg_views';" |\
@@ -461,11 +468,57 @@ mv $PGDATA/base/template1/xpg_views $PGDATA/base/template1/pg_views
 
 echo "CREATE RULE \"_RETpg_views\" AS ON SELECT TO pg_views DO INSTEAD \
        SELECT relname AS viewname,                 \
+          pg_get_userbyid(relowner) AS viewowner,      \
               pg_get_viewdef(relname) AS definition        \
          FROM pg_class WHERE relhasrules AND           \
               pg_get_viewdef(relname) != 'Not a view';" | \
    postgres $PGSQL_OPT template1 > /dev/null
 
+echo "Creating view pg_tables"
+echo "CREATE TABLE xpg_tables (        \
+       tablename   name,       \
+       tableowner  name,       \
+       hasindexes  bool,       \
+       hasrules    bool,       \
+       hastriggers bool);" | postgres $PGSQL_OPT template1 > /dev/null
+#move it into pg_tables
+echo "UPDATE pg_class SET relname = 'pg_tables' WHERE relname = 'xpg_tables';" |\
+   postgres $PGSQL_OPT template1 > /dev/null
+echo "UPDATE pg_type SET typname = 'pg_tables' WHERE typname = 'xpg_tables';" |\
+   postgres $PGSQL_OPT template1 > /dev/null
+mv $PGDATA/base/template1/xpg_tables $PGDATA/base/template1/pg_tables
+
+echo "CREATE RULE \"_RETpg_tables\" AS ON SELECT TO pg_tables DO INSTEAD   \
+       SELECT relname AS tablename,                \
+          pg_get_userbyid(relowner) AS tableowner,     \
+          relhasindex AS hasindexes,               \
+          relhasrules AS hasrules,             \
+          (reltriggers > 0) AS hastriggers         \
+         FROM pg_class WHERE relkind IN ('r', 's')         \
+              AND pg_get_viewdef(relname) = 'Not a view';" | \
+   postgres $PGSQL_OPT template1 > /dev/null
+
+echo "Creating view pg_indexes"
+echo "CREATE TABLE xpg_indexes (   \
+       tablename   name,       \
+       indexname   name,       \
+       indexdef    text);" | postgres $PGSQL_OPT template1 > /dev/null
+#move it into pg_indexes
+echo "UPDATE pg_class SET relname = 'pg_indexes' WHERE relname = 'xpg_indexes';" |\
+   postgres $PGSQL_OPT template1 > /dev/null
+echo "UPDATE pg_type SET typname = 'pg_indexes' WHERE typname = 'xpg_indexes';" |\
+   postgres $PGSQL_OPT template1 > /dev/null
+mv $PGDATA/base/template1/xpg_indexes $PGDATA/base/template1/pg_indexes
+
+echo "CREATE RULE \"_RETpg_indexes\" AS ON SELECT TO pg_indexes DO INSTEAD \
+       SELECT C.relname AS tablename,              \
+          I.relname AS indexname,              \
+          pg_get_indexdef(X.indexrelid) AS indexdef        \
+         FROM pg_index X, pg_class C, pg_class I           \
+              WHERE C.oid = X.indrelid             \
+              AND I.oid = X.indexrelid;" | \
+   postgres $PGSQL_OPT template1 > /dev/null
+
 echo "Loading pg_description"
 echo "copy pg_description from '$TEMPLATE_DESCR'" | \
    postgres $PGSQL_OPT template1 > /dev/null
index 06cb99d0c15d9e7d21a6f930b40a129d4bd78e7c..1f8ca515837227b5444a887f7f165ec2bba085f1 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.69 1998/09/01 04:35:10 momjian Exp $
+ * $Id: pg_proc.h,v 1.70 1998/10/02 16:27:55 momjian Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -2040,6 +2040,10 @@ DATA(insert OID = 1640 (  pg_get_ruledef    PGUID 11 f t f 1 f 25 "19" 100 0 0 1
 DESCR("source text of a rule");
 DATA(insert OID = 1641 (  pg_get_viewdef      PGUID 11 f t f 1 f 25 "19" 100 0 0 100  foo bar ));
 DESCR("select statement of a view");
+DATA(insert OID = 1642 (  pg_get_userbyid     PGUID 11 f t f 1 f 19 "23" 100 0 0 100  foo bar ));
+DESCR("user name by UID (with fallback)");
+DATA(insert OID = 1643 (  pg_get_indexdef     PGUID 11 f t f 1 f 25 "26" 100 0 0 100  foo bar ));
+DESCR("index description");
 
 /*
  * prototypes for functions pg_proc.c
index b51b02463edb90953dde5325f38455532c19f45a..ad258d9063e214b2c2c886df881b96cf63c3bbc1 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: locks.h,v 1.9 1998/09/01 04:37:57 momjian Exp $
+ * $Id: locks.h,v 1.10 1998/10/02 16:27:58 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,5 +19,6 @@
 
 extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno,
           Query *parsetree);
+extern void checkLockPerms(List *locks, Query *parsetree, int rt_index);
 
 #endif  /* LOCKS_H */
index 447b1499fafee74a68629785891c355a064ce345..f77a532d6e2e7f4762a8e98468cea71051185395 100644 (file)
@@ -671,3 +671,181 @@ QUERY: select * from rtest_nothn3;
 200|OK
 (2 rows)
 
+QUERY: create table rtest_view1 (a int4, b text, v bool);
+QUERY: create table rtest_view2 (a int4);
+QUERY: create table rtest_view3 (a int4, b text);
+QUERY: create table rtest_view4 (a int4, b text, c int4);
+QUERY: 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);
+QUERY: create view rtest_vview2 as select a, b from rtest_view1 where v;
+QUERY: 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);
+QUERY: 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
+   group by X.a, X.b;
+QUERY: create function rtest_viewfunc1(int4) returns int4 as
+   'select count(*) from rtest_view2 where a = $1'
+   language 'sql';
+QUERY: create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
+   from rtest_view1;
+QUERY: insert into rtest_view1 values (1, 'item 1', 't');
+QUERY: insert into rtest_view1 values (2, 'item 2', 't');
+QUERY: insert into rtest_view1 values (3, 'item 3', 't');
+QUERY: insert into rtest_view1 values (4, 'item 4', 'f');
+QUERY: insert into rtest_view1 values (5, 'item 5', 't');
+QUERY: insert into rtest_view1 values (6, 'item 6', 'f');
+QUERY: insert into rtest_view1 values (7, 'item 7', 't');
+QUERY: insert into rtest_view1 values (8, 'item 8', 't');
+QUERY: insert into rtest_view2 values (2);
+QUERY: insert into rtest_view2 values (2);
+QUERY: insert into rtest_view2 values (4);
+QUERY: insert into rtest_view2 values (5);
+QUERY: insert into rtest_view2 values (7);
+QUERY: insert into rtest_view2 values (7);
+QUERY: insert into rtest_view2 values (7);
+QUERY: insert into rtest_view2 values (7);
+QUERY: select * from rtest_vview1;
+a|b     
+-+------
+2|item 2
+4|item 4
+5|item 5
+7|item 7
+(4 rows)
+
+QUERY: select * from rtest_vview2;
+a|b     
+-+------
+1|item 1
+2|item 2
+3|item 3
+5|item 5
+7|item 7
+8|item 8
+(6 rows)
+
+QUERY: select * from rtest_vview3;
+a|b     
+-+------
+2|item 2
+5|item 5
+7|item 7
+(3 rows)
+
+QUERY: select * from rtest_vview4;
+a|b     |refcount
+-+------+--------
+2|item 2|       2
+4|item 4|       1
+5|item 5|       1
+7|item 7|       4
+(4 rows)
+
+QUERY: select * from rtest_vview5;
+a|b     |refcount
+-+------+--------
+1|item 1|       0
+2|item 2|       2
+3|item 3|       0
+4|item 4|       1
+5|item 5|       1
+6|item 6|       0
+7|item 7|       4
+8|item 8|       0
+(8 rows)
+
+QUERY: insert into rtest_view3 select * from rtest_vview1 where a < 7;
+QUERY: select * from rtest_view3;
+a|b     
+-+------
+2|item 2
+4|item 4
+5|item 5
+(3 rows)
+
+QUERY: delete from rtest_view3;
+QUERY: insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
+QUERY: select * from rtest_view3;
+a|b     
+-+------
+1|item 1
+3|item 3
+7|item 7
+8|item 8
+(4 rows)
+
+QUERY: delete from rtest_view3;
+QUERY: insert into rtest_view3 select * from rtest_vview3;
+QUERY: select * from rtest_view3;
+a|b     
+-+------
+2|item 2
+5|item 5
+7|item 7
+(3 rows)
+
+QUERY: delete from rtest_view3;
+QUERY: insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
+QUERY: select * from rtest_view4;
+a|b     |c
+-+------+-
+2|item 2|2
+4|item 4|1
+5|item 5|1
+(3 rows)
+
+QUERY: delete from rtest_view4;
+QUERY: insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
+QUERY: select * from rtest_view4;
+a|b     |c
+-+------+-
+3|item 3|0
+6|item 6|0
+8|item 8|0
+(3 rows)
+
+QUERY: delete from rtest_view4;
+QUERY: create table rtest_comp (
+   part    text,
+   unit    char(4),
+   size    float
+);
+QUERY: create table rtest_unitfact (
+   unit    char(4),
+   factor  float
+);
+QUERY: create view rtest_vcomp as
+   select X.part, (X.size * Y.factor) as size_in_cm
+           from rtest_comp X, rtest_unitfact Y
+           where X.unit = Y.unit;
+QUERY: insert into rtest_unitfact values ('m', 100.0);
+QUERY: insert into rtest_unitfact values ('cm', 1.0);
+QUERY: insert into rtest_unitfact values ('inch', 2.54);
+QUERY: insert into rtest_comp values ('p1', 'm', 5.0);
+QUERY: insert into rtest_comp values ('p2', 'm', 3.0);
+QUERY: insert into rtest_comp values ('p3', 'cm', 5.0);
+QUERY: insert into rtest_comp values ('p4', 'cm', 15.0);
+QUERY: insert into rtest_comp values ('p5', 'inch', 7.0);
+QUERY: insert into rtest_comp values ('p6', 'inch', 4.4);
+QUERY: select * from rtest_vcomp order by part;
+part|size_in_cm
+----+----------
+p1  |       500
+p2  |       300
+p3  |         5
+p4  |        15
+p5  |     17.78
+p6  |    11.176
+(6 rows)
+
+QUERY: select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;
+part|size_in_cm
+----+----------
+p1  |       500
+p2  |       300
+p5  |     17.78
+p4  |        15
+p6  |    11.176
+(5 rows)
+
index 6ca18775863183f15dc396d86196a033290f61bf..8ffefd5be00fa8b22b70f6831c4a87300a209b18 100644 (file)
@@ -404,3 +404,100 @@ insert into rtest_nothn2 select * from rtest_nothn4;
 select * from rtest_nothn2;
 select * from rtest_nothn3;
 
+create table rtest_view1 (a int4, b text, v bool);
+create table rtest_view2 (a int4);
+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);
+create view rtest_vview2 as select a, b from rtest_view1 where v;
+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);
+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
+   group by X.a, X.b;
+create function rtest_viewfunc1(int4) returns int4 as
+   'select count(*) from rtest_view2 where a = $1'
+   language 'sql';
+create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
+   from rtest_view1;
+
+insert into rtest_view1 values (1, 'item 1', 't');
+insert into rtest_view1 values (2, 'item 2', 't');
+insert into rtest_view1 values (3, 'item 3', 't');
+insert into rtest_view1 values (4, 'item 4', 'f');
+insert into rtest_view1 values (5, 'item 5', 't');
+insert into rtest_view1 values (6, 'item 6', 'f');
+insert into rtest_view1 values (7, 'item 7', 't');
+insert into rtest_view1 values (8, 'item 8', 't');
+
+insert into rtest_view2 values (2);
+insert into rtest_view2 values (2);
+insert into rtest_view2 values (4);
+insert into rtest_view2 values (5);
+insert into rtest_view2 values (7);
+insert into rtest_view2 values (7);
+insert into rtest_view2 values (7);
+insert into rtest_view2 values (7);
+
+select * from rtest_vview1;
+select * from rtest_vview2;
+select * from rtest_vview3;
+select * from rtest_vview4;
+select * from rtest_vview5;
+
+insert into rtest_view3 select * from rtest_vview1 where a < 7;
+select * from rtest_view3;
+delete from rtest_view3;
+
+insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
+select * from rtest_view3;
+delete from rtest_view3;
+
+insert into rtest_view3 select * from rtest_vview3;
+select * from rtest_view3;
+delete from rtest_view3;
+
+insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
+select * from rtest_view4;
+delete from rtest_view4;
+
+insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
+select * from rtest_view4;
+delete from rtest_view4;
+--
+-- Test for computations in views
+--
+create table rtest_comp (
+   part    text,
+   unit    char(4),
+   size    float
+);
+
+
+create table rtest_unitfact (
+   unit    char(4),
+   factor  float
+);
+
+create view rtest_vcomp as 
+   select X.part, (X.size * Y.factor) as size_in_cm
+           from rtest_comp X, rtest_unitfact Y
+           where X.unit = Y.unit;
+
+
+insert into rtest_unitfact values ('m', 100.0);
+insert into rtest_unitfact values ('cm', 1.0);
+insert into rtest_unitfact values ('inch', 2.54);
+
+insert into rtest_comp values ('p1', 'm', 5.0);
+insert into rtest_comp values ('p2', 'm', 3.0);
+insert into rtest_comp values ('p3', 'cm', 5.0);
+insert into rtest_comp values ('p4', 'cm', 15.0);
+insert into rtest_comp values ('p5', 'inch', 7.0);
+insert into rtest_comp values ('p6', 'inch', 4.4);
+
+select * from rtest_vcomp order by part;
+
+select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;