@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 String visitInListExpression(InListExpression node, StackableAstVisitorContext<Integer> indent) { StringBuilder builder = new StringBuilder("("); boolean first = true; for (Expression expression : node.getValues()) { builder.append("\n") .append(indentString(indent.getContext() + 1)) .append(first ? " " : ", ") .append(process(expression, increase(indent))); first = false; } return builder.append(")").toString(); }
/** * 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()); }
/** * 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; }
@Override protected Type visitInListExpression(InListExpression node, StackableAstVisitorContext<AnalysisContext> context) { Type type = coerceToSingleType(context, "All IN list values must be the same type: %s", node.getValues()); expressionTypes.put(node, type); return type; // TODO: this really should a be relation type }
@Override protected String visitInListExpression(InListExpression node, Void context) { return "(" + joinExpressions(node.getValues()) + ")"; }
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; }
@Override protected Boolean visitInListExpression(InListExpression node, Void context) { return node.getValues().stream().allMatch(expression -> process(expression, context)); }
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 visitInListExpression(InListExpression node, Boolean unmangleNames) { return "(" + joinExpressions(node.getValues(), unmangleNames) + ")"; }