You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by "zoudan (via GitHub)" <gi...@apache.org> on 2023/03/17 09:37:17 UTC

[GitHub] [calcite] zoudan commented on a diff in pull request #2854: [CALCITE-5160] ANY/SOME, ALL operators should support collection expressions

zoudan commented on code in PR #2854:
URL: https://github.com/apache/calcite/pull/2854#discussion_r1139988511


##########
core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java:
##########
@@ -4049,6 +4081,101 @@ private static class HopImplementor implements TableFunctionCallImplementor {
     }
   }
 
+  /**
+   * Implements
+   * <a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.16">
+   * ANY/SOME</a> and
+   * <a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.17">ALL</a>
+   * operators when the argument is an array or multiset expression.
+   */
+  private static class QuantifyCollectionImplementor extends AbstractRexCallImplementor {
+    private final SqlBinaryOperator binaryOperator;
+    private final RexCallImplementor binaryImplementor;
+
+    QuantifyCollectionImplementor(SqlBinaryOperator binaryOperator,
+        RexCallImplementor binaryImplementor) {
+      super(NullPolicy.ANY, false);
+      this.binaryOperator = binaryOperator;
+      this.binaryImplementor = binaryImplementor;
+    }
+
+    @Override String getVariableName() {
+      return "quantify";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator, RexCall call,
+        List<Expression> argValueList) {
+      Expression left = argValueList.get(0);
+      Expression right = argValueList.get(1);
+      final RelDataType rightComponentType =
+          requireNonNull(call.getOperands().get(1).getType().getComponentType());
+      // If the array expression yields a null array, the result of SOME|ALL will be null
+      if (rightComponentType.getSqlTypeName() == SqlTypeName.NULL) {
+        return NULL_EXPR;
+      }
+
+      // The expression generated by this method will look as follows:
+      // final T _quantify_left_value = <left_value>
+      // <Function1|Predicate1> lambda =
+      //    new org.apache.calcite.linq4j.function.<Function1|Predicate1>() {
+      //        public Boolean apply(T el) {
+      //          return <binaryImplementor code>(_quantify_left_value, el);
+      //        }
+      //    }
+      // If the lambda returns java.lang.Boolean then the lambda can return null.
+      // In this case nullableExists or nullableSome should be used:
+      // return org.apache.calcite.runtime.SqlFunctions.<nullableExists|nullableSome>(_list, lambda)
+      // otherwise:
+      // return org.apache.calcite.linq4j.function.Functions.<exists|all>(_list, lambda)
+      BlockBuilder lambdaBuilder = new BlockBuilder();
+      final ParameterExpression leftExpr =
+          Expressions.parameter(left.getType(),
+              translator.getBlockBuilder().newName("_" + getVariableName() + "_left_value"));
+      // left should have final modifier otherwise it can not be passed to lambda
+      translator.getBlockBuilder().add(Expressions.declare(Modifier.FINAL, leftExpr, left));
+      RexNode leftRex = call.getOperands().get(0);
+      final ParameterExpression lambdaArg =
+          Expressions.parameter(translator.typeFactory.getJavaClass(rightComponentType), "el");
+      final RexCall binaryImplementorRexCall =
+          (RexCall) translator.builder.makeCall(binaryOperator, leftRex,
+              translator.builder.makeDynamicParam(rightComponentType, 0));
+      final List<RexToLixTranslator.Result> binaryImplementorArgs =
+          ImmutableList.of(
+              new RexToLixTranslator.Result(
+                  genIsNullStatement(translator, leftExpr), leftExpr),
+              new RexToLixTranslator.Result(
+                  genIsNullStatement(translator, lambdaArg), lambdaArg));
+      final RexToLixTranslator.Result condition =
+          binaryImplementor.implement(translator, binaryImplementorRexCall, binaryImplementorArgs);
+      lambdaBuilder.add(Expressions.return_(null, condition.valueVariable));
+      final FunctionExpression<?> predicate =
+          Expressions.lambda(lambdaBuilder.toBlock(), lambdaArg);
+      return Expressions.call(getMethod(condition.valueVariable.getType(), call.getKind()), right,
+          predicate);
+    }
+
+    private Method getMethod(Type comparisonReturnType, SqlKind kind) {
+      switch (kind) {
+      case SOME:
+        return Primitive.is(comparisonReturnType)
+            ? BuiltInMethod.COLLECTION_EXISTS.method
+            // if the array contains any null elements and no true comparison result is obtained,
+            // the result of SOME will be null, not false.
+            : BuiltInMethod.COLLECTION_NULLABLE_EXISTS.method;
+      case ALL:
+        return Primitive.is(comparisonReturnType)
+            ? BuiltInMethod.COLLECTION_ALL.method
+            // if the array contains any null elements and no false comparison result is obtained,
+            // the result of ALL will be null, not true.
+            : BuiltInMethod.COLLECTION_NULLABLE_ALL.method;
+      default:
+        throw new IllegalArgumentException("Unknown quantify operator" + kind
+            + ". Only support SOME,ALL.");

Review Comment:
   add a space after comma



##########
core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java:
##########
@@ -5451,7 +5461,11 @@ ImmutableList<RelNode> retrieveCursors() {
       case CURSOR:
       case IN:
       case NOT_IN:
-        subQuery = requireNonNull(getSubQuery(expr, null));
+        subQuery = getSubQuery(expr, null);
+        if (subQuery == null && (kind == SqlKind.SOME || kind == SqlKind.ALL)) {
+          break;
+        }
+        assert subQuery != null;

Review Comment:
   Agree with @bchapuis, it is meaningless to call 'assert' here.



##########
core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java:
##########
@@ -4049,6 +4081,101 @@ private static class HopImplementor implements TableFunctionCallImplementor {
     }
   }
 
+  /**
+   * Implements
+   * <a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.16">
+   * ANY/SOME</a> and
+   * <a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.17">ALL</a>
+   * operators when the argument is an array or multiset expression.
+   */
+  private static class QuantifyCollectionImplementor extends AbstractRexCallImplementor {
+    private final SqlBinaryOperator binaryOperator;
+    private final RexCallImplementor binaryImplementor;
+
+    QuantifyCollectionImplementor(SqlBinaryOperator binaryOperator,
+        RexCallImplementor binaryImplementor) {
+      super(NullPolicy.ANY, false);
+      this.binaryOperator = binaryOperator;
+      this.binaryImplementor = binaryImplementor;
+    }
+
+    @Override String getVariableName() {
+      return "quantify";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator, RexCall call,
+        List<Expression> argValueList) {
+      Expression left = argValueList.get(0);
+      Expression right = argValueList.get(1);
+      final RelDataType rightComponentType =
+          requireNonNull(call.getOperands().get(1).getType().getComponentType());
+      // If the array expression yields a null array, the result of SOME|ALL will be null
+      if (rightComponentType.getSqlTypeName() == SqlTypeName.NULL) {
+        return NULL_EXPR;
+      }
+
+      // The expression generated by this method will look as follows:
+      // final T _quantify_left_value = <left_value>
+      // <Function1|Predicate1> lambda =
+      //    new org.apache.calcite.linq4j.function.<Function1|Predicate1>() {
+      //        public Boolean apply(T el) {
+      //          return <binaryImplementor code>(_quantify_left_value, el);
+      //        }
+      //    }
+      // If the lambda returns java.lang.Boolean then the lambda can return null.
+      // In this case nullableExists or nullableSome should be used:
+      // return org.apache.calcite.runtime.SqlFunctions.<nullableExists|nullableSome>(_list, lambda)
+      // otherwise:
+      // return org.apache.calcite.linq4j.function.Functions.<exists|all>(_list, lambda)
+      BlockBuilder lambdaBuilder = new BlockBuilder();
+      final ParameterExpression leftExpr =
+          Expressions.parameter(left.getType(),
+              translator.getBlockBuilder().newName("_" + getVariableName() + "_left_value"));
+      // left should have final modifier otherwise it can not be passed to lambda
+      translator.getBlockBuilder().add(Expressions.declare(Modifier.FINAL, leftExpr, left));
+      RexNode leftRex = call.getOperands().get(0);
+      final ParameterExpression lambdaArg =
+          Expressions.parameter(translator.typeFactory.getJavaClass(rightComponentType), "el");
+      final RexCall binaryImplementorRexCall =
+          (RexCall) translator.builder.makeCall(binaryOperator, leftRex,
+              translator.builder.makeDynamicParam(rightComponentType, 0));
+      final List<RexToLixTranslator.Result> binaryImplementorArgs =
+          ImmutableList.of(
+              new RexToLixTranslator.Result(
+                  genIsNullStatement(translator, leftExpr), leftExpr),
+              new RexToLixTranslator.Result(
+                  genIsNullStatement(translator, lambdaArg), lambdaArg));
+      final RexToLixTranslator.Result condition =
+          binaryImplementor.implement(translator, binaryImplementorRexCall, binaryImplementorArgs);
+      lambdaBuilder.add(Expressions.return_(null, condition.valueVariable));
+      final FunctionExpression<?> predicate =
+          Expressions.lambda(lambdaBuilder.toBlock(), lambdaArg);
+      return Expressions.call(getMethod(condition.valueVariable.getType(), call.getKind()), right,
+          predicate);
+    }
+
+    private Method getMethod(Type comparisonReturnType, SqlKind kind) {
+      switch (kind) {
+      case SOME:
+        return Primitive.is(comparisonReturnType)
+            ? BuiltInMethod.COLLECTION_EXISTS.method
+            // if the array contains any null elements and no true comparison result is obtained,
+            // the result of SOME will be null, not false.
+            : BuiltInMethod.COLLECTION_NULLABLE_EXISTS.method;
+      case ALL:
+        return Primitive.is(comparisonReturnType)
+            ? BuiltInMethod.COLLECTION_ALL.method
+            // if the array contains any null elements and no false comparison result is obtained,
+            // the result of ALL will be null, not true.
+            : BuiltInMethod.COLLECTION_NULLABLE_ALL.method;
+      default:
+        throw new IllegalArgumentException("Unknown quantify operator" + kind
+            + ". Only support SOME,ALL.");
+      }
+    }
+  }
+

Review Comment:
   remove one empty line



##########
core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java:
##########
@@ -4049,6 +4081,101 @@ private static class HopImplementor implements TableFunctionCallImplementor {
     }
   }
 
+  /**
+   * Implements
+   * <a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.16">
+   * ANY/SOME</a> and
+   * <a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.17">ALL</a>
+   * operators when the argument is an array or multiset expression.
+   */
+  private static class QuantifyCollectionImplementor extends AbstractRexCallImplementor {
+    private final SqlBinaryOperator binaryOperator;
+    private final RexCallImplementor binaryImplementor;
+
+    QuantifyCollectionImplementor(SqlBinaryOperator binaryOperator,
+        RexCallImplementor binaryImplementor) {
+      super(NullPolicy.ANY, false);
+      this.binaryOperator = binaryOperator;
+      this.binaryImplementor = binaryImplementor;
+    }
+
+    @Override String getVariableName() {
+      return "quantify";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator, RexCall call,
+        List<Expression> argValueList) {
+      Expression left = argValueList.get(0);
+      Expression right = argValueList.get(1);
+      final RelDataType rightComponentType =
+          requireNonNull(call.getOperands().get(1).getType().getComponentType());
+      // If the array expression yields a null array, the result of SOME|ALL will be null
+      if (rightComponentType.getSqlTypeName() == SqlTypeName.NULL) {
+        return NULL_EXPR;
+      }
+
+      // The expression generated by this method will look as follows:
+      // final T _quantify_left_value = <left_value>
+      // <Function1|Predicate1> lambda =
+      //    new org.apache.calcite.linq4j.function.<Function1|Predicate1>() {
+      //        public Boolean apply(T el) {
+      //          return <binaryImplementor code>(_quantify_left_value, el);
+      //        }
+      //    }
+      // If the lambda returns java.lang.Boolean then the lambda can return null.
+      // In this case nullableExists or nullableSome should be used:
+      // return org.apache.calcite.runtime.SqlFunctions.<nullableExists|nullableSome>(_list, lambda)
+      // otherwise:
+      // return org.apache.calcite.linq4j.function.Functions.<exists|all>(_list, lambda)
+      BlockBuilder lambdaBuilder = new BlockBuilder();
+      final ParameterExpression leftExpr =
+          Expressions.parameter(left.getType(),
+              translator.getBlockBuilder().newName("_" + getVariableName() + "_left_value"));
+      // left should have final modifier otherwise it can not be passed to lambda
+      translator.getBlockBuilder().add(Expressions.declare(Modifier.FINAL, leftExpr, left));
+      RexNode leftRex = call.getOperands().get(0);
+      final ParameterExpression lambdaArg =
+          Expressions.parameter(translator.typeFactory.getJavaClass(rightComponentType), "el");
+      final RexCall binaryImplementorRexCall =
+          (RexCall) translator.builder.makeCall(binaryOperator, leftRex,
+              translator.builder.makeDynamicParam(rightComponentType, 0));
+      final List<RexToLixTranslator.Result> binaryImplementorArgs =
+          ImmutableList.of(
+              new RexToLixTranslator.Result(
+                  genIsNullStatement(translator, leftExpr), leftExpr),
+              new RexToLixTranslator.Result(
+                  genIsNullStatement(translator, lambdaArg), lambdaArg));
+      final RexToLixTranslator.Result condition =
+          binaryImplementor.implement(translator, binaryImplementorRexCall, binaryImplementorArgs);
+      lambdaBuilder.add(Expressions.return_(null, condition.valueVariable));
+      final FunctionExpression<?> predicate =
+          Expressions.lambda(lambdaBuilder.toBlock(), lambdaArg);
+      return Expressions.call(getMethod(condition.valueVariable.getType(), call.getKind()), right,
+          predicate);
+    }
+
+    private Method getMethod(Type comparisonReturnType, SqlKind kind) {
+      switch (kind) {
+      case SOME:
+        return Primitive.is(comparisonReturnType)
+            ? BuiltInMethod.COLLECTION_EXISTS.method
+            // if the array contains any null elements and no true comparison result is obtained,
+            // the result of SOME will be null, not false.
+            : BuiltInMethod.COLLECTION_NULLABLE_EXISTS.method;
+      case ALL:
+        return Primitive.is(comparisonReturnType)
+            ? BuiltInMethod.COLLECTION_ALL.method
+            // if the array contains any null elements and no false comparison result is obtained,
+            // the result of ALL will be null, not true.
+            : BuiltInMethod.COLLECTION_NULLABLE_ALL.method;
+      default:
+        throw new IllegalArgumentException("Unknown quantify operator" + kind

Review Comment:
   add space after `operator `



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@calcite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org