@Override protected String visitLogicalBinaryExpression(LogicalBinaryExpression node, StackableAstVisitorContext<Integer> indent) { boolean sameTypeAsPreviousExpression = indent.getPreviousNode() .map(previous -> previous instanceof LogicalBinaryExpression && ((LogicalBinaryExpression) previous).getType() == node.getType()) .orElse(false); if (!sameTypeAsPreviousExpression) { indent = increase(indent); } String formattedNode = process(node.getLeft(), indent) + '\n' + indentString(indent.getContext() + 1) + node.getType().toString() + ' ' + process(node.getRight(), indent); if (sameTypeAsPreviousExpression) { return formattedNode; } else { return "(" + formattedNode + ")"; } }
/** * Assembles the ComparisonExpressions into a single Expression combined * using the specified Binary Operators, in order. * @param whereClauses * @param binaryOps * @param i * @return */ private static Expression logicallyCombineWhereClauses( List<Expression> whereClauses, List<Optional<LogicalBinaryExpression.Type>> binaryOps, int i) { if (whereClauses.isEmpty()) { return null; } if (i >= binaryOps.size()) { return whereClauses.get(i); } return new LogicalBinaryExpression( binaryOps.get(i).get(), whereClauses.get(i), logicallyCombineWhereClauses(whereClauses, binaryOps, i + 1)); }
@Override protected RowExpression visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) { return call( logicalExpressionSignature(node.getType()), BOOLEAN, process(node.getLeft(), context), process(node.getRight(), context)); }
public static List<Expression> extractConjuncts(Expression expression) { if (expression instanceof LogicalBinaryExpression && ((LogicalBinaryExpression) expression).getType() == LogicalBinaryExpression.Type.AND) { LogicalBinaryExpression and = (LogicalBinaryExpression) expression; return ImmutableList.<Expression>builder() .addAll(extractConjuncts(and.getLeft())) .addAll(extractConjuncts(and.getRight())) .build(); } return ImmutableList.of(expression); }
public static List<Expression> extractDisjuncts(Expression expression) { if (expression instanceof LogicalBinaryExpression && ((LogicalBinaryExpression) expression).getType() == LogicalBinaryExpression.Type.OR) { LogicalBinaryExpression or = (LogicalBinaryExpression) expression; return ImmutableList.<Expression>builder() .addAll(extractDisjuncts(or.getLeft())) .addAll(extractDisjuncts(or.getRight())) .build(); } return ImmutableList.of(expression); }
public static Expression binaryExpression(LogicalBinaryExpression.Type type, Iterable<Expression> expressions) { requireNonNull(type, "type is null"); requireNonNull(expressions, "expressions is null"); Preconditions.checkArgument(!Iterables.isEmpty(expressions), "expressions is empty"); // build balanced tree for efficient recursive processing Queue<Expression> queue = new ArrayDeque<>(newArrayList(expressions)); while (queue.size() > 1) { queue.add(new LogicalBinaryExpression(type, queue.remove(), queue.remove())); } return queue.remove(); }
@Override protected Object visitLogicalBinaryExpression(LogicalBinaryExpression node, Object context) { Object left = process(node.getLeft(), context); Object right = process(node.getRight(), context); switch (node.getType()) { case AND: { // if either left or right is false, result is always false regardless of nulls if (Boolean.FALSE.equals(left) || Boolean.TRUE.equals(right)) { return left; } if (Boolean.FALSE.equals(right) || Boolean.TRUE.equals(left)) { return right; } break; } case OR: { // if either left or right is true, result is always true regardless of nulls if (Boolean.TRUE.equals(left) || Boolean.FALSE.equals(right)) { return left; } if (Boolean.TRUE.equals(right) || Boolean.FALSE.equals(left)) { return right; } break; } } if (left == null && right == null) { return null; } return new LogicalBinaryExpression(node.getType(), toExpression(left, expressionTypes.get(node.getLeft())), toExpression(right, expressionTypes.get(node.getRight()))); }
private static LogicalBinaryExpression.Type flipLogicalBinaryType(LogicalBinaryExpression.Type type) { switch (type) { case AND: return LogicalBinaryExpression.Type.OR; case OR: return LogicalBinaryExpression.Type.AND; default: throw new AssertionError("Unknown type: " + type); } }
@Override protected Type visitLogicalBinaryExpression(LogicalBinaryExpression node, StackableAstVisitorContext<AnalysisContext> context) { coerceType(context, node.getLeft(), BOOLEAN, "Left side of logical expression"); coerceType(context, node.getRight(), BOOLEAN, "Right side of logical expression"); expressionTypes.put(node, BOOLEAN); return BOOLEAN; }
@Override public Node visitLogicalBinary(SqlBaseParser.LogicalBinaryContext context) { return new LogicalBinaryExpression( getLocation(context.operator), getLogicalBinaryOperator(context.operator), (Expression) visit(context.left), (Expression) visit(context.right)); }
private static LogicalBinaryExpression.Type getLogicalBinaryOperator(Token token) { switch (token.getType()) { case SqlBaseLexer.AND: return LogicalBinaryExpression.Type.AND; case SqlBaseLexer.OR: return LogicalBinaryExpression.Type.OR; } throw new IllegalArgumentException("Unsupported operator: " + token.getText()); }
public static LogicalBinaryExpression.Type getComparisonBinaryOperatorType( String op) { if (op.equals("and")) { return LogicalBinaryExpression.Type.AND; } if (op.equals("or")) { return LogicalBinaryExpression.Type.OR; } return null; }
@Override protected String visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) { return formatBinaryExpression(node.getType().toString(), node.getLeft(), node.getRight()); }
@Override protected IComparison visitExpression(Expression node, QueryState state) { if( node instanceof LogicalBinaryExpression){ LogicalBinaryExpression boolExp = (LogicalBinaryExpression)node; IComparison left = boolExp.getLeft().accept(this, state); IComparison right = boolExp.getRight().accept(this, state); return new BooleanComparison(left, right, boolExp.getType() == Type.AND); }else if( node instanceof ComparisonExpression){ ComparisonExpression compareExp = (ComparisonExpression)node; Column column = new SelectParser().visitExpression(compareExp.getLeft(), state); Column leftCol = state.getHeading().getColumnByLabel(column.getLabel()); if(leftCol == null){ state.addException("Having reference "+column+" not found in SELECT clause"); return null; } // right hand side is a concrete literal to compare with if(compareExp.getRight() instanceof Literal){ Object value; if(compareExp.getRight() instanceof LongLiteral) value = ((LongLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof BooleanLiteral) value = ((BooleanLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof DoubleLiteral) value = ((DoubleLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof StringLiteral) value = ((StringLiteral)compareExp.getRight()).getValue(); else { state.addException("Unable to get value from "+compareExp.getRight()); return null; } return new SimpleComparison(leftCol, compareExp.getType(), (Number)value); // right hand side refers to another column } else if(compareExp.getRight() instanceof DereferenceExpression || compareExp.getRight() instanceof QualifiedNameReference){ String col2; if(compareExp.getLeft() instanceof DereferenceExpression){ // parse columns like 'reference.field' col2 = SelectParser.visitDereferenceExpression((DereferenceExpression)compareExp.getRight()); }else{ col2 = ((QualifiedNameReference)compareExp.getRight()).getName().toString(); } col2 = Heading.findOriginal(state.originalSql(), col2, "having.+", "\\W"); Column rightCol = state.getHeading().getColumnByLabel(col2); if(rightCol == null){ state.addException("column "+col2+" not found in SELECT clause"); return null; } return new SimpleComparison(leftCol, compareExp.getType(), rightCol); }else { // unknown right hand side so state.addException("Unable to get value from "+compareExp.getRight()); return null; } }else if( node instanceof NotExpression){ state.addException("NOT is currently not supported, use '<>' instead"); }else{ state.addException("Unable to parse "+node+" ("+node.getClass().getName()+") is not a supported expression"); } return null; }
public static Signature logicalExpressionSignature(LogicalBinaryExpression.Type expressionType) { return internalScalarFunction(expressionType.name(), StandardTypes.BOOLEAN, StandardTypes.BOOLEAN, StandardTypes.BOOLEAN); }
public static Expression and(Iterable<Expression> expressions) { return binaryExpression(LogicalBinaryExpression.Type.AND, expressions); }
public static Expression or(Iterable<Expression> expressions) { return binaryExpression(LogicalBinaryExpression.Type.OR, expressions); }
@Override protected ExtractionResult visitLogicalBinaryExpression(LogicalBinaryExpression node, Boolean complement) { ExtractionResult leftResult = process(node.getLeft(), complement); ExtractionResult rightResult = process(node.getRight(), complement); TupleDomain<Symbol> leftTupleDomain = leftResult.getTupleDomain(); TupleDomain<Symbol> rightTupleDomain = rightResult.getTupleDomain(); LogicalBinaryExpression.Type type = complement ? flipLogicalBinaryType(node.getType()) : node.getType(); switch (type) { case AND: return new ExtractionResult( leftTupleDomain.intersect(rightTupleDomain), combineConjuncts(leftResult.getRemainingExpression(), rightResult.getRemainingExpression())); case OR: TupleDomain<Symbol> columnUnionedTupleDomain = TupleDomain.columnWiseUnion(leftTupleDomain, rightTupleDomain); // In most cases, the columnUnionedTupleDomain is only a superset of the actual strict union // and so we can return the current node as the remainingExpression so that all bounds will be double checked again at execution time. Expression remainingExpression = complementIfNecessary(node, complement); // However, there are a few cases where the column-wise union is actually equivalent to the strict union, so we if can detect // some of these cases, we won't have to double check the bounds unnecessarily at execution time. // We can only make inferences if the remaining expressions on both side are equal and deterministic if (leftResult.getRemainingExpression().equals(rightResult.getRemainingExpression()) && DeterminismEvaluator.isDeterministic(leftResult.getRemainingExpression())) { // The column-wise union is equivalent to the strict union if // 1) If both TupleDomains consist of the same exact single column (e.g. left TupleDomain => (a > 0), right TupleDomain => (a < 10)) // 2) If one TupleDomain is a superset of the other (e.g. left TupleDomain => (a > 0, b > 0 && b < 10), right TupleDomain => (a > 5, b = 5)) boolean matchingSingleSymbolDomains = !leftTupleDomain.isNone() && !rightTupleDomain.isNone() && leftTupleDomain.getDomains().get().size() == 1 && rightTupleDomain.getDomains().get().size() == 1 && leftTupleDomain.getDomains().get().keySet().equals(rightTupleDomain.getDomains().get().keySet()); boolean oneSideIsSuperSet = leftTupleDomain.contains(rightTupleDomain) || rightTupleDomain.contains(leftTupleDomain); if (matchingSingleSymbolDomains || oneSideIsSuperSet) { remainingExpression = leftResult.getRemainingExpression(); } } return new ExtractionResult(columnUnionedTupleDomain, remainingExpression); default: throw new AssertionError("Unknown type: " + node.getType()); } }
@Override protected Boolean visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) { return process(node.getLeft(), context) && process(node.getRight(), context); }
@Override protected String visitLogicalBinaryExpression(LogicalBinaryExpression node, Boolean unmangleNames) { return formatBinaryExpression(node.getType().toString(), node.getLeft(), node.getRight(), unmangleNames); }
public static Expression logicalAnd(Expression left, Expression right) { return new LogicalBinaryExpression(LogicalBinaryExpression.Type.AND, left, right); }
@Test public void testPrecedenceAndAssociativity() throws Exception { assertExpression("1 AND 2 OR 3", new LogicalBinaryExpression(LogicalBinaryExpression.Type.OR, new LogicalBinaryExpression(LogicalBinaryExpression.Type.AND, new LongLiteral("1"), new LongLiteral("2")), new LongLiteral("3"))); assertExpression("1 OR 2 AND 3", new LogicalBinaryExpression(LogicalBinaryExpression.Type.OR, new LongLiteral("1"), new LogicalBinaryExpression(LogicalBinaryExpression.Type.AND, new LongLiteral("2"), new LongLiteral("3")))); assertExpression("NOT 1 AND 2", new LogicalBinaryExpression(LogicalBinaryExpression.Type.AND, new NotExpression(new LongLiteral("1")), new LongLiteral("2"))); assertExpression("NOT 1 OR 2", new LogicalBinaryExpression(LogicalBinaryExpression.Type.OR, new NotExpression(new LongLiteral("1")), new LongLiteral("2"))); assertExpression("-1 + 2", new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.ADD, negative(new LongLiteral("1")), new LongLiteral("2"))); assertExpression("1 - 2 - 3", new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.SUBTRACT, new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.SUBTRACT, new LongLiteral("1"), new LongLiteral("2")), new LongLiteral("3"))); assertExpression("1 / 2 / 3", new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.DIVIDE, new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.DIVIDE, new LongLiteral("1"), new LongLiteral("2")), new LongLiteral("3"))); assertExpression("1 + 2 * 3", new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.ADD, new LongLiteral("1"), new ArithmeticBinaryExpression(ArithmeticBinaryExpression.Type.MULTIPLY, new LongLiteral("2"), new LongLiteral("3")))); }
public List<Optional<LogicalBinaryExpression.Type>> getComparisonBinaryOperators() { return ImmutableList.copyOf(comparisonBinaryOperators); }