Author: mdrillin
Date: 2011-06-07 17:36:50 -0400 (Tue, 07 Jun 2011)
New Revision: 3230
Modified:
branches/7.1.1.CP2/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java
branches/7.1.1.CP2/engine/src/test/java/org/teiid/query/optimizer/TestOptionalJoins.java
Log:
TEIID-1622 - Fixes issue with optimization of optional JOIN when used with UNION ALL
Modified:
branches/7.1.1.CP2/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java
===================================================================
---
branches/7.1.1.CP2/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java 2011-06-07
21:19:41 UTC (rev 3229)
+++
branches/7.1.1.CP2/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java 2011-06-07
21:36:50 UTC (rev 3230)
@@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -50,6 +51,7 @@
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.sql.visitor.ElementCollectorVisitor;
import org.teiid.query.util.CommandContext;
@@ -72,7 +74,7 @@
TeiidComponentException {
try {
- removeOptionalJoinNodes(plan, metadata, capFinder, null);
+ removeOptionalJoinNodes(plan, metadata, capFinder, null, null);
} catch (QueryResolverException e) {
throw new TeiidComponentException(e);
}
@@ -85,13 +87,22 @@
*/
private boolean removeOptionalJoinNodes(PlanNode node,
QueryMetadataInterface metadata,
- CapabilitiesFinder capFinder,
Set<ElementSymbol> elements) throws QueryPlannerException,
+ CapabilitiesFinder capFinder,
Set<ElementSymbol> elements, List<Integer> indexes) throws
QueryPlannerException,
QueryMetadataException,
TeiidComponentException, QueryResolverException {
if (node.getChildCount() == 0) {
return false;
}
+ if (indexes != null && node.getParent() != null &&
node.getParent().getType() == NodeConstants.Types.SET_OP &&
node.getParent().getFirstChild() != node) {
+ elements = new HashSet<ElementSymbol>();
+ List columns = (List)node.getProperty(NodeConstants.Info.PROJECT_COLS);
+ for (int i : indexes) {
+ SingleElementSymbol column = (SingleElementSymbol) columns.get(i);
+ ElementCollectorVisitor.getElements(column, elements);
+ }
+ }
+
boolean isRoot = false;
switch (node.getType()) {
@@ -138,15 +149,21 @@
case NodeConstants.Types.SOURCE:
{
if (elements == null) {
+ indexes = null;
break;
}
SymbolMap symbolMap =
(SymbolMap)node.getProperty(NodeConstants.Info.SYMBOL_MAP);
Set convertedElements = new HashSet();
+ indexes = new LinkedList<Integer>();
+ List<ElementSymbol> keys = symbolMap.getKeys();
+ List<Expression> values = symbolMap.getValues();
for (ElementSymbol element : elements) {
- Expression convertedExpression =
symbolMap.getMappedExpression(element);
- if (convertedExpression != null) {
+ int index = keys.indexOf(element);
+ if (index >= 0) {
+ Expression convertedExpression = values.get(index);
+ indexes.add(index);
ElementCollectorVisitor.getElements(convertedExpression,
convertedElements);
- }
+ }
}
elements = convertedElements;
isRoot = true;
@@ -170,6 +187,14 @@
break;
}
case NodeConstants.Types.SET_OP:
+ {
+ if (!node.hasBooleanProperty(NodeConstants.Info.USE_ALL)) {
+ //allow project nodes to be seen as roots
+ elements = null;
+ indexes = null;
+ }
+ break;
+ }
case NodeConstants.Types.DUP_REMOVE:
{
//allow project nodes to be seen as roots
@@ -182,7 +207,7 @@
if (isRoot) {
boolean optionalRemoved = false;
do {
- optionalRemoved = removeOptionalJoinNodes(node.getFirstChild(), metadata,
capFinder, elements);
+ optionalRemoved = removeOptionalJoinNodes(node.getFirstChild(), metadata,
capFinder, elements, indexes);
} while (optionalRemoved);
return false;
}
@@ -191,7 +216,7 @@
Iterator iter = node.getChildren().iterator();
while (node.getChildCount() >= 1 && iter.hasNext()) {
- if (removeOptionalJoinNodes((PlanNode)iter.next(), metadata, capFinder,
elements)) {
+ if (removeOptionalJoinNodes((PlanNode)iter.next(), metadata, capFinder,
elements, indexes)) {
return true;
}
}
Modified:
branches/7.1.1.CP2/engine/src/test/java/org/teiid/query/optimizer/TestOptionalJoins.java
===================================================================
---
branches/7.1.1.CP2/engine/src/test/java/org/teiid/query/optimizer/TestOptionalJoins.java 2011-06-07
21:19:41 UTC (rev 3229)
+++
branches/7.1.1.CP2/engine/src/test/java/org/teiid/query/optimizer/TestOptionalJoins.java 2011-06-07
21:36:50 UTC (rev 3230)
@@ -311,7 +311,7 @@
}
/**
- * Union should prevent the removal from happening
+ * Union all should not prevent the removal from happening
*/
@Test public void testOptionalJoinWithUnion() {
ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g2.e4 from /*
optional */ pm1.g1 inner join pm1.g2 on pm1.g1.e1 = pm1.g2.e1 union all select
convert(pm1.g2.e2, double) from /* optional */ pm1.g1 inner join pm1.g2 on pm1.g1.e1 =
pm1.g2.e1", FakeMetadataFactory.example1Cached(), //$NON-NLS-1$
@@ -335,6 +335,31 @@
});
}
+ /**
+ * The first branch should have the join removed, but not the second branch
+ */
+ @Test public void testOptionalJoinWithUnion1() {
+ ProcessorPlan plan = TestOptimizer.helpPlan("select e4 from (select e4, e2
from (select pm1.g2.e4, pm1.g1.e2 from /* optional */ pm1.g1 inner join pm1.g2 on
pm1.g1.e1 = pm1.g2.e1) as x union all select e4, e2 from (select convert(pm2.g1.e2,
double) as e4, pm2.g2.e2 from /* optional */ pm2.g1 inner join pm2.g2 on pm2.g1.e1 =
pm2.g2.e1) as x) as y", FakeMetadataFactory.example1Cached(), //$NON-NLS-1$
+ new String[] {"SELECT pm1.g2.e4 FROM pm1.g2", "SELECT g_0.e2
FROM pm2.g1 AS g_0, pm2.g2 AS g_1 WHERE g_0.e1 = g_1.e1"} ); //$NON-NLS-1$
//$NON-NLS-2$
+
+ TestOptimizer.checkNodeTypes(plan, new int[] {
+ 2, // Access
+ 0, // DependentAccess
+ 0, // DependentSelect
+ 0, // DependentProject
+ 0, // DupRemove
+ 0, // Grouping
+ 0, // Join
+ 0, // MergeJoin
+ 0, // Null
+ 0, // PlanExecution
+ 1, // Project
+ 0, // Select
+ 0, // Sort
+ 1 // UnionAll
+ });
+ }
+
@Test public void testOptionalJoinWithCompoundCriteria() {
ProcessorPlan plan = TestOptimizer.helpPlan("SELECT length(z) FROM /*
optional */ pm1.g1 inner join (select e2 as y, e3 || 'x' as z from pm1.g1 ORDER BY
z) AS x on pm1.g1.e2=x.y and concat(x.y, x.z) = '1'",
FakeMetadataFactory.example1Cached(), //$NON-NLS-1$
new String[] {"SELECT e3 FROM pm1.g1"} ); //$NON-NLS-1$