You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mm...@apache.org on 2017/09/07 01:50:46 UTC
[1/4] calcite git commit: [CALCITE-1972] Create .sha512 and .md5
digests for release artifacts [Forced Update!]
Repository: calcite
Updated Branches:
refs/heads/branch-1.14 5410e79aa -> c0adb86b0 (forced update)
[CALCITE-1972] Create .sha512 and .md5 digests for release artifacts
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/f10950b5
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/f10950b5
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/f10950b5
Branch: refs/heads/branch-1.14
Commit: f10950b58b66954eadc7c275e9549fb9dcc02864
Parents: 3520913
Author: Michael Mior <mm...@uwaterloo.ca>
Authored: Thu Aug 31 05:01:10 2017 -0400
Committer: Michael Mior <mm...@uwaterloo.ca>
Committed: Mon Sep 4 10:54:48 2017 -0400
----------------------------------------------------------------------
pom.xml | 2 +-
site/_docs/howto.md | 7 -------
site/downloads/index.md | 4 ++--
3 files changed, 3 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/f10950b5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index cb83c87..46e4dd6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -914,7 +914,7 @@ limitations under the License.
<configuration>
<algorithms>
<algorithm>MD5</algorithm>
- <algorithm>SHA-1</algorithm>
+ <algorithm>SHA-256</algorithm>
</algorithms>
<failOnError>false</failOnError>
</configuration>
http://git-wip-us.apache.org/repos/asf/calcite/blob/f10950b5/site/_docs/howto.md
----------------------------------------------------------------------
diff --git a/site/_docs/howto.md b/site/_docs/howto.md
index 8a5617d..bf6682e 100644
--- a/site/_docs/howto.md
+++ b/site/_docs/howto.md
@@ -556,13 +556,6 @@ pushd ~/dist/dev
svn co https://dist.apache.org/repos/dist/dev/calcite
popd
-# Replace digest files with a single digest
-cd target
-for f in *.tar.gz *.zip; do
- rm ${f}.md5 ${f}.sha1
- gpg --print-mds ${f} > ${f}.mds
-done
-
# Move the files into a directory
mkdir ~/dist/dev/calcite/apache-calcite-X.Y.Z-rcN
mv apache-calcite-* ~/dist/dev/calcite/apache-calcite-X.Y.Z-rcN
http://git-wip-us.apache.org/repos/asf/calcite/blob/f10950b5/site/downloads/index.md
----------------------------------------------------------------------
diff --git a/site/downloads/index.md b/site/downloads/index.md
index 31cf3d0..6e79dbe 100644
--- a/site/downloads/index.md
+++ b/site/downloads/index.md
@@ -48,9 +48,9 @@ Release | Date | Commit | Download
{% endcomment %}{% assign d = "https://archive.apache.org/dist" %}{% comment %}
{% endcomment %}{% endif %}{% comment %}
{% endcomment %}{% capture d1 %}{{ post.date | date: "%F"}}{% endcapture %}{% comment %}
-{% endcomment %}{% capture d2 %}2016-06-13{% endcapture %}{% comment %}
+{% endcomment %}{% capture d2 %}2017-08-31{% endcapture %}{% comment %}
{% endcomment %}{% if d1 > d2 %}{% comment %}
-{% endcomment %}{% assign digest = "mds" %}{% comment %}
+{% endcomment %}{% assign digest = "sha256" %}{% comment %}
{% endcomment %}{% else %}{% comment %}
{% endcomment %}{% assign digest = "md5" %}{% comment %}
{% endcomment %}{% endif %}{% comment %}
[4/4] calcite git commit: [CALCITE-1970] Release Calcite 1.14.0
Posted by mm...@apache.org.
[CALCITE-1970] Release Calcite 1.14.0
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/c0adb86b
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/c0adb86b
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/c0adb86b
Branch: refs/heads/branch-1.14
Commit: c0adb86b053fc18cce4eb3ee9c390611c053c762
Parents: 939c9a6
Author: Michael Mior <mm...@uwaterloo.ca>
Authored: Mon Aug 28 17:03:37 2017 -0400
Committer: Michael Mior <mm...@uwaterloo.ca>
Committed: Wed Sep 6 21:49:53 2017 -0400
----------------------------------------------------------------------
README | 2 +-
pom.xml | 2 +-
site/_docs/history.md | 81 +++++++++++++++++++++++++++++++++++++++++++++-
site/_docs/howto.md | 4 +--
4 files changed, 84 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/c0adb86b/README
----------------------------------------------------------------------
diff --git a/README b/README
index 48b3d4a..b2cd7e3 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-Apache Calcite release 1.13.0
+Apache Calcite release 1.14.0
This is a source or binary distribution of Apache Calcite.
http://git-wip-us.apache.org/repos/asf/calcite/blob/c0adb86b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 46e4dd6..35d40dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,7 +49,7 @@ limitations under the License.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<top.dir>${project.basedir}</top.dir>
<version.major>1</version.major>
- <version.minor>13</version.minor>
+ <version.minor>14</version.minor>
<!-- This list is in alphabetical order. -->
<airlift-tpch.version>0.1</airlift-tpch.version>
http://git-wip-us.apache.org/repos/asf/calcite/blob/c0adb86b/site/_docs/history.md
----------------------------------------------------------------------
diff --git a/site/_docs/history.md b/site/_docs/history.md
index a3f1919..4fc374d 100644
--- a/site/_docs/history.md
+++ b/site/_docs/history.md
@@ -28,7 +28,17 @@ For a full list of releases, see
Downloads are available on the
[downloads page]({{ site.baseurl }}/downloads/).
-## <a href="https://github.com/apache/calcite/releases/tag/calcite-1.14.0">1.14.0</a> / under development
+## <a href="https://github.com/apache/calcite/releases/tag/calcite-1.15.0">1.15.0</a> / under development
+{: #v1-15-0}
+
+Compatibility: This release is tested
+on Linux, macOS, Microsoft Windows;
+using Oracle JDK 1.7, 1.8, 9;
+Guava versions 14.0 to 21.0;
+Druid version 0.10.0;
+other software versions as specified in `pom.xml`.
+
+## <a href="https://github.com/apache/calcite/releases/tag/calcite-1.14.0">1.14.0</a> / 2017-09-06
{: #v1-14-0}
Compatibility: This release is tested
@@ -38,6 +48,20 @@ Guava versions 14.0 to 21.0;
Druid version 0.10.0;
other software versions as specified in `pom.xml`.
+#### New features
+
+* [CALCITE-1968] OpenGIS Simple Feature Access SQL 1.2.1: add GEOMETRY data type and first 35 functions
+ Add Spatial page, document GIS functions in SQL reference (indicating
+ which ones are implemented), and add "countries" data set for testing.
+* [CALCITE-1967] Elasticsearch 5 adapter (Christian Beikov)
+* [CALCITE-1911] In MATCH_RECOGNIZE, support WITHIN sub-clause (Dian Fu)
+* [CALCITE-1897] Add '%' operator as an alternative to 'MOD' (sunjincheng)
+* [CALCITE-1787] Add ThetaSketch and HyperUnique support to Calcite via rolled up columns (Zain Humayun)
+* Vmstat table function for sqlsh
+* [CALCITE-1896] OS adapter and sqlsh
+* [CALCITE-1864] Allow NULL literal as argument
+* [CALCITE-1834] Allow user-defined functions to have arguments that are ARRAY or MULTISET (Ankit Singhal)
+
#### Bug-fixes, API changes and minor enhancements
* [<a href="https://issues.apache.org/jira/browse/CALCITE-1931">CALCITE-1931</a>]
@@ -46,6 +70,61 @@ other software versions as specified in `pom.xml`.
types: `RANK`, `DENSE_RANK`, and `NTILE` now return `BIGINT`;
`CUME_DIST` and `PERCENT_RANK` now return `DOUBLE`.
(**This is a breaking change**.)
+* [CALCITE-1947] Add time/timestamp with local time zone types to optimizer
+* [CALCITE-1972] Create .sha512 and .md5 digests for release artifacts
+* [CALCITE-1941] Refine interface Schema#snapshot()
+* [CALCITE-1069] In Aggregate, deprecate indicators, and allow GROUPING to be used as an aggregate function
+* [CALCITE-1969] Annotate user-defined functions as strict and semi-strict
+* [CALCITE-1945] Make return types of AVG, VARIANCE, STDDEV and COVAR customizable via RelDataTypeSystem
+* [CALCITE-1966] Allow normal views to act as materialization table (Christian Beikov)
+* [CALCITE-1953] Rewrite "NOT (x IS FALSE)" to "x IS NOT FALSE"; "x IS TRUE" would be wrong
+* [CALCITE-1943] Add back NavigationExpander and NavigationReplacer in SqlValidatorImpl (Dian Fu)
+* [CALCITE-1959] Reduce the amount of metadata and tableName calls in Druid (Zain Humayun)
+* [CALCITE-1963] Upgrade checkstyle, and fix code to comply
+* [CALCITE-1944] Window function applied to sub-query that returns dynamic star gets wrong plan (Volodymyr Vysotskyi)
+* [CALCITE-1954] Column from outer join should be null, whether or not it is aliased
+* [CALCITE-1959] Reduce the amount of metadata and tableName calls in Druid (Zain Humayun)
+* [CALCITE-1930] Fix AggregateExpandDistinctAggregatesRule when there are multiple AggregateCalls referring to the same input
+* [CALCITE-1936] Allow ROUND() and TRUNCATE() to take one operand, defaulting scale to 0
+* [CALCITE-1931] Change the return type of RANK and other aggregate functions
+* [CALCITE-1932] Project.getPermutation() should return null if not a permutation (e.g. repeated InputRef)
+* [CALCITE-1925] In JaninoRelMetadataProvider, cache null values (Ted Xu)
+* [CALCITE-1849] Support RexSubQuery in RelToSqlConverter
+* [CALCITE-1909] Output rowType of Match should include PARTITION BY and ORDER BY columns
+* [CALCITE-1929] Deprecate class RelDataTypeFactory.FieldInfoBuilder
+* [CALCITE-1895] MSSQL's SUBSTRING operator has different syntax (Chris Baynes)
+* [CALCITE-1919] NullPointerException when target in ReflectiveSchema belongs to root package (Lim Chee Hau)
+* [CALCITE-1901] SQL reference should say that "ONLY" is required after "FETCH ... ROWS"
+* [CALCITE-1907] Table function with 1 column gives ClassCastException
+* [CALCITE-1841] Create handlers for JDBC dialect-specific generated SQL (Chris Baynes)
+* [CALCITE-1898] LIKE must match '.' (period) literally
+* [CALCITE-1900] Detect cyclic views and give useful error message
+* [CALCITE-1886] Support "LIMIT [offset,] row_count", per MySQL (Kaiwang Chen)
+* [CALCITE-1893] Add MYSQL_5 conformance
+* [CALCITE-1883] HepPlanner should force garbage collect whenever a root registered (Ted Xu)
+* [CALCITE-1889] Accept compound identifiers in SqlValidatorUtil.checkIdentifierListForDuplicates() (Rajeshbabu Chintaguntla)
+* [CALCITE-1881] Can't distinguish overloaded user-defined functions that have DATE and TIMESTAMP arguments (余启)
+* [CALCITE-1803] Push Project that follows Aggregate down to Druid (Junxian Wu)
+* [CALCITE-1828] Push the FILTER clause into Druid as a Filtered Aggregator (Zain Humayun)
+* [CALCITE-1871] Nesting LAST within PREV is not parsed correctly for MATCH_RECOGNIZE
+* [CALCITE-1877] Move the Pig test data files into target for the test runtime
+* [CALCITE-1815] Switch Pig adapter to depend on avatica-core instead of full avatica
+* [CALCITE-1826] Generate dialect-specific SQL for FLOOR operator when in a GROUP BY (Chris Baynes)
+* [CALCITE-1842] Sort.computeSelfCost() calls makeCost() with arguments in wrong order (Junxian Wu)
+* [CALCITE-1874] In Frameworks, make SqlToRelConverter configurable
+* [CALCITE-1873] In a "GROUP BY ordinal" query, validator gives invalid "Expression is not being grouped" error if column has alias
+* [CALCITE-1833] User-defined aggregate functions with more than one parameter (hzyuemeng1)
+* [CALCITE-1845] Quantified comparison predicates (SOME, ANY, ALL)
+* [CALCITE-1860] Duplicate null predicates cause NullPointerException in RexUtil (Ruidong Li)
+* [CALCITE-1859] NPE in validate method of VolcanoPlanner
+* [CALCITE-1818] Handle SqlKind.DYNAMIC (parameters) in SqlImplementor (Dylan Adams)
+* [CALCITE-1709] Support mixing table columns with extended columns in DML (Rajeshbabu Chintaguntla)
+* [CALCITE-1856] Add option StructKind.PEEK_FIELDS_NO_EXPAND, similar to PEEK_FIELDS but is not expanded in "SELECT *" (Shuyi Chen)
+
+#### Web site and documentation
+
+* Add committer Chris Baynes
+* Add DataEngConf talk
## <a href="https://github.com/apache/calcite/releases/tag/calcite-1.13.0">1.13.0</a> / 2017-06-20
{: #v1-13-0}
http://git-wip-us.apache.org/repos/asf/calcite/blob/c0adb86b/site/_docs/howto.md
----------------------------------------------------------------------
diff --git a/site/_docs/howto.md b/site/_docs/howto.md
index bf6682e..aad2178 100644
--- a/site/_docs/howto.md
+++ b/site/_docs/howto.md
@@ -39,8 +39,8 @@ Unpack the source distribution `.tar.gz` or `.zip` file,
then build using maven:
{% highlight bash %}
-$ tar xvfz calcite-1.13.0-source.tar.gz
-$ cd calcite-1.13.0
+$ tar xvfz calcite-1.14.0-source.tar.gz
+$ cd calcite-1.14.0
$ mvn install
{% endhighlight %}
[2/4] calcite git commit: [CALCITE-1947] Add time/timestamp with
local time zone types to optimizer
Posted by mm...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
index 7cbcfa6..ccecc98 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -16,9 +16,11 @@
*/
package org.apache.calcite.test;
+import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.Strong;
import org.apache.calcite.rel.type.RelDataType;
@@ -27,6 +29,8 @@ import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexDynamicParam;
+import org.apache.calcite.rex.RexExecutor;
+import org.apache.calcite.rex.RexExecutorImpl;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexLocalRef;
@@ -35,6 +39,7 @@ import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.rex.RexSimplify;
import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSpecialOperator;
@@ -48,6 +53,7 @@ import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.TestUtil;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
@@ -65,6 +71,7 @@ import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
+import java.util.TimeZone;
import java.util.TreeMap;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -100,7 +107,9 @@ public class RexProgramTest {
public void setUp() {
typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
rexBuilder = new RexBuilder(typeFactory);
- simplify = new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR);
+ RexExecutor executor =
+ new RexExecutorImpl(new DummyTestDataContext());
+ simplify = new RexSimplify(rexBuilder, false, executor);
trueLiteral = rexBuilder.makeLiteral(true);
falseLiteral = rexBuilder.makeLiteral(false);
final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
@@ -108,6 +117,34 @@ public class RexProgramTest {
unknownLiteral = rexBuilder.makeNullLiteral(trueLiteral.getType());
}
+ /** Dummy data context for test. */
+ private static class DummyTestDataContext implements DataContext {
+ private final ImmutableMap<String, Object> map;
+
+ DummyTestDataContext() {
+ this.map =
+ ImmutableMap.<String, Object>of(
+ Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("America/Los_Angeles"),
+ Variable.CURRENT_TIMESTAMP.camelName, new Long(1311120000000L));
+ }
+
+ public SchemaPlus getRootSchema() {
+ return null;
+ }
+
+ public JavaTypeFactory getTypeFactory() {
+ return null;
+ }
+
+ public QueryProvider getQueryProvider() {
+ return null;
+ }
+
+ public Object get(String name) {
+ return map.get(name);
+ }
+ }
+
private void checkCnf(RexNode node, String expected) {
assertThat(RexUtil.toCnf(rexBuilder, node).toString(), equalTo(expected));
}
@@ -1491,6 +1528,95 @@ public class RexProgramTest {
"1970-01-01 00:00:00"); // different from Hive
}
+ @Test public void testSimplifyCastLiteral3() {
+ // Default TimeZone is "America/Los_Angeles" (DummyDataContext)
+ final RexLiteral literalDate = rexBuilder.makeDateLiteral(new DateString("2011-07-20"));
+ final RexLiteral literalTime = rexBuilder.makeTimeLiteral(new TimeString("12:34:56"), 0);
+ final RexLiteral literalTimestamp = rexBuilder.makeTimestampLiteral(
+ new TimestampString("2011-07-20 12:34:56"), 0);
+ final RexLiteral literalTimeLTZ =
+ rexBuilder.makeTimeWithLocalTimeZoneLiteral(
+ new TimeString(1, 23, 45), 0);
+ final RexLiteral timeLTZChar1 = rexBuilder.makeLiteral("12:34:45 America/Los_Angeles");
+ final RexLiteral timeLTZChar2 = rexBuilder.makeLiteral("12:34:45 UTC");
+ final RexLiteral timeLTZChar3 = rexBuilder.makeLiteral("12:34:45 GMT+01");
+ final RexLiteral timestampLTZChar1 = rexBuilder.makeLiteral("2011-07-20 12:34:56 Asia/Tokyo");
+ final RexLiteral timestampLTZChar2 = rexBuilder.makeLiteral("2011-07-20 12:34:56 GMT+01");
+ final RexLiteral timestampLTZChar3 = rexBuilder.makeLiteral("2011-07-20 12:34:56 UTC");
+ final RexLiteral literalTimestampLTZ =
+ rexBuilder.makeTimestampWithLocalTimeZoneLiteral(
+ new TimestampString(2011, 7, 20, 8, 23, 45), 0);
+
+ final RelDataType dateType =
+ typeFactory.createSqlType(SqlTypeName.DATE);
+ final RelDataType timeType =
+ typeFactory.createSqlType(SqlTypeName.TIME);
+ final RelDataType timestampType =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP);
+ final RelDataType timeLTZType =
+ typeFactory.createSqlType(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
+ final RelDataType timestampLTZType =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ final RelDataType varCharType =
+ typeFactory.createSqlType(SqlTypeName.VARCHAR, 40);
+
+ checkSimplify(cast(timeLTZChar1, timeLTZType), "20:34:45");
+ checkSimplify(cast(timeLTZChar2, timeLTZType), "12:34:45");
+ checkSimplify(cast(timeLTZChar3, timeLTZType), "11:34:45");
+ checkSimplify(cast(literalTimeLTZ, timeLTZType), "01:23:45");
+ checkSimplify(cast(timestampLTZChar1, timestampLTZType),
+ "2011-07-20 03:34:56");
+ checkSimplify(cast(timestampLTZChar2, timestampLTZType),
+ "2011-07-20 11:34:56");
+ checkSimplify(cast(timestampLTZChar3, timestampLTZType),
+ "2011-07-20 12:34:56");
+ checkSimplify(cast(literalTimestampLTZ, timestampLTZType),
+ "2011-07-20 08:23:45");
+ checkSimplify(cast(literalDate, timestampLTZType),
+ "2011-07-20 07:00:00");
+ checkSimplify(cast(literalTime, timestampLTZType),
+ "2011-07-20 19:34:56");
+ checkSimplify(cast(literalTimestamp, timestampLTZType),
+ "2011-07-20 19:34:56");
+ checkSimplify(cast(literalTimestamp, dateType),
+ "2011-07-20");
+ checkSimplify(cast(literalTimestampLTZ, dateType),
+ "2011-07-20");
+ checkSimplify(cast(literalTimestampLTZ, timeType),
+ "01:23:45");
+ checkSimplify(cast(literalTimestampLTZ, timestampType),
+ "2011-07-20 01:23:45");
+ checkSimplify(cast(literalTimeLTZ, timeType),
+ "17:23:45");
+ checkSimplify(cast(literalTime, timeLTZType),
+ "20:34:56");
+ checkSimplify(cast(literalTimestampLTZ, timeLTZType),
+ "08:23:45");
+ checkSimplify(cast(literalTimeLTZ, varCharType),
+ "'17:23:45 America/Los_Angeles'");
+ checkSimplify(cast(literalTimestampLTZ, varCharType),
+ "'2011-07-20 01:23:45 America/Los_Angeles'");
+ checkSimplify(cast(literalTimeLTZ, timestampType),
+ "2011-07-19 18:23:45");
+ checkSimplify(cast(literalTimeLTZ, timestampLTZType),
+ "2011-07-20 01:23:45");
+ }
+
+ @Test public void testCompareTimestampWithTimeZone() {
+ final TimestampWithTimeZoneString timestampLTZChar1 =
+ new TimestampWithTimeZoneString("2011-07-20 10:34:56 America/Los_Angeles");
+ final TimestampWithTimeZoneString timestampLTZChar2 =
+ new TimestampWithTimeZoneString("2011-07-20 19:34:56 Europe/Rome");
+ final TimestampWithTimeZoneString timestampLTZChar3 =
+ new TimestampWithTimeZoneString("2011-07-20 01:34:56 Asia/Tokyo");
+ final TimestampWithTimeZoneString timestampLTZChar4 =
+ new TimestampWithTimeZoneString("2011-07-20 10:34:56 America/Los_Angeles");
+
+ assertThat(timestampLTZChar1.equals(timestampLTZChar2), is(false));
+ assertThat(timestampLTZChar1.equals(timestampLTZChar3), is(false));
+ assertThat(timestampLTZChar1.equals(timestampLTZChar4), is(true));
+ }
+
@Test public void testSimplifyLiterals() {
final RexLiteral literalAbc = rexBuilder.makeLiteral("abc");
final RexLiteral literalDef = rexBuilder.makeLiteral("def");
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java
index 3be8d20..1951396 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidConnectionImpl.java
@@ -512,7 +512,7 @@ class DruidConnectionImpl implements DruidConnection {
JsonSegmentMetadata.class);
final List<JsonSegmentMetadata> list = mapper.readValue(in, listType);
in.close();
- fieldBuilder.put(timestampColumnName, SqlTypeName.TIMESTAMP);
+ fieldBuilder.put(timestampColumnName, SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
for (JsonSegmentMetadata o : list) {
for (Map.Entry<String, JsonColumn> entry : o.columns.entrySet()) {
if (entry.getKey().equals(DruidTable.DEFAULT_TIMESTAMP_COLUMN)) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java
index 0328882..fb69353 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.adapter.druid;
+import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.avatica.util.TimeUnitRange;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
@@ -26,6 +27,7 @@ import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
@@ -40,6 +42,7 @@ import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
+import java.util.TimeZone;
/**
* Utilities for generating intervals from RexNode.
@@ -57,9 +60,9 @@ public class DruidDateTimeUtils {
* expression. Assumes that all the predicates in the input
* reference a single column: the timestamp column.
*/
- public static List<LocalInterval> createInterval(RelDataType type,
- RexNode e) {
- final List<Range<TimestampString>> ranges = extractRanges(e, false);
+ public static List<LocalInterval> createInterval(RexNode e, String timeZone) {
+ final List<Range<TimestampString>> ranges =
+ extractRanges(e, TimeZone.getTimeZone(timeZone), false);
if (ranges == null) {
// We did not succeed, bail out
return null;
@@ -71,10 +74,12 @@ public class DruidDateTimeUtils {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Inferred ranges on interval : " + condensedRanges);
}
- return toInterval(ImmutableList.<Range>copyOf(condensedRanges.asRanges()));
+ return toInterval(
+ ImmutableList.<Range>copyOf(condensedRanges.asRanges()));
}
- protected static List<LocalInterval> toInterval(List<Range<TimestampString>> ranges) {
+ protected static List<LocalInterval> toInterval(
+ List<Range<TimestampString>> ranges) {
List<LocalInterval> intervals = Lists.transform(ranges,
new Function<Range<TimestampString>, LocalInterval>() {
public LocalInterval apply(Range<TimestampString> range) {
@@ -105,7 +110,7 @@ public class DruidDateTimeUtils {
}
protected static List<Range<TimestampString>> extractRanges(RexNode node,
- boolean withNot) {
+ TimeZone timeZone, boolean withNot) {
switch (node.getKind()) {
case EQUALS:
case LESS_THAN:
@@ -114,16 +119,17 @@ public class DruidDateTimeUtils {
case GREATER_THAN_OR_EQUAL:
case BETWEEN:
case IN:
- return leafToRanges((RexCall) node, withNot);
+ return leafToRanges((RexCall) node, timeZone, withNot);
case NOT:
- return extractRanges(((RexCall) node).getOperands().get(0), !withNot);
+ return extractRanges(((RexCall) node).getOperands().get(0), timeZone, !withNot);
case OR: {
RexCall call = (RexCall) node;
List<Range<TimestampString>> intervals = Lists.newArrayList();
for (RexNode child : call.getOperands()) {
- List<Range<TimestampString>> extracted = extractRanges(child, withNot);
+ List<Range<TimestampString>> extracted =
+ extractRanges(child, timeZone, withNot);
if (extracted != null) {
intervals.addAll(extracted);
}
@@ -135,7 +141,8 @@ public class DruidDateTimeUtils {
RexCall call = (RexCall) node;
List<Range<TimestampString>> ranges = new ArrayList<>();
for (RexNode child : call.getOperands()) {
- List<Range<TimestampString>> extractedRanges = extractRanges(child, false);
+ List<Range<TimestampString>> extractedRanges =
+ extractRanges(child, timeZone, false);
if (extractedRanges == null || extractedRanges.isEmpty()) {
// We could not extract, we bail out
return null;
@@ -163,7 +170,7 @@ public class DruidDateTimeUtils {
}
protected static List<Range<TimestampString>> leafToRanges(RexCall call,
- boolean withNot) {
+ TimeZone timeZone, boolean withNot) {
switch (call.getKind()) {
case EQUALS:
case LESS_THAN:
@@ -173,11 +180,11 @@ public class DruidDateTimeUtils {
{
final TimestampString value;
if (call.getOperands().get(0) instanceof RexInputRef
- && literalValue(call.getOperands().get(1)) != null) {
- value = literalValue(call.getOperands().get(1));
+ && literalValue(call.getOperands().get(1), timeZone) != null) {
+ value = literalValue(call.getOperands().get(1), timeZone);
} else if (call.getOperands().get(1) instanceof RexInputRef
- && literalValue(call.getOperands().get(0)) != null) {
- value = literalValue(call.getOperands().get(0));
+ && literalValue(call.getOperands().get(0), timeZone) != null) {
+ value = literalValue(call.getOperands().get(0), timeZone);
} else {
return null;
}
@@ -201,10 +208,10 @@ public class DruidDateTimeUtils {
{
final TimestampString value1;
final TimestampString value2;
- if (literalValue(call.getOperands().get(2)) != null
- && literalValue(call.getOperands().get(3)) != null) {
- value1 = literalValue(call.getOperands().get(2));
- value2 = literalValue(call.getOperands().get(3));
+ if (literalValue(call.getOperands().get(2), timeZone) != null
+ && literalValue(call.getOperands().get(3), timeZone) != null) {
+ value1 = literalValue(call.getOperands().get(2), timeZone);
+ value2 = literalValue(call.getOperands().get(3), timeZone);
} else {
return null;
}
@@ -219,9 +226,10 @@ public class DruidDateTimeUtils {
}
case IN:
{
- ImmutableList.Builder<Range<TimestampString>> ranges = ImmutableList.builder();
+ ImmutableList.Builder<Range<TimestampString>> ranges =
+ ImmutableList.builder();
for (RexNode operand : Util.skip(call.operands)) {
- final TimestampString element = literalValue(operand);
+ final TimestampString element = literalValue(operand, timeZone);
if (element == null) {
return null;
}
@@ -239,16 +247,24 @@ public class DruidDateTimeUtils {
}
}
- private static TimestampString literalValue(RexNode node) {
+ private static TimestampString literalValue(RexNode node, TimeZone timeZone) {
switch (node.getKind()) {
case LITERAL:
switch (((RexLiteral) node).getTypeName()) {
- case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return ((RexLiteral) node).getValueAs(TimestampString.class);
+ case TIMESTAMP:
+ // Cast timestamp to timestamp with local time zone
+ final TimestampString t = ((RexLiteral) node).getValueAs(TimestampString.class);
+ return new TimestampWithTimeZoneString(t.toString() + " " + timeZone.getID())
+ .withTimeZone(DateTimeUtils.UTC_ZONE).getLocalTimestampString();
case DATE:
- // For uniformity, treat dates as timestamps
+ // Cast date to timestamp with local time zone
final DateString d = ((RexLiteral) node).getValueAs(DateString.class);
- return TimestampString.fromMillisSinceEpoch(d.getMillisSinceEpoch());
+ return new TimestampWithTimeZoneString(
+ TimestampString.fromMillisSinceEpoch(
+ d.getMillisSinceEpoch()).toString() + " " + timeZone.getID())
+ .withTimeZone(DateTimeUtils.UTC_ZONE).getLocalTimestampString();
}
break;
case CAST:
@@ -262,11 +278,13 @@ public class DruidDateTimeUtils {
final RelDataType callType = call.getType();
final RelDataType operandType = operand.getType();
if (operand.getKind() == SqlKind.LITERAL
- && callType.getSqlTypeName() == SqlTypeName.TIMESTAMP
+ && callType.getSqlTypeName() == operandType.getSqlTypeName()
+ && (callType.getSqlTypeName() == SqlTypeName.DATE
+ || callType.getSqlTypeName() == SqlTypeName.TIMESTAMP
+ || callType.getSqlTypeName() == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
&& callType.isNullable()
- && operandType.getSqlTypeName() == SqlTypeName.TIMESTAMP
&& !operandType.isNullable()) {
- return literalValue(operand);
+ return literalValue(operand, timeZone);
}
}
return null;
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
index b12ad9a..b4a069b 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
@@ -512,7 +512,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
ImmutableBitSet numericCollationIndexes, Integer fetch, Project postProject) {
final CalciteConnectionConfig config = getConnectionConfig();
QueryType queryType = QueryType.SELECT;
- final Translator translator = new Translator(druidTable, rowType);
+ final Translator translator = new Translator(druidTable, rowType, config.timeZone());
List<String> fieldNames = rowType.getFieldNames();
Set<String> usedFieldNames = Sets.newHashSet(fieldNames);
@@ -564,7 +564,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
String extractColumnName = SqlValidatorUtil.uniquify(EXTRACT_COLUMN_NAME_PREFIX,
usedFieldNames, SqlValidatorUtil.EXPR_SUGGESTER);
timeExtractionDimensionSpec = TimeExtractionDimensionSpec.makeFullTimeExtract(
- extractColumnName);
+ extractColumnName, config.timeZone());
dimensions.add(timeExtractionDimensionSpec);
builder.add(extractColumnName);
assert timePositionIdx == -1;
@@ -587,7 +587,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
+ "_" + funcGranularity.value, usedFieldNames,
SqlValidatorUtil.EXPR_SUGGESTER);
timeExtractionDimensionSpec = TimeExtractionDimensionSpec.makeTimeExtract(
- funcGranularity, extractColumnName);
+ funcGranularity, extractColumnName, config.timeZone());
dimensions.add(timeExtractionDimensionSpec);
builder.add(extractColumnName);
break;
@@ -600,7 +600,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
SqlValidatorUtil.EXPR_SUGGESTER);
dimensions.add(
TimeExtractionDimensionSpec.makeTimeFloor(funcGranularity,
- extractColumnName));
+ extractColumnName, config.timeZone()));
finalGranularity = Granularity.ALL;
builder.add(extractColumnName);
} else {
@@ -632,7 +632,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
String extractColumnName = SqlValidatorUtil.uniquify(EXTRACT_COLUMN_NAME_PREFIX,
usedFieldNames, SqlValidatorUtil.EXPR_SUGGESTER);
timeExtractionDimensionSpec = TimeExtractionDimensionSpec.makeFullTimeExtract(
- extractColumnName);
+ extractColumnName, config.timeZone());
dimensions.add(timeExtractionDimensionSpec);
builder.add(extractColumnName);
assert timePositionIdx == -1;
@@ -1083,8 +1083,9 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
final List<String> metrics = new ArrayList<>();
final DruidTable druidTable;
final RelDataType rowType;
+ final String timeZone;
- Translator(DruidTable druidTable, RelDataType rowType) {
+ Translator(DruidTable druidTable, RelDataType rowType, String timeZone) {
this.druidTable = druidTable;
this.rowType = rowType;
for (RelDataTypeField f : rowType.getFieldList()) {
@@ -1096,6 +1097,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
dimensions.add(fieldName);
}
}
+ this.timeZone = timeZone;
}
protected void clearFieldNameLists() {
@@ -1169,7 +1171,8 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
// in case no extraction the field will be omitted from the serialization
ExtractionFunction extractionFunction = null;
if (granularity != null) {
- extractionFunction = TimeExtractionFunction.createExtractFromGranularity(granularity);
+ extractionFunction =
+ TimeExtractionFunction.createExtractFromGranularity(granularity, timeZone);
}
String dimName = tr(e, posRef);
if (dimName.equals(DruidConnectionImpl.DEFAULT_RESPONSE_TIMESTAMP_COLUMN)) {
@@ -1279,7 +1282,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
private ColumnMetaData.Rep getPrimitive(RelDataTypeField field) {
switch (field.getType().getSqlTypeName()) {
- case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP;
case BIGINT:
return ColumnMetaData.Rep.LONG;
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
index 343f03e..562e568 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
@@ -230,8 +230,8 @@ public class DruidRules {
List<LocalInterval> intervals = null;
if (!triple.getLeft().isEmpty()) {
intervals = DruidDateTimeUtils.createInterval(
- query.getRowType().getFieldList().get(timestampFieldIdx).getType(),
- RexUtil.composeConjunction(rexBuilder, triple.getLeft(), false));
+ RexUtil.composeConjunction(rexBuilder, triple.getLeft(), false),
+ cluster.getPlanner().getContext().unwrap(CalciteConnectionConfig.class).timeZone());
if (intervals == null || intervals.isEmpty()) {
// Case we have an filter with extract that can not be written as interval push down
triple.getMiddle().addAll(triple.getLeft());
@@ -579,7 +579,7 @@ public class DruidRules {
case MINUS:
case DIVIDE:
case TIMES:
- case CAST:
+ //case CAST:
return true;
default:
return false;
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java
index d636ce8..d34e000 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java
@@ -59,7 +59,7 @@ public class DruidTableFactory implements TableFactory {
} else {
timestampColumnName = DruidTable.DEFAULT_TIMESTAMP_COLUMN;
}
- fieldBuilder.put(timestampColumnName, SqlTypeName.TIMESTAMP);
+ fieldBuilder.put(timestampColumnName, SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
final Object dimensionsRaw = operand.get("dimensions");
if (dimensionsRaw instanceof List) {
// noinspection unchecked
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java
index 656ee77..7ef19a6 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java
@@ -34,9 +34,10 @@ public class TimeExtractionDimensionSpec extends ExtractionDimensionSpec {
*
* @return the time extraction DimensionSpec instance
*/
- public static TimeExtractionDimensionSpec makeFullTimeExtract(String outputName) {
+ public static TimeExtractionDimensionSpec makeFullTimeExtract(
+ String outputName, String timeZone) {
return new TimeExtractionDimensionSpec(
- TimeExtractionFunction.createDefault(), outputName);
+ TimeExtractionFunction.createDefault(timeZone), outputName);
}
/**
@@ -51,9 +52,9 @@ public class TimeExtractionDimensionSpec extends ExtractionDimensionSpec {
* is not supported
*/
public static TimeExtractionDimensionSpec makeTimeExtract(
- Granularity granularity, String outputName) {
+ Granularity granularity, String outputName, String timeZone) {
return new TimeExtractionDimensionSpec(
- TimeExtractionFunction.createExtractFromGranularity(granularity), outputName);
+ TimeExtractionFunction.createExtractFromGranularity(granularity, timeZone), outputName);
}
/**
@@ -64,8 +65,9 @@ public class TimeExtractionDimensionSpec extends ExtractionDimensionSpec {
* @return floor time extraction DimensionSpec instance.
*/
public static TimeExtractionDimensionSpec makeTimeFloor(Granularity granularity,
- String outputName) {
- ExtractionFunction fn = TimeExtractionFunction.createFloorFromGranularity(granularity);
+ String outputName, String timeZone) {
+ ExtractionFunction fn =
+ TimeExtractionFunction.createFloorFromGranularity(granularity, timeZone);
return new TimeExtractionDimensionSpec(fn, outputName);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java
index 22733be..b1f8870 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java
@@ -75,8 +75,8 @@ public class TimeExtractionFunction implements ExtractionFunction {
*
* @return the time extraction function
*/
- public static TimeExtractionFunction createDefault() {
- return new TimeExtractionFunction(ISO_TIME_FORMAT, null, "UTC", null);
+ public static TimeExtractionFunction createDefault(String timeZone) {
+ return new TimeExtractionFunction(ISO_TIME_FORMAT, null, timeZone, null);
}
/**
@@ -87,16 +87,18 @@ public class TimeExtractionFunction implements ExtractionFunction {
* @return the time extraction function corresponding to the granularity input unit
* {@link TimeExtractionFunction#VALID_TIME_EXTRACT} for supported granularity
*/
- public static TimeExtractionFunction createExtractFromGranularity(Granularity granularity) {
+ public static TimeExtractionFunction createExtractFromGranularity(
+ Granularity granularity, String timeZone) {
switch (granularity) {
case DAY:
- return new TimeExtractionFunction("d", null, "UTC", Locale.getDefault().toLanguageTag());
+ return new TimeExtractionFunction("d", null, timeZone, Locale.getDefault().toLanguageTag());
case MONTH:
- return new TimeExtractionFunction("M", null, "UTC", Locale.getDefault().toLanguageTag());
+ return new TimeExtractionFunction("M", null, timeZone, Locale.getDefault().toLanguageTag());
case YEAR:
- return new TimeExtractionFunction("yyyy", null, "UTC", Locale.getDefault().toLanguageTag());
+ return new TimeExtractionFunction("yyyy", null, timeZone,
+ Locale.getDefault().toLanguageTag());
case WEEK:
- return new TimeExtractionFunction("w", null, "UTC", Locale.getDefault().toLanguageTag());
+ return new TimeExtractionFunction("w", null, timeZone, Locale.getDefault().toLanguageTag());
default:
throw new IllegalArgumentException("Granularity [" + granularity + "] is not supported");
}
@@ -108,8 +110,9 @@ public class TimeExtractionFunction implements ExtractionFunction {
* @param granularity granularity to apply to the column
* @return the time extraction function or null if granularity is not supported
*/
- public static TimeExtractionFunction createFloorFromGranularity(Granularity granularity) {
- return new TimeExtractionFunction(ISO_TIME_FORMAT, granularity.value, "UTC", Locale
+ public static TimeExtractionFunction createFloorFromGranularity(
+ Granularity granularity, String timeZone) {
+ return new TimeExtractionFunction(ISO_TIME_FORMAT, granularity.value, timeZone, Locale
.getDefault().toLanguageTag());
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/test/java/org/apache/calcite/adapter/druid/DruidQueryFilterTest.java
----------------------------------------------------------------------
diff --git a/druid/src/test/java/org/apache/calcite/adapter/druid/DruidQueryFilterTest.java b/druid/src/test/java/org/apache/calcite/adapter/druid/DruidQueryFilterTest.java
index b2e8635..e8e42be 100644
--- a/druid/src/test/java/org/apache/calcite/adapter/druid/DruidQueryFilterTest.java
+++ b/druid/src/test/java/org/apache/calcite/adapter/druid/DruidQueryFilterTest.java
@@ -121,7 +121,7 @@ public class DruidQueryFilterTest {
.add("dimensionName", varcharType)
.build();
final DruidQuery.Translator translatorStringKind =
- new DruidQuery.Translator(druidTable, varcharRowType);
+ new DruidQuery.Translator(druidTable, varcharRowType, "UTC");
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
----------------------------------------------------------------------
diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
index e88aaed..1a0d3d3 100644
--- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
+++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
@@ -235,12 +235,11 @@ public class DruidAdapterIT {
@Test public void testSelectTimestampColumnNoTables2() {
// Since columns are not explicitly declared, we use the default time
// column in the query.
- final String sql = "select \"__time\"\n"
+ final String sql = "select cast(\"__time\" as timestamp) as \"__time\"\n"
+ "from \"wikiticker\"\n"
+ "limit 1\n";
- final String explain = "PLAN="
- + "EnumerableInterpreter\n"
- + " DruidQuery(table=[[wiki, wikiticker]], intervals=[[1900-01-01T00:00:00.000/3000-01-01T00:00:00.000]], projects=[[$0]], fetch=[1])\n";
+ final String explain =
+ "DruidQuery(table=[[wiki, wikiticker]], intervals=[[1900-01-01T00:00:00.000/3000-01-01T00:00:00.000]], projects=[[$0]], fetch=[1])\n";
final String druidQuery = "{'queryType':'select',"
+ "'dataSource':'wikiticker','descending':false,"
+ "'intervals':['1900-01-01T00:00:00.000/3000-01-01T00:00:00.000'],"
@@ -255,12 +254,12 @@ public class DruidAdapterIT {
@Test public void testSelectTimestampColumnNoTables3() {
// Since columns are not explicitly declared, we use the default time
// column in the query.
- final String sql = "select floor(\"__time\" to DAY) as \"day\", sum(\"added\")\n"
+ final String sql =
+ "select cast(floor(\"__time\" to DAY) as timestamp) as \"day\", sum(\"added\")\n"
+ "from \"wikiticker\"\n"
+ "group by floor(\"__time\" to DAY)";
- final String explain = "PLAN="
- + "EnumerableInterpreter\n"
- + " DruidQuery(table=[[wiki, wikiticker]], intervals=[[1900-01-01T00:00:00.000/3000-01-01T00:00:00.000]], projects=[[FLOOR($0, FLAG(DAY)), $1]], groups=[{0}], aggs=[[SUM($1)]])\n";
+ final String explain =
+ "DruidQuery(table=[[wiki, wikiticker]], intervals=[[1900-01-01T00:00:00.000/3000-01-01T00:00:00.000]], projects=[[FLOOR($0, FLAG(DAY)), $1]], groups=[{0}], aggs=[[SUM($1)]])\n";
final String druidQuery = "{'queryType':'timeseries',"
+ "'dataSource':'wikiticker','descending':false,'granularity':'day',"
+ "'aggregations':[{'type':'longSum','name':'EXPR$1','fieldName':'added'}],"
@@ -276,12 +275,12 @@ public class DruidAdapterIT {
// Since columns are not explicitly declared, we use the default time
// column in the query.
final String sql = "select sum(\"added\") as \"s\", \"page\", "
- + "floor(\"__time\" to DAY) as \"day\"\n"
+ + "cast(floor(\"__time\" to DAY) as timestamp) as \"day\"\n"
+ "from \"wikiticker\"\n"
+ "group by \"page\", floor(\"__time\" to DAY)\n"
+ "order by \"s\" desc";
final String explain = "PLAN=EnumerableInterpreter\n"
- + " BindableProject(s=[$2], page=[$0], day=[$1])\n"
+ + " BindableProject(s=[$2], page=[$0], day=[CAST($1):TIMESTAMP(0)])\n"
+ " DruidQuery(table=[[wiki, wikiticker]], "
+ "intervals=[[1900-01-01T00:00:00.000/3000-01-01T00:00:00.000]], projects=[[$17, FLOOR"
+ "($0, FLAG(DAY)), $1]], groups=[{0, 1}], aggs=[[SUM($2)]], sort0=[2], dir0=[DESC])";
@@ -296,7 +295,8 @@ public class DruidAdapterIT {
}
@Test public void testSkipEmptyBuckets() {
- final String sql = "select floor(\"__time\" to SECOND) as \"second\", sum(\"added\")\n"
+ final String sql =
+ "select cast(floor(\"__time\" to SECOND) as timestamp) as \"second\", sum(\"added\")\n"
+ "from \"wikiticker\"\n"
+ "where \"page\" = 'Jeremy Corbyn'\n"
+ "group by floor(\"__time\" to SECOND)";
@@ -334,12 +334,10 @@ public class DruidAdapterIT {
* Druid adapter: Send timestamp literals to Druid as local time, not
* UTC</a>. */
@Test public void testFilterTime() {
- final String sql = "select \"__time\"\n"
+ final String sql = "select cast(\"__time\" as timestamp) as \"__time\"\n"
+ "from \"wikiticker\"\n"
- + "where \"__time\" < '2015-10-12 00:00:00'";
- final String explain = "PLAN="
- + "EnumerableInterpreter\n"
- + " DruidQuery(table=[[wiki, wikiticker]], "
+ + "where \"__time\" < '2015-10-12 00:00:00 UTC'";
+ final String explain = "\n DruidQuery(table=[[wiki, wikiticker]], "
+ "intervals=[[1900-01-01T00:00:00.000/2015-10-12T00:00:00.000]], "
+ "projects=[[$0]])\n";
final String druidQuery = "{'queryType':'select',"
@@ -357,12 +355,14 @@ public class DruidAdapterIT {
}
@Test public void testFilterTimeDistinct() {
- final String sql = "select distinct \"__time\"\n"
+ final String sql = "select CAST(\"c1\" AS timestamp) as \"__time\" from\n"
+ + "(select distinct \"__time\" as \"c1\"\n"
+ "from \"wikiticker\"\n"
- + "where \"__time\" < '2015-10-12 00:00:00'";
+ + "where \"__time\" < '2015-10-12 00:00:00 UTC')";
final String explain = "PLAN="
+ "EnumerableInterpreter\n"
- + " DruidQuery(table=[[wiki, wikiticker]], "
+ + " BindableProject(__time=[CAST($0):TIMESTAMP(0)])\n"
+ + " DruidQuery(table=[[wiki, wikiticker]], "
+ "intervals=[[1900-01-01T00:00:00.000/2015-10-12T00:00:00.000]], "
+ "groups=[{0}], aggs=[[]])\n";
final String subDruidQuery = "{'queryType':'groupBy','dataSource':'wikiticker',"
@@ -371,10 +371,10 @@ public class DruidAdapterIT {
+ "'extractionFn':{'type':'timeFormat'";
sql(sql, WIKI_AUTO2)
.limit(2)
- .returnsUnordered("__time=2015-09-12 00:46:58",
- "__time=2015-09-12 00:47:00")
.explainContains(explain)
- .queryContains(druidChecker(subDruidQuery));
+ .queryContains(druidChecker(subDruidQuery))
+ .returnsUnordered("__time=2015-09-12 00:46:58",
+ "__time=2015-09-12 00:47:00");
}
@Test public void testMetadataColumns() throws Exception {
@@ -390,10 +390,11 @@ public class DruidAdapterIT {
while (r.next()) {
map.put(r.getString("TYPE_NAME"), true);
}
+ System.out.println(map);
// 1 timestamp, 2 float measure, 1 int measure, 88 dimensions
assertThat(map.keySet().size(), is(4));
assertThat(map.values().size(), is(92));
- assertThat(map.get("TIMESTAMP(0)").size(), is(1));
+ assertThat(map.get("TIMESTAMP_WITH_LOCAL_TIME_ZONE(0)").size(), is(1));
assertThat(map.get("DOUBLE").size(), is(2));
assertThat(map.get("BIGINT").size(), is(1));
assertThat(map.get(VARCHAR_TYPE).size(), is(88));
@@ -689,18 +690,14 @@ public class DruidAdapterIT {
* <p>Before CALCITE-1578 was fixed, this would use a "topN" query but return
* the wrong results. */
@Test public void testGroupByDaySortDescLimit() {
- final String sql = "select \"brand_name\", floor(\"timestamp\" to DAY) as d,"
+ final String sql = "select \"brand_name\","
+ + " cast(floor(\"timestamp\" to DAY) as timestamp) as d,"
+ " sum(\"unit_sales\") as s\n"
+ "from \"foodmart\"\n"
+ "group by \"brand_name\", floor(\"timestamp\" to DAY)\n"
+ "order by s desc limit 30";
- final String druidQuery = "{'queryType':'groupBy','dataSource':'foodmart',"
- + "'granularity':'day','dimensions':[{'type':'default','dimension':'brand_name'}],"
- + "'limitSpec':{'type':'default'},"
- + "'aggregations':[{'type':'longSum','name':'S','fieldName':'unit_sales'}],"
- + "'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000']}";
- final String explain = "PLAN=EnumerableInterpreter\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ final String explain =
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[$2, FLOOR"
+ "($0, FLAG(DAY)), $89]], groups=[{0, 1}], aggs=[[SUM($2)]], sort0=[2], dir0=[DESC], "
+ "fetch=[30])";
@@ -725,7 +722,8 @@ public class DruidAdapterIT {
* wrongly try to use a {@code limitSpec} to sort and filter. (A "topN" query
* was not possible because the sort was {@code ASC}.) */
@Test public void testGroupByDaySortLimit() {
- final String sql = "select \"brand_name\", floor(\"timestamp\" to DAY) as d,"
+ final String sql = "select \"brand_name\","
+ + " cast(floor(\"timestamp\" to DAY) as timestamp) as d,"
+ " sum(\"unit_sales\") as s\n"
+ "from \"foodmart\"\n"
+ "group by \"brand_name\", floor(\"timestamp\" to DAY)\n"
@@ -739,8 +737,7 @@ public class DruidAdapterIT {
+ "'dimensionOrder':'numeric'}]},'aggregations':[{'type':'longSum',"
+ "'name':'S','fieldName':'unit_sales'}],"
+ "'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000']}";
- final String explain = "PLAN=EnumerableInterpreter\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ final String explain = "DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[$2, FLOOR"
+ "($0, FLAG(DAY)), $89]], groups=[{0, 1}], aggs=[[SUM($2)]], sort0=[2], dir0=[DESC], "
+ "fetch=[30])";
@@ -757,7 +754,8 @@ public class DruidAdapterIT {
* <a href="https://issues.apache.org/jira/browse/CALCITE-1580">[CALCITE-1580]
* Druid adapter: Wrong semantics for ordering within groupBy queries</a>. */
@Test public void testGroupByDaySortDimension() {
- final String sql = "select \"brand_name\", floor(\"timestamp\" to DAY) as d,"
+ final String sql =
+ "select \"brand_name\", cast(floor(\"timestamp\" to DAY) as timestamp) as d,"
+ " sum(\"unit_sales\") as s\n"
+ "from \"foodmart\"\n"
+ "group by \"brand_name\", floor(\"timestamp\" to DAY)\n"
@@ -766,8 +764,7 @@ public class DruidAdapterIT {
+ "'granularity':'all','dimensions':[{'type':'default',"
+ "'dimension':'brand_name'},{'type':'extraction','dimension':'__time',"
+ "'outputName':'floor_day','extractionFn':{'type':'timeFormat'";
- final String explain = "PLAN=EnumerableInterpreter\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ final String explain = " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[$2, FLOOR"
+ "($0, FLAG(DAY)), $89]], groups=[{0, 1}], aggs=[[SUM($2)]], sort0=[0], dir0=[ASC])";
sql(sql)
@@ -790,8 +787,7 @@ public class DruidAdapterIT {
+ "'filter':{'type':'and','fields':["
+ "{'type':'bound','dimension':'product_id','lower':'1500','lowerStrict':false,'ordering':'lexicographic'},"
+ "{'type':'bound','dimension':'product_id','upper':'1502','upperStrict':false,'ordering':'lexicographic'}]},"
- + "'dimensions':['product_name','state_province','product_id'],"
- + "'metrics':[],'granularity':'all',"
+ + "'dimensions':['product_name','state_province','product_id'],'metrics':[],'granularity':'all',"
+ "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}";
sql(sql)
.limit(4)
@@ -819,13 +815,13 @@ public class DruidAdapterIT {
final String sql = "select \"product_name\" from \"foodmart\"\n"
+ "where \"product_id\" BETWEEN 1500 AND 1502\n"
+ "order by \"state_province\" desc, \"product_id\"";
- final String druidQuery = "{'queryType':'select','dataSource':'foodmart','descending':false,"
- + "'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000'],'filter':{'type':"
- + "'and','fields':[{'type':'bound','dimension':'product_id','lower':'1500',"
- + "'lowerStrict':false,'ordering':'numeric'},{'type':'bound','dimension':'product_id',"
- + "'upper':'1502','upperStrict':false,'ordering':'numeric'}]},'dimensions':"
- + "['product_name','state_province','product_id'],'metrics':[],'granularity':'all','pagingSpec':"
- + "{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}";
+ final String druidQuery = "{'queryType':'select','dataSource':'foodmart',"
+ + "'descending':false,'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000'],"
+ + "'filter':{'type':'and','fields':["
+ + "{'type':'bound','dimension':'product_id','lower':'1500','lowerStrict':false,'ordering':'numeric'},"
+ + "{'type':'bound','dimension':'product_id','upper':'1502','upperStrict':false,'ordering':'numeric'}]},"
+ + "'dimensions':['product_name','state_province','product_id'],'metrics':[],'granularity':'all',"
+ + "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}";
sql(sql)
.limit(4)
.returns(
@@ -854,8 +850,7 @@ public class DruidAdapterIT {
final String druidQuery = "{'queryType':'select','dataSource':'foodmart',"
+ "'descending':false,'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000'],"
+ "'filter':{'type':'selector','dimension':'product_id','value':'-1'},"
- + "'dimensions':['product_name'],"
- + "'metrics':[],'granularity':'all',"
+ + "'dimensions':['product_name'],'metrics':[],'granularity':'all',"
+ "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}";
sql(sql)
.limit(4)
@@ -871,8 +866,7 @@ public class DruidAdapterIT {
+ "order by \"state_province\" desc, \"product_id\"";
final String druidQuery = "{'queryType':'select','dataSource':'foodmart',"
+ "'descending':false,'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000'],"
- + "'dimensions':['product_id','product_name','state_province'],"
- + "'metrics':[],'granularity':'all',"
+ + "'dimensions':['product_id','product_name','state_province'],'metrics':[],'granularity':'all',"
+ "'pagingSpec':{'threshold':16384,'fromNext':true},'context':{'druid.query.fetch':false}}";
sql(sql)
.limit(4)
@@ -959,7 +953,8 @@ public class DruidAdapterIT {
* "topN" because we have a global limit, and that requires
* {@code granularity: all}. */
@Test public void testGroupByTimeAndOneColumnNotProjectedWithLimit() {
- final String sql = "select count(*) as \"c\", floor(\"timestamp\" to MONTH) as \"month\"\n"
+ final String sql = "select count(*) as \"c\","
+ + " cast(floor(\"timestamp\" to MONTH) as timestamp) as \"month\"\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH), \"state_province\"\n"
+ "order by \"c\" desc limit 3";
@@ -972,7 +967,7 @@ public class DruidAdapterIT {
@Test public void testGroupByTimeAndOneMetricNotProjected() {
final String sql =
- "select count(*) as \"c\", floor(\"timestamp\" to MONTH) as \"month\", floor"
+ "select count(*) as \"c\", cast(floor(\"timestamp\" to MONTH) as timestamp) as \"month\", floor"
+ "(\"store_sales\") as sales\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH), \"state_province\", floor"
@@ -986,7 +981,7 @@ public class DruidAdapterIT {
@Test public void testGroupByTimeAndOneColumnNotProjected() {
final String sql = "select count(*) as \"c\",\n"
- + " floor(\"timestamp\" to MONTH) as \"month\"\n"
+ + " cast(floor(\"timestamp\" to MONTH) as timestamp) as \"month\"\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH), \"state_province\"\n"
+ "having count(*) > 3500";
@@ -1066,46 +1061,45 @@ public class DruidAdapterIT {
* <a href="https://issues.apache.org/jira/browse/CALCITE-1577">[CALCITE-1577]
* Druid adapter: Incorrect result - limit on timestamp disappears</a>. */
@Test public void testGroupByMonthGranularitySort() {
- final String sql = "select floor(\"timestamp\" to MONTH) as m,\n"
- + " sum(\"unit_sales\") as s,\n"
+ final String sql = "select sum(\"unit_sales\") as s,\n"
+ " count(\"store_sqft\") as c\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH)\n"
+ "order by floor(\"timestamp\" to MONTH) ASC";
final String explain = "PLAN=EnumerableInterpreter\n"
- + " BindableSort(sort0=[$0], dir0=[ASC])\n"
- + " BindableAggregate(group=[{0}], S=[SUM($1)], C=[COUNT($2)])\n"
- + " BindableProject(M=[FLOOR($0, FLAG(MONTH))], unit_sales=[$2], store_sqft=[$1])\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ + " BindableSort(sort0=[$2], dir0=[ASC])\n"
+ + " BindableProject(S=[$1], C=[$2], EXPR$2=[$0])\n"
+ + " BindableAggregate(group=[{0}], S=[SUM($1)], C=[COUNT($2)])\n"
+ + " BindableProject($f0=[FLOOR($0, FLAG(MONTH))], unit_sales=[$2], store_sqft=[$1])\n"
+ + " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[$0, $71, $89]])";
sql(sql)
- .returnsOrdered("M=1997-01-01 00:00:00; S=21628; C=5957",
- "M=1997-02-01 00:00:00; S=20957; C=5842",
- "M=1997-03-01 00:00:00; S=23706; C=6528",
- "M=1997-04-01 00:00:00; S=20179; C=5523",
- "M=1997-05-01 00:00:00; S=21081; C=5793",
- "M=1997-06-01 00:00:00; S=21350; C=5863",
- "M=1997-07-01 00:00:00; S=23763; C=6762",
- "M=1997-08-01 00:00:00; S=21697; C=5915",
- "M=1997-09-01 00:00:00; S=20388; C=5591",
- "M=1997-10-01 00:00:00; S=19958; C=5606",
- "M=1997-11-01 00:00:00; S=25270; C=7026",
- "M=1997-12-01 00:00:00; S=26796; C=7338")
- .explainContains(explain);
+ .explainContains(explain)
+ .returnsOrdered("S=21628; C=5957",
+ "S=20957; C=5842",
+ "S=23706; C=6528",
+ "S=20179; C=5523",
+ "S=21081; C=5793",
+ "S=21350; C=5863",
+ "S=23763; C=6762",
+ "S=21697; C=5915",
+ "S=20388; C=5591",
+ "S=19958; C=5606",
+ "S=25270; C=7026",
+ "S=26796; C=7338");
}
@Test public void testGroupByMonthGranularitySortLimit() {
- final String sql = "select floor(\"timestamp\" to MONTH) as m,\n"
+ final String sql = "select cast(floor(\"timestamp\" to MONTH) as timestamp) as m,\n"
+ " sum(\"unit_sales\") as s,\n"
+ " count(\"store_sqft\") as c\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH)\n"
+ "order by floor(\"timestamp\" to MONTH) limit 3";
- final String explain = "PLAN=EnumerableInterpreter\n"
- + " BindableSort(sort0=[$0], dir0=[ASC], fetch=[3])\n"
- + " BindableAggregate(group=[{0}], S=[SUM($1)], C=[COUNT($2)])\n"
- + " BindableProject(M=[FLOOR($0, FLAG(MONTH))], unit_sales=[$2], store_sqft=[$1])\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ final String explain = "BindableSort(sort0=[$0], dir0=[ASC], fetch=[3])\n"
+ + " BindableAggregate(group=[{0}], S=[SUM($1)], C=[COUNT($2)])\n"
+ + " BindableProject($f0=[FLOOR($0, FLAG(MONTH))], unit_sales=[$2], store_sqft=[$1])\n"
+ + " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[$0, $71, $89]])";
sql(sql)
.returnsOrdered("M=1997-01-01 00:00:00; S=21628; C=5957",
@@ -1122,16 +1116,16 @@ public class DruidAdapterIT {
String druidQuery = "{'queryType':'select','dataSource':'foodmart'";
sql(sql)
.limit(3)
- .returnsUnordered("S=1244; C=391", "S=550; C=112", "S=580; C=171")
- .queryContains(druidChecker(druidQuery));
+ .queryContains(druidChecker(druidQuery))
+ .returnsUnordered("S=1244; C=391", "S=550; C=112", "S=580; C=171");
}
@Test public void testGroupByMonthGranularityFiltered() {
final String sql = "select sum(\"unit_sales\") as s,\n"
+ " count(\"store_sqft\") as c\n"
+ "from \"foodmart\"\n"
- + "where \"timestamp\" >= '1996-01-01 00:00:00' and "
- + " \"timestamp\" < '1998-01-01 00:00:00'\n"
+ + "where \"timestamp\" >= '1996-01-01 00:00:00 UTC' and "
+ + " \"timestamp\" < '1998-01-01 00:00:00 UTC'\n"
+ "group by floor(\"timestamp\" to MONTH)";
String druidQuery = "{'queryType':'select','dataSource':'foodmart'";
sql(sql)
@@ -1179,8 +1173,8 @@ public class DruidAdapterIT {
+ "max(\"unit_sales\") as m,\n"
+ "\"state_province\" as p\n"
+ "from \"foodmart\"\n"
- + "where \"timestamp\" >= '1997-01-01 00:00:00' and "
- + " \"timestamp\" < '1997-09-01 00:00:00'\n"
+ + "where \"timestamp\" >= '1997-01-01 00:00:00 UTC' and "
+ + " \"timestamp\" < '1997-09-01 00:00:00 UTC'\n"
+ "group by \"state_province\", floor(\"timestamp\" to DAY)\n"
+ "order by s desc limit 6";
final String explain = "PLAN=EnumerableInterpreter\n"
@@ -1383,12 +1377,11 @@ public class DruidAdapterIT {
+ "from \"foodmart\"\n"
+ "where extract(year from \"timestamp\") = 1997\n"
+ "and extract(month from \"timestamp\") in (4, 6)\n";
- final String explain = "PLAN=EnumerableInterpreter\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ final String explain = "DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[AND(="
- + "(EXTRACT_DATE(FLAG(YEAR), /INT(Reinterpret($0), 86400000)), 1997), OR(=(EXTRACT_DATE"
- + "(FLAG(MONTH), /INT(Reinterpret($0), 86400000)), 4), =(EXTRACT_DATE(FLAG(MONTH), /INT"
- + "(Reinterpret($0), 86400000)), 6)))], groups=[{}], aggs=[[COUNT()]])";
+ + "(EXTRACT_DATE(FLAG(YEAR), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), 1997), OR(=(EXTRACT_DATE"
+ + "(FLAG(MONTH), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), 4), =(EXTRACT_DATE(FLAG(MONTH), "
+ + "/INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), 6)))], groups=[{}], aggs=[[COUNT()]])";
sql(sql)
.explainContains(explain)
.returnsUnordered("C=13500");
@@ -1430,17 +1423,17 @@ public class DruidAdapterIT {
@Test public void testFieldBasedCostColumnPruning() {
// A query where filter cannot be pushed to Druid but
// the project can still be pushed in order to prune extra columns.
- String sql = "select \"countryName\", floor(\"time\" to DAY),\n"
+ String sql = "select \"countryName\", floor(CAST(\"time\" AS TIMESTAMP) to DAY),\n"
+ " cast(count(*) as integer) as c\n"
+ "from \"wiki\"\n"
- + "where floor(\"time\" to DAY) >= '1997-01-01 00:00:00'\n"
- + "and floor(\"time\" to DAY) < '1997-09-01 00:00:00'\n"
- + "group by \"countryName\", floor(\"time\" TO DAY)\n"
+ + "where floor(\"time\" to DAY) >= '1997-01-01 00:00:00 UTC'\n"
+ + "and floor(\"time\" to DAY) < '1997-09-01 00:00:00 UTC'\n"
+ + "group by \"countryName\", floor(CAST(\"time\" AS TIMESTAMP) TO DAY)\n"
+ "order by c limit 5";
String plan = "BindableProject(countryName=[$0], EXPR$1=[$1], C=[CAST($2):INTEGER NOT NULL])\n"
+ " BindableSort(sort0=[$2], dir0=[ASC], fetch=[5])\n"
+ " BindableAggregate(group=[{0, 1}], agg#0=[COUNT()])\n"
- + " BindableProject(countryName=[$1], EXPR$1=[FLOOR($0, FLAG(DAY))])\n"
+ + " BindableProject(countryName=[$1], EXPR$1=[FLOOR(CAST($0):TIMESTAMP(0), FLAG(DAY))])\n"
+ " BindableFilter(condition=[AND(>=(FLOOR($0, FLAG(DAY)), 1997-01-01 00:00:00), <(FLOOR($0, FLAG(DAY)), 1997-09-01 00:00:00))])\n"
+ " DruidQuery(table=[[wiki, wiki]], intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[$0, $5]])";
// NOTE: Druid query only has countryName as the dimension
@@ -1459,10 +1452,11 @@ public class DruidAdapterIT {
}
@Test public void testGroupByMetricAndExtractTime() {
- final String sql = "SELECT count(*), floor(\"timestamp\" to DAY), \"store_sales\" "
- + "FROM \"foodmart\"\n"
- + "GROUP BY \"store_sales\", floor(\"timestamp\" to DAY)\n ORDER BY \"store_sales\" DESC\n"
- + "LIMIT 10\n";
+ final String sql =
+ "SELECT count(*), cast(floor(\"timestamp\" to DAY) as timestamp), \"store_sales\" "
+ + "FROM \"foodmart\"\n"
+ + "GROUP BY \"store_sales\", floor(\"timestamp\" to DAY)\n ORDER BY \"store_sales\" DESC\n"
+ + "LIMIT 10\n";
sql(sql).queryContains(druidChecker("{\"queryType\":\"select\""));
}
@@ -1475,10 +1469,11 @@ public class DruidAdapterIT {
}
@Test public void testPushAggregateOnTime() {
- String sql = "select \"product_id\", \"timestamp\" as \"time\" from \"foodmart\" "
+ String sql = "select \"product_id\", cast(\"timestamp\" as timestamp) as \"time\" "
+ + "from \"foodmart\" "
+ "where \"product_id\" = 1016 "
- + "and \"timestamp\" < cast('1997-01-03' as timestamp) "
- + "and \"timestamp\" > cast('1990-01-01' as timestamp) "
+ + "and \"timestamp\" < '1997-01-03 00:00:00 UTC' "
+ + "and \"timestamp\" > '1990-01-01 00:00:00 UTC' "
+ "group by \"timestamp\", \"product_id\" ";
String druidQuery = "{'queryType':'groupBy','dataSource':'foodmart',"
+ "'granularity':'all','dimensions':[{'type':'extraction',"
@@ -1592,9 +1587,9 @@ public class DruidAdapterIT {
.explainContains("PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1997-01-01T00:00:00.001/1997-01-20T00:00:00.000]], filter=[=($1, 1016)"
- + "], projects=[[EXTRACT_DATE(FLAG(DAY), /INT(Reinterpret($0), 86400000)), "
- + "EXTRACT_DATE(FLAG(MONTH), /INT(Reinterpret($0), 86400000)), EXTRACT_DATE(FLAG"
- + "(YEAR), /INT(Reinterpret($0), 86400000)), $1]], groups=[{0, 1, 2, 3}], aggs=[[]])\n")
+ + "], projects=[[EXTRACT_DATE(FLAG(DAY), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), "
+ + "EXTRACT_DATE(FLAG(MONTH), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), EXTRACT_DATE(FLAG"
+ + "(YEAR), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), $1]], groups=[{0, 1, 2, 3}], aggs=[[]])\n")
.returnsUnordered("day=2; month=1; year=1997; product_id=1016",
"day=10; month=1; year=1997; product_id=1016",
"day=13; month=1; year=1997; product_id=1016",
@@ -1626,9 +1621,9 @@ public class DruidAdapterIT {
.explainContains("PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1997-01-01T00:00:00.001/1997-01-20T00:00:00.000]], filter=[=($1, 1016)"
- + "], projects=[[EXTRACT_DATE(FLAG(DAY), /INT(Reinterpret($0), 86400000)), "
- + "EXTRACT_DATE(FLAG(MONTH), /INT(Reinterpret($0), 86400000)), EXTRACT_DATE(FLAG"
- + "(YEAR), /INT(Reinterpret($0), 86400000)), $1]], groups=[{0, 1, 2, 3}], aggs=[[]])\n")
+ + "], projects=[[EXTRACT_DATE(FLAG(DAY), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), "
+ + "EXTRACT_DATE(FLAG(MONTH), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), EXTRACT_DATE(FLAG"
+ + "(YEAR), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), $1]], groups=[{0, 1, 2, 3}], aggs=[[]])\n")
.returnsUnordered("EXPR$0=2; EXPR$1=1; EXPR$2=1997; product_id=1016",
"EXPR$0=10; EXPR$1=1; EXPR$2=1997; product_id=1016",
"EXPR$0=13; EXPR$1=1; EXPR$2=1997; product_id=1016",
@@ -1653,7 +1648,7 @@ public class DruidAdapterIT {
.explainContains("PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1997-01-01T00:00:00.001/1997-01-20T00:00:00.000]], filter=[=($1, 1016)], "
- + "projects=[[EXTRACT_DATE(FLAG(DAY), /INT(Reinterpret($0), 86400000)), $1]], "
+ + "projects=[[EXTRACT_DATE(FLAG(DAY), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), $1]], "
+ "groups=[{0, 1}], aggs=[[]])\n")
.returnsUnordered("EXPR$0=2; dayOfMonth=1016", "EXPR$0=10; dayOfMonth=1016",
"EXPR$0=13; dayOfMonth=1016", "EXPR$0=16; dayOfMonth=1016");
@@ -1682,7 +1677,7 @@ public class DruidAdapterIT {
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[AND(>=(CAST"
+ "($11):BIGINT, 8), <=(CAST($11):BIGINT, 10), <(CAST($10):BIGINT, 15), =(EXTRACT_DATE"
- + "(FLAG(YEAR), /INT(Reinterpret($0), 86400000)), 1997))], groups=[{}], "
+ + "(FLAG(YEAR), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), 1997))], groups=[{}], "
+ "aggs=[[SUM($90)]])")
.queryContains(druidChecker(druidQuery))
.returnsUnordered("EXPR$0=75364.09998679161");
@@ -1830,33 +1825,33 @@ public class DruidAdapterIT {
.explainContains("PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[>=(CAST($1)"
- + ":BIGINT, 1558)], projects=[[EXTRACT_DATE(FLAG(MONTH), /INT(Reinterpret($0), "
+ + ":BIGINT, 1558)], projects=[[EXTRACT_DATE(FLAG(MONTH), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), "
+ "86400000)), $1, $89]], groups=[{0, 1}], aggs=[[SUM($2)]], sort0=[0], sort1=[2], "
+ "sort2=[1], dir0=[ASC], dir1=[ASC], dir2=[ASC])");
}
@Test public void testGroupByFloorTimeWithoutLimit() {
- final String sql = "select floor(\"timestamp\" to MONTH) as \"month\"\n"
+ final String sql = "select cast(floor(\"timestamp\" to MONTH) as timestamp) as \"month\"\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH)\n"
+ "order by \"month\" DESC";
sql(sql)
- .explainContains("PLAN=EnumerableInterpreter\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ .explainContains("DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[FLOOR($0, "
+ "FLAG(MONTH))]], groups=[{0}], aggs=[[]], sort0=[0], dir0=[DESC])")
.queryContains(druidChecker("'queryType':'timeseries'", "'descending':true"));
}
@Test public void testGroupByFloorTimeWithLimit() {
- final String sql = "select floor(\"timestamp\" to MONTH) as \"floor_month\"\n"
+ final String sql =
+ "select cast(floor(\"timestamp\" to MONTH) as timestamp) as \"floor_month\"\n"
+ "from \"foodmart\"\n"
+ "group by floor(\"timestamp\" to MONTH)\n"
+ "order by \"floor_month\" DESC LIMIT 3";
- final String explain = "PLAN=EnumerableInterpreter\n"
- + " BindableSort(sort0=[$0], dir0=[DESC], fetch=[3])\n"
- + " DruidQuery(table=[[foodmart, foodmart]], "
+ final String explain =
+ " BindableSort(sort0=[$0], dir0=[DESC], fetch=[3])\n"
+ + " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], "
+ "projects=[[FLOOR($0, FLAG(MONTH))]], groups=[{0}], aggs=[[]], "
+ "sort0=[0], dir0=[DESC])";
@@ -1876,8 +1871,8 @@ public class DruidAdapterIT {
final String expectedPlan = "PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[>=(CAST($1)"
- + ":BIGINT, 1558)], projects=[[EXTRACT_DATE(FLAG(YEAR), /INT(Reinterpret($0), 86400000)),"
- + " EXTRACT_DATE(FLAG(MONTH), /INT(Reinterpret($0), 86400000)), $1, $89]], groups=[{0, 1,"
+ + ":BIGINT, 1558)], projects=[[EXTRACT_DATE(FLAG(YEAR), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)),"
+ + " EXTRACT_DATE(FLAG(MONTH), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), $1, $89]], groups=[{0, 1,"
+ " 2}], aggs=[[SUM($3)]], sort0=[0], sort1=[1], sort2=[3], sort3=[2], dir0=[DESC], "
+ "dir1=[ASC], dir2=[DESC], dir3=[ASC], fetch=[3])";
final String expectedDruidQuery = "{'queryType':'groupBy','dataSource':'foodmart',"
@@ -1912,8 +1907,8 @@ public class DruidAdapterIT {
final String expectedPlan = "PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[>=(CAST($1)"
- + ":BIGINT, 1558)], projects=[[EXTRACT_DATE(FLAG(YEAR), /INT(Reinterpret($0), 86400000)),"
- + " EXTRACT_DATE(FLAG(MONTH), /INT(Reinterpret($0), 86400000)), $1, $89]], groups=[{0, 1,"
+ + ":BIGINT, 1558)], projects=[[EXTRACT_DATE(FLAG(YEAR), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)),"
+ + " EXTRACT_DATE(FLAG(MONTH), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000)), $1, $89]], groups=[{0, 1,"
+ " 2}], aggs=[[SUM($3)]], sort0=[3], sort1=[1], sort2=[2], dir0=[DESC], dir1=[DESC], "
+ "dir2=[ASC], fetch=[3])";
final String expectedDruidQuery = "{'queryType':'groupBy','dataSource':'foodmart',"
@@ -1939,12 +1934,13 @@ public class DruidAdapterIT {
}
@Test public void testGroupByTimeSortOverMetrics() {
- final String sqlQuery = "SELECT count(*) as c , SUM(\"unit_sales\") as s, floor(\"timestamp\""
- + " to month) FROM \"foodmart\" group by floor(\"timestamp\" to month) order by s DESC";
+ final String sqlQuery = "SELECT count(*) as c , SUM(\"unit_sales\") as s,"
+ + " cast(floor(\"timestamp\" to month) as timestamp)"
+ + " FROM \"foodmart\" group by floor(\"timestamp\" to month) order by s DESC";
sql(sqlQuery)
.explainContains("PLAN=EnumerableInterpreter\n"
+ " BindableSort(sort0=[$1], dir0=[DESC])\n"
- + " BindableProject(C=[$1], S=[$2], EXPR$2=[$0])\n"
+ + " BindableProject(C=[$1], S=[$2], EXPR$2=[CAST($0):TIMESTAMP(0)])\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], projects=[[FLOOR($0, "
+ "FLAG(MONTH)), $89]], groups=[{0}], aggs=[[COUNT(), SUM($1)]])")
@@ -1964,8 +1960,8 @@ public class DruidAdapterIT {
}
@Test public void testNumericOrderingOfOrderByOperatorFullTime() {
- final String sqlQuery = "SELECT \"timestamp\", count(*) as c, SUM(\"unit_sales\") "
- + "as s FROM "
+ final String sqlQuery = "SELECT cast(\"timestamp\" as timestamp) as \"timestamp\","
+ + " count(*) as c, SUM(\"unit_sales\") as s FROM "
+ "\"foodmart\" group by \"timestamp\" order by \"timestamp\" DESC, c DESC, s LIMIT 5";
final String druidSubQuery = "'limitSpec':{'type':'default','limit':5,"
+ "'columns':[{'dimension':'extract','direction':'descending',"
@@ -2044,7 +2040,7 @@ public class DruidAdapterIT {
+ "\"product_id\" = 1558 group by extract(CENTURY from \"timestamp\")";
final String plan = "PLAN=EnumerableInterpreter\n"
+ " BindableAggregate(group=[{0}])\n"
- + " BindableProject(EXPR$0=[EXTRACT_DATE(FLAG(CENTURY), /INT(Reinterpret($0), 86400000))])\n"
+ + " BindableProject(EXPR$0=[EXTRACT_DATE(FLAG(CENTURY), /INT(CAST(Reinterpret($0)):TIMESTAMP(0), 86400000))])\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
+ "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[=($1, 1558)], "
+ "projects=[[$0]])";
@@ -2491,7 +2487,7 @@ public class DruidAdapterIT {
@Test public void testOrderByOnMetricsInSelectDruidQuery() {
final String sqlQuery = "select \"store_sales\" as a, \"store_cost\" as b, \"store_sales\" - "
+ "\"store_cost\" as c from \"foodmart\" where \"timestamp\" "
- + ">= '1997-01-01 00:00:00' and \"timestamp\" < '1997-09-01 00:00:00' order by c "
+ + ">= '1997-01-01 00:00:00 UTC' and \"timestamp\" < '1997-09-01 00:00:00 UTC' order by c "
+ "limit 5";
String postAggString = "'queryType':'select'";
final String plan = "PLAN=EnumerableInterpreter\n"
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java
----------------------------------------------------------------------
diff --git a/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java b/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java
index 74ce10c..0e1948d 100644
--- a/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java
+++ b/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java
@@ -158,7 +158,7 @@ public class DruidDateRangeRulesTest {
operandRanges));
}
final List<LocalInterval> intervals =
- DruidDateTimeUtils.createInterval(f.timeStampDataType, e);
+ DruidDateTimeUtils.createInterval(e, "UTC");
assertThat(intervals, notNullValue());
assertThat(intervals.toString(), intervalMatcher);
}
@@ -178,7 +178,7 @@ public class DruidDateRangeRulesTest {
}
final RexNode e2 = f.simplify.simplify(e);
List<LocalInterval> intervals =
- DruidDateTimeUtils.createInterval(f.timeStampDataType, e2);
+ DruidDateTimeUtils.createInterval(e2, "UTC");
if (intervals == null) {
throw new AssertionError("null interval");
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 209d39a..2bc70fd 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -978,6 +978,7 @@ name will have been converted to upper case also.
| DATE | Date | Example: DATE '1969-07-20'
| TIME | Time of day | Example: TIME '20:17:40'
| TIMESTAMP [ WITHOUT TIME ZONE ] | Date and time | Example: TIMESTAMP '1969-07-20 20:17:40'
+| TIMESTAMP WITH LOCAL TIME ZONE | Date and time with local time zone | Example: TIMESTAMP '1969-07-20 20:17:40 America/Los Angeles'
| TIMESTAMP WITH TIME ZONE | Date and time with time zone | Example: TIMESTAMP '1969-07-20 20:17:40 America/Los Angeles'
| INTERVAL timeUnit [ TO timeUnit ] | Date time interval | Examples: INTERVAL '1-5' YEAR TO MONTH, INTERVAL '45' DAY, INTERVAL '1 2:34:56.789' DAY TO SECOND
| GEOMETRY | Geometry | Examples: ST_GeomFromText('POINT (30 10)')
@@ -991,9 +992,11 @@ timeUnit:
Note:
-* DATE, TIME and TIMESTAMP have no time zone. There is not even an implicit
- time zone, such as UTC (as in Java) or the local time zone. It is left to
- the user or application to supply a time zone.
+* DATE, TIME and TIMESTAMP have no time zone. For those types, there is not
+ even an implicit time zone, such as UTC (as in Java) or the local time zone.
+ It is left to the user or application to supply a time zone. In turn,
+ TIMESTAMP WITH LOCAL TIME ZONE does not store the time zone internally, but
+ it will rely on the supplied time zone to provide correct semantics.
* GEOMETRY is allowed only in certain
[conformance levels]({{ site.apiRoot }}/org/apache/calcite/sql/validate/SqlConformance.html#allowGeometry--).
[3/4] calcite git commit: [CALCITE-1947] Add time/timestamp with
local time zone types to optimizer
Posted by mm...@apache.org.
[CALCITE-1947] Add time/timestamp with local time zone types to optimizer
Close apache/calcite#519
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/939c9a62
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/939c9a62
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/939c9a62
Branch: refs/heads/branch-1.14
Commit: 939c9a62b4905d2cfffcb4adaefd76a809aa3520
Parents: f10950b
Author: Jesus Camacho Rodriguez <jc...@apache.org>
Authored: Mon Aug 14 17:46:42 2017 -0700
Committer: Jesus Camacho Rodriguez <jc...@apache.org>
Committed: Tue Sep 5 15:00:36 2017 -0700
----------------------------------------------------------------------
.../calcite/adapter/enumerable/RexImpTable.java | 15 +-
.../adapter/enumerable/RexToLixTranslator.java | 153 +++++++++++
.../calcite/jdbc/JavaTypeFactoryImpl.java | 2 +
.../apache/calcite/rel/metadata/RelMdSize.java | 4 +
.../rel/rules/SortProjectTransposeRule.java | 3 +-
.../calcite/rel/type/RelDataTypeSystemImpl.java | 8 +
.../java/org/apache/calcite/rex/RexBuilder.java | 57 +++++
.../java/org/apache/calcite/rex/RexLiteral.java | 26 ++
.../org/apache/calcite/rex/RexSimplify.java | 1 +
.../apache/calcite/runtime/SqlFunctions.java | 98 ++++++++
.../java/org/apache/calcite/schema/Schemas.java | 4 +-
.../apache/calcite/sql/SqlJdbcDataTypeName.java | 2 +
.../sql/type/SqlTypeAssignmentRules.java | 34 +++
.../apache/calcite/sql/type/SqlTypeFamily.java | 6 +-
.../apache/calcite/sql/type/SqlTypeName.java | 11 +-
.../sql2rel/StandardConvertletTable.java | 26 ++
.../org/apache/calcite/util/BuiltInMethod.java | 28 ++-
.../org/apache/calcite/util/DateString.java | 2 +-
.../calcite/util/DateTimeStringUtils.java | 88 +++++++
.../org/apache/calcite/util/TimeString.java | 6 +-
.../calcite/util/TimeWithTimeZoneString.java | 188 ++++++++++++++
.../apache/calcite/util/TimestampString.java | 52 +---
.../util/TimestampWithTimeZoneString.java | 194 ++++++++++++++
.../calcite/jdbc/CalciteRemoteDriverTest.java | 2 +-
.../org/apache/calcite/rex/RexBuilderTest.java | 73 ++++++
.../org/apache/calcite/test/CalciteAssert.java | 3 +
.../org/apache/calcite/test/RexProgramTest.java | 128 +++++++++-
.../adapter/druid/DruidConnectionImpl.java | 2 +-
.../adapter/druid/DruidDateTimeUtils.java | 74 +++---
.../calcite/adapter/druid/DruidQuery.java | 19 +-
.../calcite/adapter/druid/DruidRules.java | 6 +-
.../adapter/druid/DruidTableFactory.java | 2 +-
.../druid/TimeExtractionDimensionSpec.java | 14 +-
.../adapter/druid/TimeExtractionFunction.java | 21 +-
.../adapter/druid/DruidQueryFilterTest.java | 2 +-
.../org/apache/calcite/test/DruidAdapterIT.java | 252 +++++++++----------
.../calcite/test/DruidDateRangeRulesTest.java | 4 +-
site/_docs/reference.md | 9 +-
38 files changed, 1361 insertions(+), 258 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 0c1f542..0b3aa84 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -1655,7 +1655,14 @@ public class RexImpTable {
case 2:
final Type type;
final Method floorMethod;
+ Expression operand = translatedOperands.get(0);
switch (call.getType().getSqlTypeName()) {
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ operand = Expressions.call(
+ BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, translator.getRoot()));
+ // fall through
case TIMESTAMP:
type = long.class;
floorMethod = timestampMethod;
@@ -1671,19 +1678,19 @@ public class RexImpTable {
case YEAR:
case MONTH:
return Expressions.call(floorMethod, tur,
- call(translatedOperands, type, TimeUnit.DAY));
+ call(operand, type, TimeUnit.DAY));
default:
- return call(translatedOperands, type, timeUnitRange.startUnit);
+ return call(operand, type, timeUnitRange.startUnit);
}
default:
throw new AssertionError();
}
}
- private Expression call(List<Expression> translatedOperands, Type type,
+ private Expression call(Expression operand, Type type,
TimeUnit timeUnit) {
return Expressions.call(SqlFunctions.class, methodName,
- Types.castIfNecessary(type, translatedOperands.get(0)),
+ Types.castIfNecessary(type, operand),
Types.castIfNecessary(type,
Expressions.constant(timeUnit.multiplier)));
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
index 10e969f..270db82 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
@@ -245,6 +245,14 @@ public class RexToLixTranslator {
Expressions.call(BuiltInMethod.FLOOR_DIV.method,
operand, Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
int.class);
+ break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_DATE.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
}
break;
case TIME:
@@ -254,6 +262,14 @@ public class RexToLixTranslator {
convert =
Expressions.call(BuiltInMethod.STRING_TO_TIME.method, operand);
break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_TIME.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
+ break;
case TIMESTAMP:
convert = Expressions.convert_(
Expressions.call(
@@ -261,6 +277,49 @@ public class RexToLixTranslator {
operand,
Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
int.class);
+ break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
+ }
+ break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ switch (sourceType.getSqlTypeName()) {
+ case CHAR:
+ case VARCHAR:
+ convert =
+ Expressions.call(BuiltInMethod.STRING_TO_TIME_WITH_LOCAL_TIME_ZONE.method, operand);
+ break;
+ case TIME:
+ convert = Expressions.call(
+ BuiltInMethod.TIME_STRING_TO_TIME_WITH_LOCAL_TIME_ZONE.method,
+ RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.UNIX_TIME_TO_STRING.method,
+ operand)),
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
+ break;
+ case TIMESTAMP:
+ convert = Expressions.call(
+ BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
+ RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
+ operand)),
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
+ break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME_WITH_LOCAL_TIME_ZONE.method,
+ operand));
}
break;
case TIMESTAMP:
@@ -285,6 +344,82 @@ public class RexToLixTranslator {
Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
Expressions.convert_(operand, long.class));
break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method,
+ Expressions.call(
+ BuiltInMethod.UNIX_DATE_TO_STRING.method,
+ Expressions.call(BuiltInMethod.CURRENT_DATE.method, root)),
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
+ break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
+ }
+ break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ switch (sourceType.getSqlTypeName()) {
+ case CHAR:
+ case VARCHAR:
+ convert =
+ Expressions.call(
+ BuiltInMethod.STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
+ operand);
+ break;
+ case DATE:
+ convert = Expressions.call(
+ BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
+ RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
+ Expressions.multiply(
+ Expressions.convert_(operand, long.class),
+ Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)))),
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
+ break;
+ case TIME:
+ convert = Expressions.call(
+ BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
+ RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
+ Expressions.add(
+ Expressions.multiply(
+ Expressions.convert_(
+ Expressions.call(BuiltInMethod.CURRENT_DATE.method, root),
+ long.class),
+ Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
+ Expressions.convert_(operand, long.class)))),
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
+ break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
+ Expressions.call(
+ BuiltInMethod.UNIX_DATE_TO_STRING.method,
+ Expressions.call(BuiltInMethod.CURRENT_DATE.method, root)),
+ operand));
+ break;
+ case TIMESTAMP:
+ convert = Expressions.call(
+ BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
+ RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
+ operand)),
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
}
break;
case BOOLEAN:
@@ -315,6 +450,14 @@ public class RexToLixTranslator {
BuiltInMethod.UNIX_TIME_TO_STRING.method,
operand));
break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_STRING.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
+ break;
case TIMESTAMP:
convert = RexImpTable.optimize2(
operand,
@@ -322,6 +465,14 @@ public class RexToLixTranslator {
BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
operand));
break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ convert = RexImpTable.optimize2(
+ operand,
+ Expressions.call(
+ BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_STRING.method,
+ operand,
+ Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
+ break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -621,6 +772,7 @@ public class RexToLixTranslator {
Expressions.constant(bd.toString()));
case DATE:
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -628,6 +780,7 @@ public class RexToLixTranslator {
javaClass = int.class;
break;
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
index 2e0ded2..8999f44 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
@@ -176,12 +176,14 @@ public class JavaTypeFactoryImpl
return String.class;
case DATE:
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case INTEGER:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
return type.isNullable() ? Integer.class : int.class;
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case BIGINT:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
index bd12347..085fd7c 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
@@ -281,6 +281,7 @@ public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
case DECIMAL:
case DATE:
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -289,6 +290,7 @@ public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
case DOUBLE:
case FLOAT: // sic
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
@@ -339,6 +341,7 @@ public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
case REAL:
case DATE:
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -346,6 +349,7 @@ public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
case BIGINT:
case DOUBLE:
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
index e7b4467..127e95e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
@@ -23,6 +23,7 @@ import org.apache.calcite.plan.RelOptRuleOperand;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollationTraitDef;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Project;
@@ -102,7 +103,7 @@ public class SortProjectTransposeRule extends RelOptRule {
final RexCall cast = (RexCall) node;
final RexCallBinding binding =
RexCallBinding.create(cluster.getTypeFactory(), cast,
- ImmutableList.of(RexUtil.apply(map, sort.getCollation())));
+ ImmutableList.of(RelCollations.of(RexUtil.apply(map, fc))));
if (cast.getOperator().getMonotonicity(binding) == SqlMonotonicity.NOT_MONOTONIC) {
return;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
index 3e0eebd..b7b8839 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java
@@ -97,9 +97,11 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
case DOUBLE:
return 15;
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case DATE:
return 0; // SQL99 part 2 section 6.1 syntax rule 30
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
// farrago supports only 0 (see
// SqlTypeName.getDefaultPrecision), but it should be 6
// (microseconds) per SQL99 part 2 section 6.1 syntax rule 30.
@@ -120,7 +122,9 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
case BINARY:
return 65536;
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return SqlTypeName.MAX_DATETIME_PRECISION;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
@@ -159,6 +163,8 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
return isPrefix ? "x'" : "'";
case TIMESTAMP:
return isPrefix ? "TIMESTAMP '" : "'";
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ return isPrefix ? "TIMESTAMP WITH LOCAL TIME ZONE '" : "'";
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
@@ -176,6 +182,8 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
return isPrefix ? "INTERVAL '" : "' YEAR TO MONTH";
case TIME:
return isPrefix ? "TIME '" : "'";
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ return isPrefix ? "TIME WITH LOCAL TIME ZONE '" : "'";
case DATE:
return isPrefix ? "DATE '" : "'";
case ARRAY:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index adf7fee..2144ab8 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -863,6 +863,14 @@ public class RexBuilder {
}
o = ((TimeString) o).round(p);
break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ assert o instanceof TimeString;
+ p = type.getPrecision();
+ if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
+ p = 0;
+ }
+ o = ((TimeString) o).round(p);
+ break;
case TIMESTAMP:
assert o instanceof TimestampString;
p = type.getPrecision();
@@ -871,6 +879,13 @@ public class RexBuilder {
}
o = ((TimestampString) o).round(p);
break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ assert o instanceof TimestampString;
+ p = type.getPrecision();
+ if (p == RelDataType.PRECISION_NOT_SPECIFIED) {
+ p = 0;
+ }
+ o = ((TimestampString) o).round(p);
}
return new RexLiteral(o, type, typeName);
}
@@ -1084,6 +1099,17 @@ public class RexBuilder {
SqlTypeName.TIME);
}
+ /**
+ * Creates a Time with local time-zone literal.
+ */
+ public RexLiteral makeTimeWithLocalTimeZoneLiteral(
+ TimeString time,
+ int precision) {
+ return makeLiteral(Preconditions.checkNotNull(time),
+ typeFactory.createSqlType(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, precision),
+ SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
+ }
+
/** @deprecated Use {@link #makeTimestampLiteral(TimestampString, int)}. */
@Deprecated // to be removed before 2.0
public RexLiteral makeTimestampLiteral(Calendar calendar, int precision) {
@@ -1102,6 +1128,17 @@ public class RexBuilder {
}
/**
+ * Creates a Timestamp with local time-zone literal.
+ */
+ public RexLiteral makeTimestampWithLocalTimeZoneLiteral(
+ TimestampString timestamp,
+ int precision) {
+ return makeLiteral(Preconditions.checkNotNull(timestamp),
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, precision),
+ SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ }
+
+ /**
* Creates a literal representing an interval type, for example
* {@code YEAR TO MONTH} or {@code DOW}.
*/
@@ -1225,6 +1262,10 @@ public class RexBuilder {
case DATE:
case TIMESTAMP:
return DateTimeUtils.ZERO_CALENDAR;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ return new TimeString(0, 0, 0);
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ return new TimestampString(0, 0, 0, 0, 0, 0);
default:
throw Util.unexpected(type.getSqlTypeName());
}
@@ -1288,10 +1329,14 @@ public class RexBuilder {
return (Boolean) value ? booleanTrue : booleanFalse;
case TIME:
return makeTimeLiteral((TimeString) value, type.getPrecision());
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ return makeTimeWithLocalTimeZoneLiteral((TimeString) value, type.getPrecision());
case DATE:
return makeDateLiteral((DateString) value);
case TIMESTAMP:
return makeTimestampLiteral((TimestampString) value, type.getPrecision());
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ return makeTimestampWithLocalTimeZoneLiteral((TimestampString) value, type.getPrecision());
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -1423,6 +1468,12 @@ public class RexBuilder {
} else {
return TimeString.fromMillisOfDay((Integer) o);
}
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ if (o instanceof TimeString) {
+ return o;
+ } else {
+ return TimeString.fromMillisOfDay((Integer) o);
+ }
case DATE:
if (o instanceof DateString) {
return o;
@@ -1445,6 +1496,12 @@ public class RexBuilder {
} else {
return TimestampString.fromMillisSinceEpoch((Long) o);
}
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ if (o instanceof TimestampString) {
+ return o;
+ } else {
+ return TimestampString.fromMillisSinceEpoch((Long) o);
+ }
default:
return o;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
index 8c4c732..a5ed918 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
@@ -255,8 +255,12 @@ public class RexLiteral extends RexNode {
return value instanceof DateString;
case TIME:
return value instanceof TimeString;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ return value instanceof TimeString;
case TIMESTAMP:
return value instanceof TimestampString;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ return value instanceof TimestampString;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -498,10 +502,18 @@ public class RexLiteral extends RexNode {
assert value instanceof TimeString;
pw.print(value);
break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ assert value instanceof TimeString;
+ pw.print(value);
+ break;
case TIMESTAMP:
assert value instanceof TimestampString;
pw.print(value);
break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ assert value instanceof TimestampString;
+ pw.print(value);
+ break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
@@ -724,9 +736,11 @@ public class RexLiteral extends RexNode {
return getValueAs(String.class);
case DECIMAL:
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return getValueAs(Long.class);
case DATE:
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
return getValueAs(Integer.class);
default:
return value;
@@ -835,6 +849,12 @@ public class RexLiteral extends RexNode {
return clazz.cast(((TimeString) value).toCalendar());
}
break;
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ if (clazz == Integer.class) {
+ // Milliseconds since 1970-01-01 00:00:00
+ return clazz.cast(((TimeString) value).getMillisOfDay());
+ }
+ break;
case TIMESTAMP:
if (clazz == Long.class) {
// Milliseconds since 1970-01-01 00:00:00
@@ -844,6 +864,12 @@ public class RexLiteral extends RexNode {
return clazz.cast(((TimestampString) value).toCalendar());
}
break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ if (clazz == Long.class) {
+ // Milliseconds since 1970-01-01 00:00:00
+ return clazz.cast(((TimestampString) value).getMillisSinceEpoch());
+ }
+ break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index 67ac1b8..52c4795 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -811,6 +811,7 @@ public class RexSimplify {
case TIMESTAMP:
return e;
}
+ break;
}
final List<RexNode> reducedValues = new ArrayList<>();
executor.reduce(rexBuilder, ImmutableList.<RexNode>of(e), reducedValues);
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 6832ee4..736034c 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -33,6 +33,8 @@ import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.runtime.FlatLists.ComparableList;
import org.apache.calcite.util.Bug;
import org.apache.calcite.util.NumberUtil;
+import org.apache.calcite.util.TimeWithTimeZoneString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -1695,6 +1697,50 @@ public class SqlFunctions {
return v == null ? null : internalToTime(v.intValue());
}
+ public static Integer toTimeWithLocalTimeZone(String v) {
+ return v == null ? null : new TimeWithTimeZoneString(v)
+ .withTimeZone(DateTimeUtils.UTC_ZONE)
+ .getLocalTimeString()
+ .getMillisOfDay();
+ }
+
+ public static Integer toTimeWithLocalTimeZone(String v, TimeZone timeZone) {
+ return v == null ? null : new TimeWithTimeZoneString(v + " " + timeZone.getID())
+ .withTimeZone(DateTimeUtils.UTC_ZONE)
+ .getLocalTimeString()
+ .getMillisOfDay();
+ }
+
+ public static int timeWithLocalTimeZoneToTime(int v, TimeZone timeZone) {
+ return TimeWithTimeZoneString.fromMillisOfDay(v)
+ .withTimeZone(timeZone)
+ .getLocalTimeString()
+ .getMillisOfDay();
+ }
+
+ public static long timeWithLocalTimeZoneToTimestamp(String date, int v, TimeZone timeZone) {
+ final TimeWithTimeZoneString tTZ = TimeWithTimeZoneString.fromMillisOfDay(v)
+ .withTimeZone(DateTimeUtils.UTC_ZONE);
+ return new TimestampWithTimeZoneString(date + " " + tTZ.toString())
+ .withTimeZone(timeZone)
+ .getLocalTimestampString()
+ .getMillisSinceEpoch();
+ }
+
+ public static long timeWithLocalTimeZoneToTimestampWithLocalTimeZone(String date, int v) {
+ final TimeWithTimeZoneString tTZ = TimeWithTimeZoneString.fromMillisOfDay(v)
+ .withTimeZone(DateTimeUtils.UTC_ZONE);
+ return new TimestampWithTimeZoneString(date + " " + tTZ.toString())
+ .getLocalTimestampString()
+ .getMillisSinceEpoch();
+ }
+
+ public static String timeWithLocalTimeZoneToString(int v, TimeZone timeZone) {
+ return TimeWithTimeZoneString.fromMillisOfDay(v)
+ .withTimeZone(timeZone)
+ .toString();
+ }
+
/** Converts the internal representation of a SQL TIMESTAMP (long) to the Java
* type used for UDF parameters ({@link java.sql.Timestamp}). */
public static java.sql.Timestamp internalToTimestamp(long v) {
@@ -1705,6 +1751,53 @@ public class SqlFunctions {
return v == null ? null : internalToTimestamp(v.longValue());
}
+ public static int timestampWithLocalTimeZoneToDate(long v, TimeZone timeZone) {
+ return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
+ .withTimeZone(timeZone)
+ .getLocalDateString()
+ .getDaysSinceEpoch();
+ }
+
+ public static int timestampWithLocalTimeZoneToTime(long v, TimeZone timeZone) {
+ return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
+ .withTimeZone(timeZone)
+ .getLocalTimeString()
+ .getMillisOfDay();
+ }
+
+ public static long timestampWithLocalTimeZoneToTimestamp(long v, TimeZone timeZone) {
+ return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
+ .withTimeZone(timeZone)
+ .getLocalTimestampString()
+ .getMillisSinceEpoch();
+ }
+
+ public static String timestampWithLocalTimeZoneToString(long v, TimeZone timeZone) {
+ return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
+ .withTimeZone(timeZone)
+ .toString();
+ }
+
+ public static int timestampWithLocalTimeZoneToTimeWithLocalTimeZone(long v) {
+ return TimestampWithTimeZoneString.fromMillisSinceEpoch(v)
+ .getLocalTimeString()
+ .getMillisOfDay();
+ }
+
+ public static Long toTimestampWithLocalTimeZone(String v) {
+ return v == null ? null : new TimestampWithTimeZoneString(v)
+ .withTimeZone(DateTimeUtils.UTC_ZONE)
+ .getLocalTimestampString()
+ .getMillisSinceEpoch();
+ }
+
+ public static Long toTimestampWithLocalTimeZone(String v, TimeZone timeZone) {
+ return v == null ? null : new TimestampWithTimeZoneString(v + " " + timeZone.getID())
+ .withTimeZone(DateTimeUtils.UTC_ZONE)
+ .getLocalTimestampString()
+ .getMillisSinceEpoch();
+ }
+
// Don't need shortValueOf etc. - Short.valueOf is sufficient.
/** Helper for CAST(... AS VARCHAR(maxLength)). */
@@ -1867,6 +1960,11 @@ public class SqlFunctions {
return (int) (localTimestamp(root) % DateTimeUtils.MILLIS_PER_DAY);
}
+ @NonDeterministic
+ public static TimeZone timeZone(DataContext root) {
+ return (TimeZone) DataContext.Variable.TIME_ZONE.get(root);
+ }
+
/** SQL {@code TRANSLATE(string, search_chars, replacement_chars)}
* function. */
public static String translate3(String s, String search, String replacement) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/schema/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index edb833a..6466d31 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -54,7 +54,6 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.TimeZone;
/**
* Utility functions for schemas.
@@ -564,8 +563,7 @@ public final class Schemas {
DummyDataContext(CalciteConnection connection, SchemaPlus rootSchema) {
this.connection = connection;
this.rootSchema = rootSchema;
- this.map =
- ImmutableMap.<String, Object>of("timeZone", TimeZone.getDefault());
+ this.map = ImmutableMap.<String, Object>of();
}
public SchemaPlus getRootSchema() {
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java b/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
index 90b0597..2f9d974 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java
@@ -34,7 +34,9 @@ public enum SqlJdbcDataTypeName {
SQL_VARCHAR(SqlTypeName.VARCHAR),
SQL_DATE(SqlTypeName.DATE),
SQL_TIME(SqlTypeName.TIME),
+ SQL_TIME_WITH_LOCAL_TIME_ZONE(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE),
SQL_TIMESTAMP(SqlTypeName.TIMESTAMP),
+ SQL_TIMESTAMP_WITH_LOCAL_TIME_ZONE(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE),
SQL_DECIMAL(SqlTypeName.DECIMAL),
SQL_NUMERIC(SqlTypeName.DECIMAL),
SQL_BOOLEAN(SqlTypeName.BOOLEAN),
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
index f77d639..cd46c39 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
@@ -158,9 +158,17 @@ public class SqlTypeAssignmentRules {
rule.add(SqlTypeName.TIMESTAMP);
rules.put(SqlTypeName.TIME, rule);
+ // Time with local time-zone is assignable from ...
+ rules.put(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
+ EnumSet.of(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE));
+
// Timestamp is assignable from ...
rules.put(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP));
+ // Timestamp with local time-zone is assignable from ...
+ rules.put(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
+ EnumSet.of(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE));
+
// Geometry is assignable from ...
rules.put(SqlTypeName.GEOMETRY, EnumSet.of(SqlTypeName.GEOMETRY));
@@ -275,6 +283,7 @@ public class SqlTypeAssignmentRules {
rule = new HashSet<>();
rule.add(SqlTypeName.DATE);
rule.add(SqlTypeName.TIMESTAMP);
+ rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
rule.add(SqlTypeName.CHAR);
rule.add(SqlTypeName.VARCHAR);
coerceRules.put(SqlTypeName.DATE, rule);
@@ -282,19 +291,44 @@ public class SqlTypeAssignmentRules {
// Time is castable from ...
rule = new HashSet<>();
rule.add(SqlTypeName.TIME);
+ rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
rule.add(SqlTypeName.TIMESTAMP);
+ rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
rule.add(SqlTypeName.CHAR);
rule.add(SqlTypeName.VARCHAR);
coerceRules.put(SqlTypeName.TIME, rule);
+ // Time with local time-zone is castable from ...
+ rule = new HashSet<>();
+ rule.add(SqlTypeName.TIME);
+ rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
+ rule.add(SqlTypeName.TIMESTAMP);
+ rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ rule.add(SqlTypeName.CHAR);
+ rule.add(SqlTypeName.VARCHAR);
+ coerceRules.put(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, rule);
+
// Timestamp is castable from ...
rule = new HashSet<>();
rule.add(SqlTypeName.TIMESTAMP);
+ rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
rule.add(SqlTypeName.DATE);
rule.add(SqlTypeName.TIME);
+ rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
rule.add(SqlTypeName.CHAR);
rule.add(SqlTypeName.VARCHAR);
coerceRules.put(SqlTypeName.TIMESTAMP, rule);
+
+ // Timestamp with local time-zone is castable from ...
+ rule = new HashSet<>();
+ rule.add(SqlTypeName.TIMESTAMP);
+ rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ rule.add(SqlTypeName.DATE);
+ rule.add(SqlTypeName.TIME);
+ rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
+ rule.add(SqlTypeName.CHAR);
+ rule.add(SqlTypeName.VARCHAR);
+ coerceRules.put(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, rule);
}
//~ Methods ----------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
index e88f96a..af1733b 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
@@ -99,7 +99,9 @@ public enum SqlTypeFamily implements RelDataTypeFamily {
.put(Types.DATE, DATE)
.put(Types.TIME, TIME)
+ .put(Types.TIME_WITH_TIMEZONE, TIME)
.put(Types.TIMESTAMP, TIMESTAMP)
+ .put(Types.TIMESTAMP_WITH_TIMEZONE, TIMESTAMP)
.put(Types.BOOLEAN, BOOLEAN)
.put(ExtraSqlTypes.REF_CURSOR, CURSOR)
@@ -130,9 +132,9 @@ public enum SqlTypeFamily implements RelDataTypeFamily {
case DATE:
return ImmutableList.of(SqlTypeName.DATE);
case TIME:
- return ImmutableList.of(SqlTypeName.TIME);
+ return ImmutableList.of(SqlTypeName.TIME, SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
case TIMESTAMP:
- return ImmutableList.of(SqlTypeName.TIMESTAMP);
+ return ImmutableList.of(SqlTypeName.TIMESTAMP, SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
case BOOLEAN:
return SqlTypeName.BOOLEAN_TYPES;
case INTERVAL_YEAR_MONTH:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
index fcae1ec..e56b9cf 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
@@ -63,8 +63,12 @@ public enum SqlTypeName {
DATE(PrecScale.NO_NO, false, Types.DATE, SqlTypeFamily.DATE),
TIME(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME,
SqlTypeFamily.TIME),
+ TIME_WITH_LOCAL_TIME_ZONE(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.OTHER,
+ SqlTypeFamily.TIME),
TIMESTAMP(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIMESTAMP,
SqlTypeFamily.TIMESTAMP),
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.OTHER,
+ SqlTypeFamily.TIMESTAMP),
INTERVAL_YEAR(PrecScale.NO_NO, false, Types.OTHER,
SqlTypeFamily.INTERVAL_YEAR_MONTH),
INTERVAL_YEAR_MONTH(PrecScale.NO_NO, false, Types.OTHER,
@@ -147,7 +151,7 @@ public enum SqlTypeName {
INTERVAL_DAY, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
INTERVAL_DAY_SECOND, INTERVAL_HOUR, INTERVAL_HOUR_MINUTE,
INTERVAL_HOUR_SECOND, INTERVAL_MINUTE, INTERVAL_MINUTE_SECOND,
- INTERVAL_SECOND,
+ INTERVAL_SECOND, TIME_WITH_LOCAL_TIME_ZONE, TIMESTAMP_WITH_LOCAL_TIME_ZONE,
FLOAT, MULTISET, DISTINCT, STRUCTURED, ROW, CURSOR, COLUMN_LIST);
public static final List<SqlTypeName> BOOLEAN_TYPES =
@@ -178,7 +182,8 @@ public enum SqlTypeName {
combine(CHAR_TYPES, BINARY_TYPES);
public static final List<SqlTypeName> DATETIME_TYPES =
- ImmutableList.of(DATE, TIME, TIMESTAMP);
+ ImmutableList.of(DATE, TIME, TIME_WITH_LOCAL_TIME_ZONE,
+ TIMESTAMP, TIMESTAMP_WITH_LOCAL_TIME_ZONE);
public static final Set<SqlTypeName> YEAR_INTERVAL_TYPES =
Sets.immutableEnumSet(SqlTypeName.INTERVAL_YEAR,
@@ -740,7 +745,9 @@ public enum SqlTypeName {
case VARBINARY:
case BINARY:
case TIME:
+ case TIME_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return 1;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
index 8940629..14bff94 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -678,6 +678,14 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
break;
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ RelDataType type =
+ cx.getTypeFactory().createSqlType(SqlTypeName.TIMESTAMP);
+ type = cx.getTypeFactory().createTypeWithNullability(
+ type,
+ exprs.get(1).getType().isNullable());
+ res = rexBuilder.makeCast(type, res);
+ // fall through
case TIMESTAMP:
res = divide(rexBuilder, res, TimeUnit.DAY.multiplier);
// fall through
@@ -690,6 +698,14 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
break;
case DECADE:
switch (sqlTypeName) {
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ RelDataType type =
+ cx.getTypeFactory().createSqlType(SqlTypeName.TIMESTAMP);
+ type = cx.getTypeFactory().createTypeWithNullability(
+ type,
+ exprs.get(1).getType().isNullable());
+ res = rexBuilder.makeCast(type, res);
+ // fall through
case TIMESTAMP:
res = divide(rexBuilder, res, TimeUnit.DAY.multiplier);
// fall through
@@ -709,6 +725,16 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
case TIMESTAMP:
// convert to seconds
return divide(rexBuilder, res, TimeUnit.SECOND.multiplier);
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ RelDataType type =
+ cx.getTypeFactory().createSqlType(SqlTypeName.TIMESTAMP);
+ type = cx.getTypeFactory().createTypeWithNullability(
+ type,
+ exprs.get(1).getType().isNullable());
+ return divide(
+ rexBuilder,
+ rexBuilder.makeCast(type, res),
+ TimeUnit.SECOND.multiplier);
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 3265db5..648f1b0 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -297,8 +297,33 @@ public enum BuiltInMethod {
INTERNAL_TO_TIMESTAMP(SqlFunctions.class, "internalToTimestamp", long.class),
STRING_TO_DATE(DateTimeUtils.class, "dateStringToUnixDate", String.class),
STRING_TO_TIME(DateTimeUtils.class, "timeStringToUnixDate", String.class),
- STRING_TO_TIMESTAMP(DateTimeUtils.class, "timestampStringToUnixDate",
+ STRING_TO_TIMESTAMP(DateTimeUtils.class, "timestampStringToUnixDate", String.class),
+ STRING_TO_TIME_WITH_LOCAL_TIME_ZONE(SqlFunctions.class, "toTimeWithLocalTimeZone",
String.class),
+ TIME_STRING_TO_TIME_WITH_LOCAL_TIME_ZONE(SqlFunctions.class, "toTimeWithLocalTimeZone",
+ String.class, TimeZone.class),
+ STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE(SqlFunctions.class, "toTimestampWithLocalTimeZone",
+ String.class),
+ TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE(SqlFunctions.class,
+ "toTimestampWithLocalTimeZone", String.class, TimeZone.class),
+ TIME_WITH_LOCAL_TIME_ZONE_TO_TIME(SqlFunctions.class, "timeWithLocalTimeZoneToTime",
+ int.class, TimeZone.class),
+ TIME_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP(SqlFunctions.class, "timeWithLocalTimeZoneToTimestamp",
+ String.class, int.class, TimeZone.class),
+ TIME_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE(SqlFunctions.class,
+ "timeWithLocalTimeZoneToTimestampWithLocalTimeZone", String.class, int.class),
+ TIME_WITH_LOCAL_TIME_ZONE_TO_STRING(SqlFunctions.class, "timeWithLocalTimeZoneToString",
+ int.class, TimeZone.class),
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_DATE(SqlFunctions.class, "timestampWithLocalTimeZoneToDate",
+ long.class, TimeZone.class),
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME(SqlFunctions.class, "timestampWithLocalTimeZoneToTime",
+ long.class, TimeZone.class),
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME_WITH_LOCAL_TIME_ZONE(SqlFunctions.class,
+ "timestampWithLocalTimeZoneToTimeWithLocalTimeZone", long.class),
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP(SqlFunctions.class,
+ "timestampWithLocalTimeZoneToTimestamp", long.class, TimeZone.class),
+ TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_STRING(SqlFunctions.class,
+ "timestampWithLocalTimeZoneToString", long.class, TimeZone.class),
UNIX_DATE_TO_STRING(DateTimeUtils.class, "unixDateToString", int.class),
UNIX_TIME_TO_STRING(DateTimeUtils.class, "unixTimeToString", int.class),
UNIX_TIMESTAMP_TO_STRING(DateTimeUtils.class, "unixTimestampToString",
@@ -322,6 +347,7 @@ public enum BuiltInMethod {
CURRENT_DATE(SqlFunctions.class, "currentDate", DataContext.class),
LOCAL_TIMESTAMP(SqlFunctions.class, "localTimestamp", DataContext.class),
LOCAL_TIME(SqlFunctions.class, "localTime", DataContext.class),
+ TIME_ZONE(SqlFunctions.class, "timeZone", DataContext.class),
BOOLEAN_TO_STRING(SqlFunctions.class, "toString", boolean.class),
JDBC_ARRAY_TO_LIST(SqlFunctions.class, "arrayToList", java.sql.Array.class),
OBJECT_TO_STRING(Object.class, "toString"),
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/DateString.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/DateString.java b/core/src/main/java/org/apache/calcite/util/DateString.java
index cea9df3..5aaa1b8 100644
--- a/core/src/main/java/org/apache/calcite/util/DateString.java
+++ b/core/src/main/java/org/apache/calcite/util/DateString.java
@@ -42,7 +42,7 @@ public class DateString implements Comparable<DateString> {
/** Creates a DateString for year, month, day values. */
public DateString(int year, int month, int day) {
- this(TimestampString.ymd(new StringBuilder(), year, month, day).toString());
+ this(DateTimeStringUtils.ymd(new StringBuilder(), year, month, day).toString());
}
@Override public String toString() {
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/DateTimeStringUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/DateTimeStringUtils.java b/core/src/main/java/org/apache/calcite/util/DateTimeStringUtils.java
new file mode 100644
index 0000000..c0d38df
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/DateTimeStringUtils.java
@@ -0,0 +1,88 @@
+/*
+ * 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.calcite.util;
+
+import java.util.TimeZone;
+
+/**
+ * Utility methods to manipulate String representation of DateTime values.
+ */
+public class DateTimeStringUtils {
+
+ private DateTimeStringUtils() {}
+
+ static String pad(int length, long v) {
+ StringBuilder s = new StringBuilder(Long.toString(v));
+ while (s.length() < length) {
+ s.insert(0, "0");
+ }
+ return s.toString();
+ }
+
+ static StringBuilder hms(StringBuilder b, int h, int m, int s) {
+ int2(b, h);
+ b.append(':');
+ int2(b, m);
+ b.append(':');
+ int2(b, s);
+ return b;
+ }
+
+ static StringBuilder ymdhms(StringBuilder b, int year, int month, int day,
+ int h, int m, int s) {
+ ymd(b, year, month, day);
+ b.append(' ');
+ hms(b, h, m, s);
+ return b;
+ }
+
+ static StringBuilder ymd(StringBuilder b, int year, int month, int day) {
+ int4(b, year);
+ b.append('-');
+ int2(b, month);
+ b.append('-');
+ int2(b, day);
+ return b;
+ }
+
+ private static void int4(StringBuilder buf, int i) {
+ buf.append((char) ('0' + (i / 1000) % 10));
+ buf.append((char) ('0' + (i / 100) % 10));
+ buf.append((char) ('0' + (i / 10) % 10));
+ buf.append((char) ('0' + i % 10));
+ }
+
+ private static void int2(StringBuilder buf, int i) {
+ buf.append((char) ('0' + (i / 10) % 10));
+ buf.append((char) ('0' + i % 10));
+ }
+
+ static boolean isValidTimeZone(final String timeZone) {
+ if (timeZone.equals("GMT")) {
+ return true;
+ } else {
+ String id = TimeZone.getTimeZone(timeZone).getID();
+ if (!id.equals("GMT")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+
+// End DateTimeStringUtils.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/TimeString.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/TimeString.java b/core/src/main/java/org/apache/calcite/util/TimeString.java
index 75aa96b..ce03d87 100644
--- a/core/src/main/java/org/apache/calcite/util/TimeString.java
+++ b/core/src/main/java/org/apache/calcite/util/TimeString.java
@@ -44,7 +44,7 @@ public class TimeString implements Comparable<TimeString> {
/** Creates a TimeString for hour, minute, second and millisecond values. */
public TimeString(int h, int m, int s) {
- this(TimestampString.hms(new StringBuilder(), h, m, s).toString());
+ this(DateTimeStringUtils.hms(new StringBuilder(), h, m, s).toString());
}
/** Sets the fraction field of a {@code TimeString} to a given number
@@ -55,7 +55,7 @@ public class TimeString implements Comparable<TimeString> {
* yields {@code TIME '1970-01-01 02:03:04.056'}. */
public TimeString withMillis(int millis) {
Preconditions.checkArgument(millis >= 0 && millis < 1000);
- return withFraction(TimestampString.pad(3, millis));
+ return withFraction(DateTimeStringUtils.pad(3, millis));
}
/** Sets the fraction field of a {@code TimeString} to a given number
@@ -66,7 +66,7 @@ public class TimeString implements Comparable<TimeString> {
* yields {@code TIME '1970-01-01 02:03:04.000056789'}. */
public TimeString withNanos(int nanos) {
Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000);
- return withFraction(TimestampString.pad(9, nanos));
+ return withFraction(DateTimeStringUtils.pad(9, nanos));
}
/** Sets the fraction field of a {@code TimeString}.
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java b/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java
new file mode 100644
index 0000000..6547f06
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java
@@ -0,0 +1,188 @@
+/*
+ * 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.calcite.util;
+
+import org.apache.calcite.avatica.util.DateTimeUtils;
+
+import com.google.common.base.Preconditions;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Time with time-zone literal.
+ *
+ * <p>Immutable, internally represented as a string (in ISO format),
+ * and can support unlimited precision (milliseconds, nanoseconds).
+ */
+public class TimeWithTimeZoneString implements Comparable<TimeWithTimeZoneString> {
+
+ final TimeString localTime;
+ final TimeZone timeZone;
+ final String v;
+
+ /** Creates a TimeWithTimeZoneString. */
+ public TimeWithTimeZoneString(TimeString localTime, TimeZone timeZone) {
+ this.localTime = localTime;
+ this.timeZone = timeZone;
+ this.v = localTime.toString() + " " + timeZone.getID();
+ }
+
+ /** Creates a TimeWithTimeZoneString. */
+ public TimeWithTimeZoneString(String v) {
+ this.localTime = new TimeString(v.substring(0, 8));
+ String timeZoneString = v.substring(9);
+ Preconditions.checkArgument(DateTimeStringUtils.isValidTimeZone(timeZoneString));
+ this.timeZone = TimeZone.getTimeZone(timeZoneString);
+ this.v = v;
+ }
+
+ /** Creates a TimeWithTimeZoneString for hour, minute, second and millisecond values
+ * in the given time-zone. */
+ public TimeWithTimeZoneString(int h, int m, int s, String timeZone) {
+ this(DateTimeStringUtils.hms(new StringBuilder(), h, m, s).toString() + " " + timeZone);
+ }
+
+ /** Sets the fraction field of a {@code TimeWithTimeZoneString} to a given number
+ * of milliseconds. Nukes the value set via {@link #withNanos}.
+ *
+ * <p>For example,
+ * {@code new TimeWithTimeZoneString(1970, 1, 1, 2, 3, 4, "UTC").withMillis(56)}
+ * yields {@code TIME WITH LOCAL TIME ZONE '1970-01-01 02:03:04.056 UTC'}. */
+ public TimeWithTimeZoneString withMillis(int millis) {
+ Preconditions.checkArgument(millis >= 0 && millis < 1000);
+ return withFraction(DateTimeStringUtils.pad(3, millis));
+ }
+
+ /** Sets the fraction field of a {@code TimeString} to a given number
+ * of nanoseconds. Nukes the value set via {@link #withMillis(int)}.
+ *
+ * <p>For example,
+ * {@code new TimeWithTimeZoneString(1970, 1, 1, 2, 3, 4, "UTC").withNanos(56789)}
+ * yields {@code TIME WITH LOCAL TIME ZONE '1970-01-01 02:03:04.000056789 UTC'}. */
+ public TimeWithTimeZoneString withNanos(int nanos) {
+ Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000);
+ return withFraction(DateTimeStringUtils.pad(9, nanos));
+ }
+
+ /** Sets the fraction field of a {@code TimeWithTimeZoneString}.
+ * The precision is determined by the number of leading zeros.
+ * Trailing zeros are stripped.
+ *
+ * <p>For example,
+ * {@code new TimeWithTimeZoneString(1970, 1, 1, 2, 3, 4, "UTC").withFraction("00506000")}
+ * yields {@code TIME WITH LOCAL TIME ZONE '1970-01-01 02:03:04.00506 UTC'}. */
+ public TimeWithTimeZoneString withFraction(String fraction) {
+ String v = this.v;
+ int i = v.indexOf('.');
+ if (i >= 0) {
+ v = v.substring(0, i);
+ } else {
+ v = v.substring(0, 8);
+ }
+ while (fraction.endsWith("0")) {
+ fraction = fraction.substring(0, fraction.length() - 1);
+ }
+ if (fraction.length() > 0) {
+ v = v + "." + fraction;
+ }
+ v = v + this.v.substring(8); // time-zone
+ return new TimeWithTimeZoneString(v);
+ }
+
+ public TimeWithTimeZoneString withTimeZone(TimeZone timeZone) {
+ if (this.timeZone.equals(timeZone)) {
+ return this;
+ }
+ String localTimeString = localTime.toString();
+ String v;
+ String fraction;
+ int i = localTimeString.indexOf('.');
+ if (i >= 0) {
+ v = localTimeString.substring(0, i);
+ fraction = localTimeString.substring(i + 1);
+ } else {
+ v = localTimeString;
+ fraction = null;
+ }
+ final DateTimeUtils.PrecisionTime pt =
+ DateTimeUtils.parsePrecisionDateTimeLiteral(v,
+ new SimpleDateFormat(DateTimeUtils.TIME_FORMAT_STRING, Locale.ROOT),
+ this.timeZone, -1);
+ pt.getCalendar().setTimeZone(timeZone);
+ if (fraction != null) {
+ return new TimeWithTimeZoneString(
+ pt.getCalendar().get(Calendar.HOUR_OF_DAY),
+ pt.getCalendar().get(Calendar.MINUTE),
+ pt.getCalendar().get(Calendar.SECOND),
+ timeZone.getID())
+ .withFraction(fraction);
+ }
+ return new TimeWithTimeZoneString(
+ pt.getCalendar().get(Calendar.HOUR_OF_DAY),
+ pt.getCalendar().get(Calendar.MINUTE),
+ pt.getCalendar().get(Calendar.SECOND),
+ timeZone.getID());
+ }
+
+ @Override public String toString() {
+ return v;
+ }
+
+ @Override public boolean equals(Object o) {
+ // The value is in canonical form (no trailing zeros).
+ return o == this
+ || o instanceof TimeWithTimeZoneString
+ && ((TimeWithTimeZoneString) o).v.equals(v);
+ }
+
+ @Override public int hashCode() {
+ return v.hashCode();
+ }
+
+ @Override public int compareTo(TimeWithTimeZoneString o) {
+ return v.compareTo(o.v);
+ }
+
+ public TimeWithTimeZoneString round(int precision) {
+ Preconditions.checkArgument(precision >= 0);
+ return new TimeWithTimeZoneString(
+ localTime.round(precision), timeZone);
+ }
+
+ public static TimeWithTimeZoneString fromMillisOfDay(int i) {
+ return new TimeWithTimeZoneString(
+ DateTimeUtils.unixTimeToString(i) + " " + DateTimeUtils.UTC_ZONE.getID())
+ .withMillis((int) DateTimeUtils.floorMod(i, 1000));
+ }
+
+ /** Converts this TimeWithTimeZoneString to a string, truncated or padded with
+ * zeroes to a given precision. */
+ public String toString(int precision) {
+ Preconditions.checkArgument(precision >= 0);
+ return localTime.toString(precision) + " " + timeZone.getID();
+ }
+
+ public TimeString getLocalTimeString() {
+ return localTime;
+ }
+
+}
+
+// End TimeWithTimeZoneString.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/TimestampString.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/TimestampString.java b/core/src/main/java/org/apache/calcite/util/TimestampString.java
index 4e392f0..604d5a4 100644
--- a/core/src/main/java/org/apache/calcite/util/TimestampString.java
+++ b/core/src/main/java/org/apache/calcite/util/TimestampString.java
@@ -47,7 +47,7 @@ public class TimestampString implements Comparable<TimestampString> {
/** Creates a TimestampString for year, month, day, hour, minute, second,
* millisecond values. */
public TimestampString(int year, int month, int day, int h, int m, int s) {
- this(ymdhms(new StringBuilder(), year, month, day, h, m, s).toString());
+ this(DateTimeStringUtils.ymdhms(new StringBuilder(), year, month, day, h, m, s).toString());
}
/** Sets the fraction field of a {@code TimestampString} to a given number
@@ -58,7 +58,7 @@ public class TimestampString implements Comparable<TimestampString> {
* yields {@code TIMESTAMP '1970-01-01 02:03:04.056'}. */
public TimestampString withMillis(int millis) {
Preconditions.checkArgument(millis >= 0 && millis < 1000);
- return withFraction(pad(3, millis));
+ return withFraction(DateTimeStringUtils.pad(3, millis));
}
/** Sets the fraction field of a {@code TimestampString} to a given number
@@ -69,7 +69,7 @@ public class TimestampString implements Comparable<TimestampString> {
* yields {@code TIMESTAMP '1970-01-01 02:03:04.000056789'}. */
public TimestampString withNanos(int nanos) {
Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000);
- return withFraction(pad(9, nanos));
+ return withFraction(DateTimeStringUtils.pad(9, nanos));
}
/** Sets the fraction field of a {@code TimestampString}.
@@ -113,44 +113,6 @@ public class TimestampString implements Comparable<TimestampString> {
return v.compareTo(o.v);
}
- static StringBuilder hms(StringBuilder b, int h, int m, int s) {
- int2(b, h);
- b.append(':');
- int2(b, m);
- b.append(':');
- int2(b, s);
- return b;
- }
-
- static StringBuilder ymdhms(StringBuilder b, int year, int month, int day,
- int h, int m, int s) {
- ymd(b, year, month, day);
- b.append(' ');
- hms(b, h, m, s);
- return b;
- }
-
- static StringBuilder ymd(StringBuilder b, int year, int month, int day) {
- int4(b, year);
- b.append('-');
- int2(b, month);
- b.append('-');
- int2(b, day);
- return b;
- }
-
- private static void int4(StringBuilder buf, int i) {
- buf.append((char) ('0' + (i / 1000) % 10));
- buf.append((char) ('0' + (i / 100) % 10));
- buf.append((char) ('0' + (i / 10) % 10));
- buf.append((char) ('0' + i % 10));
- }
-
- private static void int2(StringBuilder buf, int i) {
- buf.append((char) ('0' + (i / 10) % 10));
- buf.append((char) ('0' + i % 10));
- }
-
/** Creates a TimestampString from a Calendar. */
public static TimestampString fromCalendarFields(Calendar calendar) {
return new TimestampString(
@@ -214,14 +176,6 @@ public class TimestampString implements Comparable<TimestampString> {
.withMillis((int) DateTimeUtils.floorMod(millis, 1000));
}
- static String pad(int length, long v) {
- StringBuilder s = new StringBuilder(Long.toString(v));
- while (s.length() < length) {
- s.insert(0, "0");
- }
- return s.toString();
- }
-
public Calendar toCalendar() {
return Util.calendar(getMillisSinceEpoch());
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java b/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
new file mode 100644
index 0000000..3f32762
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
@@ -0,0 +1,194 @@
+/*
+ * 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.calcite.util;
+
+import org.apache.calcite.avatica.util.DateTimeUtils;
+
+import com.google.common.base.Preconditions;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Timestamp with time-zone literal.
+ *
+ * <p>Immutable, internally represented as a string (in ISO format),
+ * and can support unlimited precision (milliseconds, nanoseconds).
+ */
+public class TimestampWithTimeZoneString
+ implements Comparable<TimestampWithTimeZoneString> {
+
+ final TimestampString localDateTime;
+ final TimeZone timeZone;
+ final String v;
+
+ /** Creates a TimestampWithTimeZoneString. */
+ public TimestampWithTimeZoneString(TimestampString localDateTime, TimeZone timeZone) {
+ this.localDateTime = localDateTime;
+ this.timeZone = timeZone;
+ this.v = localDateTime.toString() + " " + timeZone.getID();
+ }
+
+ /** Creates a TimestampWithTimeZoneString. */
+ public TimestampWithTimeZoneString(String v) {
+ this.localDateTime = new TimestampString(v.substring(0, v.indexOf(' ', 11)));
+ String timeZoneString = v.substring(v.indexOf(' ', 11) + 1);
+ Preconditions.checkArgument(DateTimeStringUtils.isValidTimeZone(timeZoneString));
+ this.timeZone = TimeZone.getTimeZone(timeZoneString);
+ this.v = v;
+ }
+
+ /** Creates a TimestampWithTimeZoneString for year, month, day, hour, minute, second,
+ * millisecond values in the given time-zone. */
+ public TimestampWithTimeZoneString(int year, int month, int day, int h, int m, int s,
+ String timeZone) {
+ this(DateTimeStringUtils.ymdhms(new StringBuilder(), year, month, day, h, m, s).toString()
+ + " " + timeZone);
+ }
+
+ /** Sets the fraction field of a {@code TimestampWithTimeZoneString} to a given number
+ * of milliseconds. Nukes the value set via {@link #withNanos}.
+ *
+ * <p>For example,
+ * {@code new TimestampWithTimeZoneString(1970, 1, 1, 2, 3, 4, "GMT").withMillis(56)}
+ * yields {@code TIMESTAMP WITH LOCAL TIME ZONE '1970-01-01 02:03:04.056 GMT'}. */
+ public TimestampWithTimeZoneString withMillis(int millis) {
+ Preconditions.checkArgument(millis >= 0 && millis < 1000);
+ return withFraction(DateTimeStringUtils.pad(3, millis));
+ }
+
+ /** Sets the fraction field of a {@code TimestampWithTimeZoneString} to a given number
+ * of nanoseconds. Nukes the value set via {@link #withMillis(int)}.
+ *
+ * <p>For example,
+ * {@code new TimestampWithTimeZoneString(1970, 1, 1, 2, 3, 4, "GMT").withNanos(56789)}
+ * yields {@code TIMESTAMP WITH LOCAL TIME ZONE '1970-01-01 02:03:04.000056789 GMT'}. */
+ public TimestampWithTimeZoneString withNanos(int nanos) {
+ Preconditions.checkArgument(nanos >= 0 && nanos < 1000000000);
+ return withFraction(DateTimeStringUtils.pad(9, nanos));
+ }
+
+ /** Sets the fraction field of a {@code TimestampString}.
+ * The precision is determined by the number of leading zeros.
+ * Trailing zeros are stripped.
+ *
+ * <p>For example, {@code
+ * new TimestampWithTimeZoneString(1970, 1, 1, 2, 3, 4, "GMT").withFraction("00506000")}
+ * yields {@code TIMESTAMP WITH LOCAL TIME ZONE '1970-01-01 02:03:04.00506 GMT'}. */
+ public TimestampWithTimeZoneString withFraction(String fraction) {
+ return new TimestampWithTimeZoneString(
+ localDateTime.withFraction(fraction), timeZone);
+ }
+
+ public TimestampWithTimeZoneString withTimeZone(TimeZone timeZone) {
+ if (this.timeZone.equals(timeZone)) {
+ return this;
+ }
+ String localDateTimeString = localDateTime.toString();
+ String v;
+ String fraction;
+ int i = localDateTimeString.indexOf('.');
+ if (i >= 0) {
+ v = localDateTimeString.substring(0, i);
+ fraction = localDateTimeString.substring(i + 1);
+ } else {
+ v = localDateTimeString;
+ fraction = null;
+ }
+ final DateTimeUtils.PrecisionTime pt =
+ DateTimeUtils.parsePrecisionDateTimeLiteral(v,
+ new SimpleDateFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING, Locale.ROOT),
+ this.timeZone, -1);
+ pt.getCalendar().setTimeZone(timeZone);
+ if (fraction != null) {
+ return new TimestampWithTimeZoneString(
+ pt.getCalendar().get(Calendar.YEAR),
+ pt.getCalendar().get(Calendar.MONTH) + 1,
+ pt.getCalendar().get(Calendar.DAY_OF_MONTH),
+ pt.getCalendar().get(Calendar.HOUR_OF_DAY),
+ pt.getCalendar().get(Calendar.MINUTE),
+ pt.getCalendar().get(Calendar.SECOND),
+ timeZone.getID())
+ .withFraction(fraction);
+ }
+ return new TimestampWithTimeZoneString(
+ pt.getCalendar().get(Calendar.YEAR),
+ pt.getCalendar().get(Calendar.MONTH) + 1,
+ pt.getCalendar().get(Calendar.DAY_OF_MONTH),
+ pt.getCalendar().get(Calendar.HOUR_OF_DAY),
+ pt.getCalendar().get(Calendar.MINUTE),
+ pt.getCalendar().get(Calendar.SECOND),
+ timeZone.getID());
+ }
+
+ @Override public String toString() {
+ return v;
+ }
+
+ @Override public boolean equals(Object o) {
+ // The value is in canonical form (no trailing zeros).
+ return o == this
+ || o instanceof TimestampWithTimeZoneString
+ && ((TimestampWithTimeZoneString) o).v.equals(v);
+ }
+
+ @Override public int hashCode() {
+ return v.hashCode();
+ }
+
+ @Override public int compareTo(TimestampWithTimeZoneString o) {
+ return v.compareTo(o.v);
+ }
+
+ public TimestampWithTimeZoneString round(int precision) {
+ Preconditions.checkArgument(precision >= 0);
+ return new TimestampWithTimeZoneString(
+ localDateTime.round(precision), timeZone);
+ }
+
+ /** Creates a TimestampWithTimeZoneString that is a given number of milliseconds since
+ * the epoch UTC. */
+ public static TimestampWithTimeZoneString fromMillisSinceEpoch(long millis) {
+ return new TimestampWithTimeZoneString(
+ DateTimeUtils.unixTimestampToString(millis) + " " + DateTimeUtils.UTC_ZONE.getID())
+ .withMillis((int) DateTimeUtils.floorMod(millis, 1000));
+ }
+
+ /** Converts this TimestampWithTimeZoneString to a string, truncated or padded with
+ * zeroes to a given precision. */
+ public String toString(int precision) {
+ Preconditions.checkArgument(precision >= 0);
+ return localDateTime.toString(precision) + " " + timeZone.getID();
+ }
+
+ public DateString getLocalDateString() {
+ return new DateString(localDateTime.toString().substring(0, 10));
+ }
+
+ public TimeString getLocalTimeString() {
+ return new TimeString(localDateTime.toString().substring(11));
+ }
+
+ public TimestampString getLocalTimestampString() {
+ return localDateTime;
+ }
+
+}
+
+// End TimestampWithTimeZoneString.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
index 9dd3019..1e6f1a3 100644
--- a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
+++ b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
@@ -274,7 +274,7 @@ public class CalciteRemoteDriverTest {
@Test public void testRemoteTypeInfo() throws Exception {
CalciteAssert.hr().with(REMOTE_CONNECTION_FACTORY)
.metaData(GET_TYPEINFO)
- .returns(CalciteAssert.checkResultCount(is(43)));
+ .returns(CalciteAssert.checkResultCount(is(45)));
}
@Test public void testRemoteTableTypes() throws Exception {
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
index 0fd99f8..f9a61fc 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
@@ -24,11 +24,13 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
+import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import org.junit.Test;
import java.util.Calendar;
+import java.util.TimeZone;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.core.Is.is;
@@ -176,6 +178,77 @@ public class RexBuilderTest {
assertThat(literal.getValueAs(TimestampString.class), notNullValue());
}
+ /** Tests
+ * {@link RexBuilder#makeTimestampWithLocalTimeZoneLiteral(TimestampWithTimeZoneString, int)}. */
+ @Test public void testTimestampWithLocalTimeZoneLiteral() {
+ final RelDataTypeFactory typeFactory =
+ new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ final RelDataType timestampType =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
+ final RelDataType timestampType3 =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 3);
+ final RelDataType timestampType9 =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 9);
+ final RelDataType timestampType18 =
+ typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 18);
+ final RexBuilder builder = new RexBuilder(typeFactory);
+
+ // The new way
+ final TimestampWithTimeZoneString ts = new TimestampWithTimeZoneString(
+ 1969, 7, 21, 2, 56, 15, TimeZone.getTimeZone("PST").getID());
+ checkTimestampWithLocalTimeZone(
+ builder.makeLiteral(ts.getLocalTimestampString(), timestampType, false));
+
+ // Now with milliseconds
+ final TimestampWithTimeZoneString ts2 = ts.withMillis(56);
+ assertThat(ts2.toString(), is("1969-07-21 02:56:15.056 PST"));
+ final RexNode literal2 = builder.makeLiteral(
+ ts2.getLocalTimestampString(), timestampType3, false);
+ assertThat(((RexLiteral) literal2).getValue().toString(), is("1969-07-21 02:56:15.056"));
+
+ // Now with nanoseconds
+ final TimestampWithTimeZoneString ts3 = ts.withNanos(56);
+ final RexNode literal3 = builder.makeLiteral(
+ ts3.getLocalTimestampString(), timestampType9, false);
+ assertThat(((RexLiteral) literal3).getValueAs(TimestampString.class)
+ .toString(), is("1969-07-21 02:56:15"));
+ final TimestampWithTimeZoneString ts3b = ts.withNanos(2345678);
+ final RexNode literal3b = builder.makeLiteral(
+ ts3b.getLocalTimestampString(), timestampType9, false);
+ assertThat(((RexLiteral) literal3b).getValueAs(TimestampString.class)
+ .toString(), is("1969-07-21 02:56:15.002"));
+
+ // Now with a very long fraction
+ final TimestampWithTimeZoneString ts4 = ts.withFraction("102030405060708090102");
+ final RexNode literal4 = builder.makeLiteral(
+ ts4.getLocalTimestampString(), timestampType18, false);
+ assertThat(((RexLiteral) literal4).getValueAs(TimestampString.class)
+ .toString(), is("1969-07-21 02:56:15.102"));
+
+ // toString
+ assertThat(ts2.round(1).toString(), is("1969-07-21 02:56:15 PST"));
+ assertThat(ts2.round(2).toString(), is("1969-07-21 02:56:15.05 PST"));
+ assertThat(ts2.round(3).toString(), is("1969-07-21 02:56:15.056 PST"));
+ assertThat(ts2.round(4).toString(), is("1969-07-21 02:56:15.056 PST"));
+
+ assertThat(ts2.toString(6), is("1969-07-21 02:56:15.056000 PST"));
+ assertThat(ts2.toString(1), is("1969-07-21 02:56:15.0 PST"));
+ assertThat(ts2.toString(0), is("1969-07-21 02:56:15 PST"));
+
+ assertThat(ts2.round(0).toString(), is("1969-07-21 02:56:15 PST"));
+ assertThat(ts2.round(0).toString(0), is("1969-07-21 02:56:15 PST"));
+ assertThat(ts2.round(0).toString(1), is("1969-07-21 02:56:15.0 PST"));
+ assertThat(ts2.round(0).toString(2), is("1969-07-21 02:56:15.00 PST"));
+ }
+
+ private void checkTimestampWithLocalTimeZone(RexNode node) {
+ assertThat(node.toString(), is("1969-07-21 02:56:15"));
+ RexLiteral literal = (RexLiteral) node;
+ assertThat(literal.getValue() instanceof TimestampString, is(true));
+ assertThat(literal.getValue2() instanceof Long, is(true));
+ assertThat(literal.getValue3() instanceof Long, is(true));
+ }
+
/** Tests {@link RexBuilder#makeTimeLiteral(TimeString, int)}. */
@Test public void testTimeLiteral() {
final RelDataTypeFactory typeFactory =
http://git-wip-us.apache.org/repos/asf/calcite/blob/939c9a62/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index 6db9435..3e6b414 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -548,6 +548,9 @@ public class CalciteAssert {
calciteConnection.getProperties().setProperty(
CalciteConnectionProperty.CREATE_MATERIALIZATIONS.camelName(),
Boolean.toString(materializationsEnabled));
+ calciteConnection.getProperties().setProperty(
+ CalciteConnectionProperty.TIME_ZONE.camelName(),
+ DateTimeUtils.UTC_ZONE.getID());
}
for (Pair<Hook, Function> hook : hooks) {
closer.add(hook.left.addThread(hook.right));