use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RulePushSelectCriteria method splitSet.
private boolean splitSet(PlanNode critNode, DependentNodeTest test, DependentSetCriteria dscOrig, PlanNode destination) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
boolean result = false;
List<DependentSetCriteria> dscList = splitDependentSetCriteria(dscOrig, false, null);
List<DependentSetCriteria.AttributeComparison> pushable = new ArrayList<AttributeComparison>();
List<DependentSetCriteria.AttributeComparison> nonPushable = new ArrayList<AttributeComparison>();
for (DependentSetCriteria dsc : dscList) {
PlanNode copyNode = copyNode(critNode);
setCriteria(dsc, copyNode);
if (test.isValid(copyNode)) {
pushable.add(dsc.getAttributes().get(0));
} else {
nonPushable.add(dsc.getAttributes().get(0));
}
}
if (!pushable.isEmpty()) {
// signal that we should run again
result = true;
if (nonPushable.isEmpty()) {
// $NON-NLS-1$
throw new AssertionError("should not be completely pushed");
}
setCriteria(RuleChooseDependent.createDependentSetCriteria(dscOrig.getContextSymbol(), nonPushable), critNode);
PlanNode copyNode = copyNode(critNode);
setCriteria(RuleChooseDependent.createDependentSetCriteria(dscOrig.getContextSymbol(), pushable), copyNode);
// it should be pushed in the next run
destination.addAsParent(copyNode);
}
return result;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RulePushSelectCriteria method pushAcrossSetOp.
boolean pushAcrossSetOp(PlanNode critNode, PlanNode setOp, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
// Find source node above union and grab the symbol map
PlanNode sourceNode = NodeEditor.findParent(setOp, NodeConstants.Types.SOURCE);
GroupSymbol virtualGroup = sourceNode.getGroups().iterator().next();
if (createdNodes == null) {
satisfyConditions(critNode, sourceNode, metadata);
}
SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
SymbolMap childMap = symbolMap;
// Move criteria to first child of union - names are the same, so no symbol mapping
LinkedList<PlanNode> unionChildren = new LinkedList<PlanNode>();
collectUnionChildren(setOp, unionChildren);
int movedCount = 0;
for (PlanNode planNode : unionChildren) {
// Find first project node
PlanNode projectNode = NodeEditor.findNodePreOrder(planNode, NodeConstants.Types.PROJECT);
if (childMap == null) {
childMap = SymbolMap.createSymbolMap(symbolMap.getKeys(), (List) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS));
}
// we cannot simply move the node in the case where placing above or below the access would be invalid
boolean handleSetOp = false;
PlanNode accessNode = NodeEditor.findNodePreOrder(planNode, NodeConstants.Types.ACCESS, NodeConstants.Types.PROJECT);
if (accessNode != null && NodeEditor.findParent(projectNode, NodeConstants.Types.SET_OP, NodeConstants.Types.ACCESS) != null) {
handleSetOp = true;
}
// Move the node
if (placeConvertedSelectNode(critNode, virtualGroup, projectNode, childMap, metadata)) {
if (handleSetOp) {
PlanNode newSelect = projectNode.getFirstChild();
projectNode.replaceChild(newSelect, newSelect.getFirstChild());
Object modelID = RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata);
Criteria crit = (Criteria) newSelect.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (newSelect.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET) && context != null && CapabilitiesUtil.supportsInlineView(modelID, metadata, capFinder) && CriteriaCapabilityValidatorVisitor.canPushLanguageObject(crit, modelID, metadata, capFinder, null)) {
accessNode.getFirstChild().addAsParent(newSelect);
List<Expression> old = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
// create a project node based upon the created group and add it as the parent of the select
PlanNode project = RelationalPlanner.createProjectNode(LanguageObject.Util.deepClone(old, Expression.class));
newSelect.addAsParent(project);
// $NON-NLS-1$
PlanNode newSourceNode = RuleDecomposeJoin.rebuild(new GroupSymbol("intermediate"), null, newSelect.getFirstChild(), metadata, context, projectNode);
newSourceNode.setProperty(NodeConstants.Info.INLINE_VIEW, true);
accessNode.addGroups(newSourceNode.getGroups());
markDependent(newSelect, accessNode, metadata, capFinder);
} else {
// or an inline view could be used similar to the above
if (createdNodes != null) {
createdNodes.remove(newSelect);
}
childMap = null;
continue;
}
}
movedCount++;
}
// create a new symbol map for the other children
childMap = null;
}
// TODO - the logic here could be made more intelligent about EXCEPT and INTERSECT.
if (movedCount == unionChildren.size()) {
critNode.setProperty(NodeConstants.Info.IS_PHANTOM, Boolean.TRUE);
return true;
}
// otherwise mark it as pushed so we don't consider it again
critNode.setProperty(NodeConstants.Info.IS_PUSHED, Boolean.TRUE);
// if any moved, then we need to continue
return movedCount != 0;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleRaiseAccess method canRaiseOverJoin.
static Object canRaiseOverJoin(List<PlanNode> children, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, List<Criteria> crits, JoinType type, AnalysisRecord record, CommandContext context, boolean considerOptional, boolean considerLateral) throws QueryMetadataException, TeiidComponentException {
// we only want to consider binary joins
if (children.size() != 2) {
return null;
}
Object modelID = null;
boolean multiSource = false;
Set<Object> groupIDs = new HashSet<Object>();
int groupCount = 0;
LinkedList<CompareCriteria> thetaCriteria = new LinkedList<CompareCriteria>();
SupportedJoinCriteria sjc = null;
for (PlanNode childNode : children) {
boolean lateral = false;
boolean procedure = false;
if (considerLateral && childNode.getType() == NodeConstants.Types.SOURCE && childNode.getFirstChild() != null && childNode.getProperty(Info.CORRELATED_REFERENCES) != null) {
if (FrameUtil.getNestedPlan(childNode.getFirstChild()) != null) {
return null;
}
Command command = FrameUtil.getNonQueryCommand(childNode.getFirstChild());
if (command instanceof StoredProcedure) {
procedure = true;
if (!CapabilitiesUtil.supports(Capability.QUERY_FROM_PROCEDURE_TABLE, modelID, metadata, capFinder)) {
return null;
}
// this should look like source/project/access - if not, then back out
if (childNode.getFirstChild().getType() == NodeConstants.Types.PROJECT) {
childNode = childNode.getFirstChild();
}
}
if (childNode.getFirstChild().getType() == NodeConstants.Types.ACCESS) {
childNode = childNode.getFirstChild();
} else {
return null;
}
lateral = true;
}
if (childNode.getType() != NodeConstants.Types.ACCESS) {
return null;
}
if (childNode.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) {
// $NON-NLS-1$ //$NON-NLS-2$
childNode.recordDebugAnnotation("access pattern not satisfied by join", modelID, "not pushing parent join", record, metadata);
return null;
}
Object accessModelID = getModelIDFromAccess(childNode, metadata);
if (accessModelID == null) {
return null;
}
groupCount += childNode.getGroups().size();
// Add all group metadata IDs to the list but check before each to make
// sure group hasn't already been seen - if so, bail out - this is a self join
// Unless model supports self joins, in which case, don't bail out.
boolean supportsSelfJoins = CapabilitiesUtil.supportsSelfJoins(accessModelID, metadata, capFinder);
if (!supportsSelfJoins) {
for (GroupSymbol groupSymbol : childNode.getGroups()) {
Object groupID = groupSymbol.getMetadataID();
if (!groupIDs.add(groupID)) {
// Already seen group - can't raise access over self join
return null;
}
}
}
// check the join criteria now that we know the model
if (modelID == null) {
if (!CapabilitiesUtil.supportsJoin(accessModelID, type, metadata, capFinder)) {
return null;
}
sjc = CapabilitiesUtil.getSupportedJoinCriteria(accessModelID, metadata, capFinder);
// see if we can emulate the inner join using an outer
if (!type.isOuter() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_INNER, accessModelID, metadata, capFinder) && (crits != null) && !crits.isEmpty()) {
// TODO: the IS NOT NULL check is not strictly needed as we could check predicates to see if we are already null filtering
if (!CapabilitiesUtil.supports(Capability.CRITERIA_ISNULL, accessModelID, metadata, capFinder) || !CapabilitiesUtil.supports(Capability.CRITERIA_NOT, accessModelID, metadata, capFinder)) {
return null;
}
if (sjc == SupportedJoinCriteria.ANY) {
// quick check to see if we can find an element to be nullable
boolean valid = false;
for (Criteria crit : crits) {
if (!(crit instanceof CompareCriteria)) {
continue;
}
CompareCriteria cc = (CompareCriteria) crit;
if ((cc.getLeftExpression() instanceof ElementSymbol) || (cc.getRightExpression() instanceof ElementSymbol)) {
valid = true;
}
}
if (!valid) {
// TODO: check if any of the already pushed predicates can satisfy
return null;
}
}
}
if (crits != null && !crits.isEmpty()) {
for (Criteria crit : crits) {
if (!isSupportedJoinCriteria(sjc, crit, accessModelID, metadata, capFinder, record)) {
if (crit instanceof CompareCriteria) {
CompareCriteria cc = (CompareCriteria) crit;
if (cc.isOptional()) {
cc.setOptional(true);
continue;
}
}
// TODO: plan based upon a predicate subset when possible
return null;
} else if (crit instanceof CompareCriteria) {
thetaCriteria.add((CompareCriteria) crit);
}
}
if (sjc == SupportedJoinCriteria.KEY) {
PlanNode left = children.get(0);
PlanNode right = children.get(1);
if (left.getGroups().size() != 1) {
if (right.getGroups().size() != 1) {
// require the simple case of 1 side being a single group
return null;
}
if (type != JoinType.JOIN_INNER) {
return null;
}
left = children.get(1);
right = children.get(0);
}
LinkedList<Expression> leftExpressions = new LinkedList<Expression>();
LinkedList<Expression> rightExpressions = new LinkedList<Expression>();
RuleChooseJoinStrategy.separateCriteria(left.getGroups(), right.getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
ArrayList<Object> leftIds = new ArrayList<Object>(leftExpressions.size());
ArrayList<Object> rightIds = new ArrayList<Object>(rightExpressions.size());
for (Expression expr : leftExpressions) {
if (expr instanceof ElementSymbol) {
leftIds.add(((ElementSymbol) expr).getMetadataID());
}
}
GroupSymbol rightGroup = null;
for (Expression expr : rightExpressions) {
if (expr instanceof ElementSymbol) {
ElementSymbol es = (ElementSymbol) expr;
if (rightGroup == null) {
rightGroup = es.getGroupSymbol();
} else if (!rightGroup.equals(es.getGroupSymbol())) {
return null;
}
rightIds.add(es.getMetadataID());
}
}
if (rightGroup == null) {
return null;
}
if (!matchesForeignKey(metadata, leftIds, rightIds, left.getGroups().iterator().next(), true, !type.isOuter() || type == JoinType.JOIN_LEFT_OUTER) && !matchesForeignKey(metadata, rightIds, leftIds, rightGroup, true, !type.isOuter())) {
return null;
}
}
}
if (sjc != SupportedJoinCriteria.ANY && thetaCriteria.isEmpty()) {
// cross join not supported
return null;
}
if (type == JoinType.JOIN_LEFT_OUTER && !CapabilitiesUtil.supports(Capability.CRITERIA_ON_SUBQUERY, accessModelID, metadata, capFinder)) {
PlanNode right = children.get(1);
for (PlanNode node : NodeEditor.findAllNodes(right, NodeConstants.Types.SELECT, NodeConstants.Types.SOURCE)) {
for (SubqueryContainer<?> subqueryContainer : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders((Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA))) {
if (!(subqueryContainer instanceof Evaluatable) || subqueryContainer.getCommand().getCorrelatedReferences() != null) {
return null;
}
}
}
}
modelID = accessModelID;
multiSource = childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
} else if (!CapabilitiesUtil.isSameConnector(modelID, accessModelID, metadata, capFinder) && !isConformed(metadata, capFinder, (Set<Object>) childNode.getProperty(Info.CONFORMED_SOURCES), modelID, (Set<Object>) children.get(0).getProperty(Info.CONFORMED_SOURCES), accessModelID)) {
return null;
} else if ((multiSource || childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE)) && !context.getOptions().isImplicitMultiSourceJoin()) {
// only allow raise if partitioned
boolean multiSourceOther = childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
if (multiSource && multiSourceOther && (type == JoinType.JOIN_ANTI_SEMI || type == JoinType.JOIN_CROSS)) {
return null;
}
ArrayList<Expression> leftExpressions = new ArrayList<Expression>();
ArrayList<Expression> rightExpressions = new ArrayList<Expression>();
RuleChooseJoinStrategy.separateCriteria(children.get(0).getGroups(), children.get(1).getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
boolean needsOtherCrit = sjc != SupportedJoinCriteria.ANY;
boolean partitioned = !multiSource || !multiSourceOther;
for (int i = 0; i < leftExpressions.size() && (!partitioned || needsOtherCrit); i++) {
boolean multi = isMultiSourceColumn(metadata, leftExpressions.get(i), children.get(0)) && isMultiSourceColumn(metadata, rightExpressions.get(i), children.get(1));
if (multi) {
partitioned = true;
} else {
needsOtherCrit = false;
}
}
if (needsOtherCrit || !partitioned) {
return null;
}
}
if (lateral) {
if ((!CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_LATERAL, modelID, metadata, capFinder) || (crits != null && !crits.isEmpty() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_LATERAL_CONDITION, accessModelID, metadata, capFinder)))) {
return null;
}
if (!procedure && CapabilitiesUtil.supports(Capability.QUERY_ONLY_FROM_JOIN_LATERAL_PROCEDURE, accessModelID, metadata, capFinder)) {
return null;
}
}
}
// end walking through join node's children
int maxGroups = CapabilitiesUtil.getMaxFromGroups(modelID, metadata, capFinder);
if (maxGroups != -1 && maxGroups < groupCount) {
return null;
}
if (crits != null && !crits.isEmpty()) {
if (considerOptional) {
for (CompareCriteria criteria : thetaCriteria) {
criteria.setOptional(false);
}
} else {
boolean hasCriteria = false;
for (CompareCriteria criteria : thetaCriteria) {
if (criteria.getIsOptional() == null || !criteria.isOptional()) {
hasCriteria = true;
break;
}
}
if (!hasCriteria) {
return null;
}
}
}
return modelID;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleRaiseAccess method canRaiseOverJoin.
/**
* Determine whether an access node can be raised over the specified join node.
*
* This method can also be used to determine if a join node "A", parent of another join
* node "B", will have it's access raised. This is needed to help determine if node
* "B" will have access raised over it. In this scenario, the parameter will be true.
* When this method is called normally from the "execute" method, that param will be false.
*
* @param joinNode Join node that might be pushed underneath the access node
* @param metadata Metadata information
* @param capFinder CapabilitiesFinder
* @param context
* @return The modelID if the raise can proceed and what common model these combined
* nodes will be sent to
*/
private static Object canRaiseOverJoin(Object modelId, PlanNode joinNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, boolean afterJoinPlanning, AnalysisRecord record, CommandContext context) throws QueryMetadataException, TeiidComponentException {
List crits = (List) joinNode.getProperty(NodeConstants.Info.JOIN_CRITERIA);
JoinType type = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
// let ruleplanjoins handle this case
if (!afterJoinPlanning && type == JoinType.JOIN_CROSS && joinNode.getParent().getType() == NodeConstants.Types.JOIN) {
JoinType jt = (JoinType) joinNode.getParent().getProperty(NodeConstants.Info.JOIN_TYPE);
if (!jt.isOuter()) {
return null;
}
}
if (joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null) {
return null;
}
// if a join has access patterns that are unsatisfied, then the raise cannot occur
if (joinNode.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) {
return null;
}
// if I'm on the inner side of an outer join, then and we have a criteria restriction, then I can't be pushed
if (type.isOuter() && CapabilitiesUtil.getSupportedJoinCriteria(modelId, metadata, capFinder) != SupportedJoinCriteria.ANY) {
PlanNode critNode = NodeEditor.findNodePreOrder(joinNode.getLastChild(), NodeConstants.Types.SELECT, NodeConstants.Types.SOURCE);
if (critNode != null) {
return null;
}
if (type == JoinType.JOIN_FULL_OUTER) {
critNode = NodeEditor.findNodePreOrder(joinNode.getFirstChild(), NodeConstants.Types.SELECT, NodeConstants.Types.SOURCE);
if (critNode != null) {
return null;
}
}
}
return canRaiseOverJoin(joinNode.getChildren(), metadata, capFinder, crits, type, record, context, afterJoinPlanning, true);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleRaiseAccess method canRaiseOverSetQuery.
private static boolean canRaiseOverSetQuery(PlanNode setOpNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException {
Object modelID = null;
String sourceName = null;
boolean multiSource = false;
for (PlanNode childNode : setOpNode.getChildren()) {
if (childNode.getType() != NodeConstants.Types.ACCESS) {
return false;
}
if (FrameUtil.getNonQueryCommand(childNode) != null || FrameUtil.getNestedPlan(childNode) != null) {
return false;
}
// Get model and check that it exists
Object accessModelID = getModelIDFromAccess(childNode, metadata);
if (accessModelID == null) {
return false;
}
// TODO: see if the children are actually multiSourced
multiSource |= childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
String name = (String) childNode.getProperty(Info.SOURCE_NAME);
// Reconcile this access node's model ID with existing
if (modelID == null) {
modelID = accessModelID;
Operation op = (Operation) setOpNode.getProperty(NodeConstants.Info.SET_OPERATION);
if (!CapabilitiesUtil.supportsSetOp(accessModelID, op, metadata, capFinder)) {
return false;
}
if (multiSource && op != Operation.UNION) {
return false;
}
} else if (!CapabilitiesUtil.isSameConnector(modelID, accessModelID, metadata, capFinder)) {
return false;
}
if (!multiSource) {
if (sourceName == null) {
sourceName = name;
} else if (name != null && !sourceName.equals(name)) {
return false;
}
}
if (!setOpNode.hasBooleanProperty(NodeConstants.Info.USE_ALL) && !supportsDistinct(metadata, childNode, multiSource)) {
return false;
}
}
return true;
}
Aggregations