use of org.teiid.query.sql.lang.CompareCriteria in project teiid by teiid.
the class RulePlanJoins method groupJoinsForPushing.
/**
* This is a heuristic that checks for joins that may be pushed so they can be removed
* before considering the joins that must be evaluated in MetaMatrix.
*
* By running this, we eliminate the need for running RuleRaiseAccess during join ordering
*
* @param metadata
* @param joinRegion
* @throws QueryMetadataException
* @throws TeiidComponentException
* @throws QueryPlannerException
*/
private void groupJoinsForPushing(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, JoinRegion joinRegion, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
// TODO: consider moving select criteria if it is preventing a join from being pushed down
// TODO: make the criteria checks based upon a guess at selectivity
Map accessMap = getAccessMap(metadata, capFinder, joinRegion);
boolean structureChanged = false;
// search for combinations of join sources that should be pushed down
for (Iterator accessNodeIter = accessMap.entrySet().iterator(); accessNodeIter.hasNext(); ) {
Map.Entry entry = (Map.Entry) accessNodeIter.next();
List<PlanNode> accessNodes = (List) entry.getValue();
if (accessNodes.size() < 2) {
continue;
}
int secondPass = -1;
for (int i = accessNodes.size() - 1; i >= 0; i--) {
PlanNode accessNode1 = accessNodes.get(i);
Object modelId = RuleRaiseAccess.getModelIDFromAccess(accessNode1, metadata);
SupportedJoinCriteria sjc = CapabilitiesUtil.getSupportedJoinCriteria(modelId, metadata, capFinder);
int discoveredJoin = -1;
for (int k = (secondPass == -1 ? accessNodes.size() - 1 : secondPass); k >= 0; k--) {
if (k == i) {
continue;
}
PlanNode accessNode2 = accessNodes.get(k);
List<PlanNode> criteriaNodes = joinRegion.getCriteriaNodes();
List<PlanNode> joinCriteriaNodes = new LinkedList<PlanNode>();
/* hasJoinCriteria will be true if
* 1. there is criteria between accessNode1 and accessNode2 exclusively
* 2. there is criteria between some other source (not the same logical connector) and accessNode1 or accessNode2
*
* Ideally we should be a little smarter in case 2
* - pushing down a same source cross join can be done if we know that a dependent join will be performed
*/
boolean hasJoinCriteria = false;
LinkedList<Criteria> joinCriteria = new LinkedList<Criteria>();
for (PlanNode critNode : criteriaNodes) {
Set<PlanNode> sources = joinRegion.getCritieriaToSourceMap().get(critNode);
if (sources == null) {
continue;
}
if (sources.contains(accessNode1)) {
if (sources.contains(accessNode2) && sources.size() == 2) {
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (RuleRaiseAccess.isSupportedJoinCriteria(sjc, crit, modelId, metadata, capFinder, null)) {
joinCriteriaNodes.add(critNode);
joinCriteria.add(crit);
}
} else if (!accessNodes.containsAll(sources)) {
hasJoinCriteria = true;
}
} else if (sources.contains(accessNode2) && !accessNodes.containsAll(sources)) {
hasJoinCriteria = true;
}
}
/*
* If we failed to find direct criteria, a cross join may still be acceptable
*/
if (joinCriteriaNodes.isEmpty() && (hasJoinCriteria || !canPushCrossJoin(metadata, accessNode1, accessNode2))) {
continue;
}
List<PlanNode> toTest = Arrays.asList(accessNode1, accessNode2);
JoinType joinType = joinCriteria.isEmpty() ? JoinType.JOIN_CROSS : JoinType.JOIN_INNER;
/*
* We need to limit the heuristic grouping as we don't want to create larger source queries than necessary
*/
boolean shouldPush = true;
int sourceCount = NodeEditor.findAllNodes(accessNode1, NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE).size();
sourceCount += NodeEditor.findAllNodes(accessNode2, NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE).size();
if (!context.getOptions().isAggressiveJoinGrouping() && accessMap.size() > 1 && joinType == JoinType.JOIN_INNER && (sourceCount > 2 && (accessNode1.hasProperty(Info.MAKE_DEP) || accessNode2.hasProperty(Info.MAKE_DEP)) || sourceCount > 3) && !canPushCrossJoin(metadata, accessNode1, accessNode2)) {
Collection<GroupSymbol> leftGroups = accessNode1.getGroups();
Collection<GroupSymbol> rightGroups = accessNode2.getGroups();
List<Expression> leftExpressions = new ArrayList<Expression>();
List<Expression> rightExpressions = new ArrayList<Expression>();
List<Criteria> nonEquiJoinCriteria = new ArrayList<Criteria>();
RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, joinCriteria, nonEquiJoinCriteria);
// allow a 1-1 join
if (!NewCalculateCostUtil.usesKey(accessNode1, leftExpressions, metadata) || !NewCalculateCostUtil.usesKey(accessNode2, rightExpressions, metadata)) {
// don't push heuristically
shouldPush = false;
}
}
// try to push to the source
if (!shouldPush || RuleRaiseAccess.canRaiseOverJoin(toTest, metadata, capFinder, joinCriteria, joinType, null, context, secondPass != -1, false) == null) {
if (secondPass == -1 && sjc != SupportedJoinCriteria.KEY && discoveredJoin == -1) {
for (Criteria criteria : joinCriteria) {
if (criteria instanceof CompareCriteria && ((CompareCriteria) criteria).isOptional()) {
discoveredJoin = k;
}
}
}
continue;
}
secondPass = -1;
discoveredJoin = -1;
structureChanged = true;
// remove the information that is no longer relevant to the join region
joinRegion.getCritieriaToSourceMap().keySet().removeAll(joinCriteriaNodes);
joinRegion.getCriteriaNodes().removeAll(joinCriteriaNodes);
joinRegion.getJoinSourceNodes().remove(accessNode1);
joinRegion.getJoinSourceNodes().remove(accessNode2);
accessNodes.remove(i);
accessNodes.remove(k < i ? k : k - 1);
// build a new join node
PlanNode joinNode = createJoinNode(accessNode1, accessNode2, joinCriteria, joinType);
PlanNode newAccess = RuleRaiseAccess.raiseAccessOverJoin(joinNode, joinNode.getFirstChild(), entry.getKey(), capFinder, metadata, false);
for (PlanNode critNode : joinCriteriaNodes) {
critNode.removeFromParent();
critNode.removeAllChildren();
}
for (Set<PlanNode> source : joinRegion.getCritieriaToSourceMap().values()) {
if (source.remove(accessNode1) || source.remove(accessNode2)) {
source.add(newAccess);
}
}
joinRegion.getJoinSourceNodes().put(newAccess, newAccess);
accessNodes.add(newAccess);
i = accessNodes.size();
k = accessNodes.size();
break;
}
if (discoveredJoin != -1) {
// rerun with the discoveredJoin criteria
i++;
secondPass = discoveredJoin;
}
}
}
if (structureChanged) {
joinRegion.reconstructJoinRegoin();
}
}
use of org.teiid.query.sql.lang.CompareCriteria in project teiid by teiid.
the class RulePlanProcedures method findInputNodes.
private void findInputNodes(final HashSet<ElementSymbol> inputs, PlanNode critNode, final List<Criteria> conjuncts, final Set<ElementSymbol> params) {
while (critNode.getType() == NodeConstants.Types.SELECT) {
final PlanNode currentNode = critNode;
final Criteria crit = (Criteria) currentNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
critNode = currentNode.getParent();
if (!currentNode.getGroups().isEmpty()) {
continue;
}
LanguageVisitor visitor = new LanguageVisitor() {
public void visit(CompareCriteria compCrit) {
if (compCrit.getOperator() == CompareCriteria.EQ && checkForInput(compCrit.getLeftExpression()) && !checkForAnyInput(compCrit.getRightExpression())) {
addInputNode((Reference) compCrit.getLeftExpression());
}
}
private void addInputNode(Reference param) {
params.add(param.getExpression());
conjuncts.add(crit);
NodeEditor.removeChildNode(currentNode.getParent(), currentNode);
}
public void visit(IsNullCriteria isNull) {
if (!isNull.isNegated() && checkForInput(isNull.getExpression())) {
addInputNode((Reference) isNull.getExpression());
}
}
public void visit(SetCriteria obj) {
if (!obj.isNegated() && checkForInput(obj.getExpression()) && !checkForAnyInput(obj.getValues())) {
addInputNode((Reference) obj.getExpression());
}
}
public void visit(DependentSetCriteria obj) {
if (obj.isNegated()) {
// just a sanity check
return;
}
if (obj.hasMultipleAttributes()) {
for (AttributeComparison comp : obj.getAttributes()) {
if (!checkForInput(comp.dep)) {
return;
}
}
for (AttributeComparison comp : obj.getAttributes()) {
params.add(((Reference) comp.dep).getExpression());
}
conjuncts.add(crit);
NodeEditor.removeChildNode(currentNode.getParent(), currentNode);
} else if (checkForInput(obj.getExpression())) {
addInputNode((Reference) obj.getExpression());
}
}
boolean checkForInput(Expression expr) {
if (!(expr instanceof Reference)) {
return false;
}
// if the expr is a function containing a reference should give a warning
Reference ref = (Reference) expr;
return inputs.contains(ref.getExpression());
}
boolean checkForAnyInput(LanguageObject expr) {
for (Reference ref : ReferenceCollectorVisitor.getReferences(expr)) {
if (checkForInput(ref)) {
return true;
}
}
return false;
}
boolean checkForAnyInput(Collection<Expression> expressions) {
for (Expression expr : expressions) {
if (checkForAnyInput(expr)) {
return true;
}
}
return false;
}
};
for (Criteria conjunct : Criteria.separateCriteriaByAnd(crit)) {
conjunct.acceptVisitor(visitor);
}
}
}
use of org.teiid.query.sql.lang.CompareCriteria in project teiid by teiid.
the class RuleCopyCriteria method createCriteria.
private boolean createCriteria(boolean copyingJoinCriteria, Collection<Criteria> toCopy, Set<Criteria> combinedCriteria, Map<Expression, Expression> srcToTgt, List<Criteria> newJoinCrits, QueryMetadataInterface metadata, boolean underAccess) {
boolean changedTree = false;
if (srcToTgt.size() == 0) {
return changedTree;
}
Iterator<Criteria> i = toCopy.iterator();
while (i.hasNext()) {
Criteria crit = i.next();
Integer endGroups = copyCriteria(crit, srcToTgt, newJoinCrits, combinedCriteria, copyingJoinCriteria, metadata, underAccess);
if (endGroups != null) {
changedTree = true;
if (copyingJoinCriteria && endGroups < 2) {
if (crit instanceof CompareCriteria) {
CompareCriteria cc = (CompareCriteria) crit;
// don't remove theta criteria, just mark it as optional
cc.setOptional(null);
continue;
}
i.remove();
}
}
}
return changedTree;
}
use of org.teiid.query.sql.lang.CompareCriteria in project teiid by teiid.
the class RulePushSelectCriteria method satisfyConditions.
/**
* @param critNode
* @param sourceNode
* @throws TeiidComponentException
* @throws QueryMetadataException
*/
static void satisfyConditions(PlanNode critNode, PlanNode sourceNode, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
List aps = (List) sourceNode.getProperty(NodeConstants.Info.ACCESS_PATTERNS);
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (sourceNode.hasBooleanProperty(Info.IS_MULTI_SOURCE) && crit instanceof CompareCriteria) {
CompareCriteria cc = (CompareCriteria) crit;
if (cc.getLeftExpression() instanceof ElementSymbol && cc.getRightExpression() instanceof Constant) {
ElementSymbol es = (ElementSymbol) cc.getLeftExpression();
if (metadata.isMultiSourceElement(es.getMetadataID())) {
sourceNode.setProperty(Info.IS_MULTI_SOURCE, false);
sourceNode.setProperty(Info.SOURCE_NAME, ((Constant) cc.getRightExpression()).getValue());
}
}
}
if (aps == null) {
return;
}
Collection<ElementSymbol> elements = getElementsIncriteria(crit);
boolean removeAps = satisfyAccessPatterns(aps, elements);
if (removeAps) {
sourceNode.removeProperty(NodeConstants.Info.ACCESS_PATTERNS);
return;
}
Collections.sort(aps);
}
use of org.teiid.query.sql.lang.CompareCriteria in project teiid by teiid.
the class JoinRegion method getJoinCriteriaForGroups.
// TODO: this should be better than a linear search
protected List<PlanNode> getJoinCriteriaForGroups(Set<GroupSymbol> groups, Collection<PlanNode> nodes) {
List<PlanNode> result = new LinkedList<PlanNode>();
for (PlanNode critNode : nodes) {
if (groups.containsAll(critNode.getGroups())) {
Criteria crit = (Criteria) critNode.getProperty(Info.SELECT_CRITERIA);
if (crit instanceof CompareCriteria && ((CompareCriteria) crit).isOptional()) {
continue;
}
result.add(critNode);
}
}
return result;
}
Aggregations