From f93b6974f91491a895e875d37b474de48d4b9d8e Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Fri, 2 Oct 1998 16:28:04 +0000
Subject: [PATCH]     Here's a combination of all the patches I'm currently
 waiting     for against a just updated CVS tree. It contains

        Partial new rewrite system that handles subselects,  view
        aggregate  columns, insert into select from view, updates
        with set col = view-value and select rules restriction to
        view definition.

        Updates  for  rule/view  backparsing utility functions to
        handle subselects correct.


        New system views pg_tables and pg_indexes (where you  can
        see the complete index definition in the latter one).

        Enabling array references on query parameters.

        Bugfix for functional index.

        Little changes to system views pg_rules and pg_views.


    The rule system isn't a release-stopper any longer.

    But  another  stopper  is  that  I  don't  know if the latest
    changes to PL/pgSQL (not already in CVS) made it  compile  on
    AIX. Still wait for some response from Dave.

Jan
---
 src/backend/access/index/indexam.c   |    8 +-
 src/backend/rewrite/locks.c          |    5 +-
 src/backend/rewrite/rewriteDefine.c  |   79 +-
 src/backend/rewrite/rewriteHandler.c | 2749 +++++++++++++++++++++-----
 src/backend/rewrite/rewriteManip.c   |  109 +-
 src/backend/utils/adt/ruleutils.c    |  532 ++++-
 src/bin/initdb/initdb.sh             |   59 +-
 src/include/catalog/pg_proc.h        |    6 +-
 src/include/rewrite/locks.h          |    3 +-
 src/test/regress/expected/rules.out  |  178 ++
 src/test/regress/sql/rules.sql       |   97 +
 11 files changed, 3164 insertions(+), 661 deletions(-)

diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index f695d71107e..aa2d9446504 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -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);
diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c
index f57a4364207..5c9b0887a1a 100644
--- a/src/backend/rewrite/locks.c
+++ b/src/backend/rewrite/locks.c
@@ -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;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 20eff2fbb03..64d46efa408 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -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);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 7a4637e91e5..0bbeeb0c51b 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.21 1998/09/01 04:31:33 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.22 1998/10/02 16:27:47 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include "utils/rel.h"
 #include "nodes/pg_list.h"
 #include "nodes/primnodes.h"
+#include "nodes/relation.h"
 
 #include "parser/parsetree.h"	/* for parsetree manipulation */
 #include "parser/parse_relation.h"
@@ -31,20 +32,44 @@
 #include "commands/creatinh.h"
 #include "access/heapam.h"
 
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/acl.h"
 #include "catalog/pg_shadow.h"
+#include "catalog/pg_type.h"
+
+
+
+static RewriteInfo *gatherRewriteMeta(Query *parsetree,
+				  Query *rule_action,
+				  Node *rule_qual,
+				  int rt_index,
+				  CmdType event,
+				  bool *instead_flag);
+static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
+static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up);
+static void offset_varnodes(Node *node, int offset, int sublevels_up);
+static void change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up);
+static void modifyAggregUplevel(Node *node);
+static void modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up);
+static void modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr);
+static SubLink *modifyAggregMakeSublink(Expr *origexp, Query *parsetree);
+static void modifyAggregQual(Node **nodePtr, Query *parsetree);
+
+
+
+
+
+
+
+
+
+
+
+
+
+static Query *fireRIRrules(Query *parsetree);
 
-static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
-				  int rt_index, int relation_level,
-				  Relation relation, int *modified);
-static List *fireRules(Query *parsetree, int rt_index, CmdType event,
-		  bool *instead_flag, List *locks, List **qual_products);
-static void QueryRewriteSubLink(Node *node);
-static List *QueryRewriteOne(Query *parsetree);
-static List *deepRewriteQuery(Query *parsetree);
-static void RewritePreprocessQuery(Query *parsetree);
-static Query *RewritePostprocessNonSelect(Query *parsetree);
 
 /*
  * gatherRewriteMeta -
@@ -118,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("<noname>");
+	resdom->reskey	= 0;
+	resdom->reskeyop = 0;
+	resdom->resjunk	= 0;
+
+	tle->resdom	= resdom;
+	tle->expr	= (Node *)aggreg;
+
+	subqual = copyObject(parsetree->qual);
+	modifyAggregDropQual((Node **)&subqual, (Node *)parsetree->qual, origexp);
+
+	sublink = makeNode(SubLink);
+	sublink->subLinkType	= EXPR_SUBLINK;
+	sublink->useor		= FALSE;
+	sublink->lefthand	= lappend(NIL, copyObject(lfirst(exp->args)));
+	sublink->oper		= lappend(NIL, copyObject(exp));
+	sublink->subselect	= NULL;
+
+	subquery		= makeNode(Query);
+	sublink->subselect	= (Node *)subquery;
+
+	subquery->commandType		= CMD_SELECT;
+	subquery->utilityStmt		= NULL;
+	subquery->resultRelation	= 0;
+	subquery->into			= NULL;
+	subquery->isPortal		= FALSE;
+	subquery->isBinary		= FALSE;
+	subquery->unionall		= FALSE;
+	subquery->uniqueFlag		= NULL;
+	subquery->sortClause		= NULL;
+	subquery->rtable		= lappend(NIL, rte);
+	subquery->targetList		= lappend(NIL, tle);
+	subquery->qual			= subqual;
+	subquery->groupClause		= NIL;
+	subquery->havingQual		= NULL;
+	subquery->hasAggs		= TRUE;
+	subquery->hasSubLinks		= FALSE;
+	subquery->unionClause		= NULL;
+
+
+	modifyAggregUplevel((Node *)sublink);
+
+	modifyAggregChangeVarnodes((Node **)&(sublink->lefthand), target->varno,
+			1, target->varlevelsup);
+	modifyAggregChangeVarnodes((Node **)&(sublink->oper), target->varno,
+			1, target->varlevelsup);
+	modifyAggregChangeVarnodes((Node **)&(sublink->subselect), target->varno,
+			1, target->varlevelsup);
+
+	return sublink;
+}
+
+
+/*
+ * modifyAggregQual -
+ *	Search for qualification expressions that contain aggregate
+ *	functions and substiture them by sublinks. These expressions
+ *	originally come from qualifications that use aggregate columns
+ *	of a view.
+ */
+static void
+modifyAggregQual(Node **nodePtr, Query *parsetree)
+{
+	Node	*node = *nodePtr;
+
+	if (node == NULL)
+		return;
+
+	switch(nodeTag(node)) {
+		case T_Var:
+			break;
+
+		case T_Param:
+			break;
+
+		case T_Const:
+			break;
+
+		case T_GroupClause:
+			{
+				GroupClause	*grp = (GroupClause *)node;
+
+				modifyAggregQual(
+						(Node **)(&(grp->entry)),
+						parsetree);
+			}
+			break;
+
+		case T_Expr:
+			{
+				Expr	*exp = (Expr *)node;
+				SubLink	*sub;
+
+
+				if (length(exp->args) != 2) {
+					modifyAggregQual(
+						(Node **)(&(exp->args)),
+						parsetree);
+					break;
+				}
+
+				if (nodeTag(nth(0, exp->args)) != T_Aggreg &&
+					nodeTag(nth(1, exp->args)) != T_Aggreg) {
+
+					modifyAggregQual(
+						(Node **)(&(exp->args)),
+						parsetree);
+					break;
+				}
+
+				sub = modifyAggregMakeSublink(exp,
+						parsetree);
+
+				*nodePtr = (Node *)sub;
+				parsetree->hasSubLinks = TRUE;
+			}
+			break;
+
+		case T_Iter:
+			{
+				Iter	*iter = (Iter *)node;
+
+				modifyAggregQual(
+						(Node **)(&(iter->iterexpr)),
+						parsetree);
+			}
+			break;
+
+		case T_ArrayRef:
+			{
+				ArrayRef	*ref = (ArrayRef *)node;
+
+				modifyAggregQual(
+						(Node **)(&(ref->refupperindexpr)),
+						parsetree);
+				modifyAggregQual(
+						(Node **)(&(ref->reflowerindexpr)),
+						parsetree);
+				modifyAggregQual(
+						(Node **)(&(ref->refexpr)),
+						parsetree);
+				modifyAggregQual(
+						(Node **)(&(ref->refassgnexpr)),
+						parsetree);
+			}
+			break;
+
+		case T_List:
+			{
+				List	*l;
+
+				foreach (l, (List *)node)
+					modifyAggregQual(
+							(Node **)(&(lfirst(l))),
+							parsetree);
+			}
+			break;
+
+		case T_SubLink:
+			{
+				SubLink	*sub = (SubLink *)node;
+
+				modifyAggregQual(
+						(Node **)(&(sub->subselect)),
+						(Query *)(sub->subselect));
+			}
+			break;
+
+		case T_Query:
+			{
+				Query	*qry = (Query *)node;
+
+				modifyAggregQual(
+						(Node **)(&(qry->qual)),
+						parsetree);
+
+				modifyAggregQual(
+						(Node **)(&(qry->havingQual)),
+						parsetree);
+			}
+			break;
+
+		default:
+			elog(NOTICE, "unknown node tag %d in modifyAggregQual()", nodeTag(node));
+			elog(NOTICE, "Node is: %s", nodeToString(node));
+			break;
+
+
+	}
+}
+
+
+static Node *
+FindMatchingTLEntry(List *tlist, char *e_attname)
+{
+	List	   *i;
+
+	foreach(i, tlist)
+	{
+		TargetEntry *tle = lfirst(i);
+		char	   *resname;
+
+		resname = tle->resdom->resname;
+		if (!strcmp(e_attname, resname))
+			return (tle->expr);
+	}
+	return NULL;
+}
+
+
+static Node *
+make_null(Oid type)
+{
+	Const	   *c = makeNode(Const);
+
+	c->consttype = type;
+	c->constlen = get_typlen(type);
+	c->constvalue = PointerGetDatum(NULL);
+	c->constisnull = true;
+	c->constbyval = get_typbyval(type);
+	return (Node *) c;
+}
+
+
+static void
+apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up)
+{
+	Node	*node = *nodePtr;
+
+	if (node == NULL)
+		return;
+
+	switch(nodeTag(node)) {
+		case T_TargetEntry:
+			{
+				TargetEntry	*tle = (TargetEntry *)node;
+
+				apply_RIR_view(
+						(Node **)(&(tle->expr)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		case T_Aggreg:
+			{
+				Aggreg	*agg = (Aggreg *)node;
+
+				apply_RIR_view(
+						(Node **)(&(agg->target)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		case T_GroupClause:
+			{
+				GroupClause	*grp = (GroupClause *)node;
+
+				apply_RIR_view(
+						(Node **)(&(grp->entry)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		case T_Expr:
+			{
+				Expr	*exp = (Expr *)node;
+
+				apply_RIR_view(
+						(Node **)(&(exp->args)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		case T_Iter:
+			{
+				Iter	*iter = (Iter *)node;
+
+				apply_RIR_view(
+						(Node **)(&(iter->iterexpr)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		case T_ArrayRef:
+			{
+				ArrayRef	*ref = (ArrayRef *)node;
+
+				apply_RIR_view(
+						(Node **)(&(ref->refupperindexpr)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+				apply_RIR_view(
+						(Node **)(&(ref->reflowerindexpr)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+				apply_RIR_view(
+						(Node **)(&(ref->refexpr)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+				apply_RIR_view(
+						(Node **)(&(ref->refassgnexpr)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		case T_Var:
+			{
+				Var	*var = (Var *)node;
+
+				if (var->varlevelsup == sublevels_up &&
+						var->varno == rt_index) {
+					Node		*exp;
+
+					exp = FindMatchingTLEntry(
+							tlist,
+							get_attname(rte->relid,
+								var->varattno));
+
+					if (exp == NULL) {
+						*nodePtr = make_null(var->vartype);
+						return;
+					}
+
+					if (var->varlevelsup > 0 &&
+							nodeTag(exp) == T_Var) {
+						exp = copyObject(exp);
+						((Var *)exp)->varlevelsup = var->varlevelsup;
+					}
+					*nodePtr = exp;
+					*modified = TRUE;
+				}
+			}
+			break;
+
+		case T_Param:
+			break;
+
+		case T_Const:
+			break;
+
+		case T_List:
+			{
+				List	*l;
+
+				foreach (l, (List *)node)
+					apply_RIR_view(
+							(Node **)(&(lfirst(l))),
+							rt_index,
+							rte,
+							tlist,
+							modified,
+							sublevels_up);
+			}
+			break;
+
+		case T_SubLink:
+			{
+				SubLink	*sub = (SubLink *)node;
+
+				apply_RIR_view(
+						(Node **)(&(sub->lefthand)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+
+				apply_RIR_view(
+						(Node **)(&(sub->subselect)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up + 1);
+			}
+			break;
+
+		case T_Query:
+			{
+				Query	*qry = (Query *)node;
+
+				apply_RIR_view(
+						(Node **)(&(qry->targetList)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+
+				apply_RIR_view(
+						(Node **)(&(qry->qual)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+
+				apply_RIR_view(
+						(Node **)(&(qry->havingQual)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+
+				apply_RIR_view(
+						(Node **)(&(qry->groupClause)),
+						rt_index,
+						rte,
+						tlist,
+						modified,
+						sublevels_up);
+			}
+			break;
+
+		default:
+			elog(NOTICE, "unknown node tag %d in apply_RIR_view()", nodeTag(node));
+			elog(NOTICE, "Node is: %s", nodeToString(node));
+			break;
+
+
+	}
+}
+
+
+static void
+ApplyRetrieveRule(Query *parsetree,
+				  RewriteRule *rule,
+				  int rt_index,
+				  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;
+}
+
+
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 15c68064a6a..87786d9cdd0 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -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;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f9774bf62f5..b3a1db8c069 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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.
  *
@@ -52,9 +52,22 @@
 #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);
 }
 
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
index 0117885d251..1ca47f40e2f 100644
--- a/src/bin/initdb/initdb.sh
+++ b/src/bin/initdb/initdb.sh
@@ -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
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 06cb99d0c15..1f8ca515837 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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
diff --git a/src/include/rewrite/locks.h b/src/include/rewrite/locks.h
index b51b02463ed..ad258d9063e 100644
--- a/src/include/rewrite/locks.h
+++ b/src/include/rewrite/locks.h
@@ -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 */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 447b1499faf..f77a532d6e2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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)
+
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 6ca18775863..8ffefd5be00 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -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 >;
-- 
GitLab