use of org.teiid.query.sql.lang.SubqueryContainer.Evaluatable 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.sql.lang.SubqueryContainer.Evaluatable 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;
}
}
}
Aggregations