[teiid-commits] teiid SVN: r2785 - in trunk: engine/src/main/java/org/teiid/query/optimizer/relational and 7 other directories.

teiid-commits at lists.jboss.org teiid-commits at lists.jboss.org
Fri Dec 17 12:48:18 EST 2010


Author: shawkins
Date: 2010-12-17 12:48:17 -0500 (Fri, 17 Dec 2010)
New Revision: 2785

Added:
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/PartitionAnalyzer.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleDecomposeJoin.java
Modified:
   trunk/build/kits/jboss-container/teiid-releasenotes.html
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleConstants.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanUnions.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java
   trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java
   trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java
   trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java
   trunk/engine/src/main/resources/org/teiid/query/i18n.properties
   trunk/engine/src/test/java/org/teiid/query/optimizer/TestUnionPlanning.java
Log:
TEIID-1376 adding support for partition wise joins

Modified: trunk/build/kits/jboss-container/teiid-releasenotes.html
===================================================================
--- trunk/build/kits/jboss-container/teiid-releasenotes.html	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/build/kits/jboss-container/teiid-releasenotes.html	2010-12-17 17:48:17 UTC (rev 2785)
@@ -34,7 +34,9 @@
 	    </UL>                     
 	<LI><B>UDF</B> - new API objects added to teiid-api to support user defined functions that are capable of pushdown to source.
 	<LI><B>Unescape Function</B> - a new system function, unescape, was added to handle common \ escaping in strings including octal, hex/unicode, \b, \r, \n, \f, and \t.
-	<LI><B>Predicate Optimization<B> - expanded optimizations for detecting always false conditions when using IS NULL, IN, or comparison predicates with literal values.
+	<LI><B>Predicate Optimization</B> - expanded optimizations for detecting always false conditions when using IS NULL, IN, or comparison predicates with literal values.
+	<LI><B>Partition-wise Joins</B> - Views defined by partitioned unions (union alls where each branch has a projected literal or an IN predicate that makes its values mutually exclusive from the other branches) can 
+	be joined against other partitioned unions and the optimizer will reorganize the join of unions into a union of joins.
 </UL>
 
 <h2><a name="Compatibility">Compatibility Issues</a></h2>

Added: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/PartitionAnalyzer.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/PartitionAnalyzer.java	                        (rev 0)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/PartitionAnalyzer.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -0,0 +1,152 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package org.teiid.query.optimizer.relational;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.teiid.query.sql.lang.Criteria;
+import org.teiid.query.sql.lang.Query;
+import org.teiid.query.sql.lang.QueryCommand;
+import org.teiid.query.sql.lang.SetCriteria;
+import org.teiid.query.sql.lang.SetQuery;
+import org.teiid.query.sql.lang.SetQuery.Operation;
+import org.teiid.query.sql.symbol.Constant;
+import org.teiid.query.sql.symbol.ElementSymbol;
+import org.teiid.query.sql.symbol.Expression;
+import org.teiid.query.sql.symbol.SingleElementSymbol;
+import org.teiid.query.sql.util.SymbolMap;
+
+/**
+ * TODO: support recursive detection of partitions
+ * 
+ * Extracts a map of partitioning information from a union
+ */
+public class PartitionAnalyzer {
+	
+	public static Map<ElementSymbol, List<Set<Constant>>> extractPartionInfo(SetQuery setQuery, List<ElementSymbol> projectedSymbols) {
+		List<Query> queries = new LinkedList<Query>();
+    	if (!extractQueries(setQuery, queries)) {
+    		return Collections.emptyMap();
+    	}
+		Map<ElementSymbol, List<Set<Constant>>> partitions = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
+		boolean first = true;
+		for (Query query : queries) {
+			Map<ElementSymbol, Set<Constant>> info = extractPartitionInfo(query, projectedSymbols);
+			
+			partitions.keySet().retainAll(info.keySet());
+			
+			if (first) {
+    			first = false;
+    			for (Map.Entry<ElementSymbol, Set<Constant>> entry : info.entrySet()) {
+    				ArrayList<Set<Constant>> values = new ArrayList<Set<Constant>>(queries.size());
+					partitions.put(entry.getKey(), values);
+					values.add(entry.getValue());
+    			}
+    			continue;
+			} 
+			Set<ElementSymbol> keys = partitions.keySet();
+			
+			for (Iterator<ElementSymbol> iter = keys.iterator(); iter.hasNext();) {
+				ElementSymbol elementSymbol = iter.next();
+				List<Set<Constant>> values = partitions.get(elementSymbol);
+				Set<Constant> value = info.get(elementSymbol);
+				for (Set<Constant> set : values) {
+					if (!Collections.disjoint(set, value)) {
+						iter.remove();
+						continue;
+					}
+				}
+				values.add(value);
+			}
+    	}
+		return partitions;
+	}
+	
+	private static boolean extractQueries(QueryCommand queryCommand, List<Query> result) {
+		if (queryCommand instanceof SetQuery) {
+			SetQuery sq = (SetQuery)queryCommand;
+			if (sq.isAll() && sq.getOperation() == Operation.UNION && sq.getOrderBy() == null && sq.getLimit() == null) {
+				if (!extractQueries(sq.getLeftQuery(), result)) {
+					return false;
+				}
+				if (!extractQueries(sq.getRightQuery(), result)) {
+					return false;
+				}
+				return true;
+	    	}
+			return false;
+		}
+		result.add((Query)queryCommand);
+		return true;
+	}
+	
+	private static Map<ElementSymbol, Set<Constant>> extractPartitionInfo(Query query, List<ElementSymbol> projectedSymbols) {
+		List<SingleElementSymbol> projected = query.getSelect().getProjectedSymbols();
+		List<Criteria> crits = Criteria.separateCriteriaByAnd(query.getCriteria());
+		Map<Expression, TreeSet<Constant>> inMap = new HashMap<Expression, TreeSet<Constant>>();
+		for (Criteria criteria : crits) {
+			if (!(criteria instanceof SetCriteria)) {
+				continue;
+			}
+			SetCriteria sc = (SetCriteria)criteria;
+			TreeSet<Constant> values = new TreeSet<Constant>();
+			boolean allConstants = true;
+			for (Expression exp : (Collection<Expression>)sc.getValues()) {
+				if (exp instanceof Constant) {
+					values.add((Constant)exp);
+				} else {
+					allConstants = false;
+					break;
+				}
+			}
+			if (allConstants) {
+				inMap.put(sc.getExpression(), values);
+			}
+		}
+		Map<ElementSymbol, Set<Constant>> result = new HashMap<ElementSymbol, Set<Constant>>();
+		for (int i = 0; i < projected.size(); i++) {
+			Expression ex = SymbolMap.getExpression(projected.get(i));
+			
+			if (ex instanceof Constant) {
+				result.put(projectedSymbols.get(i), Collections.singleton((Constant)ex));
+			} else {
+				TreeSet<Constant> values = inMap.get(ex);
+				if (values != null) {
+					result.put(projectedSymbols.get(i), values);
+				}
+			}
+		}
+		return result;
+	}
+	
+}


Property changes on: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/PartitionAnalyzer.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -106,6 +106,8 @@
 import org.teiid.query.sql.proc.CreateUpdateProcedureCommand;
 import org.teiid.query.sql.proc.TriggerAction;
 import org.teiid.query.sql.symbol.AllSymbol;
+import org.teiid.query.sql.symbol.Constant;
+import org.teiid.query.sql.symbol.ElementSymbol;
 import org.teiid.query.sql.symbol.GroupSymbol;
 import org.teiid.query.sql.symbol.Reference;
 import org.teiid.query.sql.symbol.SelectSymbol;
@@ -427,6 +429,10 @@
         if(hints.hasCriteria) {
             rules.push(RuleConstants.PUSH_SELECT_CRITERIA);
         }
+        if (hints.hasJoin && hints.hasSetQuery) {
+            rules.push(RuleConstants.DECOMPOSE_JOIN);
+            rules.push(RuleConstants.MERGE_VIRTUAL);
+        }
         if (hints.hasJoin && hints.hasOptionalJoin) {
             rules.push(RuleConstants.REMOVE_OPTIONAL_JOINS);
         }
@@ -878,7 +884,12 @@
 		    PlanNode childRoot = generatePlan(nestedCommand);
 		    node.addFirstChild(childRoot);
 			List<SingleElementSymbol> projectCols = nestedCommand.getProjectedSymbols();
-			node.setProperty(NodeConstants.Info.SYMBOL_MAP, SymbolMap.createSymbolMap(group, projectCols, metadata));
+			SymbolMap map = SymbolMap.createSymbolMap(group, projectCols, metadata);
+			node.setProperty(NodeConstants.Info.SYMBOL_MAP, map);
+			if (nestedCommand instanceof SetQuery) {
+				Map<ElementSymbol, List<Set<Constant>>> partitionInfo = PartitionAnalyzer.extractPartionInfo((SetQuery)nestedCommand, map.getKeys());
+				node.setProperty(NodeConstants.Info.PARTITION_INFO, partitionInfo);
+			}
 		} else {
 			QueryMetadataInterface actualMetadata = metadata;
 			if (actualMetadata instanceof TempMetadataAdapter) {

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -117,6 +117,7 @@
 
         // Source node properties
         SYMBOL_MAP,         // SymbolMap
+        PARTITION_INFO,		// Map<ElementSymbol, List<Set<Constant>>>
         VIRTUAL_COMMAND,    // Command
         MAKE_DEP,           // ??? List of Groups ???
         PROCESSOR_PLAN,     // ProcessorPlan for non-relational sub plan
@@ -133,7 +134,7 @@
         OUTPUT_COLS,        // List <SingleElementSymbol>
 
         // Plan Node Cost Estimate Constants
-        EST_SET_SIZE,        // Integer represents the estimated set size this node would produce for a sibling node as the indenpendent node in a dependent join scenario
+        EST_SET_SIZE,        // Integer represents the estimated set size this node would produce for a sibling node as the independent node in a dependent join scenario
         EST_DEP_CARDINALITY, // Float value that represents the estimated cardinality (amount of rows) produced by this node as the dependent node in a dependent join scenario
         EST_DEP_JOIN_COST,   // Float value that represents the estimated cost of a dependent join (the join strategy for this could be Nested Loop or Merge)
         EST_JOIN_COST,       // Float value that represents the estimated cost of a merge join (the join strategy for this could be Nested Loop or Merge)

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleConstants.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleConstants.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleConstants.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -51,4 +51,5 @@
     public static final OptimizerRule PLAN_PROCEDURES = new RulePlanProcedures();
     public static final OptimizerRule CALCULATE_COST = new RuleCalculateCost();
     public static final OptimizerRule PLAN_SORTS = new RulePlanSorts();
+    public static final OptimizerRule DECOMPOSE_JOIN = new RuleDecomposeJoin();
 }

Added: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleDecomposeJoin.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleDecomposeJoin.java	                        (rev 0)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleDecomposeJoin.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -0,0 +1,353 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package org.teiid.query.optimizer.relational.rules;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.teiid.api.exception.query.QueryMetadataException;
+import org.teiid.api.exception.query.QueryPlannerException;
+import org.teiid.core.TeiidComponentException;
+import org.teiid.query.analysis.AnalysisRecord;
+import org.teiid.query.metadata.QueryMetadataInterface;
+import org.teiid.query.optimizer.capabilities.CapabilitiesFinder;
+import org.teiid.query.optimizer.relational.OptimizerRule;
+import org.teiid.query.optimizer.relational.RuleStack;
+import org.teiid.query.optimizer.relational.plantree.NodeConstants;
+import org.teiid.query.optimizer.relational.plantree.NodeEditor;
+import org.teiid.query.optimizer.relational.plantree.NodeFactory;
+import org.teiid.query.optimizer.relational.plantree.PlanNode;
+import org.teiid.query.optimizer.relational.plantree.NodeConstants.Info;
+import org.teiid.query.processor.relational.JoinNode.JoinStrategyType;
+import org.teiid.query.rewriter.QueryRewriter;
+import org.teiid.query.sql.LanguageObject;
+import org.teiid.query.sql.lang.Criteria;
+import org.teiid.query.sql.lang.JoinType;
+import org.teiid.query.sql.lang.Select;
+import org.teiid.query.sql.lang.SetQuery.Operation;
+import org.teiid.query.sql.symbol.Constant;
+import org.teiid.query.sql.symbol.ElementSymbol;
+import org.teiid.query.sql.symbol.Expression;
+import org.teiid.query.sql.symbol.GroupSymbol;
+import org.teiid.query.sql.symbol.SingleElementSymbol;
+import org.teiid.query.sql.util.SymbolMap;
+import org.teiid.query.util.CommandContext;
+
+/**
+ * Perform the optimization:<pre>
+ *                  source
+ * inner join         union all
+ *   source             inner join
+ *     union all  =>      source
+ *       a                  a
+ *       b                source
+ *   source                 c
+ *     union all        inner join
+ *       c                source
+ *       d                  b
+ *                        source
+ *                          d
+ * </pre>
+ * 
+ * TODO: non-ansi joins
+ */
+public class RuleDecomposeJoin implements OptimizerRule {
+
+	@Override
+	public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata,
+			CapabilitiesFinder capabilitiesFinder, RuleStack rules,
+			AnalysisRecord analysisRecord, CommandContext context)
+			throws QueryPlannerException, QueryMetadataException,
+			TeiidComponentException {
+		
+		for (PlanNode unionNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SET_OP, NodeConstants.Types.SET_OP | NodeConstants.Types.ACCESS)) {
+			plan = decomposeJoin(unionNode, plan, metadata, context);
+		}
+		
+		return plan;
+	}
+	
+	public PlanNode decomposeJoin(PlanNode unionNode, PlanNode root, QueryMetadataInterface metadata, CommandContext context) throws TeiidComponentException, QueryPlannerException {
+        Operation op = (Operation)unionNode.getProperty(NodeConstants.Info.SET_OPERATION);
+        
+		if (op != Operation.UNION
+				|| unionNode.getParent() == null 
+				|| unionNode.getParent().getType() != NodeConstants.Types.SOURCE 
+				|| unionNode.getParent().getParent().getType() != NodeConstants.Types.JOIN) {
+			return root;
+		}
+		
+		PlanNode joinNode = unionNode.getParent().getParent();
+		
+		//TODO: should be done based upon join region to allow more than a 2-way non-ansi join
+		
+		JoinType joinType = (JoinType)joinNode.getProperty(Info.JOIN_TYPE);
+		if (joinType != JoinType.JOIN_INNER) {
+			return root;
+		}
+		
+		Map<ElementSymbol, List<Set<Constant>>> partitionInfo = (Map<ElementSymbol, List<Set<Constant>>>)unionNode.getParent().getProperty(Info.PARTITION_INFO);
+		
+		if (partitionInfo == null) {
+			return root;
+		}
+		
+		boolean left = unionNode == unionNode.getParent().getFirstChild();
+		
+		PlanNode otherSide = left?joinNode.getLastChild():joinNode.getFirstChild();
+		
+		if (otherSide.getType() != NodeConstants.Types.SOURCE) {
+			return root;
+		}
+		
+		Map<ElementSymbol, List<Set<Constant>>> partitionInfoOther = (Map<ElementSymbol, List<Set<Constant>>>)otherSide.getProperty(Info.PARTITION_INFO);
+		
+		if (partitionInfoOther == null) {
+			return root;
+		}
+		
+		List<Criteria> criteria = (List<Criteria>)joinNode.getProperty(Info.JOIN_CRITERIA);
+		
+		List<Expression> expr = new ArrayList<Expression>();
+		List<Expression> exprOther = new ArrayList<Expression>();
+		RuleChooseJoinStrategy.separateCriteria(unionNode.getParent().getGroups(), otherSide.getGroups(), expr, exprOther, criteria, new LinkedList<Criteria>());
+		
+		if (expr.isEmpty()) {
+			return root; //no equi-join
+		}
+		
+		List<int[]> matches = findMatches(partitionInfo, partitionInfoOther, expr, exprOther);
+		
+		if (matches == null) {
+			return root; //no non-overlapping partitions
+		}
+
+		int branchSize = partitionInfo.values().iterator().next().size();
+		int otherBranchSize = partitionInfoOther.values().iterator().next().size();
+		
+		if (matches.isEmpty()) {
+			//no matches mean that we can just insert a null node (false criteria) and be done with it
+			PlanNode critNode = NodeFactory.getNewNode(NodeConstants.Types.SELECT);
+			critNode.setProperty(Info.SELECT_CRITERIA, QueryRewriter.FALSE_CRITERIA);
+			unionNode.addAsParent(critNode);
+			return root;
+		}
+		
+		List<PlanNode> branches = new ArrayList<PlanNode>();
+		RulePushSelectCriteria.collectUnionChildren(unionNode, branches);
+		
+		if (branches.size() != branchSize) {
+			return root; //sanity check 
+		}
+		
+		List<PlanNode> otherBranches = new ArrayList<PlanNode>();
+		RulePushSelectCriteria.collectUnionChildren(otherSide.getFirstChild(), otherBranches);
+		
+		if (otherBranches.size() != otherBranchSize) {
+			return root; //sanity check 
+		}
+
+		PlanNode newUnion = buildUnion(unionNode, otherSide, criteria, matches, branches, otherBranches);
+		PlanNode view = rebuild(unionNode, metadata, context, joinNode, otherSide, newUnion);
+
+		SymbolMap symbolmap = (SymbolMap)view.getProperty(Info.SYMBOL_MAP);
+		HashMap<ElementSymbol, List<Set<Constant>>> newPartitionInfo = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
+		for (int[] match : matches) {
+			updatePartitionInfo(partitionInfo, matches, symbolmap, newPartitionInfo, 0, match[0]);
+			updatePartitionInfo(partitionInfoOther, matches, symbolmap, newPartitionInfo, partitionInfo.size(), match[1]);
+		}
+		view.setProperty(Info.PARTITION_INFO, newPartitionInfo);
+	
+		//since we've created a new union node, there's a chance we can decompose again
+		return decomposeJoin(newUnion, root, metadata, context);
+	}
+
+	private void updatePartitionInfo(
+			Map<ElementSymbol, List<Set<Constant>>> partitionInfo,
+			List<int[]> matches, SymbolMap symbolmap,
+			HashMap<ElementSymbol, List<Set<Constant>>> newPartitionInfo, int start, int index) {
+		for (Map.Entry<ElementSymbol, List<Set<Constant>>> entry : partitionInfo.entrySet()) {
+			ElementSymbol newSymbol = symbolmap.getKeys().get(start++);
+			List<Set<Constant>> values = newPartitionInfo.get(newSymbol);
+			if (values == null) {
+				values = new ArrayList<Set<Constant>>(matches.size());
+				newPartitionInfo.put(newSymbol, values);
+			}
+			values.add(entry.getValue().get(index));
+		}
+	}
+
+	/**
+	 * Add the new union back in under a view 
+	 */
+	private PlanNode rebuild(PlanNode unionNode,
+			QueryMetadataInterface metadata, CommandContext context,
+			PlanNode joinNode, PlanNode otherSide, PlanNode newUnion)
+			throws TeiidComponentException, QueryPlannerException,
+			QueryMetadataException {
+		Set<String> groups = context.getGroups();
+        if (groups == null) {
+        	groups = new HashSet<String>();
+        	context.setGroups(groups);
+        }
+		
+		GroupSymbol group = unionNode.getParent().getGroups().iterator().next();
+		group = RulePlaceAccess.recontextSymbol(group, groups);
+		
+		PlanNode projectNode = NodeEditor.findNodePreOrder(newUnion, NodeConstants.Types.PROJECT);
+		List<? extends SingleElementSymbol> projectedSymbols = (List<? extends SingleElementSymbol>)projectNode.getProperty(Info.PROJECT_COLS);
+
+		PlanNode view = RulePushAggregates.createView(group, projectedSymbols, newUnion, metadata);
+		
+		SymbolMap newSymbolMap = (SymbolMap)view.getProperty(Info.SYMBOL_MAP);
+		
+		HashMap<ElementSymbol, ElementSymbol> inverseMap = new HashMap<ElementSymbol, ElementSymbol>();
+		List<ElementSymbol> viewSymbols = newSymbolMap.getKeys();
+		for (int i = 0; i < projectedSymbols.size(); i++) {
+			inverseMap.put((ElementSymbol)SymbolMap.getExpression(projectedSymbols.get(i)), viewSymbols.get(i));
+		}
+		joinNode.getParent().replaceChild(joinNode, view);
+		
+		FrameUtil.convertFrame(view, unionNode.getParent().getGroups().iterator().next(), Collections.singleton(group), inverseMap, metadata);
+		FrameUtil.convertFrame(view, otherSide.getGroups().iterator().next(), Collections.singleton(group), inverseMap, metadata);
+		
+		return view;
+	}
+
+	/**
+	 * Search each equi-join for partitioning
+	 */
+	private List<int[]> findMatches(
+			Map<ElementSymbol, List<Set<Constant>>> partitionInfo,
+			Map<ElementSymbol, List<Set<Constant>>> partitionInfoOther,
+			List<Expression> expr, List<Expression> exprOther) {
+		List<int[]> matches = null;
+		for (int i = 0; i < expr.size() && matches == null; i++) {
+			if (!(expr.get(i) instanceof ElementSymbol) || !(exprOther.get(i) instanceof ElementSymbol)) {
+				continue;
+			}
+			ElementSymbol es = (ElementSymbol)expr.get(i);
+			ElementSymbol esOther = (ElementSymbol)exprOther.get(i);
+			List<Set<Constant>> partLists = partitionInfo.get(es);
+			List<Set<Constant>> partListsOther = partitionInfoOther.get(esOther);
+			if (partLists == null || partListsOther == null) {
+				continue;
+			}
+			matches = findMatches(partLists, partListsOther);
+		}
+		return matches;
+	}
+	
+	/**
+	 * Find overlaps in the given partition lists
+	 */
+	private List<int[]> findMatches(List<Set<Constant>> partLists,
+			List<Set<Constant>> partListsOther) {
+		List<int[]> matches = new LinkedList<int[]>();
+		for (int j = 0; j < partLists.size(); j++) {
+			int[] match = null;
+			Set<Constant> vals = partLists.get(j);
+			for (int k = 0; k < partListsOther.size(); k++) {
+				if (!Collections.disjoint(vals, partListsOther.get(k))) {
+					if (match == null) {
+						match = new int[] {j, k};
+					} else {
+						//TODO: we currently do handle a situation where multiple 
+						//partitions overlap.
+						return null;
+					}
+				}
+			}
+			if (match != null) {
+				matches.add(match);
+			}
+		}
+		return matches;
+	}
+
+	private PlanNode buildUnion(PlanNode unionNode, PlanNode otherSide,
+			List<Criteria> criteria, List<int[]> matches,
+			List<PlanNode> branches, List<PlanNode> otherBranches) {
+		SymbolMap symbolMap = (SymbolMap)unionNode.getParent().getProperty(Info.SYMBOL_MAP);
+		SymbolMap otherSymbolMap = (SymbolMap)otherSide.getProperty(Info.SYMBOL_MAP);
+
+		List<PlanNode> joins = new LinkedList<PlanNode>();
+		for (int i = 0; i < matches.size(); i++) {
+			int[] is = matches.get(i);
+			PlanNode branch = branches.get(is[0]);
+			PlanNode branchSource = createSource(unionNode, symbolMap);
+			
+			PlanNode otherBranch = otherBranches.get(is[1]);
+			PlanNode otherBranchSource = createSource(otherSide.getFirstChild(), otherSymbolMap);
+			
+			PlanNode newJoinNode = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
+			branchSource.addFirstChild(branch);
+			otherBranchSource.addFirstChild(otherBranch);
+			newJoinNode.addLastChild(branchSource);
+			newJoinNode.addLastChild(otherBranchSource);
+			
+			newJoinNode.setProperty(Info.JOIN_STRATEGY, JoinStrategyType.NESTED_LOOP);
+			newJoinNode.setProperty(Info.JOIN_TYPE, JoinType.JOIN_INNER);
+			newJoinNode.setProperty(Info.JOIN_CRITERIA, LanguageObject.Util.deepClone(criteria, Criteria.class));
+			newJoinNode.addGroups(branchSource.getGroups());
+			newJoinNode.addGroups(otherBranchSource.getGroups());
+			
+			PlanNode projectPlanNode = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
+			newJoinNode.addAsParent(projectPlanNode);
+			
+			Select allSymbols = new Select(symbolMap.getKeys());
+			allSymbols.addSymbols(otherSymbolMap.getKeys());
+			if (i == 0) {
+				QueryRewriter.makeSelectUnique(allSymbols, false);
+			}
+			projectPlanNode.setProperty(NodeConstants.Info.PROJECT_COLS, allSymbols.getSymbols());
+	        projectPlanNode.addGroups(newJoinNode.getGroups());
+			
+			joins.add(projectPlanNode);
+		}
+		
+		PlanNode newUnion = RulePlanUnions.buildUnionTree(unionNode, joins);
+		return newUnion;
+	}
+
+	private PlanNode createSource(PlanNode unionNode, SymbolMap symbolMap) {
+		PlanNode branchSource = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
+		branchSource.addGroups(unionNode.getParent().getGroups());
+		PlanNode projectNode = NodeEditor.findNodePreOrder(unionNode, NodeConstants.Types.PROJECT);
+		branchSource.setProperty(Info.SYMBOL_MAP, SymbolMap.createSymbolMap(symbolMap.getKeys(), (List<? extends SingleElementSymbol>)projectNode.getProperty(Info.PROJECT_COLS)));
+		return branchSource;
+	}
+
+	@Override
+	public String toString() {
+		return "DecomposeJoin"; //$NON-NLS-1$
+	}
+
+}


Property changes on: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleDecomposeJoin.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanUnions.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanUnions.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanUnions.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -23,7 +23,6 @@
 package org.teiid.query.optimizer.relational.rules;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -80,7 +79,7 @@
                                                                       TeiidComponentException {
         //look for all union branches and their sources
         for (PlanNode unionNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SET_OP, NodeConstants.Types.SET_OP | NodeConstants.Types.ACCESS)) {
-            List accessNodes = NodeEditor.findAllNodes(unionNode, NodeConstants.Types.ACCESS);
+            List<PlanNode> accessNodes = NodeEditor.findAllNodes(unionNode, NodeConstants.Types.ACCESS);
             
             Object id = getModelId(metadata, accessNodes, capabilitiesFinder);
             
@@ -89,12 +88,13 @@
                 continue;
             }
             
-            //a linked hasmap is used so that the first entry is logically the first branch
-            Map sourceNodes = new LinkedHashMap();
+            //a linked hashmap is used so that the first entry is logically the first branch
+            Map<Object, List<PlanNode>> sourceNodes = new LinkedHashMap<Object, List<PlanNode>>();
             
-            boolean all = Boolean.TRUE.equals(unionNode.getProperty(NodeConstants.Info.USE_ALL));
+            boolean all = unionNode.hasBooleanProperty(NodeConstants.Info.USE_ALL);
+            Operation op = (Operation)unionNode.getProperty(NodeConstants.Info.SET_OPERATION);
             
-            collectUnionSources(metadata, capabilitiesFinder, unionNode, sourceNodes, all, (Operation)unionNode.getProperty(NodeConstants.Info.SET_OPERATION));
+            collectUnionSources(metadata, capabilitiesFinder, unionNode, sourceNodes, all, op);
             
             if (sourceNodes.size() == 1) {
                 continue;
@@ -103,26 +103,24 @@
             //rebuild unions based upon the source map
             boolean shouldRebuild = false;
             
-            for (Iterator j = sourceNodes.entrySet().iterator(); j.hasNext();) {
-                Map.Entry entry = (Map.Entry)j.next();
-                
-                if (entry.getKey() != null && ((List)entry.getValue()).size() > 1) {
-                    shouldRebuild = true;
-                    break;
-                }
+            for (Map.Entry<Object, List<PlanNode>> entry : sourceNodes.entrySet()) {
+                if (entry.getKey() != null 
+                		&& entry.getValue().size() > 1 
+            			&& CapabilitiesUtil.supportsSetOp(entry.getKey(), (Operation)unionNode.getProperty(NodeConstants.Info.SET_OPERATION), metadata, capabilitiesFinder)) {
+            		shouldRebuild = true;
+            		break;
+            	}
             }
             
             if (!shouldRebuild) {
                 continue;
             }
             
-            List sourceUnions = new LinkedList();
+            List<PlanNode> sourceUnions = new LinkedList<PlanNode>();
             
-            for (Iterator j = sourceNodes.entrySet().iterator(); j.hasNext();) {
-                Map.Entry entry = (Map.Entry)j.next();
+            for (Map.Entry<Object, List<PlanNode>> entry : sourceNodes.entrySet()) {
+                List<PlanNode> sources = entry.getValue();
                 
-                List sources = (List)entry.getValue();
-                
                 sourceUnions.add(buildUnionTree(unionNode, sources));
             }
             
@@ -133,14 +131,12 @@
         }
     }
 
-    private PlanNode buildUnionTree(PlanNode rootUnionNode,
-                                List sources) {
+    static PlanNode buildUnionTree(PlanNode rootUnionNode,
+                                List<PlanNode> sources) {
         
         PlanNode root = null;
         
-        for (Iterator k = sources.iterator(); k.hasNext();) {
-            PlanNode source = (PlanNode)k.next();
-            
+        for (PlanNode source : sources) {
             if (root == null) {
                 root = source;
             } else {
@@ -149,8 +145,6 @@
                 union.setProperty(NodeConstants.Info.USE_ALL, rootUnionNode.getProperty(NodeConstants.Info.USE_ALL));
                 union.addLastChild(root);
                 union.addLastChild(source);
-                union.addGroups(root.getGroups());
-                union.addGroups(source.getGroups());
                 root = union;
             }
         }
@@ -164,7 +158,7 @@
     private void collectUnionSources(QueryMetadataInterface metadata,
                                      CapabilitiesFinder capabilitiesFinder,
                                      PlanNode unionNode,
-                                     Map sourceNodes,
+                                     Map<Object, List<PlanNode>> sourceNodes,
                                      boolean all, Operation setOp) throws QueryMetadataException,
                                                  TeiidComponentException {
         for (PlanNode child : unionNode.getChildren()) {
@@ -174,7 +168,7 @@
             		child.setProperty(NodeConstants.Info.USE_ALL, Boolean.TRUE);
             	}
                 if ((!all || child.hasBooleanProperty(NodeConstants.Info.USE_ALL)) && setOp.equals(child.getProperty(NodeConstants.Info.SET_OPERATION)) && setOp != Operation.EXCEPT) { //keep collecting sources
-                    List accessNodes = NodeEditor.findAllNodes(child, NodeConstants.Types.ACCESS);
+                    List<PlanNode> accessNodes = NodeEditor.findAllNodes(child, NodeConstants.Types.ACCESS);
                     
                     Object id = getModelId(metadata, accessNodes, capabilitiesFinder);
                     
@@ -187,21 +181,13 @@
                     optimizeUnions(child, metadata, capabilitiesFinder);
                 }
             } else {  //this must be a source, see if it has a consistent access node
-                List accessNodes = NodeEditor.findAllNodes(child, NodeConstants.Types.ACCESS);
+                List<PlanNode> accessNodes = NodeEditor.findAllNodes(child, NodeConstants.Types.ACCESS);
                 
                 Object id = getModelId(metadata, accessNodes, capabilitiesFinder);
                 
-                //don't bother optimizing sources that don't support unions
-                boolean supportsUnions = true;
-                
-                if (id != null && !CapabilitiesUtil.supportsSetOp(id, (Operation)unionNode.getProperty(NodeConstants.Info.SET_OPERATION), metadata, capabilitiesFinder)) {
-                    supportsUnions = false;
-                    id = null;
-                }
-                
                 buildModelMap(metadata, capabilitiesFinder, sourceNodes, child, id);
 
-                if (id == null && supportsUnions) {
+                if (id == null) {
                     //recursively optimize below this point
                     optimizeUnions(child, metadata, capabilitiesFinder);
                 }
@@ -210,12 +196,11 @@
     }
 
     private Object getModelId(QueryMetadataInterface metadata,
-                            List accessNodes, CapabilitiesFinder capFinder) throws QueryMetadataException,
+                            List<PlanNode> accessNodes, CapabilitiesFinder capFinder) throws QueryMetadataException,
                                              TeiidComponentException {
         Object modelID = null;
         
-        for (Iterator k = accessNodes.iterator(); k.hasNext();) {
-            PlanNode accessNode = (PlanNode)k.next();
+        for (PlanNode accessNode : accessNodes) {
         
             Object accessModelID = RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata);
             
@@ -249,23 +234,24 @@
      */
     static void buildModelMap(QueryMetadataInterface metadata,
                                    CapabilitiesFinder capFinder,
-                                   Map accessMap,
+                                   Map<Object, List<PlanNode>> accessMap,
                                    PlanNode node,
                                    Object accessModelID) throws QueryMetadataException,
                                                         TeiidComponentException {
-        List accessNodes = null;
+        List<PlanNode> accessNodes = accessMap.get(accessModelID);
         
-        for (Iterator i = accessMap.entrySet().iterator(); i.hasNext();) {
-            Map.Entry entry = (Map.Entry)i.next();
-            if (accessModelID == entry.getKey() || CapabilitiesUtil.isSameConnector(accessModelID, entry.getKey(), metadata, capFinder)) {
-                accessNodes = (List)entry.getValue();
-                break;
-            }
-        }
-        
         if (accessNodes == null) {
-            accessNodes = new ArrayList();
-            accessMap.put(accessModelID, accessNodes);
+	        for (Map.Entry<Object, List<PlanNode>> entry : accessMap.entrySet() ) {
+	            if (accessModelID == entry.getKey() || CapabilitiesUtil.isSameConnector(accessModelID, entry.getKey(), metadata, capFinder)) {
+	                accessNodes = entry.getValue();
+	                break;
+	            }
+	        }
+	        
+	        if (accessNodes == null) {
+	            accessNodes = new ArrayList<PlanNode>();
+	            accessMap.put(accessModelID, accessNodes);
+	        }
         }
         accessNodes.add(node);
     }

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -320,22 +320,11 @@
 				updateSymbolName(projectCols, i, virtualElem, projectedSymbol);
 			}
 		}
-    	PlanNode intermediateView = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
-    	unionSource.addAsParent(intermediateView);
+		GroupSymbol group = new GroupSymbol("X"); //$NON-NLS-1$
+        
+		PlanNode intermediateView = createView(group, virtualElements, unionSource, metadata);
+    	SymbolMap symbolMap = (SymbolMap)intermediateView.getProperty(Info.SYMBOL_MAP);
     	unionSource = intermediateView;
-    	TempMetadataStore store = new TempMetadataStore();
-        TempMetadataAdapter tma = new TempMetadataAdapter(metadata, store);
-        GroupSymbol group = new GroupSymbol("X"); //$NON-NLS-1$
-        try {
-			group.setMetadataID(ResolverUtil.addTempGroup(tma, group, virtualElements, false));
-		} catch (QueryResolverException e) {
-			throw new TeiidComponentException(e);
-		}
-    	intermediateView.addGroup(group);
-    	List<ElementSymbol> projectedSymbols = ResolverUtil.resolveElementsInGroup(group, metadata);
-    	SymbolMap symbolMap = SymbolMap.createSymbolMap(projectedSymbols, 
-				(List<Expression>)NodeEditor.findNodePreOrder(unionSource, NodeConstants.Types.PROJECT).getProperty(NodeConstants.Info.PROJECT_COLS));
-    	intermediateView.setProperty(NodeConstants.Info.SYMBOL_MAP, symbolMap);
     	
         Set<SingleElementSymbol> newGroupingExpressions = Collections.emptySet();
         if (groupingExpressions != null) {
@@ -345,9 +334,9 @@
 			}
         }
 
-        List<SingleElementSymbol> projectedViewSymbols = Util.deepClone(projectedSymbols, SingleElementSymbol.class);
+        List<SingleElementSymbol> projectedViewSymbols = Util.deepClone(symbolMap.getKeys(), SingleElementSymbol.class);
 
-        SymbolMap viewMapping = SymbolMap.createSymbolMap(NodeEditor.findParent(unionSource, NodeConstants.Types.SOURCE).getGroups().iterator().next(), projectedSymbols, metadata);
+        SymbolMap viewMapping = SymbolMap.createSymbolMap(NodeEditor.findParent(unionSource, NodeConstants.Types.SOURCE).getGroups().iterator().next(), projectedViewSymbols, metadata);
         for (AggregateSymbol agg : aggregates) {
         	agg = (AggregateSymbol)agg.clone();
         	ExpressionMappingVisitor.mapExpressions(agg, viewMapping.asMap());
@@ -391,6 +380,24 @@
         	}
         }
     }
+	
+	static PlanNode createView(GroupSymbol group, List<? extends SingleElementSymbol> virtualElements, PlanNode child, QueryMetadataInterface metadata) throws TeiidComponentException {
+		PlanNode intermediateView = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
+    	TempMetadataStore store = new TempMetadataStore();
+        TempMetadataAdapter tma = new TempMetadataAdapter(metadata, store);
+        try {
+			group.setMetadataID(ResolverUtil.addTempGroup(tma, group, virtualElements, false));
+		} catch (QueryResolverException e) {
+			throw new TeiidComponentException(e);
+		}
+    	intermediateView.addGroup(group);
+    	List<ElementSymbol> projectedSymbols = ResolverUtil.resolveElementsInGroup(group, metadata);
+    	SymbolMap symbolMap = SymbolMap.createSymbolMap(projectedSymbols, 
+				(List<Expression>)NodeEditor.findNodePreOrder(child, NodeConstants.Types.PROJECT).getProperty(NodeConstants.Info.PROJECT_COLS));
+    	intermediateView.setProperty(NodeConstants.Info.SYMBOL_MAP, symbolMap);
+    	child.addAsParent(intermediateView);
+    	return intermediateView;
+	}
 
 	private void updateSymbolName(List<SingleElementSymbol> projectCols, int i,
 			ElementSymbol virtualElem, SingleElementSymbol projectedSymbol) {

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -523,7 +523,7 @@
         return false;
 	}
 
-	void collectUnionChildren(PlanNode unionNode, LinkedList<PlanNode> unionChildren) {
+	static void collectUnionChildren(PlanNode unionNode, List<PlanNode> unionChildren) {
 	    for (PlanNode child : unionNode.getChildren()) {
 	        if(child.getType() == NodeConstants.Types.SET_OP) {
 	            collectUnionChildren(child, unionChildren);

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/command/SimpleQueryResolver.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -155,12 +155,12 @@
             }
 
             if (!discoveredGroups.add(obj.getGroupSymbol())) {
-            	throw new QueryResolverException("Duplicate WITH clause item name {0}");
+            	throw new QueryResolverException(QueryPlugin.Util.getString("SimpleQueryResolver.duplicate_with", obj.getGroupSymbol())); //$NON-NLS-1$
             }
             List<? extends SingleElementSymbol> projectedSymbols = obj.getCommand().getProjectedSymbols();
             if (obj.getColumns() != null && !obj.getColumns().isEmpty()) {
             	if (obj.getColumns().size() != projectedSymbols.size()) {
-            		throw new QueryResolverException("The number of WITH clause columns for item {0} do not match the query expression");
+            		throw new QueryResolverException(QueryPlugin.Util.getString("SimpleQueryResolver.mismatched_with_columns", obj.getGroupSymbol())); //$NON-NLS-1$
             	}
             	Iterator<ElementSymbol> iter = obj.getColumns().iterator();
             	for (SingleElementSymbol singleElementSymbol : projectedSymbols) {
@@ -460,7 +460,7 @@
 			storedProcedureCommand.setProcedureRelational(true);
 			storedProcedureCommand.setProcedureName(fullName);
 			
-			List metadataParams = storedProcedureInfo.getParameters();
+			List<SPParameter> metadataParams = storedProcedureInfo.getParameters();
 			
 			Query procQuery = new Query();
 			From from = new From();
@@ -474,8 +474,7 @@
 			
 			int paramIndex = 1;
 			
-			for(Iterator paramIter = metadataParams.iterator(); paramIter.hasNext();){
-			    SPParameter metadataParameter  = (SPParameter)paramIter.next();
+			for (SPParameter metadataParameter : metadataParams) {
 			    SPParameter clonedParam = (SPParameter)metadataParameter.clone();
 			    if (clonedParam.getParameterType()==ParameterInfo.IN || metadataParameter.getParameterType()==ParameterInfo.INOUT) {
 			        ElementSymbol paramSymbol = clonedParam.getParameterSymbol();
@@ -499,12 +498,11 @@
 			
 			QueryResolver.resolveCommand(procQuery, Collections.EMPTY_MAP, metadata.getMetadata(), analysis);
 			
-			List projectedSymbols = procQuery.getProjectedSymbols();
+			List<SingleElementSymbol> projectedSymbols = procQuery.getProjectedSymbols();
 			
 			HashSet<String> foundNames = new HashSet<String>();
 			
-			for (Iterator i = projectedSymbols.iterator(); i.hasNext();) {
-			    SingleElementSymbol ses = (SingleElementSymbol)i.next();
+			for (SingleElementSymbol ses : projectedSymbols) {
 			    if (!foundNames.add(ses.getShortCanonicalName())) {
 			        throw new QueryResolverException(QueryPlugin.Util.getString("SimpleQueryResolver.Proc_Relational_Name_conflict", fullName)); //$NON-NLS-1$                            
 			    }

Modified: trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/resolver/util/ResolverUtil.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -983,20 +983,27 @@
 		}
 		HashMap<List<GroupSymbol>, List<HashSet<Object>>> crits = createGroupMap(leftExpressions, rightExpressions);
 		HashSet<GroupSymbol> tempSet = new HashSet<GroupSymbol>();
+		HashSet<GroupSymbol> nonKeyPreserved = new HashSet<GroupSymbol>();
 		for (GroupSymbol group : groups) {
 			LinkedHashSet<GroupSymbol> visited = new LinkedHashSet<GroupSymbol>();
 			LinkedList<GroupSymbol> toVisit = new LinkedList<GroupSymbol>();
 			toVisit.add(group);
 			while (!toVisit.isEmpty()) {
 				GroupSymbol visiting = toVisit.removeLast();
-				if (!visited.add(visiting)) {
+				if (!visited.add(visiting) || nonKeyPreserved.contains(visiting)) {
 					continue;
 				}
+				if (keyPreservingGroups.contains(visiting)) {
+					visited.addAll(groups);
+					break;
+				}
 				toVisit.addAll(findKeyPreserved(tempSet, Collections.singleton(visiting), crits, true, metadata, groups));
 				toVisit.addAll(findKeyPreserved(tempSet, Collections.singleton(visiting), crits, false, metadata, groups));
 			}
 			if (visited.containsAll(groups)) {
 				keyPreservingGroups.add(group);
+			} else {
+				nonKeyPreserved.add(group);
 			}
 		}
 	}

Modified: trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -1240,10 +1240,14 @@
 	                    		exprMap.put(sc.getExpression(), converted);
 	                    	} else if (crit instanceof SetCriteria) {
 	                    		SetCriteria sc1 = (SetCriteria)crit;
+	                    		newCrits.remove(sc1);
 	                    		sc1.getValues().retainAll(sc.getValues());
 	                    		if (sc1.getValues().isEmpty()) {
 	                    			return FALSE_CRITERIA;
 	                    		}
+	                    		//TODO: single value as compare criteria
+	                    		newCrits.add(sc1);
+	                    		exprMap.put(sc1.getExpression(), sc1);
 	                    		continue;
 	                    	} else {
 	                    		CompareCriteria cc = (CompareCriteria)crit;
@@ -1252,12 +1256,12 @@
 										exprIter.remove();
 									}
 								}
-	                    		//TODO: single value as compare criteria
 	                    		if (sc.getValues().isEmpty()) {
 	                    			return FALSE_CRITERIA;
 	                    		}
 	                    		if (cc.getOperator() != CompareCriteria.EQ) {
 		                    		newCrits.remove(cc);
+		                    		//TODO: single value as compare criteria
 		                    		exprMap.put(sc.getExpression(), sc);
 	                    		} else {
 	                    			continue;
@@ -1293,6 +1297,7 @@
 	                        		exprMap.put(cc.getLeftExpression(), cc);
 	                    		} else if (modified) {
 	                    			newCrits.add(sc);
+	                    			exprMap.put(sc.getExpression(), sc);
 		                    		continue;
 	                    		}
 	                    	} else {

Modified: trunk/engine/src/main/resources/org/teiid/query/i18n.properties
===================================================================
--- trunk/engine/src/main/resources/org/teiid/query/i18n.properties	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/main/resources/org/teiid/query/i18n.properties	2010-12-17 17:48:17 UTC (rev 2785)
@@ -660,6 +660,8 @@
 SimpleQueryResolver.Query_was_redirected_to_Mat_table=The query against {0} was redirected to the materialization table {1}.
 SimpleQueryResolver.ambiguous_all_in_group=The symbol {0} refers to more than one group defined in the FROM clause.
 SimpleQueryResolver.Proc_Relational_Name_conflict=Cannot access procedure {0} using table semantics since the parameter and result set column names are not all unique.
+SimpleQueryResolver.duplicate_with=Duplicate WITH clause item name {0}
+SimpleQueryResolver.mismatched_with_columns=The number of WITH clause columns for item {0} do not match the query expression
 QueryResolver.invalid_xpath=Invalid xpath value: {0}
 ResolveVariablesVisitor.reserved_word_for_temporary_used=Cursor names cannot begin with "#" as that indicates the name of a temporary table: {0}.
 SimpleQueryResolver.materialized_table_not_used=The query against {0} did not use materialization table {1} due to the use of OPTION NOCACHE.

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/TestUnionPlanning.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/TestUnionPlanning.java	2010-12-15 21:45:25 UTC (rev 2784)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/TestUnionPlanning.java	2010-12-17 17:48:17 UTC (rev 2785)
@@ -22,18 +22,18 @@
 
 package org.teiid.query.optimizer;
 
+import org.junit.Test;
+import org.teiid.query.optimizer.TestOptimizer.ComparisonMode;
 import org.teiid.query.optimizer.capabilities.BasicSourceCapabilities;
 import org.teiid.query.optimizer.capabilities.FakeCapabilitiesFinder;
 import org.teiid.query.optimizer.capabilities.SourceCapabilities.Capability;
 import org.teiid.query.processor.ProcessorPlan;
 import org.teiid.query.unittest.FakeMetadataFactory;
 
+ at SuppressWarnings("nls")
+public class TestUnionPlanning {
 
-import junit.framework.TestCase;
-
-public class TestUnionPlanning extends TestCase {
-
-    public void testUnionPushDown() {
+    @Test public void testUnionPushDown() {
         FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
         BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
         caps.setCapabilitySupport(Capability.QUERY_UNION, true);
@@ -65,7 +65,7 @@
     /**
      * Here the change in the all causes us not to pushdown
      */
-    public void testUnionPushDown1() {
+    @Test public void testUnionPushDown1() {
         FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
         BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
         caps.setCapabilitySupport(Capability.QUERY_UNION, true);
@@ -94,7 +94,7 @@
         });                                    
     }
 
-    public void testUnionPushDown2() {
+    @Test public void testUnionPushDown2() {
         FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
         BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
         caps.setCapabilitySupport(Capability.QUERY_UNION, true);
@@ -155,5 +155,56 @@
             2       // UnionAll
         });                                    
     }
+        
+    @Test public void testUnionPushDownWithJoin() {
+        ProcessorPlan plan = TestOptimizer.helpPlan("select * from (SELECT IntKey FROM BQT1.SmallA where intkey in (1, 2) UNION ALL SELECT intkey FROM BQT2.SmallA where intkey in (3, 4)) A inner join (SELECT intkey FROM BQT1.SmallB where intkey in (1, 2) UNION ALL SELECT intkey FROM BQT2.SmallB where intkey in (3, 4)) B on a.intkey = b.intkey", FakeMetadataFactory.exampleBQTCached(), null, TestOptimizer.getGenericFinder(),//$NON-NLS-1$
+            new String[] { "SELECT g_1.intkey, g_0.intkey FROM BQT2.SmallA AS g_0, BQT2.SmallB AS g_1 WHERE (g_0.intkey = g_1.intkey) AND (g_0.intkey IN (3, 4)) AND (g_1.intkey IN (3, 4))", 
+        	"SELECT g_1.intkey, g_0.IntKey FROM BQT1.SmallA AS g_0, BQT1.SmallB AS g_1 WHERE (g_0.IntKey = g_1.intkey) AND (g_0.intkey IN (1, 2)) AND (g_1.intkey IN (1, 2))" }, TestOptimizer.SHOULD_SUCCEED); 
+
+        TestOptimizer.checkNodeTypes(plan, new int[] {
+            2,      // Access
+            0,      // DependentAccess
+            0,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            0,      // Project
+            0,      // Select
+            0,      // Sort
+            1       // UnionAll
+        });                                    
+    }
     
+    @Test public void testUnionPushDownWithJoinNoMatches() {
+        TestOptimizer.helpPlan("select * from (SELECT IntKey FROM BQT1.SmallA where intkey in (1, 2) UNION ALL SELECT intkey FROM BQT2.SmallA where intkey in (3, 4)) A inner join (SELECT intkey FROM BQT1.SmallB where intkey in (5, 6) UNION ALL SELECT intkey FROM BQT2.SmallB where intkey in (7, 8)) B on a.intkey = b.intkey", FakeMetadataFactory.exampleBQTCached(), null, TestOptimizer.getGenericFinder(),//$NON-NLS-1$
+            new String[] {}, TestOptimizer.SHOULD_SUCCEED); //$NON-NLS-1$  
+    }
+    
+    @Test public void testUnionPushDownWithJoin1() throws Exception {
+        ProcessorPlan plan = TestOptimizer.helpPlan("select * from (SELECT IntKey FROM BQT1.SmallA where intkey in (1, 2) UNION ALL SELECT intkey FROM BQT2.SmallA where intkey in (3, 4)) A inner join (SELECT intkey FROM BQT1.SmallB where intkey in (1, 2) UNION ALL SELECT intkey FROM BQT2.SmallB where intkey in (3, 4)) B on a.intkey = b.intkey where a.intkey in (1, 4)", FakeMetadataFactory.exampleBQTCached(), null, TestOptimizer.getGenericFinder(),//$NON-NLS-1$
+            new String[] { "SELECT g_1.intkey, g_0.intkey FROM BQT2.SmallA AS g_0, BQT2.SmallB AS g_1 WHERE (g_0.intkey = g_1.intkey) AND (g_0.intkey IN (4)) AND (g_0.intkey = 4) AND (g_1.intkey = 4)", 
+        	"SELECT g_1.intkey, g_0.IntKey FROM BQT1.SmallA AS g_0, BQT1.SmallB AS g_1 WHERE (g_0.IntKey = g_1.intkey) AND (g_0.intkey IN (1)) AND (g_0.IntKey = 1) AND (g_1.intkey IN (1)) AND (g_1.intkey = 1)" }, ComparisonMode.EXACT_COMMAND_STRING); 
+
+        TestOptimizer.checkNodeTypes(plan, new int[] {
+            2,      // Access
+            0,      // DependentAccess
+            0,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            0,      // Project
+            0,      // Select
+            0,      // Sort
+            1       // UnionAll
+        });                                    
+    }
+    
 }



More information about the teiid-commits mailing list