teiid SVN: r4632 - in branches/7.7.x/engine/src: test/java/org/teiid/query/optimizer and 1 other directory.
by teiid-commits@lists.jboss.org
Author: jolee
Date: 2014-04-21 16:33:53 -0400 (Mon, 21 Apr 2014)
New Revision: 4632
Modified:
branches/7.7.x/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanSorts.java
branches/7.7.x/engine/src/test/java/org/teiid/query/optimizer/TestSortOptimization.java
Log:
TEIID-2921: Allow the projection raising (or order pushing) logic to work with non-simple ordering expressions
Modified: branches/7.7.x/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanSorts.java
===================================================================
--- branches/7.7.x/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanSorts.java 2014-04-16 18:26:12 UTC (rev 4631)
+++ branches/7.7.x/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanSorts.java 2014-04-21 20:33:53 UTC (rev 4632)
@@ -23,6 +23,7 @@
package org.teiid.query.optimizer.relational.rules;
import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
import org.teiid.api.exception.query.QueryMetadataException;
@@ -34,9 +35,10 @@
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.NodeConstants.Info;
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.processor.relational.MergeJoinStrategy.SortOption;
import org.teiid.query.sql.lang.OrderBy;
@@ -236,17 +238,34 @@
List<SingleElementSymbol> childOutputCols = (List<SingleElementSymbol>) projectNode.getFirstChild().getProperty(Info.OUTPUT_COLS);
OrderBy orderBy = (OrderBy) node.getProperty(Info.SORT_ORDER);
List<SingleElementSymbol> orderByKeys = orderBy.getSortKeys();
+ LinkedHashSet<SingleElementSymbol> toProject = new LinkedHashSet<SingleElementSymbol>();
for (SingleElementSymbol ss : orderByKeys) {
+ SingleElementSymbol original = ss;
if(ss instanceof AliasSymbol) {
ss = ((AliasSymbol)ss).getSymbol();
}
if (ss instanceof ExpressionSymbol && !(ss instanceof AggregateSymbol)) {
- return root; //TODO: insert a new project node to handle this case
+ if (!raiseAccess) {
+ return root; //TODO: insert a new project node to handle this case
+ }
}
if (!childOutputCols.contains(ss)) {
- return root;
+ if (!raiseAccess) {
+ return root;
+ }
+ toProject.add(original);
}
}
+ PlanNode toRepair = projectNode.getParent();
+ if (!toProject.isEmpty()) {
+ PlanNode intermediateProject = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
+ toProject.addAll(childOutputCols);
+ List<SingleElementSymbol> projectCols = new ArrayList<SingleElementSymbol>(toProject);
+ childOutputCols = projectCols;
+ intermediateProject.setProperty(NodeConstants.Info.PROJECT_COLS, projectCols);
+ intermediateProject.setProperty(NodeConstants.Info.OUTPUT_COLS, new ArrayList<Expression>(projectCols));
+ toRepair.getFirstChild().addAsParent(intermediateProject);
+ }
NodeEditor.removeChildNode(projectNode.getParent(), projectNode);
if (parent != null && parent.getType() == NodeConstants.Types.TUPLE_LIMIT && parent.getParent() != null) {
parent.addAsParent(projectNode);
@@ -270,14 +289,16 @@
unrelated = true;
}
for (OrderByItem item : orderBy.getOrderByItems()) {
- if (unrelated) {
+ if (unrelated || !toProject.isEmpty()) {
//update sort order
int index = childOutputCols.indexOf(item.getSymbol());
item.setExpressionPosition(index);
}
- //strip alias as project was raised
- if (item.getSymbol() instanceof AliasSymbol) {
- item.setSymbol(((AliasSymbol)item.getSymbol()).getSymbol());
+ if (toProject.isEmpty()) {
+ //strip alias as project was raised
+ if (item.getSymbol() instanceof AliasSymbol) {
+ item.setSymbol(((AliasSymbol)item.getSymbol()).getSymbol());
+ }
}
}
projectNode.setProperty(Info.OUTPUT_COLS, orderByOutputSymbols);
@@ -287,17 +308,25 @@
parent.setProperty(Info.OUTPUT_COLS, childOutputCols);
}
if (raiseAccess) {
- PlanNode accessNode = node.getFirstChild();
+ PlanNode accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS);
+
//instead of just calling ruleraiseaccess, we're more selective
//we do not want to raise the access node over a project that is handling an unrelated sort
PlanNode newRoot = RuleRaiseAccess.raiseAccessNode(root, accessNode, metadata, capFinder, true, record);
if (newRoot != null) {
+ accessNode.setProperty(NodeConstants.Info.OUTPUT_COLS, childOutputCols);
root = newRoot;
- if (accessNode.getParent().getType() == NodeConstants.Types.TUPLE_LIMIT) {
- newRoot = RulePushLimit.raiseAccessOverLimit(root, accessNode, metadata, capFinder, accessNode.getParent(), record);
+ if (!toProject.isEmpty()) {
+ newRoot = RuleRaiseAccess.raiseAccessNode(root, accessNode, metadata, capFinder, true, record);
}
if (newRoot != null) {
root = newRoot;
+ if (accessNode.getParent().getType() == NodeConstants.Types.TUPLE_LIMIT) {
+ newRoot = RulePushLimit.raiseAccessOverLimit(root, accessNode, metadata, capFinder, accessNode.getParent(), record);
+ }
+ if (newRoot != null) {
+ root = newRoot;
+ }
}
}
}
Modified: branches/7.7.x/engine/src/test/java/org/teiid/query/optimizer/TestSortOptimization.java
===================================================================
--- branches/7.7.x/engine/src/test/java/org/teiid/query/optimizer/TestSortOptimization.java 2014-04-16 18:26:12 UTC (rev 4631)
+++ branches/7.7.x/engine/src/test/java/org/teiid/query/optimizer/TestSortOptimization.java 2014-04-21 20:33:53 UTC (rev 4632)
@@ -25,18 +25,25 @@
import static org.junit.Assert.*;
import static org.teiid.query.optimizer.TestOptimizer.*;
+import java.util.Arrays;
+import java.util.List;
+
import org.junit.Test;
import org.teiid.query.optimizer.TestOptimizer.ComparisonMode;
import org.teiid.query.optimizer.TestOptimizer.DupRemoveSortNode;
import org.teiid.query.optimizer.capabilities.BasicSourceCapabilities;
+import org.teiid.query.optimizer.capabilities.CapabilitiesFinder;
import org.teiid.query.optimizer.capabilities.DefaultCapabilitiesFinder;
import org.teiid.query.optimizer.capabilities.FakeCapabilitiesFinder;
import org.teiid.query.optimizer.capabilities.SourceCapabilities.Capability;
+import org.teiid.query.processor.HardcodedDataManager;
import org.teiid.query.processor.ProcessorPlan;
+import org.teiid.query.processor.TestProcessor;
import org.teiid.query.processor.relational.LimitNode;
import org.teiid.query.processor.relational.ProjectNode;
import org.teiid.query.processor.relational.RelationalPlan;
import org.teiid.query.unittest.RealMetadataFactory;
+import org.teiid.translator.SourceSystemFunctions;
public class TestSortOptimization {
@@ -300,6 +307,33 @@
new String[] {"SELECT g_0.e1 AS c_0, g_0.e2 AS c_1 FROM pm1.g1 AS g_0 ORDER BY c_0 LIMIT 1"}, ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$
}
+ @Test public void testProjectionRaisingWithComplexOrdering() {
+ String sql = "select e1 || 1, e2 / 2 from pm1.g1 as x order by e1 || 1 limit 2"; //$NON-NLS-1$
+ BasicSourceCapabilities bsc = TestOptimizer.getTypicalCapabilities();
+ bsc.setFunctionSupport(SourceSystemFunctions.CONCAT, true);
+ bsc.setCapabilitySupport(Capability.ROW_LIMIT, true);
+ CapabilitiesFinder finder = new DefaultCapabilitiesFinder(bsc);
+ RelationalPlan plan = (RelationalPlan)helpPlan(sql, RealMetadataFactory.example1Cached(), null, finder,
+ new String[] {"SELECT concat(g_0.e1, '1') AS c_0, g_0.e1 AS c_1, g_0.e2 AS c_2 FROM pm1.g1 AS g_0 ORDER BY c_0 LIMIT 2"}, TestOptimizer.SHOULD_SUCCEED); //$NON-NLS-1$
+
+ assertTrue(plan.getRootNode() instanceof ProjectNode);
+ }
+
+ @Test public void testProjectionRaisingWithComplexOrdering1() {
+ String sql = "select e1 || 1 as a, e2 / 2 from pm1.g1 as x order by a, e2 limit 2"; //$NON-NLS-1$
+ BasicSourceCapabilities bsc = TestOptimizer.getTypicalCapabilities();
+ bsc.setFunctionSupport(SourceSystemFunctions.CONCAT, true);
+ bsc.setCapabilitySupport(Capability.ROW_LIMIT, true);
+ CapabilitiesFinder finder = new DefaultCapabilitiesFinder(bsc);
+ RelationalPlan plan = (RelationalPlan)helpPlan(sql, RealMetadataFactory.example1Cached(), null, finder,
+ new String[] {"SELECT concat(g_0.e1, '1') AS c_0, g_0.e1 AS c_1, g_0.e2 AS c_2 FROM pm1.g1 AS g_0 ORDER BY c_0, c_2 LIMIT 2"}, TestOptimizer.SHOULD_SUCCEED); //$NON-NLS-1$
+
+ assertTrue(plan.getRootNode() instanceof ProjectNode);
+ HardcodedDataManager hdm = new HardcodedDataManager();
+ hdm.addData("SELECT concat(g_0.e1, '1') AS c_0, g_0.e1 AS c_1, g_0.e2 AS c_2 FROM pm1.g1 AS g_0 ORDER BY c_0, c_2 LIMIT 2", new List<?>[] {Arrays.asList("c1", "c", 2), Arrays.asList("d1", "d", 3)});
+ TestProcessor.helpProcess(plan, hdm, new List<?>[] {Arrays.asList("c1", 1), Arrays.asList("d1", 1)});
+ }
+
//TODO this should trigger another view removal and thus the combination of the grouping/dup operation
@Test public void testGroupDupCombination1Pushdown() {
FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();