This is an automated email from the ASF dual-hosted git repository. arina pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/drill.git
commit 1a28fd8c666a81bec2a453dbc26150349ac66cc0 Author: Hsuan-Yi Chu <hsua...@usc.edu> AuthorDate: Mon Mar 21 14:43:54 2016 -0700 DRILL-4525: Allow SqlBetweenOperator to accept LOWER_OPERAND and UPPER_OPERAND with different types --- .../sql/DrillCalciteSqlBetweenOperatorWrapper.java | 82 ++++++++++++++++++++++ .../drill/exec/planner/sql/DrillOperatorTable.java | 18 ++++- .../drill/TestFunctionsWithTypeExpoQueries.java | 44 ++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillCalciteSqlBetweenOperatorWrapper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillCalciteSqlBetweenOperatorWrapper.java new file mode 100644 index 0000000..e26da95 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillCalciteSqlBetweenOperatorWrapper.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.drill.exec.planner.sql; + +import com.google.common.collect.Lists; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.fun.SqlBetweenOperator; +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.type.SqlTypeUtil; + +import org.apache.drill.common.types.TypeProtos; +import org.apache.drill.exec.resolver.TypeCastRules; + +import java.util.List; + +/** + * This class serves as a wrapper class for SqlBetweenOperator. The motivation is to plug-in the return type inference and operand + * type check algorithms of Drill into Calcite's sql validation procedure. + * + * Except for the methods which are relevant to the return type inference and operand type check algorithms, the wrapper + * simply forwards the method calls to the wrapped SqlOperator. + * + * Note that SqlBetweenOperator cannot be wrapped in {@link DrillCalciteSqlOperatorWrapper}. The reason is when RexNode + * conversion is happening, StandardConvertletTable.convertBetween expects the SqlOperator to be a subclass of SqlBetweenOperator. + */ +public class DrillCalciteSqlBetweenOperatorWrapper extends SqlBetweenOperator implements DrillCalciteSqlWrapper { + private final SqlBetweenOperator operator; + + public DrillCalciteSqlBetweenOperatorWrapper(SqlBetweenOperator sqlBetweenOperator) { + super(sqlBetweenOperator.flag, sqlBetweenOperator.isNegated()); + operator = sqlBetweenOperator; + } + + @Override + public SqlOperator getOperator() { + return operator; + } + + /** + * + * Since Calcite has its rule for type compatibility (see {@link SqlTypeUtil#isComparable(RelDataType, RelDataType)}), + * which is usually different from Drill's, this method is overridden here to avoid Calcite early terminating the + * queries. + */ + @Override + public boolean checkOperandTypes( + SqlCallBinding callBinding, + boolean throwOnFailure) { + final List<TypeProtos.MinorType> types = Lists.newArrayList(); + for(int i = 0; i < callBinding.getOperandCount(); ++i) { + final TypeProtos.MinorType inMinorType = TypeInferenceUtils.getDrillTypeFromCalciteType(callBinding.getOperandType(i)); + if(inMinorType == TypeProtos.MinorType.LATE) { + return true; + } + types.add(inMinorType); + } + + final boolean isCompatible = TypeCastRules.getLeastRestrictiveType(types) != null; + if(!isCompatible && throwOnFailure) { + throw callBinding.newValidationSignatureError(); + } + return isCompatible; + } +} diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillOperatorTable.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillOperatorTable.java index cf858d3..d533c19 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillOperatorTable.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillOperatorTable.java @@ -22,6 +22,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.fun.SqlBetweenOperator; import org.apache.drill.common.expression.FunctionCallFactory; import org.apache.drill.exec.expr.fn.DrillFuncHolder; import org.apache.drill.exec.expr.fn.FunctionImplementationRegistry; @@ -174,10 +175,23 @@ public class DrillOperatorTable extends SqlStdOperatorTable { } else if(calciteOperator instanceof SqlFunction) { wrapper = new DrillCalciteSqlFunctionWrapper((SqlFunction) calciteOperator, getFunctionListWithInference(calciteOperator.getName())); + } else if(calciteOperator instanceof SqlBetweenOperator) { + // During the procedure of converting to RexNode, + // StandardConvertletTable.convertBetween expects the SqlOperator to be a subclass of SqlBetweenOperator + final SqlBetweenOperator sqlBetweenOperator = (SqlBetweenOperator) calciteOperator; + wrapper = new DrillCalciteSqlBetweenOperatorWrapper(sqlBetweenOperator); } else { - final String drillOpName = FunctionCallFactory.replaceOpWithFuncName(calciteOperator.getName()); + final String drillOpName; + // For UNARY_MINUS (-) or UNARY_PLUS (+), we do not rename them as function_add or function_subtract. + // Otherwise, Calcite will mix them up with binary operator subtract (-) or add (+) + if(calciteOperator == SqlStdOperatorTable.UNARY_MINUS || calciteOperator == SqlStdOperatorTable.UNARY_PLUS) { + drillOpName = calciteOperator.getName(); + } else { + drillOpName = FunctionCallFactory.replaceOpWithFuncName(calciteOperator.getName()); + } + final List<DrillFuncHolder> drillFuncHolders = getFunctionListWithInference(drillOpName); - if(drillFuncHolders.isEmpty() || calciteOperator == SqlStdOperatorTable.UNARY_MINUS || calciteOperator == SqlStdOperatorTable.UNARY_PLUS) { + if(drillFuncHolders.isEmpty()) { continue; } diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsWithTypeExpoQueries.java b/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsWithTypeExpoQueries.java index d298acc..74676de 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsWithTypeExpoQueries.java +++ b/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsWithTypeExpoQueries.java @@ -740,4 +740,48 @@ public class TestFunctionsWithTypeExpoQueries extends BaseTestQuery { final String[] excludedPlan = {}; PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan); } + + @Test // DRILL-4525 + public void testBetweenDateAndTimeStamp() throws Exception { + final String query = "select count(*) as col \n" + + "from cp.`employee.json` \n" + + "where cast(birth_date as DATE) BETWEEN cast('1970-01-01' AS DATE) AND (cast('1999-01-01' AS DATE) + INTERVAL '60' day) \n" + + "limit 0"; + + final TypeProtos.MajorType majorType = TypeProtos.MajorType.newBuilder() + .setMinorType(TypeProtos.MinorType.BIGINT) + .setMode(TypeProtos.DataMode.REQUIRED) + .build(); + + final List<Pair<SchemaPath, TypeProtos.MajorType>> expectedSchema = Lists.newArrayList(); + expectedSchema.add(Pair.of(SchemaPath.getSimplePath("col"), majorType)); + + testBuilder() + .sqlQuery(query) + .schemaBaseLine(expectedSchema) + .build() + .run(); + } + + @Test // DRILL-4525 + public void testBetweenDecimalAndDouble() throws Exception { + final String query = "select cast(r_regionkey as Integer) as col \n" + + "from cp.`tpch/region.parquet` \n" + + "where cast(r_regionkey as double) BETWEEN 1.1 AND 4.5 \n" + + "limit 0"; + + final TypeProtos.MajorType majorType = TypeProtos.MajorType.newBuilder() + .setMinorType(TypeProtos.MinorType.INT) + .setMode(TypeProtos.DataMode.OPTIONAL) + .build(); + + final List<Pair<SchemaPath, TypeProtos.MajorType>> expectedSchema = Lists.newArrayList(); + expectedSchema.add(Pair.of(SchemaPath.getSimplePath("col"), majorType)); + + testBuilder() + .sqlQuery(query) + .schemaBaseLine(expectedSchema) + .build() + .run(); + } } -- To stop receiving notification emails like this one, please contact ar...@apache.org.