You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2021/09/21 14:13:37 UTC
[ignite] branch sql-calcite updated: IGNITE-15109 Fix TIMESTAMPDIFF
for MICROSECOND time unit - Fixes #9368.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch sql-calcite
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/sql-calcite by this push:
new bcf055b IGNITE-15109 Fix TIMESTAMPDIFF for MICROSECOND time unit - Fixes #9368.
bcf055b is described below
commit bcf055b76771e0b90164f5293cb20b2fe7abb85f
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Tue Sep 21 17:03:32 2021 +0300
IGNITE-15109 Fix TIMESTAMPDIFF for MICROSECOND time unit - Fixes #9368.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../query/calcite/CalciteQueryProcessor.java | 2 +
.../calcite/prepare/IgniteConvertletTable.java | 130 +++++++++++++++++++++
.../processors/query/calcite/FunctionsTest.java | 10 ++
.../sql/function/timestamp/test_timestampdiff.test | 30 +++++
.../timestamp/test_timestampdiff.test_ignore | 12 +-
5 files changed, 173 insertions(+), 11 deletions(-)
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
index 7b4a909..00bdc66 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
@@ -59,6 +59,7 @@ import org.apache.ignite.internal.processors.query.calcite.metadata.AffinityServ
import org.apache.ignite.internal.processors.query.calcite.metadata.MappingService;
import org.apache.ignite.internal.processors.query.calcite.metadata.MappingServiceImpl;
import org.apache.ignite.internal.processors.query.calcite.metadata.cost.IgniteCostFactory;
+import org.apache.ignite.internal.processors.query.calcite.prepare.IgniteConvertletTable;
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCache;
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCacheImpl;
import org.apache.ignite.internal.processors.query.calcite.schema.SchemaHolder;
@@ -94,6 +95,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
.build()
)
)
+ .convertletTable(IgniteConvertletTable.INSTANCE)
.parserConfig(
SqlParser.config()
.withParserFactory(IgniteSqlParserImpl.FACTORY)
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteConvertletTable.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteConvertletTable.java
new file mode 100644
index 0000000..39ff956
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteConvertletTable.java
@@ -0,0 +1,130 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.prepare;
+
+import java.math.BigDecimal;
+import com.google.common.collect.ImmutableList;
+import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.avatica.util.TimeUnit;
+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.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlIntervalQualifier;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.sql2rel.ReflectiveConvertletTable;
+import org.apache.calcite.sql2rel.SqlRexContext;
+import org.apache.calcite.sql2rel.SqlRexConvertlet;
+import org.apache.calcite.sql2rel.StandardConvertletTable;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Ignite convertlet table.
+ */
+public class IgniteConvertletTable extends ReflectiveConvertletTable {
+ /** Instance. */
+ public static final IgniteConvertletTable INSTANCE = new IgniteConvertletTable();
+
+ /** */
+ private IgniteConvertletTable() {
+ // Replace Calcite's convertlet with our own.
+ registerOp(SqlStdOperatorTable.TIMESTAMP_DIFF, new TimestampDiffConvertlet());
+ }
+
+ /** {@inheritDoc} */
+ @Nullable
+ @Override public SqlRexConvertlet get(SqlCall call) {
+ SqlRexConvertlet res = super.get(call);
+
+ return res == null ? StandardConvertletTable.INSTANCE.get(call) : res;
+ }
+
+ /** Convertlet that handles the {@code TIMESTAMPDIFF} function. */
+ private static class TimestampDiffConvertlet implements SqlRexConvertlet {
+ /** {@inheritDoc} */
+ @Override public RexNode convertCall(SqlRexContext cx, SqlCall call) {
+ // TIMESTAMPDIFF(unit, t1, t2)
+ // => (t2 - t1) UNIT
+ final RexBuilder rexBuilder = cx.getRexBuilder();
+ final SqlLiteral unitLiteral = call.operand(0);
+ TimeUnit unit = unitLiteral.getValueAs(TimeUnit.class);
+ BigDecimal multiplier = BigDecimal.ONE;
+ BigDecimal divider = BigDecimal.ONE;
+ SqlTypeName sqlTypeName = unit == TimeUnit.NANOSECOND
+ ? SqlTypeName.BIGINT
+ : SqlTypeName.INTEGER;
+ switch (unit) {
+ case MICROSECOND:
+ case MILLISECOND:
+ case NANOSECOND:
+ divider = unit.multiplier;
+ unit = TimeUnit.MILLISECOND;
+ break;
+ case WEEK:
+ multiplier = BigDecimal.valueOf(DateTimeUtils.MILLIS_PER_SECOND);
+ divider = unit.multiplier;
+ unit = TimeUnit.SECOND;
+ break;
+ case QUARTER:
+ divider = unit.multiplier;
+ unit = TimeUnit.MONTH;
+ break;
+ default:
+ break;
+ }
+ final SqlIntervalQualifier qualifier =
+ new SqlIntervalQualifier(unit, null, SqlParserPos.ZERO);
+ final RexNode op2 = cx.convertExpression(call.operand(2));
+ final RexNode op1 = cx.convertExpression(call.operand(1));
+ final RelDataType intervalType =
+ cx.getTypeFactory().createTypeWithNullability(
+ cx.getTypeFactory().createSqlIntervalType(qualifier),
+ op1.getType().isNullable() || op2.getType().isNullable());
+ final RexCall rexCall = (RexCall) rexBuilder.makeCall(
+ intervalType, SqlStdOperatorTable.MINUS_DATE,
+ ImmutableList.of(op2, op1));
+ final RelDataType intType =
+ cx.getTypeFactory().createTypeWithNullability(
+ cx.getTypeFactory().createSqlType(sqlTypeName),
+ SqlTypeUtil.containsNullable(rexCall.getType()));
+
+ RexNode e;
+
+ // Since Calcite converts internal time representation to seconds during cast we need our own cast
+ // method to keep fraction of seconds.
+ if (unit == TimeUnit.MILLISECOND)
+ e = makeCastMilliseconds(rexBuilder, intType, rexCall);
+ else
+ e = rexBuilder.makeCast(intType, rexCall);
+
+ return rexBuilder.multiplyDivide(e, multiplier, divider);
+ }
+
+ /**
+ * Creates a call to cast milliseconds interval.
+ */
+ static RexNode makeCastMilliseconds(RexBuilder builder, RelDataType type, RexNode exp) {
+ return builder.ensureType(type, builder.decodeIntervalOrDecimal(exp), false);
+ }
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java
index 5bd22df..7a331bd 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java
@@ -54,6 +54,16 @@ public class FunctionsTest extends GridCommonAbstractTest {
/** */
@Test
+ public void testTimestampDiffWithFractionsOfSecond() {
+ checkQuery("SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:28.000', " +
+ "TIMESTAMP '2022-02-01 10:30:28.128')").returns(128000).check();
+
+ checkQuery("SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:28.000', " +
+ "TIMESTAMP '2022-02-01 10:30:28.128')").returns(128000000L).check();
+ }
+
+ /** */
+ @Test
public void testLength() {
checkQuery("SELECT LENGTH('TEST')").returns(4).check();
checkQuery("SELECT LENGTH(NULL)").returns(NULL_RESULT).check();
diff --git a/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test b/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test
index 3d71ce6..6d3ef34 100644
--- a/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test
+++ b/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test
@@ -106,3 +106,33 @@ query T
SELECT TIMESTAMPDIFF(YEAR, TIMESTAMP '2022-02-02 11:32:26', TIMESTAMP '2099-01-01 10:30:25')
----
76
+
+query T
+SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:28.000', TIMESTAMP '2022-02-01 10:30:28.128')
+----
+128000
+
+query T
+SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:26.000', TIMESTAMP '2022-02-01 10:30:28.122')
+----
+2122000
+
+query T
+SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:28.000', TIMESTAMP '2022-02-01 10:30:28.128')
+----
+128000000
+
+query T
+SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:26.000', TIMESTAMP '2022-02-01 10:30:28.122')
+----
+2122000000
+
+query T
+SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:26.000', TIMESTAMP '2022-05-01 10:30:28.122')
+----
+7689602122000000
+
+query T
+SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-05-01 10:30:28.122', TIMESTAMP '2022-02-01 10:30:26.398')
+----
+-7689601724000000
diff --git a/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test_ignore b/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test_ignore
index 5bd500b..cd792fb 100644
--- a/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test_ignore
+++ b/modules/calcite/src/test/sql/function/timestamp/test_timestampdiff.test_ignore
@@ -1,17 +1,7 @@
# name: test/sql/function/test/test_timestampdiff.test
# description: Just simple test to check support of timestampdiff functions
# group: [timestamp]
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-15109
-
-query T
-SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:28.000', TIMESTAMP '2022-02-01 10:30:28.128')
-----
-128
-
-query T
-SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:26.000', TIMESTAMP '2022-02-01 10:30:28.122')
-----
-2122000
+# Ignored: https://issues.apache.org/jira/browse/IGNITE-15418
query T
SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:26.000', TIMESTAMP '2022-05-01 10:30:28.122')