use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class TestOptimizer method getPlan.
public static ProcessorPlan getPlan(Command command, QueryMetadataInterface md, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, boolean shouldSucceed, CommandContext cc) {
ProcessorPlan plan = null;
if (analysisRecord == null) {
analysisRecord = new AnalysisRecord(false, DEBUG);
}
Exception exception = null;
try {
// do planning
plan = QueryOptimizer.optimizePlan(command, md, null, capFinder, analysisRecord, cc);
} catch (QueryPlannerException e) {
exception = e;
} catch (TeiidComponentException e) {
exception = e;
} catch (Throwable e) {
throw new TeiidRuntimeException(e);
} finally {
if (DEBUG) {
System.out.println(analysisRecord.getDebugLog());
}
}
if (!shouldSucceed) {
// $NON-NLS-1$
assertNotNull("Expected exception but did not get one.", exception);
return null;
}
if (plan == null) {
throw new TeiidRuntimeException(exception);
}
// $NON-NLS-1$
assertNotNull("Output elements are null", plan.getOutputElements());
// $NON-NLS-1$
if (DEBUG)
System.out.println("\n" + plan);
return plan;
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class TestBatchedUpdatePlanner method helpPlanCommand.
private BatchedUpdatePlan helpPlanCommand(Command command, QueryMetadataInterface md, CapabilitiesFinder capFinder, boolean shouldSucceed) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
// plan
ProcessorPlan plan = null;
AnalysisRecord analysisRecord = new AnalysisRecord(false, DEBUG);
if (shouldSucceed) {
try {
// do planning
plan = QueryOptimizer.optimizePlan(command, md, null, capFinder, analysisRecord, null);
} finally {
if (DEBUG) {
System.out.println(analysisRecord.getDebugLog());
}
}
return (BatchedUpdatePlan) plan;
}
Exception exception = null;
try {
// do planning
QueryOptimizer.optimizePlan(command, md, null, capFinder, analysisRecord, null);
} catch (QueryPlannerException e) {
exception = e;
} catch (TeiidComponentException e) {
exception = e;
} finally {
if (DEBUG) {
System.out.println(analysisRecord.getDebugLog());
}
}
// $NON-NLS-1$
assertNotNull("Expected exception but did not get one.", exception);
return null;
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class RuleMergeCriteria method planMergeJoin.
/**
* Look for:
* [NOT] EXISTS ( )
* IN ( ) / SOME ( )
*
* and replace with a semi join
*/
private PlanNode planMergeJoin(PlanNode current, PlanNode root) throws QueryMetadataException, TeiidComponentException {
float sourceCost = NewCalculateCostUtil.computeCostForTree(current.getFirstChild(), metadata);
Criteria crit = (Criteria) current.getProperty(NodeConstants.Info.SELECT_CRITERIA);
PlannedResult plannedResult = findSubquery(crit, true);
if (plannedResult.query == null) {
return current;
}
if (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceCost < RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY && !plannedResult.mergeJoin) {
// TODO: see if a dependent join applies the other direction
return current;
}
RelationalPlan originalPlan = (RelationalPlan) plannedResult.query.getProcessorPlan();
Number originalCardinality = originalPlan.getRootNode().getEstimateNodeCardinality();
if (!plannedResult.mergeJoin && originalCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE) {
// if it's currently unknown, removing criteria won't make it any better
return current;
}
Collection<GroupSymbol> leftGroups = FrameUtil.findJoinSourceNode(current).getGroups();
if (!planQuery(leftGroups, false, plannedResult)) {
if (plannedResult.mergeJoin && analysisRecord != null && analysisRecord.recordAnnotations()) {
// $NON-NLS-1$ //$NON-NLS-2$
this.analysisRecord.addAnnotation(new Annotation(Annotation.HINTS, "Could not plan as a merge join: " + crit, "ignoring MJ hint", Priority.HIGH));
}
return current;
}
// check if the child is already ordered. TODO: see if the ordering is compatible.
PlanNode childSort = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN);
if (childSort != null) {
if (plannedResult.mergeJoin && analysisRecord != null && analysisRecord.recordAnnotations()) {
// $NON-NLS-1$ //$NON-NLS-2$
this.analysisRecord.addAnnotation(new Annotation(Annotation.HINTS, "Could not plan as a merge join since the parent join requires a sort: " + crit, "ignoring MJ hint", Priority.HIGH));
}
return current;
}
// add an order by, which hopefully will get pushed down
plannedResult.query.setOrderBy(new OrderBy(plannedResult.rightExpressions).clone());
for (OrderByItem item : plannedResult.query.getOrderBy().getOrderByItems()) {
int index = plannedResult.query.getProjectedSymbols().indexOf(item.getSymbol());
if (index >= 0 && !(item.getSymbol() instanceof ElementSymbol)) {
item.setSymbol((Expression) plannedResult.query.getProjectedSymbols().get(index).clone());
}
item.setExpressionPosition(index);
}
try {
// clone the symbols as they may change during planning
List<Expression> projectedSymbols = LanguageObject.Util.deepClone(plannedResult.query.getProjectedSymbols(), Expression.class);
// NOTE: we could tap into the relationalplanner at a lower level to get this in a plan node form,
// the major benefit would be to reuse the dependent join planning logic if possible.
RelationalPlan subPlan = (RelationalPlan) QueryOptimizer.optimizePlan(plannedResult.query, metadata, idGenerator, capFinder, analysisRecord, context);
Number planCardinality = subPlan.getRootNode().getEstimateNodeCardinality();
if (!plannedResult.mergeJoin) {
// if we don't have a specific hint, then use costing
if (planCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE || planCardinality.floatValue() > 10000000 || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() > 1000) || (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceCost * originalCardinality.floatValue() < planCardinality.floatValue() / (100 * Math.log(Math.max(4, sourceCost))))) {
// bail-out if both are unknown or the new plan is too large
if (analysisRecord != null && analysisRecord.recordDebug()) {
// $NON-NLS-1$ //$NON-NLS-2$
current.recordDebugAnnotation("cost of merge join plan was not favorable", null, "semi merge join will not be used", analysisRecord, metadata);
}
return current;
}
}
// assume dependent
if ((sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() != NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() < sourceCost / 8) || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() <= 1000)) {
plannedResult.makeInd = true;
}
/*if (plannedResult.makeInd
&& plannedResult.query.getCorrelatedReferences() == null
&& !plannedResult.not
&& plannedResult.leftExpressions.size() == 1) {
//TODO: this should just be a dependent criteria node to avoid sorts
}*/
// $NON-NLS-1$ //$NON-NLS-2$
current.recordDebugAnnotation("Conditions met (hint or cost)", null, "Converting to a semi merge join", analysisRecord, metadata);
PlanNode semiJoin = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
semiJoin.addGroups(current.getGroups());
Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(plannedResult.rightExpressions);
semiJoin.addGroups(groups);
semiJoin.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE);
semiJoin.setProperty(NodeConstants.Info.JOIN_TYPE, plannedResult.not ? JoinType.JOIN_ANTI_SEMI : JoinType.JOIN_SEMI);
semiJoin.setProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA, plannedResult.nonEquiJoinCriteria);
List<Criteria> joinCriteria = new ArrayList<Criteria>();
joinCriteria.addAll(plannedResult.nonEquiJoinCriteria);
for (int i = 0; i < plannedResult.leftExpressions.size(); i++) {
joinCriteria.add(new CompareCriteria((Expression) plannedResult.rightExpressions.get(i), CompareCriteria.EQ, (Expression) plannedResult.leftExpressions.get(i)));
}
semiJoin.setProperty(NodeConstants.Info.JOIN_CRITERIA, joinCriteria);
// nested subqueries are possibly being promoted, so they need their references updated
List<SymbolMap> refMaps = semiJoin.getAllReferences();
SymbolMap parentRefs = plannedResult.query.getCorrelatedReferences();
for (SymbolMap refs : refMaps) {
for (Map.Entry<ElementSymbol, Expression> ref : refs.asUpdatableMap().entrySet()) {
Expression expr = ref.getValue();
if (expr instanceof ElementSymbol) {
Expression convertedExpr = parentRefs.getMappedExpression((ElementSymbol) expr);
if (convertedExpr != null) {
ref.setValue(convertedExpr);
}
}
semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(ref.getValue()));
}
}
semiJoin.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, plannedResult.leftExpressions);
semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(plannedResult.leftExpressions));
semiJoin.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, plannedResult.rightExpressions);
semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(plannedResult.rightExpressions));
semiJoin.setProperty(NodeConstants.Info.SORT_RIGHT, SortOption.ALREADY_SORTED);
semiJoin.setProperty(NodeConstants.Info.OUTPUT_COLS, root.getProperty(NodeConstants.Info.OUTPUT_COLS));
List childOutput = (List) current.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
PlanNode toCorrect = root;
while (toCorrect != current) {
toCorrect.setProperty(NodeConstants.Info.OUTPUT_COLS, childOutput);
toCorrect = toCorrect.getFirstChild();
}
PlanNode node = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
node.setProperty(NodeConstants.Info.PROCESSOR_PLAN, subPlan);
node.setProperty(NodeConstants.Info.OUTPUT_COLS, projectedSymbols);
node.setProperty(NodeConstants.Info.EST_CARDINALITY, planCardinality);
node.addGroups(groups);
root.addAsParent(semiJoin);
semiJoin.addLastChild(node);
PlanNode result = current.getParent();
NodeEditor.removeChildNode(result, current);
RuleImplementJoinStrategy.insertSort(semiJoin.getFirstChild(), (List<Expression>) plannedResult.leftExpressions, semiJoin, metadata, capFinder, true, context);
if (plannedResult.makeInd && !plannedResult.not) {
// TODO: would like for an enhanced sort merge with the semi dep option to avoid the sorting
// this is a little different than a typical dependent join in that the right is the independent side
String id = RuleChooseDependent.nextId();
PlanNode dep = RuleChooseDependent.getDependentCriteriaNode(id, plannedResult.rightExpressions, plannedResult.leftExpressions, node, metadata, null, false, null);
semiJoin.getFirstChild().addAsParent(dep);
semiJoin.setProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE, id);
this.dependent = true;
}
return result;
} catch (QueryPlannerException e) {
// can't be done - probably access patterns - what about dependent
return current;
}
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class RulePlanProcedures method execute.
/**
* @see org.teiid.query.optimizer.relational.OptimizerRule#execute(org.teiid.query.optimizer.relational.plantree.PlanNode, org.teiid.query.metadata.QueryMetadataInterface, org.teiid.query.optimizer.capabilities.CapabilitiesFinder, org.teiid.query.optimizer.relational.RuleStack, org.teiid.query.analysis.AnalysisRecord, org.teiid.query.util.CommandContext)
*/
public PlanNode execute(PlanNode plan, final QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
for (PlanNode node : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
if (!FrameUtil.isProcedure(node.getFirstChild())) {
continue;
}
StoredProcedure proc = (StoredProcedure) node.getProperty(NodeConstants.Info.NESTED_COMMAND);
if (!proc.isProcedureRelational()) {
continue;
}
HashSet<ElementSymbol> inputSymbols = new HashSet<ElementSymbol>();
List<Reference> inputReferences = new LinkedList<Reference>();
PlanNode critNode = node.getParent();
List<Criteria> conjuncts = new LinkedList<Criteria>();
HashSet<ElementSymbol> coveredParams = new HashSet<ElementSymbol>();
for (Iterator<SPParameter> params = proc.getInputParameters().iterator(); params.hasNext(); ) {
SPParameter param = params.next();
ElementSymbol symbol = param.getParameterSymbol();
Expression input = param.getExpression();
inputReferences.add((Reference) input);
inputSymbols.add(symbol);
}
findInputNodes(inputSymbols, critNode, conjuncts, coveredParams);
List<Expression> defaults = new LinkedList<Expression>();
for (Reference ref : inputReferences) {
ElementSymbol symbol = ref.getExpression();
Expression defaultValue = null;
/*try {
defaultValue = ResolverUtil.getDefault(symbol, metadata);
} catch (QueryResolverException qre) {
//Just ignore
}*/
defaults.add(defaultValue);
if (defaultValue == null && !coveredParams.contains(symbol)) {
throw new QueryPlannerException(QueryPlugin.Event.TEIID30270, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30270, symbol));
}
}
/*if (conjuncts.isEmpty()) {
for (int j = 0; j < inputReferences.size(); j++) {
Reference ref = (Reference)inputReferences.get(j);
ref.setValue(defaults.get(j));
}
continue;
}*/
PlanNode accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS);
Criteria crit = Criteria.combineCriteria(conjuncts);
if (crit != null) {
accessNode.setProperty(NodeConstants.Info.PROCEDURE_CRITERIA, crit);
accessNode.setProperty(NodeConstants.Info.PROCEDURE_INPUTS, inputReferences);
accessNode.setProperty(NodeConstants.Info.PROCEDURE_DEFAULTS, defaults);
accessNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE);
}
}
return plan;
}
use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.
the class RuleAssignOutputElements method assignOutputElements.
/**
* <p>Assign the output elements at a particular node and recurse the tree. The
* outputElements needed from above the node have been collected in
* outputElements.</p>
*
* <p>SOURCE nodes: If we find a SOURCE node, this must define the top
* of a virtual group. Physical groups can be identified by ACCESS nodes
* at this point in the planning stage. So, we filter the virtual elements
* in the virtual source based on the required output elements.</p>
*
* <p>SET_OP nodes: If we hit a SET_OP node, this must be a union. Unions
* require a lot of special care. Unions have many branches and the projected
* elements in each branch are "equivalent" in terms of nodes above the union.
* This means that any filtering must occur in an identical way in all branches
* of a union.</p>
*
* @param root Node to assign
* @param outputElements Output elements needed for this node
* @param metadata Metadata implementation
*/
private void assignOutputElements(PlanNode root, List<Expression> outputElements, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
int nodeType = root.getType();
// Update this node's output columns based on parent's columns
List<Expression> oldOutput = (List<Expression>) root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
if (root.getChildCount() == 0) {
// update temp access
if (root.getType() == NodeConstants.Types.SOURCE && root.getGroups().size() == 1) {
GroupSymbol gs = root.getGroups().iterator().next();
if (gs.getMetadataID() instanceof TempMetadataID) {
for (Expression ex : outputElements) {
if (ex instanceof ElementSymbol) {
Object id = ((ElementSymbol) ex).getMetadataID();
if (id instanceof TempMetadataID) {
((TempMetadataID) id).setAccessed(true);
}
}
}
}
}
return;
}
switch(nodeType) {
case NodeConstants.Types.ACCESS:
Command command = FrameUtil.getNonQueryCommand(root);
if (command instanceof StoredProcedure) {
// if the access node represents a stored procedure, then we can't actually change the output symbols
root.setProperty(NodeConstants.Info.OUTPUT_COLS, command.getProjectedSymbols());
} else {
ProcessorPlan plan = FrameUtil.getNestedPlan(root);
if (plan != null && (command == null || !RelationalNodeUtil.isUpdate(command))) {
// nested with clauses are handled as sub plans, which have a fixed set of output symbols
root.setProperty(NodeConstants.Info.OUTPUT_COLS, ResolverUtil.resolveElementsInGroup(root.getGroups().iterator().next(), metadata));
}
if (checkSymbols) {
Object modelId = RuleRaiseAccess.getModelIDFromAccess(root, metadata);
for (Expression symbol : outputElements) {
if (!RuleRaiseAccess.canPushSymbol(symbol, true, modelId, metadata, capFinder, analysisRecord)) {
throw new QueryPlannerException(QueryPlugin.Event.TEIID30258, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30258, symbol, modelId));
}
}
}
if (NodeEditor.findParent(root, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE) != null) {
// there's a chance that partial projection was used. we are not a defacto project node
// take credit for creating anything that is not an element symbol
LinkedHashSet<Expression> filteredElements = new LinkedHashSet<Expression>();
for (Expression element : outputElements) {
if (element instanceof ElementSymbol) {
filteredElements.add(element);
} else {
filteredElements.addAll(ElementCollectorVisitor.getElements(element, false));
}
}
outputElements = new ArrayList<Expression>(filteredElements);
}
}
assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
break;
case NodeConstants.Types.DUP_REMOVE:
// targeted optimization based upon swapping the dup remove for a limit 1
// TODO: may need to also check for grouping over constants
boolean allConstants = true;
for (Expression ex : outputElements) {
if (!(EvaluatableVisitor.willBecomeConstant(SymbolMap.getExpression(ex)))) {
allConstants = false;
break;
}
}
if (allConstants && addLimit(rules, root, metadata, capFinder)) {
// TODO we could more gracefully handle the !addLimit case
PlanNode parent = root.getParent();
if (parent != null) {
NodeEditor.removeChildNode(root.getParent(), root);
execute(parent, metadata, capFinder, rules, analysisRecord, context);
return;
}
}
case NodeConstants.Types.SORT:
// correct expression positions and update the unrelated flag
OrderBy order = (OrderBy) root.getProperty(NodeConstants.Info.SORT_ORDER);
if (order != null && (oldOutput == null || !oldOutput.equals(outputElements))) {
outputElements = new ArrayList<Expression>(outputElements);
boolean hasUnrelated = false;
for (OrderByItem item : order.getOrderByItems()) {
int index = outputElements.indexOf(item.getSymbol());
if (index != -1) {
item.setExpressionPosition(index);
} else {
hasUnrelated = true;
outputElements.add(item.getSymbol());
}
}
if (!hasUnrelated) {
root.setProperty(NodeConstants.Info.UNRELATED_SORT, false);
} else {
root.setProperty(NodeConstants.Info.UNRELATED_SORT, true);
}
}
assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
break;
case NodeConstants.Types.TUPLE_LIMIT:
assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
break;
case NodeConstants.Types.SOURCE:
{
outputElements = (List<Expression>) determineSourceOutput(root, outputElements, metadata, capFinder);
if (!finalRun && root.getProperty(Info.PARTITION_INFO) != null && NodeEditor.findParent(root, NodeConstants.Types.JOIN) != null) {
GroupSymbol group = root.getGroups().iterator().next();
Object modelId = RuleDecomposeJoin.getEffectiveModelId(metadata, group);
String name = metadata.getExtensionProperty(modelId, RuleDecomposeJoin.IMPLICIT_PARTITION_COLUMN_NAME, true);
if (name != null) {
// keep projecting the implicit partitioning column through the source so that it can
// be used in the decomposition logic
ElementSymbol es = new ElementSymbol(name, group);
if (!outputElements.contains(es)) {
es.setMetadataID(metadata.getElementID(es.getName()));
outputElements.add(es);
}
}
}
root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
List<Expression> childElements = filterVirtualElements(root, outputElements, metadata);
assignOutputElements(root.getFirstChild(), childElements, metadata, capFinder, rules, analysisRecord, context);
break;
}
case NodeConstants.Types.SET_OP:
{
for (PlanNode childNode : root.getChildren()) {
PlanNode projectNode = NodeEditor.findNodePreOrder(childNode, NodeConstants.Types.PROJECT);
List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
assignOutputElements(childNode, projectCols, metadata, capFinder, rules, analysisRecord, context);
}
break;
}
default:
{
PlanNode sortNode = null;
if (root.getType() == NodeConstants.Types.PROJECT) {
GroupSymbol intoGroup = (GroupSymbol) root.getProperty(NodeConstants.Info.INTO_GROUP);
if (intoGroup != null) {
// if this is a project into, treat the nodes under the source as a new plan root
PlanNode intoRoot = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SOURCE);
execute(intoRoot.getFirstChild(), metadata, capFinder, rules, analysisRecord, context);
return;
}
List<Expression> projectCols = outputElements;
sortNode = NodeEditor.findParent(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
if (finalRun && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
root.getGroups().clear();
root.addGroups(GroupsUsedByElementsVisitor.getGroups(projectCols));
root.addGroups(GroupsUsedByElementsVisitor.getGroups(root.getCorrelatedReferenceElements()));
}
root.setProperty(NodeConstants.Info.PROJECT_COLS, projectCols);
if (root.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
Set<WindowFunction> windowFunctions = getWindowFunctions(projectCols);
if (windowFunctions.isEmpty()) {
root.setProperty(Info.HAS_WINDOW_FUNCTIONS, false);
}
}
}
List<Expression> requiredInput = collectRequiredInputSymbols(root, metadata, capFinder);
// targeted optimization for unnecessary aggregation
if (root.getType() == NodeConstants.Types.GROUP && root.hasBooleanProperty(Info.IS_OPTIONAL) && NodeEditor.findParent(root, NodeConstants.Types.ACCESS) == null) {
PlanNode parent = removeGroupBy(root, metadata);
if (!root.hasCollectionProperty(Info.GROUP_COLS)) {
// just lob off everything under the projection
PlanNode project = NodeEditor.findNodePreOrder(parent, NodeConstants.Types.PROJECT);
project.removeAllChildren();
} else if (!addLimit(rules, parent, metadata, capFinder)) {
// $NON-NLS-1$
throw new AssertionError("expected limit node to be added");
}
execute(parent, metadata, capFinder, rules, analysisRecord, context);
return;
}
// Call children recursively
if (root.getChildCount() == 1) {
assignOutputElements(root.getLastChild(), requiredInput, metadata, capFinder, rules, analysisRecord, context);
if (!finalRun && root.getType() == NodeConstants.Types.PROJECT && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
// if this is the initial rule run, remove unrelated order to preserve the original projection
OrderBy elements = (OrderBy) sortNode.getProperty(NodeConstants.Info.SORT_ORDER);
outputElements = new ArrayList<Expression>(outputElements);
for (OrderByItem item : elements.getOrderByItems()) {
if (item.getExpressionPosition() == -1) {
outputElements.remove(item.getSymbol());
}
}
root.setProperty(NodeConstants.Info.PROJECT_COLS, outputElements);
}
} else {
// determine which elements go to each side of the join
for (PlanNode childNode : root.getChildren()) {
Set<GroupSymbol> filterGroups = FrameUtil.findJoinSourceNode(childNode).getGroups();
List<Expression> filteredElements = filterElements(requiredInput, filterGroups);
// Call child recursively
assignOutputElements(childNode, filteredElements, metadata, capFinder, rules, analysisRecord, context);
}
}
}
}
}
Aggregations