@Override protected String visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, Void context) { return new StringBuilder() .append("(") .append(process(node.getValue(), context)) .append(' ') .append(node.getOperator().getValue()) .append(' ') .append(node.getQuantifier().toString()) .append(' ') .append(process(node.getSubquery(), context)) .append(")") .toString(); }
private static boolean shouldCompareValueWithLowerBound(QuantifiedComparisonExpression quantifiedComparison) { switch (quantifiedComparison.getQuantifier()) { case ALL: switch (quantifiedComparison.getOperator()) { case LESS_THAN: case LESS_THAN_OR_EQUAL: return true; case GREATER_THAN: case GREATER_THAN_OR_EQUAL: return false; } break; case ANY: case SOME: switch (quantifiedComparison.getOperator()) { case LESS_THAN: case LESS_THAN_OR_EQUAL: return false; case GREATER_THAN: case GREATER_THAN_OR_EQUAL: return true; } break; } throw new IllegalArgumentException("Unexpected quantifier: " + quantifiedComparison.getQuantifier()); }
@Override protected R visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, C context) { process(node.getValue(), context); process(node.getSubquery(), context); return null; }
private Expression getBoundComparisons(QuantifiedComparisonExpression quantifiedComparison, Symbol minValue, Symbol maxValue) { if (quantifiedComparison.getOperator() == EQUAL && quantifiedComparison.getQuantifier() == ALL) { // A = ALL B <=> min B = max B && A = min B return combineConjuncts( new ComparisonExpression(EQUAL, minValue.toSymbolReference(), maxValue.toSymbolReference()), new ComparisonExpression(EQUAL, quantifiedComparison.getValue(), maxValue.toSymbolReference())); } if (EnumSet.of(LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL).contains(quantifiedComparison.getOperator())) { // A < ALL B <=> A < min B // A > ALL B <=> A > max B // A < ANY B <=> A < max B // A > ANY B <=> A > min B Symbol boundValue = shouldCompareValueWithLowerBound(quantifiedComparison) ? minValue : maxValue; return new ComparisonExpression(quantifiedComparison.getOperator(), quantifiedComparison.getValue(), boundValue.toSymbolReference()); } throw new IllegalArgumentException("Unsupported quantified comparison: " + quantifiedComparison); }
@Override protected Type visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, StackableAstVisitorContext<Context> context) { Expression value = node.getValue(); process(value, context); Expression subquery = node.getSubquery(); process(subquery, context); Type comparisonType = coerceToSingleType(context, node, "Value expression and result of subquery must be of the same type for quantified comparison: %s vs %s", value, subquery); switch (node.getOperator()) { case LESS_THAN: case LESS_THAN_OR_EQUAL: case GREATER_THAN: case GREATER_THAN_OR_EQUAL: if (!comparisonType.isOrderable()) { throw new SemanticException(TYPE_MISMATCH, node, "Type [%s] must be orderable in order to be used in quantified comparison", comparisonType); } break; case EQUAL: case NOT_EQUAL: if (!comparisonType.isComparable()) { throw new SemanticException(TYPE_MISMATCH, node, "Type [%s] must be comparable in order to be used in quantified comparison", comparisonType); } break; default: throw new IllegalStateException(format("Unexpected comparison type: %s", node.getOperator())); } return setExpressionType(node, BOOLEAN); }
@Override public Node visitQuantifiedComparison(SqlBaseParser.QuantifiedComparisonContext context) { return new QuantifiedComparisonExpression( getLocation(context.comparisonOperator()), getComparisonOperator(((TerminalNode) context.comparisonOperator().getChild(0)).getSymbol()), getComparisonQuantifier(((TerminalNode) context.comparisonQuantifier().getChild(0)).getSymbol()), (Expression) visit(context.value), new SubqueryExpression(getLocation(context.query()), (Query) visit(context.query()))); }
public Set<QuantifiedComparisonExpression> collectQuantifiedComparisonSubqueries(Expression expression, Node node) { return analysis.getQuantifiedComparisonSubqueries(node) .stream() .filter(quantifiedComparison -> nodeContains(expression, quantifiedComparison.getSubquery())) .collect(toImmutableSet()); }
public Expression rewriteUsingBounds(QuantifiedComparisonExpression quantifiedComparison, Symbol minValue, Symbol maxValue, Symbol countAllValue, Symbol countNonNullValue) { BooleanLiteral emptySetResult = quantifiedComparison.getQuantifier().equals(ALL) ? TRUE_LITERAL : FALSE_LITERAL; Function<List<Expression>, Expression> quantifier = quantifiedComparison.getQuantifier().equals(ALL) ? ExpressionUtils::combineConjuncts : ExpressionUtils::combineDisjuncts; Expression comparisonWithExtremeValue = getBoundComparisons(quantifiedComparison, minValue, maxValue); return new SimpleCaseExpression( countAllValue.toSymbolReference(), ImmutableList.of(new WhenClause( new GenericLiteral("bigint", "0"), emptySetResult)), Optional.of(quantifier.apply(ImmutableList.of( comparisonWithExtremeValue, new SearchedCaseExpression( ImmutableList.of( new WhenClause( new ComparisonExpression(NOT_EQUAL, countAllValue.toSymbolReference(), countNonNullValue.toSymbolReference()), new Cast(new NullLiteral(), BooleanType.BOOLEAN.toString()))), Optional.of(emptySetResult)))))); }
private Expression getBoundComparisons(QuantifiedComparisonExpression quantifiedComparison, Symbol minValue, Symbol maxValue) { if (quantifiedComparison.getOperator() == EQUAL && quantifiedComparison.getQuantifier() == ALL) { // A = ALL B <=> min B = max B && A = min B return combineConjuncts( new ComparisonExpression(EQUAL, minValue.toSymbolReference(), maxValue.toSymbolReference()), new ComparisonExpression(EQUAL, quantifiedComparison.getValue(), maxValue.toSymbolReference())); } if (EnumSet.of(LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL).contains(quantifiedComparison.getOperator())) { // A < ALL B <=> A < min B // A > ALL B <=> A > max B // A < ANY B <=> A < max B // A > ANY B <=> A > min B Symbol boundValue = shouldCompareValueWithLowerBound(quantifiedComparison) ? minValue : maxValue; return new ComparisonExpression(quantifiedComparison.getOperator(), quantifiedComparison.getValue(), boundValue.toSymbolReference()); } throw new IllegalArgumentException("Unsupported quantified comparison: " + quantifiedComparison); }
@Override protected Type visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, StackableAstVisitorContext<Context> context) { Expression value = node.getValue(); process(value, context); Expression subquery = node.getSubquery(); process(subquery, context); Type comparisonType = coerceToSingleType(context, node, "Value expression and result of subquery must be of the same type for quantified comparison: %s vs %s", value, subquery); switch (node.getOperator()) { case LESS_THAN: case LESS_THAN_OR_EQUAL: case GREATER_THAN: case GREATER_THAN_OR_EQUAL: if (!comparisonType.isOrderable()) { throw new SemanticException(TYPE_MISMATCH, node, "Type [%s] must be orderable in order to be used in quantified comparison", comparisonType); } break; case EQUAL: case NOT_EQUAL: if (!comparisonType.isComparable()) { throw new SemanticException(TYPE_MISMATCH, node, "Type [%s] must be comparable in order to be used in quantified comparison", comparisonType); } break; default: throw new IllegalStateException(format("Unexpected comparison type: %s", node.getOperator())); } return setExpressionType(node, BOOLEAN); }
@Override protected R visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, C context) { process(node.getValue(), context); process(node.getSubquery(), context); return null; }
private static boolean shouldCompareValueWithLowerBound(QuantifiedComparisonExpression quantifiedComparison) { switch (quantifiedComparison.getQuantifier()) { case ALL: switch (quantifiedComparison.getOperator()) { case LESS_THAN: case LESS_THAN_OR_EQUAL: return true; case GREATER_THAN: case GREATER_THAN_OR_EQUAL: return false; } break; case ANY: case SOME: switch (quantifiedComparison.getOperator()) { case LESS_THAN: case LESS_THAN_OR_EQUAL: return false; case GREATER_THAN: case GREATER_THAN_OR_EQUAL: return true; } break; } throw new IllegalArgumentException("Unexpected quantifier: " + quantifiedComparison.getQuantifier()); }
@Override public Node visitQuantifiedComparison(SqlBaseParser.QuantifiedComparisonContext context) { return new QuantifiedComparisonExpression( getLocation(context.comparisonOperator()), getComparisonOperator(((TerminalNode) context.comparisonOperator().getChild(0)).getSymbol()), getComparisonQuantifier(((TerminalNode) context.comparisonQuantifier().getChild(0)).getSymbol()), (Expression) visit(context.value), new SubqueryExpression(getLocation(context.query()), (Query) visit(context.query()))); }
public Set<QuantifiedComparisonExpression> collectQuantifiedComparisonSubqueries(Expression expression, Node node) { return analysis.getQuantifiedComparisonSubqueries(node) .stream() .filter(quantifiedComparison -> nodeContains(expression, quantifiedComparison.getSubquery())) .collect(toImmutableSet()); }
public Expression rewriteUsingBounds(QuantifiedComparisonExpression quantifiedComparison, Symbol minValue, Symbol maxValue, Symbol countAllValue, Symbol countNonNullValue) { BooleanLiteral emptySetResult = quantifiedComparison.getQuantifier().equals(ALL) ? TRUE_LITERAL : FALSE_LITERAL; Function<List<Expression>, Expression> quantifier = quantifiedComparison.getQuantifier().equals(ALL) ? ExpressionUtils::combineConjuncts : ExpressionUtils::combineDisjuncts; Expression comparisonWithExtremeValue = getBoundComparisons(quantifiedComparison, minValue, maxValue); return new SimpleCaseExpression( countAllValue.toSymbolReference(), ImmutableList.of(new WhenClause( new GenericLiteral("bigint", "0"), emptySetResult)), Optional.of(quantifier.apply(ImmutableList.of( comparisonWithExtremeValue, new SearchedCaseExpression( ImmutableList.of( new WhenClause( new ComparisonExpression(NOT_EQUAL, countAllValue.toSymbolReference(), countNonNullValue.toSymbolReference()), new Cast(new NullLiteral(), BooleanType.BOOLEAN.toString()))), Optional.of(emptySetResult)))))); }
@Override protected String visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, Void context) { return new StringBuilder() .append("(") .append(process(node.getValue(), context)) .append(' ') .append(node.getOperator().getValue()) .append(' ') .append(node.getQuantifier().toString()) .append(' ') .append(process(node.getSubquery(), context)) .append(")") .toString(); }
@Test public void testQuantifiedComparison() { assertExpression("col1 < ANY (SELECT col2 FROM table1)", new QuantifiedComparisonExpression( LESS_THAN, QuantifiedComparisonExpression.Quantifier.ANY, identifier("col1"), new SubqueryExpression(simpleQuery(selectList(new SingleColumn(identifier("col2"))), table(QualifiedName.of("table1")))))); assertExpression("col1 = ALL (VALUES ROW(1), ROW(2))", new QuantifiedComparisonExpression( ComparisonExpression.Operator.EQUAL, QuantifiedComparisonExpression.Quantifier.ALL, identifier("col1"), new SubqueryExpression(query(values(row(new LongLiteral("1")), row(new LongLiteral("2"))))))); assertExpression("col1 >= SOME (SELECT 10)", new QuantifiedComparisonExpression( ComparisonExpression.Operator.GREATER_THAN_OR_EQUAL, QuantifiedComparisonExpression.Quantifier.SOME, identifier("col1"), new SubqueryExpression(simpleQuery(selectList(new LongLiteral("10")))))); }
private PlanBuilder planQuantifiedApplyNode(PlanBuilder subPlan, QuantifiedComparisonExpression quantifiedComparison, boolean correlationAllowed) { subPlan = subPlan.appendProjections(ImmutableList.of(quantifiedComparison.getValue()), symbolAllocator, idAllocator); checkState(quantifiedComparison.getSubquery() instanceof SubqueryExpression); SubqueryExpression quantifiedSubquery = (SubqueryExpression) quantifiedComparison.getSubquery(); SubqueryExpression uncoercedQuantifiedSubquery = uncoercedSubquery(quantifiedSubquery); PlanBuilder subqueryPlan = createPlanBuilder(uncoercedQuantifiedSubquery); subqueryPlan = subqueryPlan.appendProjections(ImmutableList.of(quantifiedSubquery), symbolAllocator, idAllocator); QuantifiedComparisonExpression coercedQuantifiedComparison = new QuantifiedComparisonExpression( quantifiedComparison.getOperator(), quantifiedComparison.getQuantifier(), subPlan.translate(quantifiedComparison.getValue()).toSymbolReference(), subqueryPlan.translate(quantifiedSubquery).toSymbolReference()); Symbol coercedQuantifiedComparisonSymbol = symbolAllocator.newSymbol(coercedQuantifiedComparison, BOOLEAN); subPlan.getTranslations().put(quantifiedComparison, coercedQuantifiedComparisonSymbol); return appendApplyNode( subPlan, quantifiedComparison.getSubquery(), subqueryPlan.getRoot(), Assignments.of(coercedQuantifiedComparisonSymbol, coercedQuantifiedComparison), correlationAllowed); }
private PlanBuilder planQuantifiedApplyNode(PlanBuilder subPlan, QuantifiedComparisonExpression quantifiedComparison, boolean correlationAllowed) { subPlan = subPlan.appendProjections(ImmutableList.of(quantifiedComparison.getValue()), symbolAllocator, idAllocator); checkState(quantifiedComparison.getSubquery() instanceof SubqueryExpression); SubqueryExpression quantifiedSubquery = (SubqueryExpression) quantifiedComparison.getSubquery(); SubqueryExpression uncoercedQuantifiedSubquery = uncoercedSubquery(quantifiedSubquery); PlanBuilder subqueryPlan = createPlanBuilder(uncoercedQuantifiedSubquery); subqueryPlan = subqueryPlan.appendProjections(ImmutableList.of(quantifiedSubquery), symbolAllocator, idAllocator); QuantifiedComparisonExpression coercedQuantifiedComparison = new QuantifiedComparisonExpression( quantifiedComparison.getOperator(), quantifiedComparison.getQuantifier(), subPlan.translate(quantifiedComparison.getValue()).toSymbolReference(), subqueryPlan.translate(quantifiedSubquery).toSymbolReference()); Symbol coercedQuantifiedComparisonSymbol = symbolAllocator.newSymbol(coercedQuantifiedComparison, BOOLEAN); subPlan.getTranslations().put(quantifiedComparison, coercedQuantifiedComparisonSymbol); return appendApplyNode( subPlan, quantifiedComparison.getSubquery(), subqueryPlan.getRoot(), Assignments.of(coercedQuantifiedComparisonSymbol, coercedQuantifiedComparison), correlationAllowed); }
switch (quantifiedComparison.getOperator()) { case EQUAL: switch (quantifiedComparison.getQuantifier()) { case ALL: return planQuantifiedApplyNode(subPlan, quantifiedComparison, correlationAllowed); InPredicate inPredicate = new InPredicate(quantifiedComparison.getValue(), quantifiedComparison.getSubquery()); subPlan = appendInPredicateApplyNode(subPlan, inPredicate, correlationAllowed, node); subPlan.getTranslations().put(quantifiedComparison, subPlan.translate(inPredicate)); switch (quantifiedComparison.getQuantifier()) { case ALL: QuantifiedComparisonExpression rewrittenAny = new QuantifiedComparisonExpression( EQUAL, Quantifier.ANY, quantifiedComparison.getValue(), quantifiedComparison.getSubquery()); Expression notAny = new NotExpression(rewrittenAny); QuantifiedComparisonExpression rewrittenAll = new QuantifiedComparisonExpression( EQUAL, QuantifiedComparisonExpression.Quantifier.ALL, quantifiedComparison.getValue(), quantifiedComparison.getSubquery()); Expression notAll = new NotExpression(rewrittenAll); format("Unexpected quantified comparison: '%s %s'", quantifiedComparison.getOperator().getValue(), quantifiedComparison.getQuantifier()));