use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinRegion method scoreRegion.
/**
* Will provide an estimate of cost by summing the estimated tuples flowing through
* each intermediate join.
*
* @param joinOrder
* @param metadata
* @return
* @throws TeiidComponentException
* @throws QueryMetadataException
* @throws QueryPlannerException
*/
public double scoreRegion(Object[] joinOrder, int startIndex, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context, boolean partial) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
List<Map.Entry<PlanNode, PlanNode>> joinSourceEntries = new ArrayList<Map.Entry<PlanNode, PlanNode>>(joinSourceNodes.entrySet());
double totalIntermediatCost = 0;
double cost = 1;
HashSet<PlanNode> criteria = new HashSet<PlanNode>(this.criteriaNodes);
HashSet<GroupSymbol> groups = new HashSet<GroupSymbol>(this.joinSourceNodes.size());
HashSet<GroupSymbol> rightGroups = new HashSet<GroupSymbol>();
List<Expression> leftExpressions = new ArrayList<Expression>();
List<Expression> rightExpressions = new ArrayList<Expression>();
HashSet<Criteria> nonEquiJoinCriteria = new HashSet<Criteria>();
// only calculate up to the second to last as the last is not an intermediate result
for (int i = 0; i < joinOrder.length - (partial ? 0 : 1); i++) {
boolean hasUnknown = false;
boolean shouldFilter = true;
Integer source = (Integer) joinOrder[i];
Map.Entry<PlanNode, PlanNode> entry = joinSourceEntries.get(source.intValue());
PlanNode joinSourceRoot = entry.getValue();
if (i >= startIndex) {
// check to make sure that this group ordering satisfies the access patterns
if (!this.unsatisfiedAccessPatterns.isEmpty() || this.containsNestedTable) {
PlanNode joinSource = entry.getKey();
Collection<GroupSymbol> requiredGroups = (Collection<GroupSymbol>) joinSource.getProperty(NodeConstants.Info.REQUIRED_ACCESS_PATTERN_GROUPS);
if (requiredGroups != null && !groups.containsAll(requiredGroups)) {
return Double.MAX_VALUE;
}
}
}
rightGroups.clear();
rightGroups.addAll(groups);
groups.addAll(joinSourceRoot.getGroups());
if (startIndex > 0 && i < startIndex) {
continue;
}
float sourceCost = joinSourceRoot.getCardinality();
List<PlanNode> applicableCriteria = null;
CompoundCriteria cc = null;
if (!criteria.isEmpty() && i > 0) {
applicableCriteria = getJoinCriteriaForGroups(groups, criteria);
if (applicableCriteria != null && !applicableCriteria.isEmpty()) {
cc = new CompoundCriteria();
for (PlanNode planNode : applicableCriteria) {
cc.addCriteria((Criteria) planNode.getProperty(NodeConstants.Info.SELECT_CRITERIA));
}
}
}
if (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE) {
sourceCost = UNKNOWN_TUPLE_EST;
hasUnknown = true;
if (cc != null) {
shouldFilter = false;
sourceCost = (float) cost;
criteria.removeAll(applicableCriteria);
if (NewCalculateCostUtil.usesKey(cc, metadata) || (i >= 1 && joinSourceRoot.hasProperty(Info.MAKE_DEP) && !joinSourceRoot.hasBooleanProperty(Info.MAKE_NOT_DEP))) {
sourceCost = Math.min(UNKNOWN_TUPLE_EST, sourceCost * Math.min(NewCalculateCostUtil.UNKNOWN_JOIN_SCALING, sourceCost));
} else {
sourceCost = Math.min(UNKNOWN_TUPLE_EST, sourceCost * NewCalculateCostUtil.UNKNOWN_JOIN_SCALING * 8);
}
}
} else if (Double.isInfinite(sourceCost) || Double.isNaN(sourceCost)) {
return Double.MAX_VALUE;
} else if (i == 1 && applicableCriteria != null && !applicableCriteria.isEmpty()) {
List<Object> key = Arrays.asList(joinOrder[0], joinOrder[1]);
Float depJoinCost = null;
if (depCache != null && depCache.containsKey(key)) {
depJoinCost = depCache.get(key);
} else {
Integer indIndex = (Integer) joinOrder[0];
Map.Entry<PlanNode, PlanNode> indEntry = joinSourceEntries.get(indIndex.intValue());
PlanNode possibleInd = indEntry.getValue();
depJoinCost = getDepJoinCost(metadata, capFinder, context, possibleInd, applicableCriteria, joinSourceRoot);
if (depCache == null) {
depCache = new HashMap<List<Object>, Float>();
}
depCache.put(key, depJoinCost);
}
if (depJoinCost != null) {
sourceCost = depJoinCost;
}
}
if (i > 0 && (applicableCriteria == null || applicableCriteria.isEmpty()) && hasUnknown) {
// cross join penalty
sourceCost *= 10;
}
double rightCost = cost;
cost *= sourceCost;
if (cc != null && applicableCriteria != null && shouldFilter) {
// filter based upon notion of join
leftExpressions.clear();
rightExpressions.clear();
nonEquiJoinCriteria.clear();
Collection<GroupSymbol> leftGroups = joinSourceRoot.getGroups();
RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, cc.getCriteria(), nonEquiJoinCriteria);
if (!leftExpressions.isEmpty()) {
float leftNdv = NewCalculateCostUtil.getNDVEstimate(joinSourceRoot, metadata, sourceCost, leftExpressions, null);
float rightNdv = NewCalculateCostUtil.UNKNOWN_VALUE;
if (leftNdv != NewCalculateCostUtil.UNKNOWN_VALUE) {
Set<GroupSymbol> usedRight = GroupsUsedByElementsVisitor.getGroups(rightExpressions);
for (int j = 0; j < i; j++) {
Entry<PlanNode, PlanNode> previousEntry = joinSourceEntries.get((int) joinOrder[j]);
if (previousEntry.getValue().getGroups().containsAll(usedRight)) {
rightNdv = NewCalculateCostUtil.getNDVEstimate(previousEntry.getValue(), metadata, sourceCost, rightExpressions, null);
break;
}
}
}
if (leftNdv != NewCalculateCostUtil.UNKNOWN_VALUE && rightNdv != NewCalculateCostUtil.UNKNOWN_VALUE) {
cost = (sourceCost / leftNdv) * (rightCost / rightNdv) * Math.min(leftNdv, rightNdv);
} else {
// check for a key
// just use the default logic
nonEquiJoinCriteria.clear();
}
} else {
// just use the default logic
nonEquiJoinCriteria.clear();
}
for (PlanNode criteriaNode : applicableCriteria) {
Criteria crit = (Criteria) criteriaNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (!nonEquiJoinCriteria.contains(crit)) {
continue;
}
float filter = ((Float) criteriaNode.getProperty(NodeConstants.Info.EST_SELECTIVITY)).floatValue();
cost *= filter;
}
criteria.removeAll(applicableCriteria);
}
totalIntermediatCost += cost;
}
return totalIntermediatCost;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinRegion method initializeJoinInformation.
/**
* Initializes information on the joinRegion about dependency information, etc.
*
* TODO: assumptions are made here about how dependent criteria must look that are a little restrictive
*/
public void initializeJoinInformation() {
critieriaToSourceMap = new HashMap<PlanNode, Set<PlanNode>>();
LinkedList<PlanNode> crits = new LinkedList<PlanNode>(criteriaNodes);
crits.addAll(dependentCritieraNodes);
LinkedHashMap<PlanNode, PlanNode> source = new LinkedHashMap<PlanNode, PlanNode>(joinSourceNodes);
source.putAll(dependentJoinSourceNodes);
for (PlanNode critNode : crits) {
for (GroupSymbol group : critNode.getGroups()) {
for (PlanNode node : source.keySet()) {
if (node.getGroups().contains(group)) {
Set<PlanNode> sources = critieriaToSourceMap.get(critNode);
if (sources == null) {
sources = new HashSet<PlanNode>();
critieriaToSourceMap.put(critNode, sources);
}
sources.add(node);
break;
}
}
}
}
if (unsatisfiedAccessPatterns.isEmpty()) {
return;
}
Map<GroupSymbol, PlanNode> dependentGroupToSourceMap = new HashMap<GroupSymbol, PlanNode>();
for (PlanNode node : dependentJoinSourceNodes.keySet()) {
for (GroupSymbol symbol : node.getGroups()) {
dependentGroupToSourceMap.put(symbol, node);
}
}
for (Iterator<PlanNode> i = getCriteriaNodes().iterator(); i.hasNext(); ) {
PlanNode node = i.next();
for (GroupSymbol symbol : node.getGroups()) {
if (dependentGroupToSourceMap.containsKey(symbol)) {
i.remove();
dependentCritieraNodes.add(node);
break;
}
}
}
dependentCriteriaElements = new HashMap<ElementSymbol, Set<Collection<GroupSymbol>>>();
for (PlanNode critNode : dependentCritieraNodes) {
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (!(crit instanceof CompareCriteria)) {
continue;
}
CompareCriteria compCrit = (CompareCriteria) crit;
if (compCrit.getOperator() != CompareCriteria.EQ) {
continue;
}
CompareCriteria compareCriteria = (CompareCriteria) crit;
// this may be a proper dependent join criteria
Collection<ElementSymbol>[] critElements = new Collection[2];
critElements[0] = ElementCollectorVisitor.getElements(compareCriteria.getLeftExpression(), true);
if (critElements[0].isEmpty()) {
continue;
}
critElements[1] = ElementCollectorVisitor.getElements(compareCriteria.getRightExpression(), true);
if (critElements[1].isEmpty()) {
continue;
}
for (int expr = 0; expr < critElements.length; expr++) {
// simplifying assumption that there will be a single element on the dependent side
if (critElements[expr].size() != 1) {
continue;
}
ElementSymbol elem = critElements[expr].iterator().next();
if (!dependentGroupToSourceMap.containsKey(elem.getGroupSymbol())) {
continue;
}
// this is also a simplifying assumption. don't consider criteria that can't be pushed
if (containsFunctionsThatCannotBePushed(expr == 0 ? compareCriteria.getRightExpression() : compareCriteria.getLeftExpression())) {
continue;
}
Set<Collection<GroupSymbol>> independentGroups = dependentCriteriaElements.get(elem);
if (independentGroups == null) {
independentGroups = new HashSet<Collection<GroupSymbol>>();
dependentCriteriaElements.put(elem, independentGroups);
}
// set the other side as independent elements
independentGroups.add(GroupsUsedByElementsVisitor.getGroups(critElements[(expr + 1) % 2]));
}
}
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinRegion method reconstructJoinRegoin.
/**
* This will rebuild the join tree starting at the join root.
*
* A left linear tree will be constructed out of the ordering of the
* join sources.
*
* Criteria nodes are simply placed at the top of the join region in order
* to be pushed by rule PushSelectSriteria.
*/
public void reconstructJoinRegoin() {
LinkedHashMap<PlanNode, PlanNode> combined = new LinkedHashMap<PlanNode, PlanNode>(joinSourceNodes);
combined.putAll(dependentJoinSourceNodes);
PlanNode root = null;
if (combined.size() < 2) {
root = combined.values().iterator().next();
} else {
root = RulePlanJoins.createJoinNode();
for (Map.Entry<PlanNode, PlanNode> entry : combined.entrySet()) {
PlanNode joinSourceRoot = entry.getValue();
if (root.getChildCount() == 2) {
PlanNode parentJoin = RulePlanJoins.createJoinNode();
parentJoin.addFirstChild(root);
parentJoin.addGroups(root.getGroups());
root = parentJoin;
}
root.addLastChild(joinSourceRoot);
root.addGroups(entry.getKey().getGroups());
}
}
LinkedList<PlanNode> criteria = new LinkedList<PlanNode>(dependentCritieraNodes);
criteria.addAll(criteriaNodes);
PlanNode parent = this.joinRoot.getParent();
boolean isLeftChild = parent.getFirstChild() == this.joinRoot;
parent.removeChild(joinRoot);
for (PlanNode critNode : criteria) {
critNode.removeFromParent();
critNode.removeAllChildren();
critNode.addFirstChild(root);
root = critNode;
critNode.removeProperty(NodeConstants.Info.IS_COPIED);
critNode.removeProperty(NodeConstants.Info.EST_CARDINALITY);
}
if (isLeftChild) {
parent.addFirstChild(root);
} else {
parent.addLastChild(root);
}
this.joinRoot = root;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinRegion method getDepJoinCost.
private Float getDepJoinCost(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context, PlanNode indNode, List<PlanNode> applicableCriteria, PlanNode depNode) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
if (depNode.hasBooleanProperty(Info.MAKE_NOT_DEP)) {
return null;
}
float indCost = indNode.getCardinality();
if (indCost == NewCalculateCostUtil.UNKNOWN_VALUE) {
return null;
}
List<Criteria> crits = new ArrayList<Criteria>(applicableCriteria.size());
for (PlanNode planNode : applicableCriteria) {
crits.add((Criteria) planNode.getProperty(NodeConstants.Info.SELECT_CRITERIA));
}
List<Expression> leftExpressions = new LinkedList<Expression>();
List<Expression> rightExpressions = new LinkedList<Expression>();
RuleChooseJoinStrategy.separateCriteria(indNode.getGroups(), depNode.getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
if (leftExpressions.isEmpty()) {
return null;
}
return NewCalculateCostUtil.computeCostForDepJoin(indNode, depNode, leftExpressions, rightExpressions, metadata, capFinder, context).expectedCardinality;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinRegion method estimateCriteriaSelectivity.
/**
* @param metadata
* @throws QueryMetadataException
* @throws TeiidComponentException
*/
private void estimateCriteriaSelectivity(QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
for (PlanNode node : criteriaNodes) {
Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
float[] baseCosts = new float[] { 100, 10000, 1000000 };
float filterValue = 0;
for (int j = 0; j < baseCosts.length; j++) {
float filter = NewCalculateCostUtil.recursiveEstimateCostOfCriteria(baseCosts[j], node, crit, metadata);
filterValue += filter / baseCosts[j];
}
filterValue /= baseCosts.length;
node.setProperty(NodeConstants.Info.EST_SELECTIVITY, new Float(filterValue));
}
}
Aggregations