use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class NewCalculateCostUtil method estimateJoinNodeCost.
/**
* Method estimateJoinNodeCost.
* @param node
* @param metadata
*/
private static void estimateJoinNodeCost(PlanNode node, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
Iterator<PlanNode> children = node.getChildren().iterator();
PlanNode child1 = children.next();
float childCost1 = child1.getCardinality();
PlanNode child2 = children.next();
float childCost2 = child2.getCardinality();
if (childCost1 == UNKNOWN_VALUE || childCost2 == UNKNOWN_VALUE) {
setCardinalityEstimate(node, null, true, metadata);
return;
}
JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
List joinCriteria = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
float baseCost = childCost1 * childCost2;
float leftPercent = 1;
float rightPercent = 1;
if (joinCriteria != null && !joinCriteria.isEmpty()) {
List<Expression> leftExpressions = null;
List<Expression> rightExpressions = null;
List<Criteria> nonEquiJoinCriteria = null;
if (!node.hasCollectionProperty(NodeConstants.Info.LEFT_EXPRESSIONS)) {
Collection<GroupSymbol> leftGroups = child1.getGroups();
Collection<GroupSymbol> rightGroups = child2.getGroups();
leftExpressions = new ArrayList<Expression>();
rightExpressions = new ArrayList<Expression>();
nonEquiJoinCriteria = new ArrayList<Criteria>();
RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, joinCriteria, nonEquiJoinCriteria);
} else {
leftExpressions = (List<Expression>) node.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
rightExpressions = (List<Expression>) node.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
nonEquiJoinCriteria = (List<Criteria>) node.getProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA);
}
float leftNdv = getNDVEstimate(child1, metadata, childCost1, leftExpressions, false);
float rightNdv = getNDVEstimate(child2, metadata, childCost2, rightExpressions, false);
float leftNdv1 = getNDVEstimate(child1, metadata, childCost1, leftExpressions, null);
float rightNdv1 = getNDVEstimate(child2, metadata, childCost2, rightExpressions, null);
if (leftNdv == UNKNOWN_VALUE) {
leftNdv = leftNdv1;
}
if (rightNdv == UNKNOWN_VALUE) {
rightNdv = rightNdv1;
}
if (leftNdv != UNKNOWN_VALUE && rightNdv != UNKNOWN_VALUE) {
// Compensate for estimates by assuming a 1-many relationship
if (leftNdv1 > 2 * leftNdv && leftNdv > rightNdv) {
leftNdv = (float) Math.sqrt(rightNdv * leftNdv);
}
if (rightNdv1 > 2 * rightNdv && rightNdv > leftNdv) {
rightNdv = (float) Math.sqrt(rightNdv * leftNdv);
}
baseCost = (childCost1 / leftNdv) * (childCost2 / rightNdv) * Math.min(leftNdv, rightNdv);
leftPercent = Math.min(leftNdv, rightNdv) / leftNdv;
rightPercent = Math.min(leftNdv, rightNdv) / rightNdv;
} else {
nonEquiJoinCriteria = joinCriteria;
}
if (!nonEquiJoinCriteria.isEmpty()) {
Criteria crit = Criteria.combineCriteria(nonEquiJoinCriteria);
// TODO: we may be able to get a fairly accurate join estimate if the
// unknown side is being joined with a key
baseCost = recursiveEstimateCostOfCriteria(baseCost, node, crit, metadata);
}
}
Float cost = null;
if (JoinType.JOIN_CROSS.equals(joinType) || JoinType.JOIN_INNER.equals(joinType)) {
cost = baseCost;
} else if (JoinType.JOIN_FULL_OUTER.equals(joinType)) {
cost = Math.max((childCost1 + childCost2), baseCost);
} else if (JoinType.JOIN_LEFT_OUTER.equals(joinType)) {
cost = Math.max(childCost1, baseCost);
} else if (JoinType.JOIN_SEMI.equals(joinType) || JoinType.JOIN_ANTI_SEMI.equals(joinType)) {
cost = Math.min(childCost1, baseCost);
}
setCardinalityEstimate(node, cost, true, metadata, leftPercent, rightPercent);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class NewCalculateCostUtil method getOutputCols.
private static List<? extends Expression> getOutputCols(PlanNode node, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
List<Expression> outputCols = (List<Expression>) node.getProperty(Info.OUTPUT_COLS);
if (outputCols != null) {
return outputCols;
}
PlanNode projectNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.PROJECT | NodeConstants.Types.GROUP | NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN | NodeConstants.Types.NULL);
if (projectNode != null) {
node = projectNode;
}
if (node.getType() == NodeConstants.Types.PROJECT) {
return (List<? extends Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS);
} else if (node.getType() == NodeConstants.Types.GROUP) {
SymbolMap map = (SymbolMap) node.getProperty(Info.SYMBOL_MAP);
return map.getKeys();
}
LinkedList<ElementSymbol> elements = new LinkedList<ElementSymbol>();
for (GroupSymbol group : node.getGroups()) {
elements.addAll(ResolverUtil.resolveElementsInGroup(group, metadata));
}
return elements;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleAssignOutputElements method collectRequiredInputSymbols.
/**
* Collect all required input symbols for a given node. Input symbols
* are any symbols that are required in the processing of this node,
* for instance to create a new element symbol or sort on it, etc.
* @param node Node to collect for
* @param metadata
* @param capFinder
* @throws TeiidComponentException
* @throws QueryMetadataException
*/
private List<Expression> collectRequiredInputSymbols(PlanNode node, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException {
Set<Expression> requiredSymbols = new LinkedHashSet<Expression>();
Set<Expression> createdSymbols = new HashSet<Expression>();
List<Expression> outputCols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
switch(node.getType()) {
case NodeConstants.Types.PROJECT:
{
List<Expression> projectCols = (List<Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS);
PlanNode accessParent = NodeEditor.findParent(node, NodeConstants.Types.ACCESS);
PlanNode accessNode = null;
if (accessParent == null) {
// find the direct access node
accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP | NodeConstants.Types.GROUP);
}
for (Expression ss : projectCols) {
if (ss instanceof AliasSymbol) {
createdSymbols.add(ss);
ss = ((AliasSymbol) ss).getSymbol();
}
if (ss instanceof WindowFunction || ss instanceof ExpressionSymbol) {
createdSymbols.add(ss);
}
if (!pushProjection(node, metadata, capFinder, requiredSymbols, accessParent, accessNode, ss)) {
ElementCollectorVisitor.getElements(ss, requiredSymbols);
}
}
break;
}
case NodeConstants.Types.SELECT:
Criteria selectCriteria = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
ElementCollectorVisitor.getElements(selectCriteria, requiredSymbols);
break;
case NodeConstants.Types.JOIN:
List<Criteria> crits = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
if (crits != null) {
for (Criteria joinCriteria : crits) {
ElementCollectorVisitor.getElements(joinCriteria, requiredSymbols);
}
}
break;
case NodeConstants.Types.GROUP:
List<Expression> groupCols = (List<Expression>) node.getProperty(NodeConstants.Info.GROUP_COLS);
PlanNode accessParent = NodeEditor.findParent(node, NodeConstants.Types.ACCESS);
PlanNode accessNode = null;
if (accessParent == null) {
// find the direct access node
accessNode = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.ACCESS, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP | NodeConstants.Types.GROUP);
}
if (groupCols != null) {
for (Expression expression : groupCols) {
if (!pushProjection(node, metadata, capFinder, requiredSymbols, accessParent, accessNode, expression)) {
ElementCollectorVisitor.getElements(expression, requiredSymbols);
}
}
}
SymbolMap symbolMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
Set<ElementSymbol> usedAggregates = new HashSet<ElementSymbol>();
// Take credit for creating any aggregates that are needed above
for (Expression outputSymbol : outputCols) {
if (!(outputSymbol instanceof ElementSymbol)) {
continue;
}
createdSymbols.add(outputSymbol);
Expression ex = symbolMap.getMappedExpression((ElementSymbol) outputSymbol);
if (ex instanceof AggregateSymbol) {
AggregateSymbol agg = (AggregateSymbol) ex;
Expression[] aggExprs = agg.getArgs();
for (Expression expression : aggExprs) {
if (!pushProjection(node, metadata, capFinder, requiredSymbols, accessParent, accessNode, expression)) {
ElementCollectorVisitor.getElements(expression, requiredSymbols);
}
}
OrderBy orderBy = agg.getOrderBy();
if (orderBy != null) {
ElementCollectorVisitor.getElements(orderBy, requiredSymbols);
}
Expression condition = agg.getCondition();
if (condition != null) {
ElementCollectorVisitor.getElements(condition, requiredSymbols);
}
usedAggregates.add((ElementSymbol) outputSymbol);
}
}
// update the aggs in the symbolmap
for (Map.Entry<ElementSymbol, Expression> entry : new ArrayList<Map.Entry<ElementSymbol, Expression>>(symbolMap.asMap().entrySet())) {
if (entry.getValue() instanceof AggregateSymbol && !usedAggregates.contains(entry.getKey())) {
symbolMap.asUpdatableMap().remove(entry.getKey());
}
}
if (requiredSymbols.isEmpty() && usedAggregates.isEmpty()) {
node.setProperty(Info.IS_OPTIONAL, true);
}
break;
}
// Gather elements from correlated subquery references;
for (SymbolMap refs : node.getAllReferences()) {
for (Expression expr : refs.asMap().values()) {
ElementCollectorVisitor.getElements(expr, requiredSymbols);
}
}
// Add any columns to required that are in this node's output but were not created here
for (Expression currentOutputSymbol : outputCols) {
if (!createdSymbols.contains(currentOutputSymbol) && (finalRun || node.getType() != NodeConstants.Types.PROJECT || currentOutputSymbol instanceof ElementSymbol)) {
requiredSymbols.add(currentOutputSymbol);
}
}
// TODO: this should depend upon whether the expressions are deterministic
if (node.getType() == NodeConstants.Types.PROJECT) {
Set<Expression> expressions = new HashSet<Expression>();
for (Iterator<Expression> iterator = requiredSymbols.iterator(); iterator.hasNext(); ) {
Expression ses = iterator.next();
if (!expressions.add(SymbolMap.getExpression(ses))) {
iterator.remove();
}
}
}
return new ArrayList<Expression>(requiredSymbols);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleAssignOutputElements method removeGroupBy.
static PlanNode removeGroupBy(PlanNode root, QueryMetadataInterface metadata) throws QueryPlannerException {
PlanNode next = root.getFirstChild();
NodeEditor.removeChildNode(root.getParent(), root);
SymbolMap symbolMap = (SymbolMap) root.getProperty(NodeConstants.Info.SYMBOL_MAP);
if (!symbolMap.asMap().isEmpty()) {
FrameUtil.convertFrame(next.getParent(), symbolMap.asMap().keySet().iterator().next().getGroupSymbol(), null, symbolMap.asMap(), metadata);
}
PlanNode parent = next.getParent();
while (parent.getParent() != null && parent.getParent().getType() != NodeConstants.Types.SOURCE && parent.getParent().getType() != NodeConstants.Types.SET_OP) {
parent = parent.getParent();
}
return parent;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleAssignOutputElements method filterVirtualElements.
/**
* <p>This method looks at a source node, which defines a virtual group, and filters the
* virtual elements defined by the group down into just the output elements needed
* by that source node. This means, for instance, that the PROJECT node at the top
* of the virtual group might need to have some elements removed from the project as
* those elements are no longer needed. </p>
*
* <p>One special case that is handled here is when a virtual group is defined by
* a UNION ALL. In this case, the various branches of the union have elements defined
* and filtering must occur identically in all branches of the union. </p>
*
* @param sourceNode Node to filter
* @param metadata Metadata implementation
* @return The filtered list of columns for this node (used in recursing tree)
* @throws QueryPlannerException
*/
static List<Expression> filterVirtualElements(PlanNode sourceNode, List<Expression> outputColumns, QueryMetadataInterface metadata) throws QueryPlannerException {
PlanNode virtualRoot = sourceNode.getLastChild();
// Update project cols - typically there is exactly one and that node can
// just get the filteredCols determined above. In the case of one or more
// nested set operations (UNION, INTERSECT, EXCEPT) there will be 2 or more
// projects.
List<PlanNode> allProjects = NodeEditor.findAllNodes(virtualRoot, NodeConstants.Types.PROJECT, NodeConstants.Types.PROJECT);
int[] filteredIndex = new int[outputColumns.size()];
Arrays.fill(filteredIndex, -1);
SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
List<ElementSymbol> originalOrder = symbolMap.getKeys();
boolean updateGroups = outputColumns.size() != originalOrder.size();
boolean[] seenIndex = new boolean[outputColumns.size()];
boolean newSymbols = false;
int newSymbolIndex = 0;
for (int i = 0; i < outputColumns.size(); i++) {
Expression expr = outputColumns.get(i);
filteredIndex[i] = originalOrder.indexOf(expr);
if (filteredIndex[i] == -1) {
updateGroups = true;
// we're adding this symbol, which needs to be updated against respective symbol maps
newSymbols = true;
} else {
newSymbolIndex++;
}
if (!updateGroups) {
seenIndex[filteredIndex[i]] = true;
}
}
if (!updateGroups) {
for (boolean b : seenIndex) {
if (!b) {
updateGroups = true;
break;
}
}
}
List<Expression> newCols = null;
for (int i = allProjects.size() - 1; i >= 0; i--) {
PlanNode projectNode = allProjects.get(i);
List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
newCols = RelationalNode.projectTuple(filteredIndex, projectCols, true);
if (newSymbols) {
SymbolMap childMap = SymbolMap.createSymbolMap(symbolMap.getKeys(), projectCols);
for (int j = 0; j < filteredIndex.length; j++) {
if (filteredIndex[j] != -1) {
continue;
}
Expression ex = (Expression) outputColumns.get(j).clone();
ExpressionMappingVisitor.mapExpressions(ex, childMap.asMap());
newCols.set(j, ex);
if (i == 0) {
filteredIndex[j] = newSymbolIndex++;
}
}
}
projectNode.setProperty(NodeConstants.Info.PROJECT_COLS, newCols);
if (updateGroups) {
projectNode.getGroups().clear();
projectNode.addGroups(GroupsUsedByElementsVisitor.getGroups(newCols));
projectNode.addGroups(GroupsUsedByElementsVisitor.getGroups(projectNode.getCorrelatedReferenceElements()));
}
}
if (!updateGroups) {
for (int i : filteredIndex) {
if (i != filteredIndex[i]) {
updateGroups = true;
break;
}
}
}
if (updateGroups) {
SymbolMap newMap = new SymbolMap();
List<Expression> originalExpressionOrder = symbolMap.getValues();
for (int i = 0; i < filteredIndex.length; i++) {
if (filteredIndex[i] < originalOrder.size()) {
newMap.addMapping(originalOrder.get(filteredIndex[i]), originalExpressionOrder.get(filteredIndex[i]));
}
// else TODO: we may need to create a fake symbol
}
sourceNode.setProperty(NodeConstants.Info.SYMBOL_MAP, newMap);
}
// Create output columns for virtual group project
return newCols;
}
Aggregations