macroguo-ghy commented on code in PR #3502: URL: https://github.com/apache/calcite/pull/3502#discussion_r1393712543
########## core/src/main/java/org/apache/calcite/rex/RexLambdaExpression.java: ########## @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.rex; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlKind; + +import org.apache.commons.lang3.StringUtils; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Represents a lambda expression. + */ +public class RexLambdaExpression extends RexNode { + //~ Instance fields -------------------------------------------------------- + + private final List<RexLambdaRef> parameters; + private final RexNode expression; + + //~ Constructors ----------------------------------------------------------- + + RexLambdaExpression(List<RexLambdaRef> parameters, RexNode expression) { + this.expression = expression; + this.parameters = parameters; + } + + //~ Methods ---------------------------------------------------------------- + + @Override public RelDataType getType() { + return expression.getType(); + } + + @Override public SqlKind getKind() { + return SqlKind.LAMBDA; + } + + @Override public <R> R accept(RexVisitor<R> visitor) { + return visitor.visitLambda(this); + } + + @Override public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) { + return visitor.visitLambda(this, arg); + } + + public RexNode getExpression() { + return expression; + } + + public List<RexLambdaRef> getParameters() { + return parameters; + } + + @Override public boolean equals(@Nullable Object o) { + if (this == o) { Review Comment: Fixed ########## core/src/main/java/org/apache/calcite/rex/RexLambdaExpression.java: ########## @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.rex; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlKind; + +import org.apache.commons.lang3.StringUtils; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Represents a lambda expression. + */ +public class RexLambdaExpression extends RexNode { + //~ Instance fields -------------------------------------------------------- + + private final List<RexLambdaRef> parameters; + private final RexNode expression; + + //~ Constructors ----------------------------------------------------------- + + RexLambdaExpression(List<RexLambdaRef> parameters, RexNode expression) { + this.expression = expression; Review Comment: Fixed ########## core/src/main/java/org/apache/calcite/rex/RexLambdaRef.java: ########## @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.rex; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlKind; + +/** + * Variable which references a field of a lambda expression. + */ +public class RexLambdaRef extends RexInputRef { + + private final String paramName; Review Comment: After making `RexLambdaRef` extend `RexSlot`, we can now use the `name`, so there is no need for the `paramName` anymore. ########## core/src/main/java/org/apache/calcite/rex/RexLambdaRef.java: ########## @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.rex; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlKind; + +/** + * Variable which references a field of a lambda expression. + */ +public class RexLambdaRef extends RexInputRef { Review Comment: Fixed ########## core/src/main/java/org/apache/calcite/rex/RexLambdaRef.java: ########## @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.rex; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlKind; + +/** + * Variable which references a field of a lambda expression. Review Comment: Fixed ########## core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java: ########## @@ -5371,6 +5372,28 @@ public static List sortArray(List list, boolean ascending) { return list; } + /** Support the EXISTS(list, function1) function. */ Review Comment: It wasn't until you told me that I noticed this function. I modified `boolean exists(List list, Function1<Object, Boolean> function1)`. ########## core/src/main/java/org/apache/calcite/sql/fun/SqlLambdaExpressionOperator.java: ########## @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.fun; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLambdaExpression; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.validate.SqlLambdaExpressionScope; +import org.apache.calcite.sql.validate.SqlValidator; +import org.apache.calcite.sql.validate.SqlValidatorScope; + +import java.util.List; + +import static com.google.common.collect.ImmutableList.toImmutableList; + +/** + * The {@code SqlLambdaExpressionOperator} represents a lambda expression. + * The syntax : + * {@code IDENTIFIER -> EXPRESSION} or {@code (IDENTIFIER, IDENTIFIER, ...) -> EXPRESSION}. + */ +public class SqlLambdaExpressionOperator extends SqlSpecialOperator { Review Comment: Fixed ########## core/src/main/java/org/apache/calcite/sql/type/LambdaExpressionOperandTypeChecker.java: ########## @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.type; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLambdaExpression; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.sql.util.SqlBasicVisitor; +import org.apache.calcite.sql.validate.SqlLambdaExpressionScope; +import org.apache.calcite.sql.validate.SqlValidator; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +import static org.apache.calcite.util.Static.RESOURCE; + +/** + * Operand type-checking strategy where the type of the operand is a lambda + * expression with a given return type and argument types. + */ +public class LambdaExpressionOperandTypeChecker implements SqlSingleOperandTypeChecker { + + private final SqlTypeFamily returnTypeFamily; + private final List<SqlTypeFamily> argFamilies; + + public LambdaExpressionOperandTypeChecker( + SqlTypeFamily returnTypeFamily, + List<SqlTypeFamily> argFamilies) { + this.returnTypeFamily = returnTypeFamily; Review Comment: Fixed. ########## core/src/main/java/org/apache/calcite/sql/type/LambdaExpressionOperandTypeChecker.java: ########## @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.type; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLambdaExpression; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.sql.util.SqlBasicVisitor; +import org.apache.calcite.sql.validate.SqlLambdaExpressionScope; +import org.apache.calcite.sql.validate.SqlValidator; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +import static org.apache.calcite.util.Static.RESOURCE; + +/** + * Operand type-checking strategy where the type of the operand is a lambda + * expression with a given return type and argument types. + */ +public class LambdaExpressionOperandTypeChecker implements SqlSingleOperandTypeChecker { + + private final SqlTypeFamily returnTypeFamily; + private final List<SqlTypeFamily> argFamilies; + + public LambdaExpressionOperandTypeChecker( + SqlTypeFamily returnTypeFamily, + List<SqlTypeFamily> argFamilies) { + this.returnTypeFamily = returnTypeFamily; + this.argFamilies = argFamilies; + } + + @Override public String getAllowedSignatures(SqlOperator op, String opName) { + ImmutableList.Builder<SqlTypeFamily> builder = ImmutableList.builder(); + builder.addAll(argFamilies); + builder.add(returnTypeFamily); + + return SqlUtil.getAliasedSignature(op, opName, builder.build()); + } + + @Override public boolean checkOperandTypes(SqlCallBinding callBinding, + boolean throwOnFailure) { + return false; + } + + @Override public boolean checkSingleOperandType( + SqlCallBinding callBinding, SqlNode operand, int iFormalOperand, boolean throwOnFailure) { + if (!(operand instanceof SqlLambdaExpression) + || ((SqlLambdaExpression) operand).getParameters().size() != argFamilies.size()) { + if (throwOnFailure) { + throw callBinding.newValidationSignatureError(); + } + return false; + } + SqlLambdaExpression lambdaExpr = (SqlLambdaExpression) operand; + + if (lambdaExpr.getParameters().isEmpty() + || argFamilies.stream().allMatch(f -> f == SqlTypeFamily.ANY) + || returnTypeFamily == SqlTypeFamily.ANY) { + return true; + } + + if (SqlUtil.isNullLiteral(lambdaExpr.getExpression(), false)) { + if (callBinding.isTypeCoercionEnabled()) { + return true; + } else if (throwOnFailure) { + throw callBinding.getValidator().newValidationError(lambdaExpr.getExpression(), + RESOURCE.nullIllegal()); + } else { + return false; + } + } + + // Replace the parameter types in the lambda expression. + final SqlValidator validator = callBinding.getValidator(); + SqlLambdaExpressionScope scope = + (SqlLambdaExpressionScope) validator.getLambdaExpressionScope(lambdaExpr); + for (int i = 0; i < argFamilies.size(); i++) { + SqlNode param = lambdaExpr.getParameters().get(i); + RelDataType type = argFamilies.get(i).getDefaultConcreteType(callBinding.getTypeFactory()); + if (type != null) { + scope.getParameterTypes().put(param.toString(), type); + } + } + lambdaExpr.accept(new TypeRemover(validator)); + + // Given the new relDataType, re-validate the lambda expression. + validator.validateLambdaExpression(lambdaExpr); + final RelDataType newType = validator.getValidatedNodeType(lambdaExpr); + assert newType instanceof LambdaExpressionSqlType; + final SqlTypeName returnTypeName = + ((LambdaExpressionSqlType) newType).getReturnType().getSqlTypeName(); + if (returnTypeName == SqlTypeName.ANY + || returnTypeFamily.getTypeNames().contains(returnTypeName)) { + return true; + } + + if (throwOnFailure) { + throw callBinding.newValidationSignatureError(); + } + return false; + } + + /** Review Comment: Since lambda expressions will be validated for the second time based on the given parameter type, the type cached during the first validation must be cleared. I will write the reason in Javadoc. ########## core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java: ########## @@ -77,6 +77,7 @@ public enum SqlTypeFamily implements RelDataTypeFamily { CURSOR, COLUMN_LIST, GEO, + LAMBDA_EXPRESSION, Review Comment: Fixed. ########## core/src/main/java/org/apache/calcite/sql/type/LambdaExpressionOperandTypeChecker.java: ########## @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.type; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLambdaExpression; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.sql.util.SqlBasicVisitor; +import org.apache.calcite.sql.validate.SqlLambdaExpressionScope; +import org.apache.calcite.sql.validate.SqlValidator; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +import static org.apache.calcite.util.Static.RESOURCE; + +/** + * Operand type-checking strategy where the type of the operand is a lambda + * expression with a given return type and argument types. + */ +public class LambdaExpressionOperandTypeChecker implements SqlSingleOperandTypeChecker { Review Comment: Fixed. I made this class as inner class of `OperandTypes`. ########## core/src/main/java/org/apache/calcite/sql/validate/SqlLambdaExpressionScope.java: ########## @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.validate; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLambdaExpression; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.Litmus; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.calcite.util.Static.RESOURCE; + +/** + * Scope for a {@link SqlLambdaExpression LAMBDA EXPRESSION}. + */ +public class SqlLambdaExpressionScope extends ListScope { + private final SqlLambdaExpression lambdaExpr; + private final Map<String, RelDataType> parameterTypes; Review Comment: We need to register the type of parameter to `SqlLambdaExpressionScope` when validate lambda expression second time, so I use the map as cache. ########## testkit/src/main/java/org/apache/calcite/test/MockSqlOperatorTable.java: ########## @@ -623,4 +625,24 @@ public CompositeFunction() { return typeFactory.createSqlType(SqlTypeName.BIGINT); } } + + private static final SqlFunction HIGHER_ORDER_FUNCTION = + new SqlFunction("HIGHER_ORDER_FUNCTION", Review Comment: Fixed ########## core/src/main/java/org/apache/calcite/sql/type/LambdaExpressionSqlType.java: ########## @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.type; + +import org.apache.calcite.linq4j.Ord; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeFamily; +import org.apache.calcite.rel.type.RelDataTypeField; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * SQL lambda expression type. + */ Review Comment: I have renamed this class to `FunctionSqlType`. But I think all the `RelDataType` classes are public, `FunctionSqlType` should be consistent with them. ########## core/src/main/java/org/apache/calcite/sql/validate/SqlLambdaExpressionScope.java: ########## @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.validate; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLambdaExpression; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.Litmus; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.calcite.util.Static.RESOURCE; + +/** + * Scope for a {@link SqlLambdaExpression LAMBDA EXPRESSION}. + */ +public class SqlLambdaExpressionScope extends ListScope { + private final SqlLambdaExpression lambdaExpr; + private final Map<String, RelDataType> parameterTypes; + + public SqlLambdaExpressionScope( + SqlValidatorScope parent, SqlLambdaExpression lambdaExpr) { + super(parent); + this.lambdaExpr = lambdaExpr; + + // default parameter type is ANY Review Comment: In fact, the lambda expression will be validated twice. A simple lambda expression `a -> a`, we can not infer the type of lambda if we just know the lambda expression, this is why I say "default parameter type is ANY". But if we see the lambda expression in a function, we will validate the lambda expression second time in `LambdaExpressionOperandTypeChecker`. For example, give a function `test_func1`, whose type checker is ```java OperandTypes.sequence("TEST_FUNC1(INTEGER, FUNCTION(STRING) -> NUMERIC)", OperandTypes.family(SqlTypeFamily.INTEGER), OperandTypes.function(SqlTypeFamily.NUMERIC, SqlTypeFamily.STRING)) ``` Assuming there is an SQL statement, ```sql select test_func1(1, x -> x / 2); ``` The first validation: we assume the type of `x` is `ANY`. We can know the `x -> x / 2` is legal. The second validation: Based on the type checker, the type of `x` is string, so `x / 2` is not a valid expression. ########## core/src/main/codegen/templates/Parser.jj: ########## @@ -8787,6 +8817,7 @@ void NonReservedKeyWord2of3() : | < NE2: "!=" > | < PLUS: "+" > | < MINUS: "-" > +| < LAMBDA_OPERATOR: "->" > Review Comment: Fixed. ########## core/src/main/codegen/templates/Parser.jj: ########## @@ -3912,6 +3919,29 @@ SqlNode Expression3(ExprContext exprContext) : } } +/** + * Parses a lambda expression. + */ +SqlNode LambdaExpression() : +{ + final SqlNodeList parameters; + final SqlNode expression; + final Span s; +} +{ + ( + LOOKAHEAD(2) + <LPAREN> <RPAREN> { parameters = SqlNodeList.EMPTY; } + | + parameters = SimpleIdentifierOrList() Review Comment: I extract this rule to `SimpleIdentifierOrListOrEmpty`. ########## core/src/main/codegen/templates/Parser.jj: ########## @@ -1033,6 +1034,9 @@ void AddArg0(List<SqlNode> list, ExprContext exprContext) : ) ( e = Default() + | + LOOKAHEAD((SimpleIdentifierOrList() | <LPAREN> <RPAREN>) <LAMBDA_OPERATOR>) + e = LambdaExpression() Review Comment: The most important token is `->`, so the expense depends on how many tokens come before `->`. For example: ``` () -> true // equals LOOKAHEAD(3) (a, b) -> a + b // equals LOOKAHED(6) ``` ########## core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java: ########## @@ -779,6 +782,17 @@ public SqlNode toSql(@Nullable RexProgram program, RexNode rex) { return SqlStdOperatorTable.NOT.createCall(POS, node); } + case LAMBDA: + final RexLambdaExpression lambda = (RexLambdaExpression) rex; + final SqlNodeList parameters = new SqlNodeList(POS); + for (RexLambdaRef parameter : lambda.getParameters()) { + parameters.add(toSql(program, parameter)); + } + final SqlNode expression = toSql(program, lambda.getExpression()); + return new SqlLambdaExpression(POS, parameters, expression); + case LAMBDA_REF: Review Comment: Fixed. ########## core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java: ########## @@ -134,6 +134,18 @@ RelDataType createMapType( RelDataType keyType, RelDataType valueType); + /** + * Create a lambda expression type. Lambda expressions are functions that Review Comment: Fixed. ########## core/src/main/java/org/apache/calcite/rex/RexLambdaExpression.java: ########## @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.rex; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlKind; + +import org.apache.commons.lang3.StringUtils; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Represents a lambda expression. + */ +public class RexLambdaExpression extends RexNode { + //~ Instance fields -------------------------------------------------------- + + private final List<RexLambdaRef> parameters; + private final RexNode expression; + + //~ Constructors ----------------------------------------------------------- + + RexLambdaExpression(List<RexLambdaRef> parameters, RexNode expression) { + this.expression = expression; + this.parameters = parameters; + } + + //~ Methods ---------------------------------------------------------------- + + @Override public RelDataType getType() { + return expression.getType(); + } + + @Override public SqlKind getKind() { + return SqlKind.LAMBDA; + } + + @Override public <R> R accept(RexVisitor<R> visitor) { + return visitor.visitLambda(this); + } + + @Override public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) { + return visitor.visitLambda(this, arg); + } + + public RexNode getExpression() { + return expression; + } + + public List<RexLambdaRef> getParameters() { + return parameters; + } + + @Override public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RexLambdaExpression rexLambdaExpression = (RexLambdaExpression) o; + return Objects.equals(expression, rexLambdaExpression.expression) + && Objects.equals(parameters, rexLambdaExpression.parameters); + } + + @Override public int hashCode() { + return Objects.hash(expression, parameters); + } + + @Override public String toString() { Review Comment: Fixed, I use `for` loop instead. `RexLambdaExpression` extends `RexNode`, the `computeDigest` is defined in `RexCall`. ########## core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java: ########## @@ -101,6 +101,25 @@ public static FamilyOperandTypeChecker family(List<SqlTypeFamily> families) { return family(families, i -> false); } + /** + * Creates a checker that passes if the operand is a lambda expression with Review Comment: Fixed. ########## core/src/main/java/org/apache/calcite/sql/type/LambdaExpressionSqlType.java: ########## @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.type; + +import org.apache.calcite.linq4j.Ord; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeFamily; +import org.apache.calcite.rel.type.RelDataTypeField; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * SQL lambda expression type. + */ +public class LambdaExpressionSqlType extends AbstractSqlType { + private final @Nullable RelDataType parameterType; + private final RelDataType returnType; + + public LambdaExpressionSqlType(@Nullable RelDataType parameterType, RelDataType returnType) { + super(SqlTypeName.LAMBDA_EXPRESSION, true, null); + assert parameterType == null || parameterType.isStruct(); Review Comment: The `parameterType` is never `null`, I will remove `@Nullable` and `assert parameterType == null`. -- 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: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
