use of org.teiid.query.sql.util.SymbolMap 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.sql.util.SymbolMap in project teiid by teiid.
the class RuleChooseDependent method fullyPush.
/**
* Check for fully pushable dependent joins
* currently we only look for the simplistic scenario where there are no intervening
* nodes above the dependent side
* @param independentExpressions
*/
private boolean fullyPush(PlanNode sourceNode, PlanNode joinNode, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, CommandContext context, PlanNode indNode, RuleStack rules, MakeDep makeDep, AnalysisRecord analysisRecord, List independentExpressions) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
if (sourceNode.getType() != NodeConstants.Types.ACCESS) {
// don't remove as we may raise an access node to make this possible
return false;
}
Object modelID = RuleRaiseAccess.getModelIDFromAccess(sourceNode, metadata);
boolean hasHint = false;
if (makeDep != null && makeDep.getJoin() != null) {
if (!makeDep.getJoin()) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("cannot pushdown dependent join", modelID, "honoring hint", analysisRecord, null);
return false;
}
hasHint = true;
}
if (!CapabilitiesUtil.supports(Capability.FULL_DEPENDENT_JOIN, modelID, metadata, capabilitiesFinder)) {
if (hasHint) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("cannot pushdown dependent join", modelID, "dependent join pushdown needs enabled at the source", analysisRecord, null);
}
return false;
}
List<? extends Expression> projected = (List<? extends Expression>) indNode.getProperty(Info.OUTPUT_COLS);
if (projected == null) {
PlanNode plan = sourceNode;
while (plan.getParent() != null) {
plan = plan.getParent();
}
new RuleAssignOutputElements(false).execute(plan, metadata, capabilitiesFinder, null, AnalysisRecord.createNonRecordingRecord(), context);
projected = (List<? extends Expression>) indNode.getProperty(Info.OUTPUT_COLS);
}
if (!hasHint) {
// require no lobs
for (Expression ex : projected) {
if (DataTypeManager.isLOB(ex.getClass())) {
return false;
}
}
// old optimizer tests had no buffermanager
if (context.getBufferManager() == null) {
return false;
}
if (makeDep != null && makeDep.getMax() != null) {
// if the user specifies a max, it's best to just use a regular dependent join
return false;
}
}
/*
* check to see how far the access node can be raised
*/
PlanNode tempAccess = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
// $NON-NLS-1$
GroupSymbol gs = RulePlaceAccess.recontextSymbol(new GroupSymbol("TEIID_TEMP"), context.getGroups());
gs.setDefinition(null);
tempAccess.addGroup(gs);
tempAccess.setProperty(Info.MODEL_ID, modelID);
indNode.addAsParent(tempAccess);
PlanNode originalSource = sourceNode;
sourceNode = originalSource.clone();
// more deeply clone
if (sourceNode.hasCollectionProperty(Info.ACCESS_PATTERNS)) {
sourceNode.setProperty(Info.ACCESS_PATTERNS, new ArrayList<AccessPattern>((List) sourceNode.getProperty(Info.ACCESS_PATTERNS)));
}
if (sourceNode.hasCollectionProperty(Info.CONFORMED_SOURCES)) {
sourceNode.setProperty(Info.CONFORMED_SOURCES, new LinkedHashSet<Object>((Set) sourceNode.getProperty(Info.CONFORMED_SOURCES)));
}
originalSource.addAsParent(sourceNode);
boolean raised = false;
boolean moreProcessing = false;
boolean first = true;
while (sourceNode.getParent() != null && RuleRaiseAccess.raiseAccessNode(sourceNode, sourceNode, metadata, capabilitiesFinder, true, null, context) != null) {
raised = true;
if (first) {
// raising over join required
first = false;
continue;
}
switch(sourceNode.getFirstChild().getType()) {
case NodeConstants.Types.PROJECT:
// TODO: check for correlated subqueries
if (sourceNode.getFirstChild().hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
moreProcessing = true;
}
break;
case NodeConstants.Types.SORT:
case NodeConstants.Types.DUP_REMOVE:
case NodeConstants.Types.GROUP:
case NodeConstants.Types.SELECT:
case NodeConstants.Types.TUPLE_LIMIT:
case NodeConstants.Types.JOIN:
moreProcessing = true;
break;
}
}
if (!raised) {
tempAccess.getParent().replaceChild(tempAccess, tempAccess.getFirstChild());
sourceNode.getParent().replaceChild(sourceNode, sourceNode.getFirstChild());
return false;
}
if (!moreProcessing && !hasHint) {
// restore the plan
if (sourceNode.getParent() != null) {
sourceNode.getParent().replaceChild(sourceNode, sourceNode.getFirstChild());
} else {
sourceNode.removeAllChildren();
}
return false;
}
originalSource.getParent().replaceChild(originalSource, originalSource.getFirstChild());
// all the references to any groups from this join have to changed over to the new group
// and we need to insert a source/project node to turn this into a proper plan
PlanNode project = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
PlanNode source = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
source.addGroup(gs);
project.setProperty(Info.OUTPUT_COLS, projected);
project.setProperty(Info.PROJECT_COLS, projected);
Set<GroupSymbol> newGroups = Collections.singleton(gs);
ArrayList<ElementSymbol> virtualSymbols = new ArrayList<ElementSymbol>(projected.size());
for (int i = 0; i < projected.size(); i++) {
// $NON-NLS-1$
ElementSymbol es = new ElementSymbol("col" + (i + 1));
Expression ex = projected.get(i);
es.setType(ex.getType());
virtualSymbols.add(es);
// TODO: set a metadata id from either side
if (ex instanceof ElementSymbol) {
es.setMetadataID(((ElementSymbol) ex).getMetadataID());
}
}
List<ElementSymbol> newCols = RulePushAggregates.defineNewGroup(gs, virtualSymbols, metadata);
SymbolMap symbolMap = SymbolMap.createSymbolMap(newCols, projected);
Map<Expression, ElementSymbol> inverse = symbolMap.inserseMapping();
// TODO: the util logic should handle multiple groups
for (GroupSymbol group : indNode.getGroups()) {
FrameUtil.convertFrame(joinNode, group, newGroups, inverse, metadata);
}
// add the source a new group for the join
indNode.addAsParent(source);
// convert the lower plan into a subplan
// it needs to be rooted by a project - a view isn't really needed
indNode.removeFromParent();
project.addFirstChild(indNode);
// run the remaining rules against the subplan
RuleStack ruleCopy = rules.clone();
RuleChooseDependent ruleChooseDependent = new RuleChooseDependent();
ruleChooseDependent.traditionalOnly = true;
ruleCopy.push(ruleChooseDependent);
if (indNode.getType() == NodeConstants.Types.ACCESS) {
PlanNode root = RuleRaiseAccess.raiseAccessNode(project, indNode, metadata, capabilitiesFinder, true, null, context);
if (root != project) {
project = root;
}
}
// fully plan the sub-plan with the remaining rules
project = rules.getPlanner().executeRules(ruleCopy, project);
source.setProperty(Info.SYMBOL_MAP, symbolMap);
source.setProperty(Info.SUB_PLAN, project);
return true;
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class RuleCollapseSource method buildQuery.
void buildQuery(PlanNode accessRoot, PlanNode node, Query query, CommandContext context, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
QueryMetadataInterface metadata = context.getMetadata();
// visit source and join nodes as they appear
Object modelID = RuleRaiseAccess.getModelIDFromAccess(accessRoot, metadata);
switch(node.getType()) {
case NodeConstants.Types.JOIN:
{
prepareSubqueries(node.getSubqueryContainers());
JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
List<Criteria> crits = (List<Criteria>) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
if (crits == null || crits.isEmpty()) {
crits = new ArrayList<Criteria>();
} else {
RuleChooseJoinStrategy.filterOptionalCriteria(crits, false);
if (crits.isEmpty() && joinType == JoinType.JOIN_INNER) {
joinType = JoinType.JOIN_CROSS;
}
}
PlanNode left = node.getFirstChild();
PlanNode right = node.getLastChild();
/* special handling is needed to determine criteria placement.
*
* if the join is a left outer join, criteria from the right side will be added to the on clause
*/
Criteria savedCriteria = null;
buildQuery(accessRoot, left, query, context, capFinder);
if (joinType == JoinType.JOIN_LEFT_OUTER) {
savedCriteria = query.getCriteria();
query.setCriteria(null);
}
buildQuery(accessRoot, right, query, context, capFinder);
if (joinType == JoinType.JOIN_LEFT_OUTER) {
moveWhereClauseIntoOnClause(query, crits);
query.setCriteria(savedCriteria);
}
if (joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_FULL_OUTER) {
boolean subqueryOn = CapabilitiesUtil.supports(Capability.CRITERIA_ON_SUBQUERY, modelID, metadata, capFinder);
if (!subqueryOn) {
for (SubqueryContainer<?> subqueryContainer : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(crits)) {
if (subqueryContainer instanceof Evaluatable && subqueryContainer.getCommand().getCorrelatedReferences() == null) {
((Evaluatable) subqueryContainer).setShouldEvaluate(true);
} else {
// $NON-NLS-1$
throw new AssertionError("On clause not expected to contain non-evaluatable subqueries");
}
}
}
}
// Get last two clauses added to the FROM and combine them into a JoinPredicate
From from = query.getFrom();
List<FromClause> clauses = from.getClauses();
int lastClause = clauses.size() - 1;
FromClause clause1 = clauses.get(lastClause - 1);
FromClause clause2 = clauses.get(lastClause);
// so this may not be needed moving forward
if (!joinType.isOuter() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_INNER, modelID, metadata, capFinder)) {
joinType = JoinType.JOIN_LEFT_OUTER;
if (!crits.isEmpty()) {
if (!useLeftOuterJoin(query, metadata, crits, right.getGroups())) {
if (!useLeftOuterJoin(query, metadata, crits, left.getGroups())) {
// $NON-NLS-1$
throw new AssertionError("Could not convert inner to outer join.");
}
FromClause temp = clause1;
clause1 = clause2;
clause2 = temp;
}
}
}
// correct the criteria or the join type if necessary
if (joinType != JoinType.JOIN_CROSS && crits.isEmpty()) {
crits.add(QueryRewriter.TRUE_CRITERIA);
} else if (joinType == JoinType.JOIN_CROSS && !crits.isEmpty()) {
joinType = JoinType.JOIN_INNER;
}
JoinPredicate jp = new JoinPredicate(clause1, clause2, joinType, crits);
// Replace last two clauses with new predicate
clauses.remove(lastClause);
clauses.set(lastClause - 1, jp);
return;
}
case NodeConstants.Types.SOURCE:
{
boolean pushedTableProcedure = false;
GroupSymbol symbol = node.getGroups().iterator().next();
if (node.hasBooleanProperty(Info.INLINE_VIEW)) {
PlanNode child = node.getFirstChild();
QueryCommand newQuery = createQuery(context, capFinder, accessRoot, child);
// ensure that the group is consistent
SubqueryFromClause sfc = new SubqueryFromClause(symbol, newQuery);
SymbolMap map = (SymbolMap) node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (map != null) {
ExpressionMappingVisitor visitor = new RuleMergeCriteria.ReferenceReplacementVisitor(map);
DeepPostOrderNavigator.doVisit(newQuery, visitor);
sfc.setLateral(true);
}
query.getFrom().addClause(sfc);
// ensure that the column names are consistent
Query q = newQuery.getProjectedQuery();
List<Expression> expressions = q.getSelect().getSymbols();
List<Expression> outputCols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
Map<Expression, String> corrected = null;
for (int i = 0; i < outputCols.size(); i++) {
Expression ex = expressions.get(i);
Expression expected = outputCols.get(i);
String name = Symbol.getShortName(expected);
if (!name.equals(Symbol.getShortName(ex))) {
expressions.set(i, new AliasSymbol(name, SymbolMap.getExpression(ex)));
corrected = new HashMap<Expression, String>();
corrected.put(ex, name);
}
}
if (corrected != null && newQuery.getOrderBy() != null) {
for (OrderByItem item : newQuery.getOrderBy().getOrderByItems()) {
String name = corrected.get(item.getSymbol());
if (name != null) {
item.setSymbol(new AliasSymbol(name, SymbolMap.getExpression(item.getSymbol())));
}
}
}
// so we'll unwrap that here
if (newQuery instanceof Query) {
q = (Query) newQuery;
if (q.getFrom() != null && q.getFrom().getClauses().size() == 1 && q.getFrom().getClauses().get(0) instanceof SubqueryFromClause) {
SubqueryFromClause nested = (SubqueryFromClause) q.getFrom().getClauses().get(0);
if (nested.getCommand() instanceof StoredProcedure) {
sfc.setCommand(nested.getCommand());
}
}
}
return;
}
// handle lateral join of a procedure
Command command = (Command) node.getProperty(NodeConstants.Info.VIRTUAL_COMMAND);
if (command instanceof StoredProcedure) {
StoredProcedure storedProcedure = (StoredProcedure) command;
storedProcedure.setPushedInQuery(true);
SubqueryFromClause subqueryFromClause = new SubqueryFromClause(symbol, storedProcedure);
// TODO: it would be better to directly add
query.getFrom().addClause(subqueryFromClause);
pushedTableProcedure = true;
}
PlanNode subPlan = (PlanNode) node.getProperty(Info.SUB_PLAN);
if (subPlan != null) {
Map<GroupSymbol, PlanNode> subPlans = (Map<GroupSymbol, PlanNode>) accessRoot.getProperty(Info.SUB_PLANS);
if (subPlans == null) {
subPlans = new HashMap<GroupSymbol, PlanNode>();
accessRoot.setProperty(Info.SUB_PLANS, subPlans);
}
subPlans.put(symbol, subPlan);
}
if (!pushedTableProcedure) {
query.getFrom().addGroup(symbol);
}
break;
}
}
for (PlanNode childNode : node.getChildren()) {
buildQuery(accessRoot, childNode, query, context, capFinder);
}
switch(node.getType()) {
case NodeConstants.Types.SELECT:
{
Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
prepareSubqueries(node.getSubqueryContainers());
if (!node.hasBooleanProperty(NodeConstants.Info.IS_HAVING)) {
query.setCriteria(CompoundCriteria.combineCriteria(query.getCriteria(), crit));
} else {
query.setHaving(CompoundCriteria.combineCriteria(query.getHaving(), crit));
}
break;
}
case NodeConstants.Types.SORT:
{
prepareSubqueries(node.getSubqueryContainers());
processOrderBy(node, query, modelID, context, capFinder);
break;
}
case NodeConstants.Types.DUP_REMOVE:
{
boolean distinct = true;
PlanNode grouping = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE);
if (grouping != null) {
List groups = (List) grouping.getProperty(NodeConstants.Info.GROUP_COLS);
if (groups == null || groups.isEmpty()) {
distinct = false;
}
}
query.getSelect().setDistinct(distinct);
break;
}
case NodeConstants.Types.GROUP:
{
List groups = (List) node.getProperty(NodeConstants.Info.GROUP_COLS);
if (groups != null && !groups.isEmpty()) {
query.setGroupBy(new GroupBy(groups));
if (node.hasBooleanProperty(Info.ROLLUP)) {
query.getGroupBy().setRollup(true);
}
}
break;
}
case NodeConstants.Types.TUPLE_LIMIT:
{
processLimit(node, query, metadata);
break;
}
}
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class RuleDecomposeJoin method buildUnion.
private PlanNode buildUnion(PlanNode unionNode, PlanNode otherSide, List<Criteria> criteria, List<int[]> matches, List<PlanNode> branches, List<PlanNode> otherBranches, JoinType joinType) {
SymbolMap symbolMap = (SymbolMap) unionNode.getParent().getProperty(Info.SYMBOL_MAP);
SymbolMap otherSymbolMap = (SymbolMap) otherSide.getProperty(Info.SYMBOL_MAP);
List<PlanNode> joins = new LinkedList<PlanNode>();
for (int i = 0; i < matches.size(); i++) {
int[] is = matches.get(i);
PlanNode branch = branches.get(is[0]);
PlanNode branchSource = createSource(unionNode.getParent().getGroups().iterator().next(), branch, symbolMap);
PlanNode otherBranch = otherBranches.get(is[1]);
PlanNode otherBranchSource = createSource(otherSide.getGroups().iterator().next(), otherBranch, otherSymbolMap);
PlanNode newJoinNode = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
newJoinNode.addLastChild(branchSource);
newJoinNode.addLastChild(otherBranchSource);
newJoinNode.setProperty(Info.JOIN_STRATEGY, JoinStrategyType.NESTED_LOOP);
newJoinNode.setProperty(Info.JOIN_TYPE, joinType);
newJoinNode.setProperty(Info.JOIN_CRITERIA, LanguageObject.Util.deepClone(criteria, Criteria.class));
newJoinNode.addGroups(branchSource.getGroups());
newJoinNode.addGroups(otherBranchSource.getGroups());
PlanNode projectPlanNode = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
newJoinNode.addAsParent(projectPlanNode);
Select allSymbols = new Select(symbolMap.getKeys());
allSymbols.addSymbols(otherSymbolMap.getKeys());
if (i == 0) {
QueryRewriter.makeSelectUnique(allSymbols, false);
}
projectPlanNode.setProperty(NodeConstants.Info.PROJECT_COLS, allSymbols.getSymbols());
projectPlanNode.addGroups(newJoinNode.getGroups());
joins.add(projectPlanNode);
}
PlanNode newUnion = RulePlanUnions.buildUnionTree(unionNode, joins);
return newUnion;
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class RuleDecomposeJoin method decomposeJoin.
public PlanNode decomposeJoin(PlanNode joinNode, PlanNode root, QueryMetadataInterface metadata, CommandContext context) throws TeiidComponentException, QueryPlannerException {
if (joinNode.getParent() == null) {
// already processed
return root;
}
JoinType joinType = (JoinType) joinNode.getProperty(Info.JOIN_TYPE);
if (joinType == JoinType.JOIN_ANTI_SEMI || joinType == JoinType.JOIN_CROSS) {
return root;
}
PlanNode left = joinNode.getFirstChild();
while (left.getType() != NodeConstants.Types.SOURCE) {
if (left.getType() == NodeConstants.Types.SELECT && left.hasBooleanProperty(Info.IS_PHANTOM)) {
left = left.getFirstChild();
} else {
return root;
}
}
Map<ElementSymbol, List<Set<Constant>>> partitionInfo = (Map<ElementSymbol, List<Set<Constant>>>) left.getProperty(Info.PARTITION_INFO);
if (partitionInfo == null) {
return root;
}
PlanNode unionNode = left.getFirstChild();
if (unionNode.getType() != NodeConstants.Types.SET_OP) {
return root;
}
PlanNode right = joinNode.getLastChild();
while (right.getType() != NodeConstants.Types.SOURCE) {
if (right.getType() == NodeConstants.Types.SELECT && right.hasBooleanProperty(Info.IS_PHANTOM)) {
right = right.getFirstChild();
} else {
return root;
}
}
Map<ElementSymbol, List<Set<Constant>>> rightPartionInfo = (Map<ElementSymbol, List<Set<Constant>>>) right.getProperty(Info.PARTITION_INFO);
if (rightPartionInfo == null) {
return root;
}
List<Criteria> criteria = (List<Criteria>) joinNode.getProperty(Info.JOIN_CRITERIA);
List<Expression> expr = new ArrayList<Expression>();
List<Expression> exprOther = new ArrayList<Expression>();
RuleChooseJoinStrategy.separateCriteria(unionNode.getParent().getGroups(), right.getGroups(), expr, exprOther, criteria, new LinkedList<Criteria>());
// if implicit, we assume that partitions match
ElementSymbol es = getImplicitPartitionColumn(metadata, left);
ElementSymbol esOther = getImplicitPartitionColumn(metadata, right);
if (es != null && esOther != null && getEffectiveModelId(metadata, es.getGroupSymbol()) == getEffectiveModelId(metadata, esOther.getGroupSymbol())) {
expr.add(es);
exprOther.add(esOther);
}
if (expr.isEmpty()) {
// no equi-join
return root;
}
List<int[]> matches = findMatches(partitionInfo, rightPartionInfo, expr, exprOther);
if (matches == null) {
// no non-overlapping partitions
return root;
}
int branchSize = partitionInfo.values().iterator().next().size();
int otherBranchSize = rightPartionInfo.values().iterator().next().size();
if (matches.isEmpty()) {
if (joinType == JoinType.JOIN_INNER || joinType == JoinType.JOIN_SEMI) {
// no matches mean that we can just insert a null node (false criteria) and be done with it
PlanNode critNode = NodeFactory.getNewNode(NodeConstants.Types.SELECT);
critNode.setProperty(Info.SELECT_CRITERIA, QueryRewriter.FALSE_CRITERIA);
unionNode.addAsParent(critNode);
} else if (joinType == JoinType.JOIN_LEFT_OUTER) {
joinNode.getParent().replaceChild(joinNode, left);
} else if (joinType == JoinType.JOIN_FULL_OUTER) {
joinNode.setProperty(Info.JOIN_CRITERIA, QueryRewriter.FALSE_CRITERIA);
}
return root;
}
List<PlanNode> branches = new ArrayList<PlanNode>();
// TODO: find union children from RulePushAggregates
RulePushSelectCriteria.collectUnionChildren(unionNode, branches);
if (branches.size() != branchSize) {
// sanity check
return root;
}
List<PlanNode> otherBranches = new ArrayList<PlanNode>();
RulePushSelectCriteria.collectUnionChildren(right.getFirstChild(), otherBranches);
if (otherBranches.size() != otherBranchSize) {
// sanity check
return root;
}
PlanNode newUnion = buildUnion(unionNode, right, criteria, matches, branches, otherBranches, joinType);
GroupSymbol leftGroup = left.getGroups().iterator().next();
PlanNode view = rebuild(leftGroup, joinNode, newUnion, metadata, context, left, right);
// preserve the model of the virtual group as we'll look for this when checking for implicit behavior
((TempMetadataID) (view.getGroups().iterator().next().getMetadataID())).getTableData().setModel(getEffectiveModelId(metadata, leftGroup));
SymbolMap symbolmap = (SymbolMap) view.getProperty(Info.SYMBOL_MAP);
HashMap<ElementSymbol, List<Set<Constant>>> newPartitionInfo = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
Map<Expression, ElementSymbol> inverse = symbolmap.inserseMapping();
for (int[] match : matches) {
updatePartitionInfo(partitionInfo, matches, inverse, newPartitionInfo, match[0]);
updatePartitionInfo(rightPartionInfo, matches, inverse, newPartitionInfo, match[1]);
}
view.setProperty(Info.PARTITION_INFO, newPartitionInfo);
// since we've created a new union node, there's a chance we can decompose again
if (view.getParent().getType() == NodeConstants.Types.JOIN) {
return decomposeJoin(view.getParent(), root, metadata, context);
}
return root;
}
Aggregations