This is an automated email from the ASF dual-hosted git repository. michaelsmith pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/impala.git
commit 30979e7d306beac65dadeedb11f3f402c2261bba Author: Steve Carlin <[email protected]> AuthorDate: Tue Nov 5 14:28:51 2024 -0800 IMPALA-13517: Support overloaded || operator The || operator is used for both "or" and "concat". A new Impala custom operator is created to handle both of them, treating the precedence of the operator as if it's an "or". The "or" is chosen if both parameters are null or boolean, as taken from logic in CompoundVerticalBarExpr. At convertlet time (when converting from SqlNode to RelNode), the real operator is placed into the RexNode. Change-Id: Iabaf02e84b769db1419bd96e1d1b30b8f83d56f5 Reviewed-on: http://gerrit.cloudera.org:8080/22105 Reviewed-by: Steve Carlin <[email protected]> Tested-by: Steve Carlin <[email protected]> --- .../src/main/codegen/templates/Parser.jj | 11 +-- .../impala/calcite/functions/RexCallConverter.java | 4 ++ .../calcite/operators/ImpalaConcatOrOperator.java | 82 ++++++++++++++++++++++ .../calcite/operators/ImpalaConvertletTable.java | 38 ++++++++++ .../operators/ImpalaCustomOperatorTable.java | 10 +-- 5 files changed, 131 insertions(+), 14 deletions(-) diff --git a/java/calcite-planner/src/main/codegen/templates/Parser.jj b/java/calcite-planner/src/main/codegen/templates/Parser.jj index b4093d7ef..0b342e16b 100644 --- a/java/calcite-planner/src/main/codegen/templates/Parser.jj +++ b/java/calcite-planner/src/main/codegen/templates/Parser.jj @@ -123,6 +123,7 @@ import org.apache.calcite.util.Util; import org.apache.calcite.util.trace.CalciteTrace; import org.apache.impala.calcite.type.ImpalaSqlIntervalQualifier; import org.apache.impala.calcite.operators.ImpalaCustomOperatorTable; +import org.apache.impala.calcite.operators.ImpalaConcatOrOperator; import org.apache.impala.calcite.util.ParserUtil; import com.google.common.collect.ImmutableList; @@ -7740,17 +7741,17 @@ SqlBinaryOperator BinaryRowOperator() : } return SqlStdOperatorTable.NOT_EQUALS; } -| <PLUS> { return SqlStdOperatorTable.PLUS; } -| <MINUS> { return SqlStdOperatorTable.MINUS; } -| <STAR> { return SqlStdOperatorTable.MULTIPLY; } -| <SLASH> { return SqlStdOperatorTable.DIVIDE; } +| <PLUS> { return ImpalaCustomOperatorTable.PLUS; } +| <MINUS> { return ImpalaCustomOperatorTable.MINUS; } +| <STAR> { return ImpalaCustomOperatorTable.MULTIPLY; } +| <SLASH> { return ImpalaCustomOperatorTable.DIVIDE; } | <PERCENT_REMAINDER> { if (!this.conformance.isPercentRemainderAllowed()) { throw SqlUtil.newContextException(getPos(), RESOURCE.percentRemainderNotAllowed()); } return SqlStdOperatorTable.PERCENT_REMAINDER; } -| <CONCAT> { return SqlStdOperatorTable.CONCAT; } +| <CONCAT> { return ImpalaCustomOperatorTable.CONCAT_OR; } | <AND> { return SqlStdOperatorTable.AND; } | <OR> { return SqlStdOperatorTable.OR; } | LOOKAHEAD(2) <IS> <DISTINCT> <FROM> { return SqlStdOperatorTable.IS_DISTINCT_FROM; } diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java index ffa30ba60..fe0ac694e 100644 --- a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java @@ -104,6 +104,10 @@ public class RexCallConverter { return null; } + if (fn.functionName().equals("or")) { + return createCompoundExpr(rexCall, params); + } + Type impalaRetType = ImpalaTypeConverter.createImpalaType(fn.getReturnType(), rexCall.getType().getPrecision(), rexCall.getType().getScale()); diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConcatOrOperator.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConcatOrOperator.java new file mode 100644 index 000000000..d25c5a953 --- /dev/null +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConcatOrOperator.java @@ -0,0 +1,82 @@ +// 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.impala.calcite.operators; + +import com.google.common.base.Preconditions; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlBinaryOperator; +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.SqlFunctionCategory; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlOperandCountRange; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlOperatorBinding; +import org.apache.calcite.sql.SqlSyntax; +import org.apache.calcite.sql.type.SqlOperandCountRanges; +import org.apache.calcite.sql.type.SqlOperandTypeChecker; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.impala.calcite.type.ImpalaTypeConverter; +import org.apache.impala.catalog.Function; +import org.apache.impala.catalog.Type; + +import java.util.List; + +/** + * Operator used for overloaded || which is used for both "or" and "concat". + * When both arguments are boolean, we assume it's an "or". This logic can be + * found in CompoundVerticalBarExpr. + */ +public class ImpalaConcatOrOperator extends SqlBinaryOperator { + public static ImpalaConcatOrOperator INSTANCE = new ImpalaConcatOrOperator(); + + public ImpalaConcatOrOperator() { + // use 22 for precedence value which is the same as SqlStdOperatorTable.OR + super("||", SqlKind.OTHER, 22, true, null, null, null); + } + + @Override + public RelDataType inferReturnType(SqlOperatorBinding opBinding) { + final List<RelDataType> operandTypes = + CommonOperatorFunctions.getOperandTypes(opBinding); + Preconditions.checkState(operandTypes.size() == 2); + if (isOrOperand(opBinding, operandTypes.get(0), 0) && + isOrOperand(opBinding, operandTypes.get(1), 1)) { + return ImpalaTypeConverter.getRelDataType(Type.BOOLEAN); + } + return ImpalaTypeConverter.getRelDataType(Type.STRING); + } + + @Override + public SqlOperandCountRange getOperandCountRange() { + return SqlOperandCountRanges.of(2); + } + + @Override + public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) { + // Validation for operand types are done when checking for signature in + // the inferReturnType method. + return true; + } + + private boolean isOrOperand(SqlOperatorBinding opBinding, RelDataType opType, + int opNum) { + return opType.getSqlTypeName() == SqlTypeName.BOOLEAN + || opBinding.isOperandNull(opNum, false); + } +} diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConvertletTable.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConvertletTable.java index fa2e2ebcc..a39b569f6 100644 --- a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConvertletTable.java +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaConvertletTable.java @@ -16,6 +16,7 @@ */ package org.apache.impala.calcite.operators; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexBuilder; @@ -23,7 +24,9 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql2rel.ReflectiveConvertletTable; import org.apache.calcite.sql2rel.SqlRexContext; import org.apache.calcite.sql2rel.SqlRexConvertlet; @@ -31,6 +34,7 @@ import org.apache.calcite.sql2rel.StandardConvertletTable; import org.apache.impala.calcite.operators.ImpalaCustomOperatorTable; import java.util.List; +import java.util.Set; /** * ImpalaConvertletTable adds the ability to override any converlets in the @@ -39,6 +43,13 @@ import java.util.List; * RexNodes from SqlNodes. */ public class ImpalaConvertletTable extends ReflectiveConvertletTable { + + // Map of Calcite names to an Impala function name when the names are different + public static Set<String> IMPALA_OVERRIDE_CONVERTLETS = + ImmutableSet.<String> builder() + .add("||") + .build(); + public static final ImpalaConvertletTable INSTANCE = new ImpalaConvertletTable(); @@ -47,6 +58,7 @@ public class ImpalaConvertletTable extends ReflectiveConvertletTable { registerOp(ImpalaCastFunction.INSTANCE, this::convertExplicitCast); registerOp(SqlStdOperatorTable.IS_DISTINCT_FROM, this::convertIsDistinctFrom); registerOp(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, this::convertIsNotDistinctFrom); + registerOp(ImpalaConcatOrOperator.INSTANCE, this::convertConcatOr); } @Override @@ -70,6 +82,12 @@ public class ImpalaConvertletTable extends ReflectiveConvertletTable { return super.get(call); } + // Registered convertlets need to call our convertlet rather than the + // StandardConvertletTable convertlet which will not find the function. + if (IMPALA_OVERRIDE_CONVERTLETS.contains(call.getOperator().getName())) { + return super.get(call); + } + return StandardConvertletTable.INSTANCE.get(call); } @@ -108,4 +126,24 @@ public class ImpalaConvertletTable extends ReflectiveConvertletTable { List<RexNode> operands = Lists.newArrayList(cx.convertExpression(expr)); return rexBuilder.makeCall(returnType, ImpalaCastFunction.INSTANCE, operands); } + + /** + * Convertlet to decide whether the || operator is an "or" or a "concat". + * If the return type is BOOLEAN, then we know it's an OR. + */ + protected RexNode convertConcatOr ( + SqlRexContext cx, SqlCall call) { + final SqlNode expr1 = call.operand(0); + final SqlNode expr2 = call.operand(1); + final RexBuilder rexBuilder = cx.getRexBuilder(); + RelDataType returnType = + cx.getValidator().getValidatedNodeTypeIfKnown(call); + List<RexNode> operands = Lists.newArrayList( + cx.convertExpression(expr1), + cx.convertExpression(expr2)); + SqlOperator op = returnType.getSqlTypeName().equals(SqlTypeName.BOOLEAN) + ? SqlStdOperatorTable.OR + : SqlStdOperatorTable.CONCAT; + return rexBuilder.makeCall(returnType, op, operands); + } } diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaCustomOperatorTable.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaCustomOperatorTable.java index b0ab24ddd..10e4b0716 100644 --- a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaCustomOperatorTable.java +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaCustomOperatorTable.java @@ -222,15 +222,7 @@ public class ImpalaCustomOperatorTable extends ReflectiveSqlOperatorTable { public static final SqlAggFunction GROUPING_ID = new ImpalaGroupingIdFunction(); - public static final SqlBinaryOperator CONCAT = - new SqlBinaryOperator( - "||", - SqlKind.OTHER, - 60, - true, - STRING_TYPE, - null, - OperandTypes.STRING_SAME_SAME_OR_ARRAY_SAME_SAME); + public static final ImpalaConcatOrOperator CONCAT_OR = ImpalaConcatOrOperator.INSTANCE; // The explicit cast function was created to deal with the cast function using // Impala behavior. The operator is in the operator table because the Calcite
