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;
+@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
+ });
+ }
+
}