This is an automated email from the ASF dual-hosted git repository.
joemcdonnell pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git
The following commit(s) were added to refs/heads/master by this push:
new 141f38197 IMPALA-12935: First pass on Calcite planner functions
141f38197 is described below
commit 141f38197be2ca23757cb8b3f283cdb9dd62de47
Author: Steve Carlin <[email protected]>
AuthorDate: Thu Mar 21 18:45:24 2024 -0700
IMPALA-12935: First pass on Calcite planner functions
This commit handles the first pass on getting functions to work
through the Calcite planner. Only basic functions will work with
this commit. Implicit conversions for parameters are not yet supported.
Custom UDFs are also not supported yet.
The ImpalaOperatorTable is used at validation time to check for
existence of the function name for Impala. At first, it will check
Calcite operators for the existence of the function name (A TODO,
IMPALA-13096, is that we need to remove non-supported names from the
parser file). It is preferable to use the Calcite Operator since
Calcite does some optimizations based on the Calcite Operator class.
If the name is not found within the Calcite Operators, a check is done
within the BuiltinsDb (TODO: IMPALA-13095 handle UDFs) for the function.
If found, and SqlOperator class is generated on the fly to handle this
function.
The validation process for Calcite includes a call into the operator
method "inferReturnType". This method will validate that there exists
a function that will handle the operands, and if so, return the "return
type" of the function. In this commit, we will assume that the Calcite
operators will match Impala functionality. In later commits, there
will be overrides where we will use Impala validation for operators
where Calcite's validation isn't good enough.
After validation is complete, the functions will be in a Calcite format.
After the rest of compilation (relnode conversion, optimization) is
complete, the function needs to be converted back into Impala form (the
Expr object) to eventually get it into its thrift request.
In this commit, all functions are converted into Expr starting in the
ImpalaProjectRel, since this is the RelNode where functions do their
thing. The RexCallConverter and RexLiteralConverter get called via the
CreateExprVisitor for this conversion.
Since Calcite is providing the analysis portion of the planning, there
is no need to go through Impala's Analyzer object. However, the Impala
planner requires Expr objects to be analyzed. To get around this, the
AnalyzedFunctionCallExpr and AnalyzedNullLiteral objects exist which
analyze the expression in the constructor. While this could potentially
be combined with the existing FunctionCallExpr and NullLiteral objects,
this fits in with the general plan to avoid changing "fe" Impala code
as much as we can until much later in the commit cycle. Also, there
will be other Analyzed*Expr classes created in the future, but this
commit is intended for basic function call expressions only.
One minor change to the parser is added with this commit. Calcite parser
does not have acknowledge the "string" datatype, so this has been
added here in Parser.jj and config.fmpp.
Change-Id: I2dd4e402d69ee10547abeeafe893164ffd789b88
Reviewed-on: http://gerrit.cloudera.org:8080/21357
Reviewed-by: Michael Smith <[email protected]>
Tested-by: Impala Public Jenkins <[email protected]>
---
java/calcite-planner/src/main/codegen/config.fmpp | 1 +
.../src/main/codegen/templates/Parser.jj | 7 +
.../functions/AnalyzedFunctionCallExpr.java | 117 +++++++++++++
.../calcite/functions/AnalyzedNullLiteral.java | 58 +++++++
.../impala/calcite/functions/FunctionResolver.java | 67 ++++++++
.../impala/calcite/functions/RexCallConverter.java | 70 ++++++++
.../calcite/functions/RexLiteralConverter.java | 126 ++++++++++++++
.../impala/calcite/operators/ImpalaOperator.java | 149 +++++++++++++++++
.../calcite/operators/ImpalaOperatorTable.java | 103 ++++++++++++
.../impala/calcite/rel/node/ImpalaProjectRel.java | 5 +-
.../impala/calcite/rel/util/CreateExprVisitor.java | 42 ++++-
.../impala/calcite/service/CalciteJniFrontend.java | 8 +
.../impala/calcite/service/CalciteValidator.java | 3 +-
.../impala/calcite/type/ImpalaTypeConverter.java | 185 ++++++++++++++++++++-
.../queries/QueryTest/calcite.test | 35 ++++
15 files changed, 962 insertions(+), 14 deletions(-)
diff --git a/java/calcite-planner/src/main/codegen/config.fmpp
b/java/calcite-planner/src/main/codegen/config.fmpp
index b66528157..0a609de35 100644
--- a/java/calcite-planner/src/main/codegen/config.fmpp
+++ b/java/calcite-planner/src/main/codegen/config.fmpp
@@ -29,6 +29,7 @@ parser: {
# List of new keywords. Example: "DATABASES", "TABLES". If the keyword is
# not a reserved keyword, add it to the 'nonReservedKeywords' section.
keywords: [
+ "STRING"
]
# List of keywords from "keywords" section that are not reserved.
diff --git a/java/calcite-planner/src/main/codegen/templates/Parser.jj
b/java/calcite-planner/src/main/codegen/templates/Parser.jj
index 8dddb1a99..ce018c0ae 100644
--- a/java/calcite-planner/src/main/codegen/templates/Parser.jj
+++ b/java/calcite-planner/src/main/codegen/templates/Parser.jj
@@ -6000,6 +6000,8 @@ SqlTypeNameSpec CharacterTypeName(Span s) :
int precision = -1;
final SqlTypeName sqlTypeName;
String charSetName = null;
+ // Added Impala code to handle String type
+ boolean isString = false;
}
{
(
@@ -6011,6 +6013,9 @@ SqlTypeNameSpec CharacterTypeName(Span s) :
)
|
<VARCHAR> { s.add(this); sqlTypeName = SqlTypeName.VARCHAR; }
+ |
+ // Added Impala code to handle String type
+ <STRING> { s.add(this); sqlTypeName = SqlTypeName.VARCHAR; isString =
true;}
)
precision = PrecisionOpt()
[
@@ -6018,6 +6023,8 @@ SqlTypeNameSpec CharacterTypeName(Span s) :
charSetName = Identifier()
]
{
+ // Added Impala code to handle String type
+ if (isString) precision = Integer.MAX_VALUE;
return new SqlBasicTypeNameSpec(sqlTypeName, precision, charSetName,
s.end(this));
}
}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/AnalyzedFunctionCallExpr.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/AnalyzedFunctionCallExpr.java
new file mode 100644
index 000000000..f13be70f2
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/AnalyzedFunctionCallExpr.java
@@ -0,0 +1,117 @@
+// 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.functions;
+
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.impala.analysis.Analyzer;
+import org.apache.impala.analysis.Expr;
+import org.apache.impala.analysis.FunctionCallExpr;
+import org.apache.impala.analysis.FunctionParams;
+import org.apache.impala.catalog.Function;
+import org.apache.impala.catalog.Type;
+import org.apache.impala.common.AnalysisException;
+import org.apache.impala.common.ImpalaException;
+
+import java.util.List;
+
+/**
+ * A FunctionCallExpr that is always in an analyzed state
+ *
+ * The analysis for Calcite expressions can be done in the constructor
+ * rather than issuing a separate call to "analyze" after the object
+ * is constructed.
+ *
+ * For this class, we also want to override the "analyzeImpl" call since
+ * the "fn_" member is passed in rather than deduced at analysis time.
+ *
+ */
+public class AnalyzedFunctionCallExpr extends FunctionCallExpr {
+
+ // Need to save the function because it is known at constructor time. The
+ // resetAnalyzeState() method can be called at various points which could
+ // set the fn_ member to null. So we save the function in the savedFunction_
+ // variable so it can be properly set in analyzeImpl()
+ private final Function savedFunction_;
+
+ private final Analyzer analyzer_;
+
+ // c'tor that takes a list of Exprs that eventually get converted to
FunctionParams
+ public AnalyzedFunctionCallExpr(Function fn, List<Expr> params,
+ RexCall rexCall, Type retType, Analyzer analyzer) throws ImpalaException
{
+ super(fn.getFunctionName(), params);
+ this.savedFunction_ = fn;
+ this.type_ = retType;
+ this.analyze(analyzer);
+ this.analyzer_ = analyzer;
+ }
+
+ // c'tor which does not depend on Calcite's RexCall but is used when Impala's
+ // FunctionParams are created or there is some modifications to it
+ public AnalyzedFunctionCallExpr(Function fn, FunctionParams funcParams,
+ Type retType, Analyzer analyzer) throws ImpalaException {
+ super(fn.getFunctionName(), funcParams);
+ this.savedFunction_ = fn;
+ this.type_ = retType;
+ this.analyze(analyzer);
+ this.analyzer_ = analyzer;
+ }
+
+ public AnalyzedFunctionCallExpr(AnalyzedFunctionCallExpr other) {
+ super(other);
+ this.savedFunction_ = other.savedFunction_;
+ this.type_ = other.type_;
+ this.analyzer_ = other.analyzer_;
+ try {
+ this.analyze(this.analyzer_);
+ } catch (ImpalaException e) {
+ //TODO: IMPALA-13097: Don't throw runtime exception
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void analyzeImpl(Analyzer analyzer) throws AnalysisException {
+ // Functions have already gone through the analysis phase in Calcite so the
+ // analyzeImpl method is overridden. However, the FunctionName object
+ // still needs to be analyzed. This allows Expr.toSql() to display the
names
+ // correctly in the explain plan.
+ getFnName().analyze(analyzer);
+ this.fn_ = savedFunction_;
+ }
+
+ @Override
+ protected float computeEvalCost() {
+ // TODO: IMPALA-13098: need to implement
+ return UNKNOWN_COST;
+ }
+
+ @Override
+ public Expr clone() { return new AnalyzedFunctionCallExpr(this); }
+
+ @Override
+ public FunctionCallExpr cloneWithNewParams(FunctionParams params) {
+ try {
+ return new AnalyzedFunctionCallExpr(this.getFn(), params,
+ this.type_, analyzer_);
+ } catch (ImpalaException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/AnalyzedNullLiteral.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/AnalyzedNullLiteral.java
new file mode 100644
index 000000000..3a3b74199
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/AnalyzedNullLiteral.java
@@ -0,0 +1,58 @@
+// 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.functions;
+
+import org.apache.impala.analysis.Analyzer;
+import org.apache.impala.analysis.Expr;
+import org.apache.impala.analysis.NullLiteral;
+import org.apache.impala.catalog.Type;
+import org.apache.impala.common.AnalysisException;
+import org.apache.impala.common.ImpalaException;
+
+/**
+ * A NulLLiteral that is always in analyzed state
+ */
+public class AnalyzedNullLiteral extends NullLiteral {
+ private final Analyzer analyzer_;
+
+ private final Type savedType_;
+
+ public AnalyzedNullLiteral(Analyzer analyzer, Type type) throws
ImpalaException {
+ this.analyzer_ = analyzer;
+ savedType_ = type;
+ this.analyze(analyzer);
+ }
+
+ public AnalyzedNullLiteral(AnalyzedNullLiteral other) {
+ super(other);
+ this.analyzer_ = other.analyzer_;
+ this.savedType_ = other.savedType_;
+ }
+
+ @Override
+ public Expr clone() {
+ return new AnalyzedNullLiteral(this);
+ }
+
+ @Override
+ protected void analyzeImpl(Analyzer analyzer) throws AnalysisException {
+ // resetAnalyzedState will remove the type so we use the savedType
+ // from the constructor
+ uncheckedCastTo(savedType_);
+ }
+}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/FunctionResolver.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/FunctionResolver.java
new file mode 100644
index 000000000..00b0980ba
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/FunctionResolver.java
@@ -0,0 +1,67 @@
+// 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.functions;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.impala.analysis.FunctionName;
+import org.apache.impala.calcite.type.ImpalaTypeConverter;
+import org.apache.impala.catalog.BuiltinsDb;
+import org.apache.impala.catalog.Db;
+import org.apache.impala.catalog.Function;
+import org.apache.impala.catalog.ScalarFunction;
+import org.apache.impala.catalog.Type;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The FunctionResolver is a wrapper around the Impala Function Resolver (via
the
+ * (Db.getFunction() method). In this current iteration, only exact matches are
+ * resolved. TODO: IMPALA-13022: change this comment when implicit conversion
is handled.
+ */
+public class FunctionResolver {
+ protected static final Logger LOG =
+ LoggerFactory.getLogger(FunctionResolver.class.getName());
+
+ public static Function getFunction(String name, List<RelDataType> argTypes) {
+ String lowercaseName = name.toLowerCase();
+
+ List<Type> impalaArgTypes =
ImpalaTypeConverter.getNormalizedImpalaTypes(argTypes);
+ Function searchDesc = new Function(new FunctionName(BuiltinsDb.NAME,
lowercaseName),
+ impalaArgTypes, Type.INVALID, false);
+
+ Function fn = BuiltinsDb.getInstance().getFunction(searchDesc,
+ Function.CompareMode.IS_INDISTINGUISHABLE);
+
+ if (fn == null) {
+ LOG.debug("Failed to find function " + lowercaseName);
+ }
+
+ return fn;
+ }
+}
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
new file mode 100644
index 000000000..66bdf85d1
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexCallConverter.java
@@ -0,0 +1,70 @@
+// 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.functions;
+
+import com.google.common.collect.Lists;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexNode;
+import org.apache.impala.analysis.Analyzer;
+import org.apache.impala.analysis.Expr;
+import org.apache.impala.calcite.type.ImpalaTypeConverter;
+import org.apache.impala.catalog.Function;
+import org.apache.impala.catalog.Type;
+import org.apache.impala.common.AnalysisException;
+import org.apache.impala.common.ImpalaException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Static Helper class that returns Exprs for RexCall nodes.
+ */
+public class RexCallConverter {
+ protected static final Logger LOG =
+ LoggerFactory.getLogger(RexCallConverter.class.getName());
+
+ /*
+ * Returns the Impala Expr object for RexCallConverter.
+ */
+ public static Expr getExpr(RexCall rexCall, List<Expr> params,
+ RexBuilder rexBuilder, Analyzer analyzer) throws ImpalaException {
+
+ String funcName = rexCall.getOperator().getName().toLowerCase();
+
+ Function fn = getFunction(funcName, rexCall.getOperands(),
rexCall.getType());
+
+ Type impalaRetType =
ImpalaTypeConverter.createImpalaType(fn.getReturnType(),
+ rexCall.getType().getPrecision(), rexCall.getType().getScale());
+
+ return new AnalyzedFunctionCallExpr(fn, params, rexCall, impalaRetType,
analyzer);
+ }
+
+ private static Function getFunction(String name, List<RexNode> args,
+ RelDataType retType) throws ImpalaException {
+ List<RelDataType> argTypes = Lists.transform(args, RexNode::getType);
+ Function fn = FunctionResolver.getFunction(name, argTypes);
+ if (fn == null) {
+ throw new AnalysisException("Could not find function \"" + name + "\" in
Impala "
+ + "with args " + argTypes + " and return type " + retType);
+ }
+ return fn;
+ }
+}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexLiteralConverter.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexLiteralConverter.java
new file mode 100644
index 000000000..5e5d706c3
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/functions/RexLiteralConverter.java
@@ -0,0 +1,126 @@
+// 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.functions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.DateString;
+import org.apache.calcite.util.TimestampString;
+import org.apache.impala.analysis.Analyzer;
+import org.apache.impala.analysis.Expr;
+import org.apache.impala.analysis.BoolLiteral;
+import org.apache.impala.analysis.DateLiteral;
+import org.apache.impala.analysis.NumericLiteral;
+import org.apache.impala.analysis.StringLiteral;
+import org.apache.impala.calcite.type.ImpalaTypeConverter;
+import org.apache.impala.catalog.Function;
+import org.apache.impala.catalog.PrimitiveType;
+import org.apache.impala.catalog.ScalarType;
+import org.apache.impala.catalog.Type;
+import org.apache.impala.common.AnalysisException;
+import org.apache.impala.common.ImpalaException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Static Helper class that returns Exprs for RexLiteral nodes.
+ */
+public class RexLiteralConverter {
+ protected static final Logger LOG =
+ LoggerFactory.getLogger(RexLiteralConverter.class.getName());
+
+ /*
+ * Returns Expr object for ImpalaRexLiteral
+ */
+ public static Expr getExpr(RexLiteral rexLiteral, Analyzer analyzer)
+ throws ImpalaException {
+ if (SqlTypeName.INTERVAL_TYPES.contains(rexLiteral.getTypeName())) {
+ return new NumericLiteral(
+ new BigDecimal(rexLiteral.getValueAs(Long.class)), Type.BIGINT);
+ }
+ switch (rexLiteral.getTypeName()) {
+ case NULL:
+ Type type = ImpalaTypeConverter.createImpalaType(rexLiteral.getType());
+ return new AnalyzedNullLiteral(null, type);
+ case BOOLEAN:
+ Expr boolExpr = new BoolLiteral(rexLiteral.getValueAs(Boolean.class));
+ boolExpr.analyze(null);
+ return boolExpr;
+ case BIGINT:
+ case DECIMAL:
+ case DOUBLE:
+ Expr numericExpr = new
NumericLiteral(rexLiteral.getValueAs(BigDecimal.class),
+ ImpalaTypeConverter.createImpalaType(rexLiteral.getType()));
+ numericExpr.analyze(null);
+ return numericExpr;
+ case CHAR:
+ case VARCHAR:
+ ScalarType charType = rexLiteral.getType().getSqlTypeName() ==
SqlTypeName.VARCHAR
+ ? Type.STRING
+ :
ScalarType.createCharType(rexLiteral.getValueAs(String.class).length());
+ if (charType.getPrimitiveType() == PrimitiveType.CHAR) {
+ // ensure no wildcards or char length 0 which will crash impalad
+ Preconditions.checkState(charType.getLength() > 0);
+ }
+ Expr charExpr = new StringLiteral(rexLiteral.getValueAs(String.class),
+ charType, false);
+ charExpr.analyze(null);
+ return charExpr;
+ case DATE:
+ DateString dateStringClass = rexLiteral.getValueAs(DateString.class);
+ String dateString = (dateStringClass == null) ? null :
dateStringClass.toString();
+ Expr dateExpr = new DateLiteral(rexLiteral.getValueAs(Integer.class),
dateString);
+ dateExpr.analyze(null);
+ return dateExpr;
+ case TIMESTAMP:
+ return createCastTimestampExpr(rexLiteral, analyzer);
+ default:
+ throw new AnalysisException("Unsupported RexLiteral: "
+ + rexLiteral.getTypeName());
+ }
+ }
+
+ /**
+ * Create a cast timestamp expression from a String to a Timestamp.
+ * The only way to create a TimestampLiteral directly in Impala is by
accessing
+ * the backend. This will normally be done earlier in Calcite via constant
folding.
+ * If constant folding was not allowed, it means we did not have access to
the backend
+ * and thus need to do a cast in order to support conversion to a Timestamp.
+ */
+ private static Expr createCastTimestampExpr(RexLiteral rexLiteral,
+ Analyzer analyzer)
+ throws ImpalaException {
+ List<RelDataType> typeNames =
+ ImmutableList.of(ImpalaTypeConverter.getRelDataType(Type.STRING));
+
+ String timestamp = rexLiteral.getValueAs(TimestampString.class).toString();
+ List<Expr> argList =
+ Lists.newArrayList(new StringLiteral(timestamp, Type.STRING, false));
+ Function castFunc = FunctionResolver.getFunction("casttotimestamp",
typeNames);
+ return new AnalyzedFunctionCallExpr(castFunc, argList, null,
Type.TIMESTAMP,
+ analyzer);
+ }
+}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaOperator.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaOperator.java
new file mode 100644
index 000000000..1024d7818
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaOperator.java
@@ -0,0 +1,149 @@
+// 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.commons.lang.StringUtils;
+import org.apache.impala.calcite.type.ImpalaTypeSystemImpl;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+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.SqlOperatorBinding;
+import org.apache.calcite.sql.SqlSyntax;
+import org.apache.calcite.sql.type.SqlOperandCountRanges;
+import org.apache.impala.analysis.FunctionName;
+import org.apache.impala.calcite.functions.FunctionResolver;
+import org.apache.impala.calcite.type.ImpalaTypeConverter;
+import org.apache.impala.catalog.BuiltinsDb;
+import org.apache.impala.catalog.Function;
+import org.apache.impala.catalog.Type;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ImpalaOperator is a custom Calcite operator that handles all generic
functions
+ * that are not defined by Calcite. It is preferable to use a Calcite operator
+ * if possible because Calcite has optimizations that are based on the operator
+ * class.
+ */
+public class ImpalaOperator extends SqlFunction {
+ protected static final Logger LOG =
+ LoggerFactory.getLogger(ImpalaOperator.class.getName());
+
+ // Allow any count because this is used for all functions. Validation for
specific
+ // number of parameters will be done when Impala function resolving is done.
+ public static SqlOperandCountRange ANY_COUNT_RANGE =
SqlOperandCountRanges.any();
+
+ public ImpalaOperator(String name) {
+ super(name.toUpperCase(), SqlKind.OTHER, null, null, null,
+ SqlFunctionCategory.USER_DEFINED_FUNCTION);
+ }
+
+ @Override
+ public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+ final List<RelDataType> operandTypes = getOperandTypes(opBinding);
+
+ RexBuilder rexBuilder =
+ new RexBuilder(new JavaTypeFactoryImpl(new ImpalaTypeSystemImpl()));
+ RelDataTypeFactory factory = rexBuilder.getTypeFactory();
+
+ // Resolve Impala function through Impala method.
+ // TODO: IMPALA-13022: Right now, CompareMode is INDISTINGUISHABLE because
this
+ // commit only deals with exact matches. This will change in a future
commit.
+ Function fn = FunctionResolver.getFunction(getName(), operandTypes);
+
+ if (fn == null) {
+ throw new IllegalArgumentException("Cannot infer return type for "
+ + getName() + "; operand types: " + operandTypes);
+ }
+
+ RelDataType returnType =
+ ImpalaTypeConverter.getRelDataType(fn.getReturnType());
+ return isNullable(operandTypes)
+ ? returnType
+ : factory.createTypeWithNullability(returnType, true);
+ }
+
+ @Override
+ public boolean checkOperandTypes(SqlCallBinding callBinding, boolean
throwOnFailure) {
+ // Validation for operand types are done when checking for signature in
+ // the inferReturnType method.
+ return true;
+ }
+
+ @Override
+ public SqlOperandCountRange getOperandCountRange() {
+ // Validation for operand count ranges are done when checking for
signature in
+ // the inferReturnType method.
+ return ANY_COUNT_RANGE;
+ }
+
+ /**
+ * getAllowedSignatures used for error messages.
+ */
+ @Override
+ public String getAllowedSignatures(String opNameToUse) {
+ // TODO: IMPALA-13099 leave blank for now since this might be hard to
derive because
+ // of implicit type support.
+ return "";
+ }
+
+ @Override
+ public SqlSyntax getSyntax() {
+ return SqlSyntax.FUNCTION;
+ }
+
+ private List<RelDataType> getOperandTypes(SqlOperatorBinding opBinding) {
+ Preconditions.checkState(opBinding instanceof SqlCallBinding);
+ SqlCallBinding callBinding = (SqlCallBinding) opBinding;
+
+ List<RelDataType> operandTypes = new ArrayList<>();
+ for (int i = 0; i < callBinding.getOperandCount(); ++i) {
+ if (callBinding.isOperandNull(i, false)) {
+ operandTypes.add(ImpalaTypeConverter.getRelDataType(Type.NULL));
+ } else {
+ operandTypes.add(callBinding.getOperandType(i));
+ }
+ }
+ return operandTypes;
+ }
+
+ // return false if all operand types are not nullable. Else return true.
+ private boolean isNullable(List<RelDataType> operandTypes) {
+ for (RelDataType rdt : operandTypes) {
+ if (rdt.isNullable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaOperatorTable.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaOperatorTable.java
new file mode 100644
index 000000000..6e2540e94
--- /dev/null
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/operators/ImpalaOperatorTable.java
@@ -0,0 +1,103 @@
+// 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.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.validate.SqlNameMatcher;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSyntax;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;
+import org.apache.impala.calcite.functions.FunctionResolver;
+import org.apache.impala.catalog.BuiltinsDb;
+import org.apache.impala.catalog.Db;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The ImpalaOperatorTable is used to hold all the operators and to resolve
+ * the functions that are being validated.
+ *
+ * The main method used to resolve the operators is lookupOperatorOverloads.
+ * The method mutates the operatorList and fills it with the matching operator.
+ * If Calcite contains an operator that matches the name passed in, the Calcite
+ * operator is the one that is used. It is preferable to use the Calcite
operator
+ * since Calcite performs optimizations based on the operator class.
+ *
+ * If a Calcite operator is not found, we check the Impala functions to see if
+ * the function name is known by Impala. If so, an ImpalaOperator class is
generated
+ * on the fly.
+ *
+ * TODO: IMPALA-13095: Handle UDFs
+ */
+public class ImpalaOperatorTable extends ReflectiveSqlOperatorTable {
+ protected static final Logger LOG =
+ LoggerFactory.getLogger(ImpalaOperatorTable.class.getName());
+
+ private static ImpalaOperatorTable INSTANCE;
+
+ /**
+ * lookupOperatorOverloads: See class comment above for details.
+ */
+ @Override
+ public void lookupOperatorOverloads(SqlIdentifier opName,
SqlFunctionCategory category,
+ SqlSyntax syntax, List<SqlOperator> operatorList, SqlNameMatcher
nameMatcher) {
+
+
+ // Check Calcite operator table for existence.
+ SqlStdOperatorTable.instance().lookupOperatorOverloads(opName, category,
syntax,
+ operatorList, nameMatcher);
+ Preconditions.checkState(operatorList.size() <= 1);
+ if (operatorList.size() == 1) {
+ return;
+ }
+
+ // There shouldn't be more than one opName with our usage, so throw an
exception
+ // if this happens.
+ if (opName.names.size() > 1) {
+ throw new RuntimeException("Cannot handle identifier with more than one
name: " +
+ opName);
+ }
+
+ // Check Impala Builtins for existence: TODO: IMPALA-13095: handle UDFs
+ if (!BuiltinsDb.getInstance().containsFunction(opName.getSimple())) {
+ return;
+ }
+
+ operatorList.add(new ImpalaOperator(opName.getSimple()));
+ }
+
+ public static synchronized void create(Db db) {
+ if (INSTANCE != null) {
+ return;
+ }
+ INSTANCE = new ImpalaOperatorTable();
+ }
+
+ public static ImpalaOperatorTable getInstance() {
+ return INSTANCE;
+ }
+}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/node/ImpalaProjectRel.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/node/ImpalaProjectRel.java
index fc9706fe8..e0e54e0d2 100644
---
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/node/ImpalaProjectRel.java
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/node/ImpalaProjectRel.java
@@ -68,7 +68,6 @@ public class ImpalaProjectRel extends Project
@Override
public NodeWithExprs getPlanNode(ParentPlanRelContext context) throws
ImpalaException {
-
NodeWithExprs inputWithExprs = getChildPlanNode(context);
// get the output exprs for this node that are needed by the parent node.
@@ -88,8 +87,8 @@ public class ImpalaProjectRel extends Project
throws ImpalaException {
ImpalaPlanRel inputRel = (ImpalaPlanRel) getInput(0);
- CreateExprVisitor visitor =
- new CreateExprVisitor(inputNodeWithExprs.outputExprs_);
+ CreateExprVisitor visitor = new
CreateExprVisitor(getCluster().getRexBuilder(),
+ inputNodeWithExprs.outputExprs_, basicAnalyzer);
ImmutableList.Builder<Expr> builder = new ImmutableList.Builder();
for (RexNode rexNode : getProjects()) {
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/util/CreateExprVisitor.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/util/CreateExprVisitor.java
index e8884741e..aec0295cd 100644
---
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/util/CreateExprVisitor.java
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rel/util/CreateExprVisitor.java
@@ -17,6 +17,8 @@
package org.apache.impala.calcite.rel.util;
+import com.google.common.collect.Lists;
+import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexCorrelVariable;
import org.apache.calcite.rex.RexDynamicParam;
@@ -31,23 +33,34 @@ import org.apache.calcite.rex.RexRangeRef;
import org.apache.calcite.rex.RexSubQuery;
import org.apache.calcite.rex.RexTableInputRef;
import org.apache.calcite.rex.RexVisitorImpl;
+
+import org.apache.impala.analysis.Analyzer;
import org.apache.impala.analysis.Expr;
import org.apache.impala.common.AnalysisException;
import org.apache.impala.common.ImpalaException;
+import org.apache.impala.calcite.functions.RexCallConverter;
+import org.apache.impala.calcite.functions.RexLiteralConverter;
+import java.util.ArrayList;
import java.util.List;
/**
* CreateExprVisitor will generate Impala expressions for function calls and
literals.
- * TODO: In this iteration, it only handles input refs.
*/
public class CreateExprVisitor extends RexVisitorImpl<Expr> {
+ private final RexBuilder rexBuilder_;
+
private final List<Expr> inputExprs_;
- public CreateExprVisitor(List<Expr> inputExprs) {
+ private final Analyzer analyzer_;
+
+ public CreateExprVisitor(RexBuilder rexBuilder, List<Expr> inputExprs,
+ Analyzer analyzer) {
super(false);
this.inputExprs_ = inputExprs;
+ this.rexBuilder_ = rexBuilder;
+ this.analyzer_ = analyzer;
}
@Override
@@ -57,12 +70,24 @@ public class CreateExprVisitor extends RexVisitorImpl<Expr>
{
@Override
public Expr visitCall(RexCall rexCall) {
- throw new RuntimeException("Not supported");
+ try {
+ List<Expr> params = Lists.newArrayList();
+ for (RexNode operand : rexCall.getOperands()) {
+ params.add(operand.accept(this));
+ }
+ return RexCallConverter.getExpr(rexCall, params, rexBuilder_, analyzer_);
+ } catch (ImpalaException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
public Expr visitLiteral(RexLiteral rexLiteral) {
- throw new RuntimeException("Not supported");
+ try {
+ return RexLiteralConverter.getExpr(rexLiteral, analyzer_);
+ } catch (ImpalaException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
@@ -122,4 +147,13 @@ public class CreateExprVisitor extends
RexVisitorImpl<Expr> {
throw new AnalysisException(e);
}
}
+
+ public static List<Expr> getExprs(CreateExprVisitor visitor, List<RexNode>
operands)
+ throws ImpalaException {
+ List<Expr> exprs = new ArrayList<>();
+ for (RexNode operand : operands) {
+ exprs.add(getExpr(visitor, operand));
+ }
+ return exprs;
+ }
}
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteJniFrontend.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteJniFrontend.java
index af5c8225f..54b582a94 100644
---
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteJniFrontend.java
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteJniFrontend.java
@@ -23,8 +23,11 @@ import
org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.sql.SqlNode;
+import org.apache.impala.calcite.functions.FunctionResolver;
+import org.apache.impala.calcite.operators.ImpalaOperatorTable;
import org.apache.impala.calcite.rel.node.NodeWithExprs;
import org.apache.impala.calcite.rel.node.ImpalaPlanRel;
+import org.apache.impala.catalog.BuiltinsDb;
import org.apache.impala.common.ImpalaException;
import org.apache.impala.common.InternalException;
import org.apache.impala.common.JniUtil;
@@ -68,6 +71,7 @@ public class CalciteJniFrontend extends JniFrontend {
public CalciteJniFrontend(byte[] thriftBackendConfig, boolean isBackendTest)
throws ImpalaException, TException {
super(thriftBackendConfig, isBackendTest);
+ loadCalciteImpalaFunctions();
}
/**
@@ -191,6 +195,10 @@ public class CalciteJniFrontend extends JniFrontend {
}
}
+ private static void loadCalciteImpalaFunctions() {
+ ImpalaOperatorTable.create(BuiltinsDb.getInstance());
+ }
+
public static class QueryContext {
private final TQueryCtx queryCtx_;
private final String stmt_;
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteValidator.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteValidator.java
index 7c455f97e..7ca3ddbb4 100644
---
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteValidator.java
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteValidator.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.impala.calcite.operators.ImpalaOperatorTable;
import org.apache.impala.calcite.type.ImpalaTypeSystemImpl;
import org.apache.impala.calcite.validate.ImpalaConformance;
@@ -52,7 +53,7 @@ public class CalciteValidator implements CompilerStep {
this.catalogReader = mdHandler.getCalciteCatalogReader();
this.sqlValidator = SqlValidatorUtil.newValidator(
- SqlStdOperatorTable.instance(),
+ ImpalaOperatorTable.getInstance(),
catalogReader, typeFactory,
SqlValidator.Config.DEFAULT
.withConformance(ImpalaConformance.INSTANCE)
diff --git
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/type/ImpalaTypeConverter.java
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/type/ImpalaTypeConverter.java
index 8a93f6541..0f6573a68 100644
---
a/java/calcite-planner/src/main/java/org/apache/impala/calcite/type/ImpalaTypeConverter.java
+++
b/java/calcite-planner/src/main/java/org/apache/impala/calcite/type/ImpalaTypeConverter.java
@@ -23,17 +23,11 @@ import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexBuilder;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.util.ConversionUtil;
-import org.apache.impala.catalog.PrimitiveType;
import org.apache.impala.catalog.ScalarType;
import org.apache.impala.catalog.Type;
import org.apache.impala.thrift.TPrimitiveType;
-import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -77,7 +71,10 @@ public class ImpalaTypeConverter {
map.put(Type.DOUBLE, factory.createSqlType(SqlTypeName.DOUBLE));
map.put(Type.TIMESTAMP, factory.createSqlType(SqlTypeName.TIMESTAMP));
map.put(Type.DATE, factory.createSqlType(SqlTypeName.DATE));
+ map.put(Type.DECIMAL, factory.createSqlType(SqlTypeName.DECIMAL));
map.put(Type.BINARY, factory.createSqlType(SqlTypeName.BINARY));
+ map.put(Type.CHAR, factory.createSqlType(SqlTypeName.CHAR, 1));
+ map.put(Type.VARCHAR, factory.createSqlType(SqlTypeName.VARCHAR, 1));
map.put(Type.STRING, factory.createSqlType(SqlTypeName.VARCHAR,
Integer.MAX_VALUE));
map.put(Type.NULL, factory.createSqlType(SqlTypeName.NULL));
@@ -118,6 +115,18 @@ public class ImpalaTypeConverter {
}
}
+ /**
+ * Get the normalized RelDataType given an impala type.
+ */
+ public static RelDataType getRelDataType(Type impalaType) {
+ if (impalaType == null) {
+ return null;
+ }
+ TPrimitiveType primitiveType = impalaType.getPrimitiveType().toThrift();
+ Type normalizedImpalaType = getImpalaType(primitiveType);
+ return impalaToCalciteMap.get(normalizedImpalaType);
+ }
+
/**
* Create Impala types given primitive types.
* Primitive types should not be exposed outside of this class.
@@ -163,4 +172,168 @@ public class ImpalaTypeConverter {
throw new RuntimeException("Unknown type " + argType);
}
}
+
+ /**
+ * Create a new impala type given a relDataType
+ */
+ public static Type createImpalaType(RelDataType relDataType) {
+ // First retrieve the normalized impala type
+ if (relDataType.getSqlTypeName() == SqlTypeName.VARCHAR &&
+ ((relDataType.getPrecision() == Integer.MAX_VALUE) ||
+ (relDataType.getPrecision() == -1))) {
+ return Type.STRING;
+ }
+ Type impalaType = getType(relDataType.getSqlTypeName());
+ // create the impala type given the normalized type, precision, and scale.
+ return createImpalaType(impalaType, relDataType.getPrecision(),
+ relDataType.getScale());
+ }
+
+ public static Type createImpalaType(Type impalaType, int precision, int
scale) {
+ TPrimitiveType primitiveType = impalaType.getPrimitiveType().toThrift();
+ // Char, varchar, decimal, and fixed_uda_intermediate contain precisions
and need to
+ // be treated separately.
+ switch (primitiveType) {
+ case CHAR:
+ return ScalarType.createCharType(precision);
+ case VARCHAR:
+ return (precision == Integer.MAX_VALUE || precision == -1)
+ ? Type.STRING
+ : ScalarType.createVarcharType(precision);
+ case DECIMAL:
+ if (precision == -1) {
+ return Type.DECIMAL;
+ }
+ return ScalarType.createDecimalType(precision, scale);
+ case FIXED_UDA_INTERMEDIATE:
+ return ScalarType.createFixedUdaIntermediateType(precision);
+ default:
+ return impalaType;
+ }
+ }
+
+ public static Type getType(SqlTypeName calciteTypeName) {
+ switch (calciteTypeName) {
+ case TINYINT:
+ return Type.TINYINT;
+ case SMALLINT:
+ return Type.SMALLINT;
+ case INTEGER:
+ return Type.INT;
+ case INTERVAL_YEAR:
+ case INTERVAL_MONTH:
+ case INTERVAL_DAY:
+ case INTERVAL_HOUR:
+ case INTERVAL_MINUTE:
+ case INTERVAL_SECOND:
+ case BIGINT:
+ return Type.BIGINT;
+ case VARCHAR:
+ return Type.VARCHAR;
+ case BOOLEAN:
+ return Type.BOOLEAN;
+ case REAL:
+ case FLOAT:
+ return Type.FLOAT;
+ case DOUBLE:
+ return Type.DOUBLE;
+ case DECIMAL:
+ return Type.DECIMAL;
+ case CHAR:
+ return Type.CHAR;
+ case TIMESTAMP:
+ return Type.TIMESTAMP;
+ case DATE:
+ return Type.DATE;
+ case NULL:
+ return Type.NULL;
+ case BINARY:
+ return Type.BINARY;
+ default:
+ throw new RuntimeException("Type " + calciteTypeName + " not
supported yet.");
+ }
+ }
+
+ // helper function to handle translation of lists.
+ public static List<Type> getNormalizedImpalaTypes(List<RelDataType>
relDataTypes) {
+ return Lists.transform(relDataTypes,
ImpalaTypeConverter::getNormalizedImpalaType);
+ }
+
+ /**
+ * Return the default impala type given a reldatatype that potentially has
precision.
+ */
+ public static ScalarType getNormalizedImpalaType(RelDataType relDataType) {
+ SqlTypeName sqlTypeName = relDataType.getSqlTypeName();
+ if (SqlTypeName.INTERVAL_TYPES.contains(sqlTypeName)) {
+ return Type.BIGINT;
+ }
+ switch (sqlTypeName) {
+ case VARCHAR:
+ return relDataType.getPrecision() == Integer.MAX_VALUE
+ ? Type.STRING : Type.VARCHAR;
+ case CHAR:
+ return Type.CHAR;
+ case DECIMAL:
+ return Type.DECIMAL;
+ case BOOLEAN:
+ return Type.BOOLEAN;
+ case TINYINT:
+ return Type.TINYINT;
+ case SMALLINT:
+ return Type.SMALLINT;
+ case INTEGER:
+ return Type.INT;
+ case BIGINT:
+ return Type.BIGINT;
+ case FLOAT:
+ case REAL:
+ return Type.FLOAT;
+ case DOUBLE:
+ return Type.DOUBLE;
+ case TIMESTAMP:
+ return Type.TIMESTAMP;
+ case DATE:
+ return Type.DATE;
+ case BINARY:
+ return Type.BINARY;
+ case SYMBOL:
+ return null;
+ case NULL:
+ return Type.NULL;
+ default:
+ throw new RuntimeException("Unknown SqlTypeName " + sqlTypeName +
+ " to convert to Impala.");
+ }
+ }
+
+ public static List<RelDataType> createRelDataTypes(List<Type> impalaTypes) {
+ List<RelDataType> result = Lists.newArrayList();
+ for (Type t : impalaTypes) {
+ result.add(createRelDataType(t));
+ }
+ return result;
+ }
+
+ /**
+ * Create a new RelDataType given the Impala type.
+ */
+ public static RelDataType createRelDataType(Type impalaType) {
+ if (impalaType == null) {
+ return null;
+ }
+ TPrimitiveType primitiveType = impalaType.getPrimitiveType().toThrift();
+ if (primitiveType == TPrimitiveType.DECIMAL) {
+ ScalarType scalarType = (ScalarType) impalaType;
+ RexBuilder rexBuilder =
+ new RexBuilder(new JavaTypeFactoryImpl(new ImpalaTypeSystemImpl()));
+ RelDataTypeFactory factory = rexBuilder.getTypeFactory();
+ RelDataType decimalDefinedRetType =
factory.createSqlType(SqlTypeName.DECIMAL,
+ scalarType.decimalPrecision(), scalarType.decimalScale());
+ return factory.createTypeWithNullability(decimalDefinedRetType, true);
+ }
+ // for all other arguments besides decimal, we just normalize the datatype
and return
+ // the previously created RelDataType.
+ Type normalizedImpalaType = getImpalaType(primitiveType);
+ return impalaToCalciteMap.get(normalizedImpalaType);
+ }
}
diff --git a/testdata/workloads/functional-query/queries/QueryTest/calcite.test
b/testdata/workloads/functional-query/queries/QueryTest/calcite.test
index b05f3089a..9c6e0cf07 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/calcite.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/calcite.test
@@ -115,3 +115,38 @@ int,string,binary
---- RUNTIME_PROFILE
row_regex: .*PlannerType: CalcitePlanner.*
====
+---- QUERY
+# Tiny test for Calcite. At the point of this commit, very few functions work.
This
+# is a test that includes one of the functions that does.
+# The ultimate goal is to include all tests in the testing framework, so there
is
+# no need to be extensive about testing in this file.
+select bigint_col, abs(cast(-3 as bigint)), abs(-3000000000) from
functional.alltypestiny;
+---- RESULTS
+0,3,3000000000
+10,3,3000000000
+0,3,3000000000
+10,3,3000000000
+0,3,3000000000
+10,3,3000000000
+0,3,3000000000
+10,3,3000000000
+---- TYPES
+bigint,bigint,bigint
+====
+---- QUERY
+# Tiny test for Calcite. At the point of this commit, very few functions work.
This
+# is a test that includes one of the functions that does.
+# The ultimate goal is to include all tests in the testing framework, so there
is
+# no need to be extensive about testing in this file.
+select cast(cast('2005-12-13 08:00:00' as string) AS TIMESTAMP) from
functional.alltypestiny;
+---- RESULTS
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+2005-12-13 08:00:00
+---- TYPES
+timestamp