use of org.teiid.query.optimizer.relational.plantree.PlanNode 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.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleCollapseSource method addDistinct.
/**
* This functions as "RulePushDistinct", however we do not bother
* checking to see if a parent dup removal can actually be removed
* - which can only happen if there are sources/selects/simple projects/limits/order by
* between the access node and the parent dup removal.
*
* @param metadata
* @param capFinder
* @param accessNode
* @param queryCommand
* @param capabilitiesFinder
* @throws QueryMetadataException
* @throws TeiidComponentException
*/
private PlanNode addDistinct(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, PlanNode accessNode, PlanNode root, QueryCommand queryCommand, CapabilitiesFinder capabilitiesFinder) throws QueryMetadataException, TeiidComponentException {
if (RuleRemoveOptionalJoins.useNonDistinctRows(accessNode.getParent())) {
return root;
}
if (queryCommand instanceof Query) {
boolean allConstants = true;
for (Expression ex : (List<Expression>) accessNode.getProperty(Info.OUTPUT_COLS)) {
if (!(EvaluatableVisitor.willBecomeConstant(SymbolMap.getExpression(ex)))) {
allConstants = false;
break;
}
}
if (allConstants) {
// distinct of all constants means just a single row
// see also the logic in RuleAssignOutputElements for a dupremove
Object mid = RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata);
if (!CapabilitiesUtil.supports(Capability.ROW_LIMIT, mid, metadata, capabilitiesFinder)) {
PlanNode limit = NodeFactory.getNewNode(NodeConstants.Types.TUPLE_LIMIT);
limit.setProperty(Info.MAX_TUPLE_LIMIT, new Constant(1));
limit.setProperty(NodeConstants.Info.OUTPUT_COLS, accessNode.getProperty(NodeConstants.Info.OUTPUT_COLS));
if (accessNode.getParent() != null) {
accessNode.addAsParent(limit);
return root;
}
limit.addFirstChild(accessNode);
return limit;
}
if (queryCommand.getLimit() != null) {
if (queryCommand.getLimit().getRowLimit() == null) {
queryCommand.getLimit().setRowLimit(new Constant(1));
}
// else could have limit 0, so it takes more logic (case statement) to set this
} else {
queryCommand.setLimit(new Limit(null, new Constant(1)));
}
return root;
}
}
if (queryCommand.getLimit() != null) {
// TODO: could create an inline view
return root;
}
boolean requireDupPush = false;
if (queryCommand.getOrderBy() == null) {
/*
* we're assuming that a pushed order by implies that the cost of the distinct operation
* will be marginal - which is not always true.
*
* TODO: we should add costing for the benefit of pushing distinct by itself
* cardinality without = c
* assume cost ~ c lg c for c' cardinality and a modification for associated bandwidth savings
* recompute cost of processing plan with c' and see if new cost + c lg c < original cost
*/
PlanNode dupRemove = NodeEditor.findParent(accessNode, NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.SOURCE);
if (dupRemove != null) {
// TODO: what about when sort/dup remove have been combined
PlanNode project = NodeEditor.findParent(accessNode, NodeConstants.Types.PROJECT, NodeConstants.Types.DUP_REMOVE);
if (project != null) {
List<Expression> projectCols = (List<Expression>) project.getProperty(Info.PROJECT_COLS);
for (Expression ex : projectCols) {
ex = SymbolMap.getExpression(ex);
if (!(ex instanceof ElementSymbol) && !(ex instanceof Constant) && !(EvaluatableVisitor.willBecomeConstant(ex, true))) {
return root;
}
}
/*
* If we can simply move the dupremove below the projection, then we'll do that as well
*/
requireDupPush = true;
}
}
if (accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE)) {
if (dupRemove == null) {
return root;
}
if (requireDupPush) {
// if multi-source we still need to process above
requireDupPush = false;
}
} else if (!requireDupPush) {
return root;
}
}
// ensure that all columns are comparable - they might not be if there is an intermediate project
for (Expression ses : queryCommand.getProjectedSymbols()) {
if (DataTypeManager.isNonComparable(DataTypeManager.getDataTypeName(ses.getType()))) {
return root;
}
}
/*
* TODO: if we are under a grouping/union not-all, then we should also fully order the results
* and update the processing logic (this requires that we can guarantee null ordering) to assume sorted
*/
if (queryCommand instanceof SetQuery) {
((SetQuery) queryCommand).setAll(false);
} else if (CapabilitiesUtil.supports(Capability.QUERY_SELECT_DISTINCT, RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata), metadata, capFinder)) {
Query query = (Query) queryCommand;
HashSet<GroupSymbol> keyPreservingGroups = new HashSet<GroupSymbol>();
ResolverUtil.findKeyPreserved(query, keyPreservingGroups, metadata);
if (!QueryRewriter.isDistinctWithGroupBy(query) && !NewCalculateCostUtil.usesKey(query.getSelect().getProjectedSymbols(), keyPreservingGroups, metadata, true)) {
if (requireDupPush) {
// remove the upper dup remove
PlanNode dupRemove = NodeEditor.findParent(accessNode, NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.SOURCE);
if (dupRemove.getParent() == null) {
root = dupRemove.getFirstChild();
dupRemove.getFirstChild().removeFromParent();
} else {
dupRemove.getParent().replaceChild(dupRemove, dupRemove.getFirstChild());
}
}
((Query) queryCommand).getSelect().setDistinct(true);
}
}
return root;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleCollapseSource method processLimit.
private void processLimit(PlanNode node, QueryCommand query, QueryMetadataInterface metadata) {
Expression limit = (Expression) node.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
Expression offset = (Expression) node.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
PlanNode limitNode = NodeFactory.getNewNode(NodeConstants.Types.TUPLE_LIMIT);
Expression childLimit = null;
Expression childOffset = null;
if (query.getLimit() != null) {
childLimit = query.getLimit().getRowLimit();
childOffset = query.getLimit().getOffset();
}
RulePushLimit.combineLimits(limitNode, metadata, limit, offset, childLimit, childOffset);
Limit lim = new Limit((Expression) limitNode.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT), (Expression) limitNode.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT));
lim.setImplicit(node.hasBooleanProperty(Info.IS_IMPLICIT_LIMIT) && (query.getLimit() == null || query.getLimit().isImplicit()));
query.setLimit(lim);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleCopyCriteria method visitChildern.
private boolean visitChildern(PlanNode node, Set<Criteria>[] criteriaInfo, boolean changedTree, QueryMetadataInterface metadata, boolean underAccess) {
if (node.getChildCount() > 0) {
underAccess |= node.getType() == NodeConstants.Types.ACCESS;
List<PlanNode> children = node.getChildren();
for (int i = 0; i < children.size(); i++) {
PlanNode childNode = children.get(i);
changedTree |= tryToCopy(childNode, i == 0 ? criteriaInfo : new Set[2], metadata, underAccess);
}
}
return changedTree;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode 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;
}
Aggregations