From b122e16a1c5030562e4a20f69e7fa2c08cc02658 Mon Sep 17 00:00:00 2001
From: Jan Wieck <JanWieck@Yahoo.com>
Date: Tue, 25 May 1999 13:16:10 +0000
Subject: [PATCH] Bugfix - Range table entries that are unused after rewriting
 should not be marked inFromCl any longer. Otherwise the planner gets confused
 and joins over them where in fact it does not have to.

Adjust hasSubLinks now with a recursive lookup - could be wrong in
multi action rules because parse state isn't reset correctly and all
actions in the rule are marked hasSubLinks if one of them has.

Jan
---
 src/backend/rewrite/rewriteHandler.c | 142 ++++++++++++++++++++++++++-
 1 file changed, 140 insertions(+), 2 deletions(-)

diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 26626d0bb8a..7769d722992 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.43 1999/05/17 18:22:19 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.44 1999/05/25 13:16:10 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1393,6 +1393,129 @@ checkQueryHasAggs(Node *node)
 }
 
 
+/*
+ * checkQueryHasSubLink -
+ *	Queries marked hasAggs might not have them any longer after
+ *	rewriting. Check it.
+ */
+static bool
+checkQueryHasSubLink(Node *node)
+{
+	if (node == NULL)
+		return FALSE;
+
+	switch(nodeTag(node)) {
+		case T_TargetEntry:
+			{
+				TargetEntry	*tle = (TargetEntry *)node;
+
+				return checkQueryHasSubLink((Node *)(tle->expr));
+			}
+			break;
+
+		case T_Aggref:
+			return TRUE;
+
+		case T_Expr:
+			{
+				Expr	*exp = (Expr *)node;
+
+				return checkQueryHasSubLink((Node *)(exp->args));
+			}
+			break;
+
+		case T_Iter:
+			{
+				Iter	*iter = (Iter *)node;
+
+				return checkQueryHasSubLink((Node *)(iter->iterexpr));
+			}
+			break;
+
+		case T_ArrayRef:
+			{
+				ArrayRef	*ref = (ArrayRef *)node;
+
+				if (checkQueryHasSubLink((Node *)(ref->refupperindexpr)))
+					return TRUE;
+				
+				if (checkQueryHasSubLink((Node *)(ref->reflowerindexpr)))
+					return TRUE;
+				
+				if (checkQueryHasSubLink((Node *)(ref->refexpr)))
+					return TRUE;
+				
+				if (checkQueryHasSubLink((Node *)(ref->refassgnexpr)))
+					return TRUE;
+				
+				return FALSE;
+			}
+			break;
+
+		case T_Var:
+			return FALSE;
+
+		case T_Param:
+			return FALSE;
+
+		case T_Const:
+			return FALSE;
+
+		case T_List:
+			{
+				List	*l;
+
+				foreach (l, (List *)node) {
+					if (checkQueryHasSubLink((Node *)lfirst(l)))
+						return TRUE;
+				}
+				return FALSE;
+			}
+			break;
+
+		case T_CaseExpr:
+			{
+				CaseExpr	*exp = (CaseExpr *)node;
+
+				if (checkQueryHasSubLink((Node *)(exp->args)))
+					return TRUE;
+
+				if (checkQueryHasSubLink((Node *)(exp->defresult)))
+					return TRUE;
+
+				return FALSE;
+			}
+			break;
+
+		case T_CaseWhen:
+			{
+				CaseWhen	*when = (CaseWhen *)node;
+
+				if (checkQueryHasSubLink((Node *)(when->expr)))
+					return TRUE;
+
+				if (checkQueryHasSubLink((Node *)(when->result)))
+					return TRUE;
+
+				return FALSE;
+			}
+			break;
+
+		case T_SubLink:
+			return TRUE;
+
+		default:
+			elog(NOTICE, "unknown node tag %d in checkQueryHasSubLink()", nodeTag(node));
+			elog(NOTICE, "Node is: %s", nodeToString(node));
+			break;
+
+
+	}
+
+	return FALSE;
+}
+
+
 static Node *
 FindMatchingTLEntry(List *tlist, char *e_attname)
 {
@@ -2116,10 +2239,23 @@ fireRIRrules(Query *parsetree)
 	while(rt_index < length(parsetree->rtable)) {
 		++rt_index;
 
+		rte = nth(rt_index - 1, parsetree->rtable);
+
 		if (!rangeTableEntry_used((Node *)parsetree, rt_index, 0))
+		{
+			/*
+			 * Unused range table entries must not be marked as coming
+			 * from a clause. Otherwise the planner will generate
+			 * joins over relations that in fact shouldn't be scanned
+			 * at all and the result will contain duplicates
+			 *
+			 * Jan
+			 *
+			 */
+			rte->inFromCl = FALSE;
 			continue;
+		}
 		
-		rte = nth(rt_index - 1, parsetree->rtable);
 		rel = heap_openr(rte->relname);
 		if (rel->rd_rules == NULL) {
 			heap_close(rel);
@@ -2705,6 +2841,8 @@ BasicQueryRewrite(Query *parsetree)
 		if (query->hasAggs)
 			query->hasAggs = checkQueryHasAggs((Node *)(query->targetList))
 						   | checkQueryHasAggs((Node *)(query->havingQual));
+		query->hasSubLinks = checkQueryHasSubLink((Node *)(query->qual))
+						   | checkQueryHasSubLink((Node *)(query->havingQual));
 		results = lappend(results, query);
 	}
 	return results;
-- 
GitLab