use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinUtil method optimizeJoinType.
/**
* Will attempt to optimize the join type based upon the criteria provided.
*
* Returns the new join type if one is found, otherwise null
*
* An outer join can be optimized if criteria that is not dependent upon null values
* is applied on the inner side of the join.
*
* @param critNode
* @param joinNode
* @return
*/
static final JoinType optimizeJoinType(PlanNode critNode, PlanNode joinNode, QueryMetadataInterface metadata, boolean modifyJoin) {
if (critNode.getGroups().isEmpty() || !joinNode.getGroups().containsAll(critNode.getGroups()) || joinNode.hasBooleanProperty(Info.PRESERVE)) {
return null;
}
JoinType joinType = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (!joinType.isOuter()) {
return null;
}
PlanNode left = joinNode.getFirstChild();
left = FrameUtil.findJoinSourceNode(left);
PlanNode right = joinNode.getLastChild();
right = FrameUtil.findJoinSourceNode(right);
Collection<GroupSymbol> outerGroups = left.getGroups();
Collection<GroupSymbol> innerGroups = right.getGroups();
if (joinType == JoinType.JOIN_RIGHT_OUTER) {
outerGroups = innerGroups;
innerGroups = left.getGroups();
}
// sanity check
if ((joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_RIGHT_OUTER) && outerGroups.containsAll(critNode.getGroups())) {
return null;
}
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
boolean isNullDepdendent = isNullDependent(metadata, innerGroups, crit);
JoinType result = JoinType.JOIN_INNER;
if (joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_RIGHT_OUTER) {
if (isNullDepdendent) {
return null;
}
} else {
boolean isNullDepdendentOther = isNullDependent(metadata, outerGroups, crit);
if (isNullDepdendent && isNullDepdendentOther) {
return null;
}
if (isNullDepdendent && !isNullDepdendentOther) {
result = JoinType.JOIN_LEFT_OUTER;
} else if (!isNullDepdendent && isNullDepdendentOther) {
if (modifyJoin) {
JoinUtil.swapJoinChildren(joinNode);
result = JoinType.JOIN_LEFT_OUTER;
}
}
}
if (modifyJoin) {
joinNode.setProperty(NodeConstants.Info.JOIN_TYPE, result);
}
return result;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinUtil method swapJoinChildren.
/**
* @param joinNode
*/
static void swapJoinChildren(PlanNode joinNode) {
PlanNode leftChild = joinNode.getFirstChild();
joinNode.removeChild(leftChild);
joinNode.addLastChild(leftChild);
List leftExpressions = (List) joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
List rightExpressions = (List) joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
joinNode.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, rightExpressions);
joinNode.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, leftExpressions);
JoinType jt = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
joinNode.setProperty(NodeConstants.Info.JOIN_TYPE, jt.getReverseType());
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class JoinUtil method getInnerSideJoinNodes.
/**
* Can be called after join planning on a join node to get the inner sides of the join
* @param joinNode
* @return
*/
static PlanNode[] getInnerSideJoinNodes(PlanNode joinNode) {
Assertion.assertTrue(joinNode.getType() == NodeConstants.Types.JOIN);
JoinType jt = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (jt == JoinType.JOIN_INNER || jt == JoinType.JOIN_CROSS) {
return new PlanNode[] { joinNode.getFirstChild(), joinNode.getLastChild() };
}
if (jt == JoinType.JOIN_RIGHT_OUTER) {
return new PlanNode[] { joinNode.getFirstChild() };
}
if (jt == JoinType.JOIN_LEFT_OUTER) {
return new PlanNode[] { joinNode.getLastChild() };
}
// must be full outer, so there is no inner side
return new PlanNode[] {};
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class NewCalculateCostUtil method getNDVEstimate.
/**
* @param indNode
* @param metadata
* @param cardinality
* @param elems
* @param useCardinalityIfUnknown - false is a low estimate, null uses a middle estimate, and true uses a high estimate
* @return
* @throws QueryMetadataException
* @throws TeiidComponentException
*/
static float getNDVEstimate(PlanNode indNode, QueryMetadataInterface metadata, float cardinality, Collection<? extends Expression> elems, Boolean useCardinalityIfUnknown) throws QueryMetadataException, TeiidComponentException {
if (elems == null || elems.isEmpty()) {
return cardinality;
}
float ndv = getStat(Stat.NDV, elems, indNode, cardinality, metadata);
// if we're using cardinality, then we want to include the high estimate
if (ndv != UNKNOWN_VALUE && (useCardinalityIfUnknown == null || useCardinalityIfUnknown)) {
float ndv_high = getStat(Stat.NDV_HIGH, elems, indNode, cardinality, metadata);
if (ndv_high != UNKNOWN_VALUE) {
if (useCardinalityIfUnknown == null) {
ndv = (float) Math.sqrt(ndv * ndv_high);
} else {
ndv = (ndv + ndv_high) / 2;
}
}
}
// special handling if cardinality has been set, but not ndv
if (ndv == UNKNOWN_VALUE && (useCardinalityIfUnknown == null || useCardinalityIfUnknown)) {
Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(elems);
PlanNode source = FrameUtil.findOriginatingNode(indNode, groups);
if (source != null) {
ndv = getStat(Stat.NDV, elems, source, source.getCardinality(), metadata);
if (ndv == UNKNOWN_VALUE) {
if (useCardinalityIfUnknown != null || source.getChildCount() == 0) {
ndv = source.getCardinality();
}
if (ndv != UNKNOWN_VALUE && !usesKey(source, elems, metadata)) {
// guess that it's non-unique
ndv /= 2;
}
}
if (ndv != UNKNOWN_VALUE) {
while (source != indNode) {
source = source.getParent();
float parentCardinality = source.getCardinality();
if (parentCardinality != UNKNOWN_VALUE && parentCardinality < ndv) {
ndv = parentCardinality;
}
}
}
}
}
if (ndv == UNKNOWN_VALUE) {
if (cardinality == UNKNOWN_VALUE) {
return UNKNOWN_VALUE;
}
if (usesKey(indNode, elems, metadata)) {
ndv = cardinality;
} else if (useCardinalityIfUnknown != null && useCardinalityIfUnknown) {
ndv = cardinality / 2;
} else {
return UNKNOWN_VALUE;
}
}
if (cardinality != UNKNOWN_VALUE && cardinality < ndv) {
ndv = cardinality;
}
return Math.max(1, ndv);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class NewCalculateCostUtil method estimateSourceNodeCost.
/**
* For a source node, the cost is basically the cardinality of the source
* (if it is known).
* @param node
* @param metadata
* @throws QueryMetadataException
* @throws TeiidComponentException
*/
private static void estimateSourceNodeCost(PlanNode node, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
float cost = UNKNOWN_VALUE;
if (node.getChildCount() > 0) {
SymbolMap references = (SymbolMap) node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
// only cost non-correlated TODO: a better estimate for correlated
if (references == null) {
PlanNode child = node.getFirstChild();
cost = child.getCardinality();
SymbolMap symbolMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
if (symbolMap != null) {
ColStats colStats = (ColStats) child.getProperty(Info.EST_COL_STATS);
if (colStats != null) {
List<? extends Expression> outputCols = getOutputCols(node, metadata);
ColStats newColStats = new ColStats();
for (Expression expr : outputCols) {
if (!(expr instanceof ElementSymbol)) {
continue;
}
ElementSymbol es = (ElementSymbol) expr;
Expression ex = symbolMap.getMappedExpression(es);
float[] value = colStats.get(ex);
if (value == null) {
Collection<ElementSymbol> elems = ElementCollectorVisitor.getElements(ex, true);
value = new float[3];
value[Stat.NDV.ordinal()] = getStat(Stat.NDV, elems, node, cost, metadata);
value[Stat.NDV_HIGH.ordinal()] = getStat(Stat.NDV_HIGH, elems, node, cost, metadata);
value[Stat.NNV.ordinal()] = getStat(Stat.NNV, elems, node, cost, metadata);
}
newColStats.put(es, value);
}
node.setProperty(Info.EST_COL_STATS, newColStats);
} else {
colStats = createColStats(node, metadata, cost);
node.setProperty(Info.EST_COL_STATS, colStats);
}
}
}
} else {
GroupSymbol group = node.getGroups().iterator().next();
float cardinality = metadata.getCardinality(group.getMetadataID());
if (cardinality <= QueryMetadataInterface.UNKNOWN_CARDINALITY) {
if (group.isTempTable() && metadata.getModelID(group.getMetadataID()) == TempMetadataAdapter.TEMP_MODEL) {
// this should be with-in the scope of a procedure or an undefined size common table
//
// the typical assumption is that this should drive other joins, thus assume
// a relatively small number of rows. This is a relatively safe assumption
// as we do not need parallel processing with the temp fetch and the
// dependent join backoff should prevent unacceptable performance
//
// another strategy (that is generally applicable) is to delay the full affect of dependent join planning
// until the size is known - however that is somewhat complicated with the current WITH logic
// as the table is loaded on demand
cardinality = BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE;
} else {
cardinality = UNKNOWN_VALUE;
}
}
cost = cardinality;
if (!node.hasProperty(Info.ATOMIC_REQUEST)) {
ColStats colStats = createColStats(node, metadata, cost);
node.setProperty(Info.EST_COL_STATS, colStats);
}
}
setCardinalityEstimate(node, new Float(cost), false, metadata);
}
Aggregations