@Override protected ExtractionResult visitInPredicate(InPredicate node, Boolean complement) { if (!(node.getValue() instanceof QualifiedNameReference) || !(node.getValueList() instanceof InListExpression)) { return super.visitInPredicate(node, complement); } InListExpression valueList = (InListExpression) node.getValueList(); checkState(!valueList.getValues().isEmpty(), "InListExpression should never be empty"); ImmutableList.Builder<Expression> disjuncts = ImmutableList.builder(); for (Expression expression : valueList.getValues()) { disjuncts.add(new ComparisonExpression(EQUAL, node.getValue(), expression)); } return process(or(disjuncts.build()), complement); }
@Override protected Type visitInPredicate(InPredicate node, StackableAstVisitorContext<AnalysisContext> context) { Expression value = node.getValue(); process(value, context); Expression valueList = node.getValueList(); process(valueList, context); if (valueList instanceof InListExpression) { InListExpression inListExpression = (InListExpression) valueList; coerceToSingleType(context, "IN value and list items must be the same type: %s", ImmutableList.<Expression>builder().add(value).addAll(inListExpression.getValues()).build()); } else if (valueList instanceof SubqueryExpression) { coerceToSingleType(context, node, "value and result of subquery must be of the same type for IN expression: %s vs %s", value, valueList); } expressionTypes.put(node, BOOLEAN); return BOOLEAN; }
@Override protected Type visitSubqueryExpression(SubqueryExpression node, StackableAstVisitorContext<AnalysisContext> context) { StatementAnalyzer analyzer = statementAnalyzerFactory.apply(node); RelationType descriptor = analyzer.process(node.getQuery(), context.getContext()); // Subquery should only produce one column if (descriptor.getVisibleFieldCount() != 1) { throw new SemanticException(MULTIPLE_FIELDS_FROM_SUBQUERY, node, "Multiple columns returned by subquery are not yet supported. Found %s", descriptor.getVisibleFieldCount()); } Optional<Node> previousNode = context.getPreviousNode(); if (previousNode.isPresent() && previousNode.get() instanceof InPredicate && ((InPredicate) previousNode.get()).getValue() != node) { subqueryInPredicates.add((InPredicate) previousNode.get()); } else { scalarSubqueries.add(node); } Type type = Iterables.getOnlyElement(descriptor.getVisibleFields()).getType(); expressionTypes.put(node, type); return type; }
/** * Parses predicates of type IN (...) * @param node * @param state * @return */ private QueryWrapper processIn(InPredicate node, QueryState state) { String field = getVariableName(node.getValue()); FieldAndType fat = getFieldAndType(field, state); field = fat.getFieldName(); if(node.getValueList() instanceof InListExpression){ InListExpression list = (InListExpression)(node).getValueList(); List<Object> values = new ArrayList<Object>(); for(Expression listItem : list.getValues()){ Object value = this.getLiteralValue(listItem, state); if(state.hasException()) return null; values.add(value); } if(field.equals(Heading.ID)) { String[] types = new String[state.getSources().size()]; for(int i=0; i<types.length; i++) types[i] = state.getSources().get(i).getSource(); String[] ids = new String[values.size()]; return new QueryWrapper(QueryBuilders.idsQuery(types).addIds(values.toArray(ids))); } if(fat.getFieldType() == Types.REF) return new QueryWrapper(QueryBuilders.termsQuery(field, values), field.split("\\.")[0]); return new QueryWrapper(QueryBuilders.termsQuery(field, values)); }else { state.addException("SELECT ... IN can only be used with a list of values!"); return null; } }
@Override protected RowExpression visitInPredicate(InPredicate node, Void context) { ImmutableList.Builder<RowExpression> arguments = ImmutableList.builder(); arguments.add(process(node.getValue(), context)); InListExpression values = (InListExpression) node.getValueList(); for (Expression value : values.getValues()) { arguments.add(process(value, context)); } return call(Signatures.inSignature(), BOOLEAN, arguments.build()); }
private PlanBuilder appendSemiJoins(PlanBuilder subPlan, Set<InPredicate> inPredicates) { for (InPredicate inPredicate : inPredicates) { subPlan = appendSemiJoin(subPlan, inPredicate); } return subPlan; }
/** * Semijoins are planned as follows: * 1) SQL constructs that need to be semijoined are extracted during Analysis phase (currently only InPredicates so far) * 2) Create a new SemiJoinNode that connects the semijoin lookup field with the planned subquery and have it output a new boolean * symbol for the result of the semijoin. * 3) Add an entry to the TranslationMap that notes to map the InPredicate into semijoin output symbol * <p> * Currently, we only support semijoins deriving from InPredicates, but we will probably need * to add support for more SQL constructs in the future. */ private PlanBuilder appendSemiJoin(PlanBuilder subPlan, InPredicate inPredicate) { TranslationMap translations = copyTranslations(subPlan); subPlan = appendProjections(subPlan, ImmutableList.of(inPredicate.getValue())); Symbol sourceJoinSymbol = subPlan.translate(inPredicate.getValue()); checkState(inPredicate.getValueList() instanceof SubqueryExpression); SubqueryExpression subqueryExpression = (SubqueryExpression) inPredicate.getValueList(); RelationPlanner relationPlanner = new RelationPlanner(analysis, symbolAllocator, idAllocator, metadata, session); RelationPlan valueListRelation = relationPlanner.process(subqueryExpression.getQuery(), null); Symbol filteringSourceJoinSymbol = getOnlyElement(valueListRelation.getRoot().getOutputSymbols()); Symbol semiJoinOutputSymbol = symbolAllocator.newSymbol("semijoinresult", BOOLEAN); translations.put(inPredicate, semiJoinOutputSymbol); return new PlanBuilder(translations, new SemiJoinNode(idAllocator.getNextId(), subPlan.getRoot(), valueListRelation.getRoot(), sourceJoinSymbol, filteringSourceJoinSymbol, semiJoinOutputSymbol, Optional.empty(), Optional.empty()), subPlan.getSampleWeight()); }
/** * Rewrite single value InPredicates as equality if possible */ private static Expression normalizeInPredicateToEquality(Expression expression) { if (expression instanceof InPredicate) { InPredicate inPredicate = (InPredicate) expression; if (inPredicate.getValueList() instanceof InListExpression) { InListExpression valueList = (InListExpression) inPredicate.getValueList(); if (valueList.getValues().size() == 1) { return new ComparisonExpression(ComparisonExpression.Type.EQUAL, inPredicate.getValue(), Iterables.getOnlyElement(valueList.getValues())); } } } return expression; }
private PlanBuilder appendSemiJoin(PlanBuilder subPlan, InPredicate inPredicate) { TranslationMap translations = new TranslationMap(subPlan.getRelationPlan(), analysis); translations.copyMappingsFrom(subPlan.getTranslations()); subPlan = appendProjections(subPlan, ImmutableList.of(inPredicate.getValue())); Symbol sourceJoinSymbol = subPlan.translate(inPredicate.getValue()); checkState(inPredicate.getValueList() instanceof SubqueryExpression); SubqueryExpression subqueryExpression = (SubqueryExpression) inPredicate.getValueList(); RelationPlanner relationPlanner = new RelationPlanner(analysis, symbolAllocator, idAllocator, metadata, session); RelationPlan valueListRelation = relationPlanner.process(subqueryExpression.getQuery(), null); Symbol filteringSourceJoinSymbol = Iterables.getOnlyElement(valueListRelation.getRoot().getOutputSymbols()); Symbol semiJoinOutputSymbol = symbolAllocator.newSymbol("semijoinresult", BOOLEAN); translations.put(inPredicate, semiJoinOutputSymbol); return new PlanBuilder(translations, new SemiJoinNode(idAllocator.getNextId(), subPlan.getRoot(), valueListRelation.getRoot(), sourceJoinSymbol, filteringSourceJoinSymbol, semiJoinOutputSymbol, Optional.empty(), Optional.empty()), subPlan.getSampleWeight()); }
public ExpressionAnalysis( IdentityHashMap<Expression, Type> expressionTypes, IdentityHashMap<Expression, Type> expressionCoercions, Set<InPredicate> subqueryInPredicates, Set<SubqueryExpression> scalarSubqueries, Set<Expression> columnReferences) { this.expressionTypes = requireNonNull(expressionTypes, "expressionTypes is null"); this.expressionCoercions = requireNonNull(expressionCoercions, "expressionCoercions is null"); this.subqueryInPredicates = requireNonNull(subqueryInPredicates, "subqueryInPredicates is null"); this.scalarSubqueries = requireNonNull(scalarSubqueries, "subqueryInPredicates is null"); this.columnReferences = ImmutableSet.copyOf(requireNonNull(columnReferences, "columnReferences is null")); }
@Override protected String visitInPredicate(InPredicate node, Void context) { return "(" + process(node.getValue(), context) + " IN " + process(node.getValueList(), context) + ")"; }
@Override protected String visitInPredicate(InPredicate node, StackableAstVisitorContext<Integer> indent) { return "(" + process(node.getValue(), indent) + " IN " + process(node.getValueList(), indent) + ")"; }
private static List<Expression> extractDisjuncts(Type type, Ranges ranges, QualifiedNameReference reference) { List<Expression> disjuncts = new ArrayList<>(); List<Expression> singleValues = new ArrayList<>(); for (Range range : ranges.getOrderedRanges()) { checkState(!range.isAll()); // Already checked if (range.isSingleValue()) { singleValues.add(toExpression(range.getSingleValue(), type)); } else if (isBetween(range)) { // Specialize the range with BETWEEN expression if possible b/c it is currently more efficient disjuncts.add(new BetweenPredicate(reference, toExpression(range.getLow().getValue(), type), toExpression(range.getHigh().getValue(), type))); } else { List<Expression> rangeConjuncts = new ArrayList<>(); if (!range.getLow().isLowerUnbounded()) { switch (range.getLow().getBound()) { case ABOVE: rangeConjuncts.add(new ComparisonExpression(GREATER_THAN, reference, toExpression(range.getLow().getValue(), type))); break; case EXACTLY: rangeConjuncts.add(new ComparisonExpression(GREATER_THAN_OR_EQUAL, reference, toExpression(range.getLow().getValue(), type))); break; case BELOW: throw new IllegalStateException("Low Marker should never use BELOW bound: " + range); default: throw new AssertionError("Unhandled bound: " + range.getLow().getBound()); } } if (!range.getHigh().isUpperUnbounded()) { switch (range.getHigh().getBound()) { case ABOVE: throw new IllegalStateException("High Marker should never use ABOVE bound: " + range); case EXACTLY: rangeConjuncts.add(new ComparisonExpression(LESS_THAN_OR_EQUAL, reference, toExpression(range.getHigh().getValue(), type))); break; case BELOW: rangeConjuncts.add(new ComparisonExpression(LESS_THAN, reference, toExpression(range.getHigh().getValue(), type))); break; default: throw new AssertionError("Unhandled bound: " + range.getHigh().getBound()); } } // If rangeConjuncts is null, then the range was ALL, which should already have been checked for checkState(!rangeConjuncts.isEmpty()); disjuncts.add(combineConjuncts(rangeConjuncts)); } } // Add back all of the possible single values either as an equality or an IN predicate if (singleValues.size() == 1) { disjuncts.add(new ComparisonExpression(EQUAL, reference, getOnlyElement(singleValues))); } else if (singleValues.size() > 1) { disjuncts.add(new InPredicate(reference, new InListExpression(singleValues))); } return disjuncts; }
public Set<InPredicate> getSubqueryInPredicates() { return subqueryInPredicates; }
@Override protected Boolean visitInPredicate(InPredicate node, Void context) { return process(node.getValue(), context) && process(node.getValueList(), context); }
public Set<InPredicate> getInPredicates(Node node) { return inPredicates.get(node); }
public JoinInPredicates(Set<InPredicate> leftInPredicates, Set<InPredicate> rightInPredicates) { this.leftInPredicates = ImmutableSet.copyOf(requireNonNull(leftInPredicates, "leftInPredicates is null")); this.rightInPredicates = ImmutableSet.copyOf(requireNonNull(rightInPredicates, "rightInPredicates is null")); }
public Set<InPredicate> getLeftInPredicates() { return leftInPredicates; }
public Set<InPredicate> getRightInPredicates() { return rightInPredicates; }
private static InPredicate in(Symbol symbol, List<?> values) { List<Type> types = nCopies(values.size(), TYPES.get(symbol)); List<Expression> expressions = LiteralInterpreter.toExpressions(values, types); return new InPredicate(reference(symbol), new InListExpression(expressions)); }
@Override protected String visitInPredicate(InPredicate node, Boolean unmangleNames) { return "(" + process(node.getValue(), unmangleNames) + " IN " + process(node.getValueList(), unmangleNames) + ")"; }