mirror of
https://github.com/apache/impala.git
synced 2025-12-25 02:03:09 -05:00
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 <scarlin@cloudera.com> Tested-by: Steve Carlin <scarlin@cloudera.com>
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user