You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2014/05/20 20:46:07 UTC
[09/48] git commit: TAJO-761: Implements INTERVAL type. (Hyoungjun
Kim via hyunsik)
TAJO-761: Implements INTERVAL type. (Hyoungjun Kim via hyunsik)
Project: http://git-wip-us.apache.org/repos/asf/tajo/repo
Commit: http://git-wip-us.apache.org/repos/asf/tajo/commit/5bbb7e82
Tree: http://git-wip-us.apache.org/repos/asf/tajo/tree/5bbb7e82
Diff: http://git-wip-us.apache.org/repos/asf/tajo/diff/5bbb7e82
Branch: refs/heads/window_function
Commit: 5bbb7e822ecde5b783ed1d6210b0e9fe9262d054
Parents: 8c92a9d
Author: Hyunsik Choi <hy...@apache.org>
Authored: Thu Apr 24 21:03:21 2014 +0900
Committer: Hyunsik Choi <hy...@apache.org>
Committed: Thu Apr 24 21:03:21 2014 +0900
----------------------------------------------------------------------
CHANGES | 4 +
.../apache/tajo/algebra/IntervalLiteral.java | 46 ++
.../java/org/apache/tajo/algebra/OpType.java | 3 +-
.../java/org/apache/tajo/datum/DateDatum.java | 75 +++-
.../org/apache/tajo/datum/DatumFactory.java | 8 +
.../java/org/apache/tajo/datum/Float4Datum.java | 7 +
.../java/org/apache/tajo/datum/Float8Datum.java | 7 +
.../java/org/apache/tajo/datum/Int2Datum.java | 7 +
.../java/org/apache/tajo/datum/Int4Datum.java | 7 +
.../java/org/apache/tajo/datum/Int8Datum.java | 8 +
.../org/apache/tajo/datum/IntervalDatum.java | 434 +++++++++++++++++++
.../java/org/apache/tajo/datum/TimeDatum.java | 31 ++
.../org/apache/tajo/datum/TimestampDatum.java | 58 ++-
.../java/org/apache/tajo/json/DatumAdapter.java | 8 +
.../org/apache/tajo/util/TimeStampUtil.java | 1 -
.../apache/tajo/datum/TestIntervalDatum.java | 199 +++++++++
.../apache/tajo/datum/TestTimestampDatum.java | 3 +
.../org/apache/tajo/engine/parser/SQLLexer.g4 | 1 +
.../org/apache/tajo/engine/parser/SQLParser.g4 | 7 +
.../org/apache/tajo/engine/eval/BinaryEval.java | 40 ++
.../tajo/engine/function/datetime/ToDate.java | 71 +++
.../apache/tajo/engine/parser/SQLAnalyzer.java | 13 +-
.../tajo/engine/planner/AlgebraVisitor.java | 1 +
.../tajo/engine/planner/BaseAlgebraVisitor.java | 9 +-
.../tajo/engine/planner/ExprAnnotator.java | 5 +
.../tajo/engine/planner/ExprNormalizer.java | 20 +-
.../tajo/engine/planner/ExprsVerifier.java | 37 +-
.../tajo/engine/eval/TestIntervalType.java | 96 ++++
.../engine/function/TestDateTimeFunctions.java | 6 +
.../org/apache/tajo/storage/RowStoreUtil.java | 12 +
.../storage/TextSerializerDeserializer.java | 5 +
31 files changed, 1212 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index d7a17d3..00f00ad 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,10 @@ Tajo Change Log
Release 0.9.0 - unreleased
+ NEW FEATURES
+
+ TAJO-761: Implements INTERVAL type. (Hyoungjun Kim via hyunsik)
+
IMPROVEMENT
TAJO-769: A minor improvements for HCatalogStore (Fengdong Yu via hyunsik)
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java
new file mode 100644
index 0000000..6e0f2d0
--- /dev/null
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java
@@ -0,0 +1,46 @@
+/**
+ * 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.tajo.algebra;
+
+public class IntervalLiteral extends Expr {
+ private String exprStr;
+
+ public IntervalLiteral(String str) {
+ super(OpType.IntervalLiteral);
+ this.exprStr = str;
+ }
+
+
+ public String getExprStr() {
+ return exprStr;
+ }
+
+ public int hashCode() {
+ return exprStr.hashCode();
+ }
+
+ @Override
+ boolean equalsTo(Expr expr) {
+ if (expr instanceof IntervalLiteral) {
+ IntervalLiteral another = (IntervalLiteral) expr;
+ return exprStr.equals(another.exprStr);
+ }
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
index a62ccfd..aeb28f0 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
@@ -105,7 +105,8 @@ public enum OpType {
NullLiteral(NullLiteral.class),
TimeLiteral(TimeLiteral.class),
DateLiteral(DateLiteral.class),
- TimestampLiteral(TimestampLiteral.class);
+ TimestampLiteral(TimestampLiteral.class),
+ IntervalLiteral(IntervalLiteral.class);
private Class baseClass;
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
index c327931..6b155ac 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
@@ -22,7 +22,7 @@ import org.apache.tajo.common.TajoDataTypes;
import org.apache.tajo.datum.exception.InvalidCastException;
import org.apache.tajo.datum.exception.InvalidOperationException;
import org.apache.tajo.util.Bytes;
-import org.joda.time.LocalDate;
+import org.joda.time.*;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
@@ -53,6 +53,11 @@ public class DateDatum extends Datum {
this.date = date;
}
+ public LocalDate getDate() {
+ //LocalDate is immutable
+ return date;
+ }
+
public DateDatum(byte [] bytes) {
this(Bytes.toInt(bytes));
}
@@ -93,6 +98,74 @@ public class DateDatum extends Datum {
return asChars();
}
+ public Datum plus(Datum datum) {
+ switch(datum.type()) {
+ case INT2:
+ case INT4:
+ case INT8:
+ case FLOAT4:
+ case FLOAT8:
+ return new DateDatum(date.plusDays(datum.asInt2()));
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ LocalDate localDate;
+ if (interval.getMonths() > 0) {
+ localDate = date.plusMonths(interval.getMonths());
+ } else {
+ localDate = date;
+ }
+ return new TimestampDatum(localDate.toDateTimeAtStartOfDay().getMillis() + interval.getMilliSeconds());
+ case TIME:
+ return new TimestampDatum(createDateTime(date, ((TimeDatum)datum).getTime(), true));
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ public Datum minus(Datum datum) {
+ switch(datum.type()) {
+ case INT2:
+ case INT4:
+ case INT8:
+ case FLOAT4:
+ case FLOAT8:
+ return new DateDatum(date.minusDays(datum.asInt2()));
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ LocalDate localDate;
+ if (interval.getMonths() > 0) {
+ localDate = date.minusMonths(interval.getMonths());
+ } else {
+ localDate = date;
+ }
+ return new TimestampDatum(localDate.toDateTimeAtStartOfDay().getMillis() - interval.getMilliSeconds());
+ case TIME:
+ return new TimestampDatum(createDateTime(date, ((TimeDatum)datum).getTime(), false));
+ case DATE:
+ return new Int4Datum(Days.daysBetween(((DateDatum)datum).date, date).getDays());
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ public static DateTime createDateTime(LocalDate date, LocalTime time, boolean plus) {
+ //TODO create too many temporary instance. This must be improved.
+ DateTime dateTime = new DateTime(date.toDate().getTime());
+ if (plus) {
+ return dateTime
+ .plusHours(time.getHourOfDay())
+ .plusMinutes(time.getMinuteOfHour())
+ .plusSeconds(time.getSecondOfMinute())
+ .plusMillis(time.getMillisOfSecond());
+ } else {
+ return dateTime
+ .minusHours(time.getHourOfDay())
+ .minusMinutes(time.getMinuteOfHour())
+ .minusSeconds(time.getSecondOfMinute())
+ .minusMillis(time.getMillisOfSecond());
+ }
+ }
+
@Override
public int asInt4() {
return encode();
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java b/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
index 2ddea9e..4eacfc3 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
@@ -51,6 +51,8 @@ public class DatumFactory {
return TextDatum.class;
case TIMESTAMP:
return TimestampDatum.class;
+ case INTERVAL:
+ return IntervalDatum.class;
case DATE:
return DateDatum.class;
case TIME:
@@ -95,6 +97,8 @@ public class DatumFactory {
return createTime(value);
case TIMESTAMP:
return createTimeStamp(value);
+ case INTERVAL:
+ return createInterval(value);
case BLOB:
return createBlob(value);
case INET4:
@@ -282,6 +286,10 @@ public class DatumFactory {
return new TimestampDatum(timeStamp);
}
+ public static IntervalDatum createInterval(String intervalStr) {
+ return new IntervalDatum(intervalStr);
+ }
+
public static DateDatum createDate(Datum datum) {
switch (datum.type()) {
case INT4:
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
index 0c65dfc..b042f2d 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
@@ -210,6 +210,8 @@ public class Float4Datum extends NumericDatum {
return DatumFactory.createFloat8(val + datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val + datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -230,6 +232,8 @@ public class Float4Datum extends NumericDatum {
return DatumFactory.createFloat8(val - datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val - datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -250,6 +254,9 @@ public class Float4Datum extends NumericDatum {
return DatumFactory.createFloat8(val * datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val * datum.asFloat8());
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ return new IntervalDatum((int)(interval.getMonths() * val), (long)(interval.getMilliSeconds() * val));
case NULL_TYPE:
return datum;
default:
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
index 010cf29..55b079f 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
@@ -199,6 +199,8 @@ public class Float8Datum extends NumericDatum {
return DatumFactory.createFloat8(val + datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val + datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -219,6 +221,8 @@ public class Float8Datum extends NumericDatum {
return DatumFactory.createFloat8(val - datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val - datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -239,6 +243,9 @@ public class Float8Datum extends NumericDatum {
return DatumFactory.createFloat8(val * datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val * datum.asFloat8());
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ return new IntervalDatum((int)(interval.getMonths() * val), (long)(interval.getMilliSeconds() * val));
case NULL_TYPE:
return datum;
default:
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
index e24d7be..46616e7 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
@@ -201,6 +201,8 @@ public class Int2Datum extends NumericDatum {
return DatumFactory.createFloat4(val + datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val + datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt2()));
case NULL_TYPE:
return datum;
default:
@@ -221,6 +223,8 @@ public class Int2Datum extends NumericDatum {
return DatumFactory.createFloat4(val - datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val - datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt2()));
case NULL_TYPE:
return datum;
default:
@@ -241,6 +245,9 @@ public class Int2Datum extends NumericDatum {
return DatumFactory.createFloat4(val * datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val * datum.asFloat8());
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ return new IntervalDatum(interval.getMonths() * val, interval.getMilliSeconds() * val);
case NULL_TYPE:
return datum;
default:
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
index 9cd0c06..8296a0f 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
@@ -205,6 +205,8 @@ public class Int4Datum extends NumericDatum {
return DatumFactory.createFloat4(val + datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val + datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -225,6 +227,8 @@ public class Int4Datum extends NumericDatum {
return DatumFactory.createFloat4(val - datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val - datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -245,6 +249,9 @@ public class Int4Datum extends NumericDatum {
return DatumFactory.createFloat4(val * datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val * datum.asFloat8());
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ return new IntervalDatum(interval.getMonths() * val, interval.getMilliSeconds() * val);
case NULL_TYPE:
return datum;
default:
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
index 1104a3c..6d3e5b2 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
@@ -213,6 +213,8 @@ public class Int8Datum extends NumericDatum {
return DatumFactory.createFloat8(val + datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val + datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -233,6 +235,8 @@ public class Int8Datum extends NumericDatum {
return DatumFactory.createFloat8(val - datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val - datum.asFloat8());
+ case DATE:
+ return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
case NULL_TYPE:
return datum;
default:
@@ -253,6 +257,10 @@ public class Int8Datum extends NumericDatum {
return DatumFactory.createFloat8(val * datum.asFloat4());
case FLOAT8:
return DatumFactory.createFloat8(val * datum.asFloat8());
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+ int intVal = asInt4();
+ return new IntervalDatum(interval.getMonths() * intVal, interval.getMilliSeconds() * asInt8());
case NULL_TYPE:
return datum;
default:
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java
new file mode 100644
index 0000000..4e04f91
--- /dev/null
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java
@@ -0,0 +1,434 @@
+/**
+ * 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.tajo.datum;
+
+import com.google.common.base.Objects;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.exception.InvalidOperationException;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IntervalDatum extends Datum {
+ static final long MINUTE_MILLIS = 60 * 1000;
+ static final long HOUR_MILLIS = 60 * MINUTE_MILLIS;
+ static final long DAY_MILLIS = 24 * HOUR_MILLIS;
+ static final long MONTH_MILLIS = 30 * DAY_MILLIS;
+
+ static enum DATE_UNIT {
+ CENTURY, DECADE, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSEC, MILLISEC, TIMEZONE,
+ }
+ static Map<String, DATE_UNIT> DATE_FORMAT_LITERAL = new HashMap<String, DATE_UNIT>();
+ static {
+ Object[][] dateFormatLiterals = new Object[][]{
+ {DATE_UNIT.CENTURY, "c,cent,centuries,century"},
+ {DATE_UNIT.DAY, "d,day,days"},
+ {DATE_UNIT.DECADE, "dec,decade,decades,decs"},
+ {DATE_UNIT.HOUR, "h,hour,hours,hr,hrs"},
+ {DATE_UNIT.MILLISEC, "millisecon,ms,msec,msecond,mseconds,msecs"},
+ {DATE_UNIT.MINUTE, "m,min,mins,minute,minutes"},
+ {DATE_UNIT.MONTH, "mon,mons,month,months"},
+ {DATE_UNIT.SECOND, "s,sec,second,seconds,secs"},
+ {DATE_UNIT.TIMEZONE, "timezone"},
+ {DATE_UNIT.MICROSEC, "microsecon,us,usec,microsecond,useconds,usecs"},
+ {DATE_UNIT.YEAR, "y,year,years,yr,yrs"}
+ };
+
+ for (Object[] eachLiteral: dateFormatLiterals) {
+ String[] tokens = ((String)eachLiteral[1]).split(",");
+ DATE_UNIT unit = (DATE_UNIT)eachLiteral[0];
+ for (String eachToken: tokens) {
+ DATE_FORMAT_LITERAL.put(eachToken, unit);
+ }
+ }
+ }
+
+ private int months;
+ private long millieconds;
+
+ public IntervalDatum(long milliseconds) {
+ this(0, milliseconds);
+ }
+
+ public IntervalDatum(int months, long milliseconds) {
+ super(TajoDataTypes.Type.INTERVAL);
+ this.months = months;
+ this.millieconds = milliseconds;
+ }
+
+ public IntervalDatum(String intervalStr) {
+ super(TajoDataTypes.Type.INTERVAL);
+
+ intervalStr = intervalStr.trim();
+ if (intervalStr.isEmpty()) {
+ throw new InvalidOperationException("interval expression is empty");
+ }
+
+ try {
+ int year = 0;
+ int month = 0;
+ int day = 0;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ int microsecond = 0;
+ int millisecond = 0;
+ long time = 0;
+
+ int length = intervalStr.getBytes().length;
+
+ int start = 0;
+ StringBuilder digitChars = new StringBuilder();
+ StringBuilder unitChars = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ char c = intervalStr.charAt(i);
+ if (Character.isDigit(c) || c == ':' || c == '.' || c == '-') {
+ digitChars.append(c);
+ } else if (c == ' ') {
+ if (digitChars.length() > 0) {
+ if (unitChars.length() > 0) {
+ String unitName = unitChars.toString();
+ DATE_UNIT foundUnit = DATE_FORMAT_LITERAL.get(unitName);
+ if (foundUnit == null) {
+ throw new InvalidOperationException("invalid input syntax for type interval: " + intervalStr);
+ }
+ int digit = Integer.parseInt(digitChars.toString());
+ switch(foundUnit) {
+ case YEAR: year = digit; break;
+ case MONTH: month = digit; break;
+ case DAY: day = digit; break;
+ case HOUR: hour = digit; break;
+ case MINUTE: minute = digit; break;
+ case SECOND: second = digit; break;
+ case MICROSEC: microsecond = digit; break;
+ case MILLISEC: millisecond = digit; break;
+ default: throw new InvalidOperationException("Unknown datetime unit: " + foundUnit);
+ }
+ digitChars.setLength(0);
+ unitChars.setLength(0);
+ } else if (digitChars.indexOf(":") >= 0) {
+ time = parseTime(digitChars.toString());
+ digitChars.setLength(0);
+ }
+ }
+ } else {
+ unitChars.append(c);
+ }
+ }
+ if (digitChars.length() > 0) {
+ if (unitChars.length() > 0) {
+ String unitName = unitChars.toString();
+ DATE_UNIT foundUnit = DATE_FORMAT_LITERAL.get(unitName);
+ if (foundUnit == null) {
+ throw new InvalidOperationException("invalid input syntax for type interval: " + intervalStr);
+ }
+ int digit = Integer.parseInt(digitChars.toString());
+ switch(foundUnit) {
+ case YEAR: year = digit; break;
+ case MONTH: month = digit; break;
+ case DAY: day = digit; break;
+ case HOUR: hour = digit; break;
+ case MINUTE: minute = digit; break;
+ case SECOND: second = digit; break;
+ case MICROSEC: microsecond = digit; break;
+ case MILLISEC: millisecond = digit; break;
+ default: throw new InvalidOperationException("Unknown datetime unit: " + foundUnit);
+ }
+ } else if (digitChars.indexOf(":") >= 0) {
+ time = parseTime(digitChars.toString());
+ digitChars.setLength(0);
+ }
+ }
+
+ if (time > 0 && (hour != 0 || minute != 0 || second != 0 || microsecond != 0 || millisecond != 0)) {
+ throw new InvalidOperationException("invalid input syntax for type interval: " + intervalStr);
+ }
+
+ this.millieconds = time + day * DAY_MILLIS + hour * HOUR_MILLIS + minute * 60 * 1000L + second * 1000L +
+ microsecond * 100L + millisecond;
+ this.months = year * 12 + month;
+ } catch (InvalidOperationException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new InvalidOperationException(t.getMessage() + ": " + intervalStr);
+ }
+ }
+
+ public static long parseTime(String timeStr) {
+ //parse HH:mm:ss.SSS
+ int hour = 0;
+ int minute = 0;
+
+ int second = 0;
+ int millisecond = 0;
+ String[] timeTokens = timeStr.split(":");
+ if (timeTokens.length == 1) {
+ //sec
+ String[] secondTokens = timeTokens[0].split("\\.");
+ if (secondTokens.length == 1) {
+ second = Integer.parseInt(secondTokens[0]);
+ } else if (secondTokens.length == 2) {
+ millisecond = Integer.parseInt(secondTokens[1]);
+ } else {
+ throw new InvalidOperationException("invalid input syntax for type interval: " + timeStr);
+ }
+ } else {
+ if (timeTokens.length > 3) {
+ throw new InvalidOperationException("invalid input syntax for type interval: " + timeStr);
+ }
+ for(int i = 0; i < timeTokens.length - 1; i++) {
+ if (i == 0) hour = Integer.parseInt(timeTokens[i]);
+ if (i == 1) minute = Integer.parseInt(timeTokens[i]);
+ }
+ if (timeTokens.length == 3) {
+ //sec
+ String[] secondTokens = timeTokens[2].split("\\.");
+ if (secondTokens.length == 1) {
+ second = Integer.parseInt(secondTokens[0]);
+ } else if (secondTokens.length == 2) {
+ second = Integer.parseInt(secondTokens[0]);
+ millisecond = Integer.parseInt(secondTokens[1]);
+ } else {
+ throw new InvalidOperationException("invalid input syntax for type interval: " + timeStr);
+ }
+ }
+ }
+
+ return hour * HOUR_MILLIS+ minute * MINUTE_MILLIS + second * 1000 + millisecond;
+ }
+
+ public int getMonths() {
+ return this.months;
+ }
+
+ public long getMilliSeconds() {
+ return millieconds;
+ }
+
+ @Override
+ public Datum plus(Datum datum) {
+ switch(datum.type()) {
+ case INTERVAL:
+ IntervalDatum other = (IntervalDatum) datum;
+ return new IntervalDatum(months + other.months, millieconds + other.millieconds);
+ case DATE:
+ LocalDate date = ((DateDatum)datum).getDate();
+ LocalDate localDate;
+ if (months > 0) {
+ localDate = date.plusMonths(months);
+ } else {
+ localDate = date;
+ }
+ return new TimestampDatum(localDate.toDateTimeAtStartOfDay().getMillis() + millieconds);
+ case TIME:
+ LocalTime localTime = ((TimeDatum)datum).getTime();
+ localTime = localTime.plusMillis((int) millieconds);
+ return new TimeDatum(localTime);
+ case TIMESTAMP:
+ DateTime dateTime = ((TimestampDatum) datum).getDateTime();
+ if (months > 0) {
+ dateTime = dateTime.plusMonths(months);
+ }
+ if (millieconds > 0) {
+ dateTime = dateTime.plusMillis((int) millieconds);
+ }
+ return new TimestampDatum(dateTime);
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ @Override
+ public Datum minus(Datum datum) {
+ if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+ IntervalDatum other = (IntervalDatum) datum;
+ return new IntervalDatum(months - other.months, millieconds - other.millieconds);
+ } else {
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ @Override
+ public Datum multiply(Datum datum) {
+ switch(datum.type()) {
+ case INT2:
+ case INT4:
+ case INT8:
+ long int8Val = datum.asInt8();
+ return createIntervalDatum((double)months * int8Val, (double) millieconds * int8Val);
+ case FLOAT4:
+ case FLOAT8:
+ double float8Val = datum.asFloat8();
+ return createIntervalDatum((double)months * float8Val, (double) millieconds * float8Val);
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ @Override
+ public Datum divide(Datum datum) {
+ switch(datum.type()) {
+ case INT2:
+ case INT4:
+ case INT8:
+ long int8Val = datum.asInt8();
+ return createIntervalDatum((double)months / int8Val, (double) millieconds / int8Val);
+ case FLOAT4:
+ case FLOAT8:
+ double float8Val = datum.asFloat8();
+ return createIntervalDatum((double)months / float8Val, (double) millieconds / float8Val);
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ private IntervalDatum createIntervalDatum(double monthValue, double millisValue) {
+ int month = (int)(monthValue);
+ return new IntervalDatum(month, Math.round((monthValue - (double)month) * (double)MONTH_MILLIS + (double)millisValue));
+ }
+
+ @Override
+ public long asInt8() {
+ return (months * 30) * DAY_MILLIS + millieconds;
+ }
+
+ public String toString() {
+ return asChars();
+ }
+
+ static DecimalFormat df = new DecimalFormat("00");
+ static DecimalFormat df2 = new DecimalFormat("000");
+ @Override
+ public String asChars() {
+ try {
+ StringBuilder sb = new StringBuilder();
+
+ String prefix = "";
+
+ if (months != 0) {
+ int positiveNum = Math.abs(months);
+ int year = positiveNum / 12;
+ int remainMonth = positiveNum - year * 12;
+
+ if (year > 0) {
+ sb.append(months < 0 ? "-" : "");
+ sb.append(year).append(year == 1 ? " year" : " years");
+ prefix = " ";
+ }
+ sb.append(prefix).append(months < 0 ? "-" : "").append(remainMonth).append(months == 1 ? " month" : " months");
+ prefix = " ";
+ }
+
+ formatMillis(sb, prefix, millieconds);
+ return sb.toString();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ public static String formatMillis(long millis) {
+ StringBuilder sb = new StringBuilder();
+ formatMillis(sb, "", millis);
+ return sb.toString();
+ }
+
+ public static void formatMillis(StringBuilder sb, String prefix, long millis) {
+ if (millis != 0) {
+ long positiveNum = Math.abs(millis);
+ int days = (int)(positiveNum / DAY_MILLIS);
+ long remainInterval = positiveNum - days * DAY_MILLIS;
+
+ if(days != 0) {
+ sb.append(prefix).append(millis < 0 ? "-" : "").append(days).append(days == 1 ? " day" : " days");
+ prefix = " ";
+ }
+ if (remainInterval != 0) {
+ int hour = (int) (remainInterval / HOUR_MILLIS);
+ int minutes = (int) (remainInterval - hour * HOUR_MILLIS) / (60 * 1000);
+ long sec = (int) (remainInterval - hour * HOUR_MILLIS - minutes * (60 * 1000L)) / 1000L;
+ long millisecond = (int) (remainInterval - hour * HOUR_MILLIS - minutes * (60 * 1000L) - sec * 1000L);
+
+ sb.append(prefix)
+ .append(millis < 0 ? "-" : "")
+ .append(df.format(hour))
+ .append(":")
+ .append(df.format(minutes))
+ .append(":")
+ .append(df.format(sec));
+
+ if (millisecond > 0) {
+ sb.append(".").append(df2.format(millisecond));
+ }
+ }
+ }
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public int compareTo(Datum datum) {
+ if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+ long val = asInt8();
+ long another = datum.asInt8();
+ if (val < another) {
+ return -1;
+ } else if (val > another) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (datum instanceof NullDatum || datum.isNull()) {
+ return -1;
+ } else {
+ throw new InvalidOperationException();
+ }
+ }
+
+ @Override
+ public Datum equalsTo(Datum datum) {
+ if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+ return DatumFactory.createBool(asInt8() == datum.asInt8());
+ } else if (datum.isNull()) {
+ return datum;
+ } else {
+ throw new InvalidOperationException();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof IntervalDatum) {
+ return asInt8() == ((IntervalDatum)obj).asInt8();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode(){
+ return Objects.hashCode(asInt8());
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
index 21ae881..e7f1684 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
@@ -22,6 +22,7 @@ import org.apache.tajo.common.TajoDataTypes;
import org.apache.tajo.datum.exception.InvalidCastException;
import org.apache.tajo.datum.exception.InvalidOperationException;
import org.apache.tajo.util.Bytes;
+import org.joda.time.DateTime;
import org.joda.time.LocalTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
@@ -125,6 +126,32 @@ public class TimeDatum extends Datum {
return Bytes.toBytes(asInt8());
}
+ public Datum plus(Datum datum) {
+ switch(datum.type()) {
+ case INTERVAL:
+ IntervalDatum interval = ((IntervalDatum)datum);
+ return new TimeDatum(time.plusMillis((int)interval.getMilliSeconds()));
+ case DATE:
+ DateTime dateTime = DateDatum.createDateTime(((DateDatum)datum).getDate(), time, true);
+ return new TimestampDatum(dateTime);
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ public Datum minus(Datum datum) {
+ switch(datum.type()) {
+ case INTERVAL:
+ IntervalDatum interval = ((IntervalDatum)datum);
+ return new TimeDatum(time.minusMillis((int)interval.getMilliSeconds()));
+ case TIME:
+ return new IntervalDatum(
+ time.toDateTimeToday().getMillis() - ((TimeDatum)datum).getTime().toDateTimeToday().getMillis() );
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
@Override
public Datum equalsTo(Datum datum) {
if (datum.type() == TajoDataTypes.Type.TIME) {
@@ -160,4 +187,8 @@ public class TimeDatum extends Datum {
public int hashCode() {
return time.hashCode();
}
+
+ public LocalTime getTime() {
+ return time;
+ }
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
index 6411bec..219d5ca 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
@@ -41,6 +41,11 @@ public class TimestampDatum extends Datum {
dateTime = new DateTime((long)timestamp * 1000);
}
+ public TimestampDatum(long timestamp) {
+ super(TajoDataTypes.Type.TIMESTAMP);
+ dateTime = new DateTime(timestamp);
+ }
+
public TimestampDatum(DateTime dateTime) {
super(TajoDataTypes.Type.TIMESTAMP);
this.dateTime = dateTime;
@@ -53,7 +58,14 @@ public class TimestampDatum extends Datum {
public TimestampDatum(String datetime) {
super(TajoDataTypes.Type.TIMESTAMP);
- this.dateTime = DateTime.parse(datetime, DEFAULT_FORMATTER);
+
+ DateTime tmpDateTime = null;
+ try {
+ tmpDateTime = DateTime.parse(datetime, DEFAULT_FORMATTER);
+ } catch (IllegalArgumentException e) {
+ tmpDateTime = DateTime.parse(datetime, FRACTION_FORMATTER);
+ }
+ this.dateTime = tmpDateTime;
}
public int getUnixTime() {
@@ -125,6 +137,50 @@ public class TimestampDatum extends Datum {
}
@Override
+ public Datum plus(Datum datum) {
+ if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+ IntervalDatum interval = (IntervalDatum)datum;
+
+ DateTime plusDateTime = null;
+ if (interval.getMonths() > 0) {
+ plusDateTime = dateTime.plusMonths(interval.getMonths());
+ } else {
+ plusDateTime = dateTime;
+ }
+ if (interval.getMilliSeconds() > 0) {
+ plusDateTime = plusDateTime.plusMillis((int) interval.getMilliSeconds());
+ }
+ return new TimestampDatum(plusDateTime);
+
+ } else {
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ @Override
+ public Datum minus(Datum datum) {
+ switch(datum.type()) {
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)datum;
+
+ DateTime minusDateTime = null;
+ if (interval.getMonths() > 0) {
+ minusDateTime = dateTime.minusMonths(interval.getMonths());
+ } else {
+ minusDateTime = dateTime;
+ }
+ if (interval.getMilliSeconds() > 0) {
+ minusDateTime = minusDateTime.minusMillis((int)interval.getMilliSeconds());
+ }
+ return new TimestampDatum(minusDateTime);
+ case TIMESTAMP:
+ return new IntervalDatum(dateTime.getMillis() - ((TimestampDatum)datum).dateTime.getMillis());
+ default:
+ throw new InvalidOperationException(datum.type());
+ }
+ }
+
+ @Override
public long asInt8() {
return dateTime.getMillis();
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java b/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
index f24d213..2a162a9 100644
--- a/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
+++ b/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
@@ -40,6 +40,10 @@ public class DatumAdapter implements GsonSerDerAdapter<Datum> {
return new TimeDatum(jsonObject.get("value").getAsLong());
case TIMESTAMP:
return new TimestampDatum(new DateTime(jsonObject.get("value").getAsLong()));
+ case INTERVAL:
+ String[] values = jsonObject.get("value").getAsString().split(",");
+
+ return new IntervalDatum(Integer.parseInt(values[0]), Long.parseLong(values[1]));
default:
return context.deserialize(jsonObject.get("body"),
DatumFactory.getDatumClass(TajoDataTypes.Type.valueOf(typeName)));
@@ -60,6 +64,10 @@ public class DatumAdapter implements GsonSerDerAdapter<Datum> {
case TIMESTAMP:
jsonObj.addProperty("value", ((TimestampDatum)src).getMillis());
break;
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum)src;
+ jsonObj.addProperty("value", interval.getMonths() + "," + interval.getMilliSeconds());
+ break;
default:
jsonObj.add("body", context.serialize(src));
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java b/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
index 830c4aa..aa1be8b 100644
--- a/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
+++ b/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
@@ -63,5 +63,4 @@ public class TimeStampUtil {
public static long convertToMicroSeconds(DateTime dateTime) {
return dateTime.getMillis() * 1000;
}
-
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java b/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java
new file mode 100644
index 0000000..5936c31
--- /dev/null
+++ b/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java
@@ -0,0 +1,199 @@
+/**
+ * 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.tajo.datum;
+
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.exception.InvalidOperationException;
+import org.joda.time.DateTime;
+import org.joda.time.LocalTime;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class TestIntervalDatum {
+ @Test
+ public final void parseIntervalDatum() {
+ IntervalDatum datum = new IntervalDatum("3y 5month 10day 23:50:40.200");
+ assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+ datum = new IntervalDatum("23:50:40.200 5month 10day 3y");
+ assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+ datum = new IntervalDatum("3y 5month 10 day 23:50:40.200");
+ assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+ datum = new IntervalDatum("3years 5months 10 day 23:50:40.200");
+ assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+ datum = new IntervalDatum("5 hour");
+ assertEquals("05:00:00", datum.asChars());
+ try {
+ datum = new IntervalDatum("3years 5months 10 day 1h 23:50:40.200");
+ fail("hour and time format can not be used at the same time in interval.");
+ } catch (InvalidOperationException e) {
+ //success
+ }
+ }
+
+ @Test
+ public final void testAsText() {
+ IntervalDatum datum = new IntervalDatum(14,
+ IntervalDatum.DAY_MILLIS + 10 * IntervalDatum.HOUR_MILLIS + 20 * IntervalDatum.MINUTE_MILLIS + 30 * 1000 + 400);
+ assertEquals("1 year 2 months 1 day 10:20:30.400", datum.asChars());
+ }
+
+ @Test
+ public final void testOperation() throws Exception {
+ // http://www.postgresql.org/docs/8.2/static/functions-datetime.html
+
+ // date '2001-09-28' + integer '7' ==> date '2001-10-05'
+ Datum datum = new DateDatum(2001, 9, 28);
+ Datum[] datums = new Datum[]{new Int2Datum((short) 7), new Int4Datum(7), new Int8Datum(7),
+ new Float4Datum(7.0f), new Float8Datum(7.0f)};
+
+ for (int i = 0; i < datums.length; i++) {
+ Datum result = datum.plus(datums[i]);
+ assertEquals(TajoDataTypes.Type.DATE, result.type());
+ assertEquals(new DateDatum(2001, 10, 5), result);
+ }
+
+ // date '2001-09-28' + interval '1 hour' ==> timestamp '2001-09-28 01:00:00'
+ datum = new DateDatum(2001, 9, 28);
+ Datum result = datum.plus(new IntervalDatum(60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 28, 1, 0, 0, 0)), result);
+
+ // date '2001-09-28' + time '03:00' ==> timestamp '2001-09-28 03:00:00'
+ datum = new DateDatum(2001, 9, 28);
+ result = datum.plus(new TimeDatum(new LocalTime(3, 0)));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 28, 3, 0, 0, 0)), result);
+
+ // interval '1 day' + interval '1 hour' interval '1 day 01:00:00'
+ datum = new IntervalDatum(IntervalDatum.DAY_MILLIS);
+ result = datum.plus(new IntervalDatum(0, 1 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+ assertEquals("1 day 01:00:00", result.asChars());
+
+ // timestamp '2001-09-28 01:00' + interval '23 hours' ==> timestamp '2001-09-29 00:00:00'
+ datum = new TimestampDatum(new DateTime(2001, 9, 28, 1, 0, 0, 0));
+ result = datum.plus(new IntervalDatum(23 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 29, 0, 0, 0, 0)), result);
+
+ // time '01:00' + interval '3 hours' ==> time '04:00:00'
+ datum = new TimeDatum(new LocalTime(1, 0, 0, 0));
+ result = datum.plus(new IntervalDatum(3 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIME, result.type());
+ assertEquals(new TimeDatum(new LocalTime(4, 0, 0, 0)), result);
+
+// // - interval '23 hours' ==> interval '-23:00:00'
+// // TODO Currently Interval's inverseSign() not supported
+
+ // date '2001-10-01' - date '2001-09-28' ==> integer '3'
+ datum = new DateDatum(2001, 10, 01);
+ result = datum.minus(new DateDatum(2001, 9, 28));
+ assertEquals(TajoDataTypes.Type.INT4, result.type());
+ assertEquals(new Int4Datum(3), result);
+
+ // date '2001-10-01' - integer '7' ==> date '2001-09-24'
+ datum = new DateDatum(2001, 10, 01);
+ for (int i = 0; i < datums.length; i++) {
+ Datum result2 = datum.minus(datums[i]);
+ assertEquals(TajoDataTypes.Type.DATE, result2.type());
+ assertEquals(new DateDatum(2001, 9, 24), result2);
+ }
+
+ // date '2001-09-28' - interval '1 hour' ==> timestamp '2001-09-27 23:00:00'
+ datum = new DateDatum(2001, 9, 28);
+ result = datum.minus(new IntervalDatum(1 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 27, 23, 0, 0, 0)), result);
+
+ // date '2001-09-28' - interval '1 day 1 hour' ==> timestamp '2001-09-26 23:00:00'
+ datum = new DateDatum(2001, 9, 28);
+ result = datum.minus(new IntervalDatum(IntervalDatum.DAY_MILLIS + 1 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 26, 23, 0, 0, 0)), result);
+
+ // time '05:00' - time '03:00' ==> interval '02:00:00'
+ datum = new TimeDatum(new LocalTime(5, 0, 0, 0));
+ result = datum.minus(new TimeDatum(new LocalTime(3, 0, 0, 0)));
+ assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+ assertEquals(new IntervalDatum(2 * 60 * 60 * 1000), result);
+
+ // time '05:00' - interval '2 hours' ==> time '03:00:00'
+ datum = new TimeDatum(new LocalTime(5, 0, 0, 0));
+ result = datum.minus(new IntervalDatum(2 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIME, result.type());
+ assertEquals(new TimeDatum(3, 0, 0, 0), result);
+
+ // timestamp '2001-09-28 23:00' - interval '23 hours' ==> timestamp '2001-09-28 00:00:00'
+ datum = new TimestampDatum(new DateTime(2001, 9, 28, 23, 0, 0, 0));
+ result = datum.minus(new IntervalDatum(23 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 28, 0, 0, 0, 0)), result);
+
+ // interval '1 day' - interval '1 hour' ==> interval '1 day -01:00:00'
+ datum = new IntervalDatum(IntervalDatum.DAY_MILLIS);
+ result = datum.minus(new IntervalDatum(1 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+ assertEquals(new IntervalDatum(23 * 60 * 60 * 1000), result);
+
+ // timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00' ==> interval '1 day 15:00:00'
+ datum = new TimestampDatum(new DateTime(2001, 9, 29, 3, 0, 0, 0));
+ result = datum.minus(new TimestampDatum(new DateTime(2001, 9, 27, 12, 0, 0, 0)));
+ assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+ assertEquals(new IntervalDatum(IntervalDatum.DAY_MILLIS + 15 * 60 * 60 * 1000), result);
+
+ // 900 * interval '1 second' ==> interval '00:15:00'
+ Datum[] datum900 = new Datum[]{new Int2Datum((short)900), new Int4Datum(900), new Int8Datum(900),
+ new Float4Datum(900.0f), new Float8Datum(900.0f)};
+
+ datum = new IntervalDatum(1000);
+ for (int i = 0; i < datum900.length; i++) {
+ Datum result2 = datum.multiply(datum900[i]);
+ assertEquals(TajoDataTypes.Type.INTERVAL, result2.type());
+ assertEquals(new IntervalDatum(15 * 60 * 1000), result2);
+
+ result2 = datum900[i].multiply(datum);
+ assertEquals(TajoDataTypes.Type.INTERVAL, result2.type());
+ assertEquals(new IntervalDatum(15 * 60 * 1000), result2);
+ }
+
+ // double precision '3.5' * interval '1 hour' ==> interval '03:30:00'
+ datum = new Float8Datum(3.5f);
+ result = datum.multiply(new IntervalDatum(1 * 60 * 60 * 1000));
+ assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+ assertEquals(new IntervalDatum(3 * 60 * 60 * 1000 + 30 * 60 * 1000), result);
+
+ // interval '1 hour' / double precision '1.5' ==> interval '00:40:00'
+ datum = new IntervalDatum(1 * 60 * 60 * 1000);
+ result = datum.divide(new Float8Datum(1.5f));
+ assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+ assertEquals(new IntervalDatum(40 * 60 * 1000), result);
+
+ // timestamp '2001-08-31 01:00:00' + interval '1 mons' ==> timestamp 2001-09-30 01:00:00
+ datum = new TimestampDatum(new DateTime(2001, 8, 31, 1, 0, 0, 0));
+ result = datum.plus(new IntervalDatum(1, 0));
+ assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+ assertEquals(new TimestampDatum(new DateTime(2001, 9, 30, 1, 0, 0, 0)), result);
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java b/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
index 246791b..7aa01ac 100644
--- a/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
+++ b/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
@@ -92,6 +92,9 @@ public class TestTimestampDatum {
public final void testAsTextBytes() {
Datum d = DatumFactory.createTimeStamp("1980-04-01 01:50:01");
assertArrayEquals(d.toString().getBytes(), d.asTextBytes());
+
+ d = DatumFactory.createTimeStamp("1980-04-01 01:50:01.578");
+ assertArrayEquals(d.toString().getBytes(), d.asTextBytes());
}
@Test
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4 b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
index 7fa7973..05312ce 100644
--- a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
+++ b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
@@ -335,6 +335,7 @@ NCHAR : N C H A R;
NVARCHAR : N V A R C H A R;
DATE : D A T E;
+INTERVAL: I N T E R V A L;
TIME : T I M E;
TIMETZ : T I M E T Z;
TIMESTAMP : T I M E S T A M P;
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4 b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
index f6385eb..0076794 100644
--- a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
+++ b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
@@ -313,6 +313,7 @@ nonreserved_keywords
| INT4
| INT8
| INTEGER
+ | INTERVAL
| NCHAR
| NUMERIC
| NVARCHAR
@@ -350,6 +351,7 @@ datetime_literal
: timestamp_literal
| time_literal
| date_literal
+ | interval_literal
;
time_literal
@@ -364,6 +366,10 @@ date_literal
: DATE date_string=Character_String_Literal
;
+interval_literal
+ : INTERVAL interval_string=Character_String_Literal
+ ;
+
boolean_literal
: TRUE | FALSE | UNKNOWN
;
@@ -462,6 +468,7 @@ boolean_type
datetime_type
: DATE
+ | INTERVAL
| TIME
| TIME WITH TIME ZONE
| TIMETZ
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
index d362927..f3e145a 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
@@ -82,6 +82,8 @@ public class BinaryEval extends EvalNode implements Cloneable {
case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
case FLOAT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+ case DATE: return CatalogUtil.newSimpleDataType(Type.DATE);
+ case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
}
}
@@ -92,6 +94,8 @@ public class BinaryEval extends EvalNode implements Cloneable {
case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
case FLOAT4:
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+ case DATE: return CatalogUtil.newSimpleDataType(Type.DATE);
+ case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
}
}
@@ -102,6 +106,7 @@ public class BinaryEval extends EvalNode implements Cloneable {
case INT8:
case FLOAT4:
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+ case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
}
}
@@ -112,6 +117,41 @@ public class BinaryEval extends EvalNode implements Cloneable {
case INT8:
case FLOAT4:
case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+ case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
+ }
+ }
+
+ case DATE: {
+ switch(right.getType()) {
+ case INT2:
+ case INT4:
+ case INT8: return CatalogUtil.newSimpleDataType(Type.DATE);
+ case INTERVAL:
+ case TIME: return CatalogUtil.newSimpleDataType(Type.TIMESTAMP);
+ case DATE: return CatalogUtil.newSimpleDataType(Type.INT4);
+ }
+ }
+
+ case TIME: {
+ switch(right.getType()) {
+ case INTERVAL: return CatalogUtil.newSimpleDataType(Type.TIME);
+ case TIME: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
+ case DATE: return CatalogUtil.newSimpleDataType(Type.INT4);
+ }
+ }
+
+ case TIMESTAMP: {
+ switch (right.getType()) {
+ case INTERVAL: return CatalogUtil.newSimpleDataType(Type.TIMESTAMP);
+ case TIMESTAMP: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
+ }
+ }
+
+ case INTERVAL: {
+ switch (right.getType()) {
+ case INTERVAL:
+ case FLOAT4:
+ case FLOAT8: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
}
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java b/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java
new file mode 100644
index 0000000..ba6a020
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java
@@ -0,0 +1,71 @@
+/**
+ * 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.tajo.engine.function.datetime;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.DateDatum;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.datum.NullDatum;
+import org.apache.tajo.engine.function.GeneralFunction;
+import org.apache.tajo.engine.function.annotation.Description;
+import org.apache.tajo.engine.function.annotation.ParamTypes;
+import org.apache.tajo.storage.Tuple;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Description(
+ functionName = "to_date",
+ description = "Convert string to date. Format should be a java format string.",
+ example = "> SELECT to_date('2014-01-01', 'yyyy-MM-dd');\n"
+ + "2014-01-01",
+ returnType = TajoDataTypes.Type.DATE,
+ paramTypes = {@ParamTypes(paramTypes = {TajoDataTypes.Type.TEXT, TajoDataTypes.Type.TEXT})}
+)
+public class ToDate extends GeneralFunction {
+ private static Map<String, DateTimeFormatter> formattercCache =
+ new ConcurrentHashMap<String, DateTimeFormatter>();
+
+ public ToDate() {
+ super(new Column[]{
+ new Column("string", TajoDataTypes.Type.TEXT),
+ new Column("format", TajoDataTypes.Type.TEXT)});
+ }
+
+ @Override
+ public Datum eval(Tuple params) {
+ if(params.isNull(0) || params.isNull(1)) {
+ return NullDatum.get();
+ }
+
+ String value = params.get(0).asChars();
+ String pattern = params.get(1).asChars();
+
+ DateTimeFormatter formatter = formattercCache.get(pattern);
+ if (formatter == null) {
+ formatter = DateTimeFormat.forPattern(pattern);
+ formattercCache.put(pattern, formatter);
+ }
+
+ return new DateDatum(formatter.parseDateTime(value).toLocalDate());
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
index cb356f8..e7de8f6 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
@@ -1384,6 +1384,8 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
return visitTime_literal(ctx.time_literal());
} else if (checkIfExist(ctx.date_literal())) {
return visitDate_literal(ctx.date_literal());
+ } else if (checkIfExist(ctx.interval_literal())) {
+ return visitInterval_literal(ctx.interval_literal());
} else {
return visitTimestamp_literal(ctx.timestamp_literal());
}
@@ -1410,6 +1412,11 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
return new TimestampLiteral(parseDate(datePart), parseTime(timePart));
}
+ @Override public Expr visitInterval_literal(@NotNull SQLParser.Interval_literalContext ctx) {
+ String intervalStr = stripQuote(ctx.interval_string.getText());
+ return new IntervalLiteral(intervalStr);
+ }
+
private DateValue parseDate(String datePart) {
// e.g., 1980-04-01
String[] parts = datePart.split("-");
@@ -1421,7 +1428,7 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
String[] parts = timePart.split(":");
TimeValue time;
- boolean hasFractionOfSeconds = parts[2].indexOf('.') > 0;
+ boolean hasFractionOfSeconds = (parts.length > 2 && parts[2].indexOf('.') > 0);
if (hasFractionOfSeconds) {
String[] secondsParts = parts[2].split("\\.");
time = new TimeValue(parts[0], parts[1], secondsParts[0]);
@@ -1429,7 +1436,9 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
time.setSecondsFraction(secondsParts[1]);
}
} else {
- time = new TimeValue(parts[0], parts[1], parts[2]);
+ time = new TimeValue(parts[0],
+ (parts.length > 1 ? parts[1] : "0"),
+ (parts.length > 2 ? parts[2] : "0"));
}
return time;
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
index aa94801..0ef8a26 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
@@ -105,6 +105,7 @@ public interface AlgebraVisitor<CONTEXT, RESULT> {
RESULT visitLiteral(CONTEXT ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException;
RESULT visitNullLiteral(CONTEXT ctx, Stack<Expr> stack, NullLiteral expr) throws PlanningException;
RESULT visitTimestampLiteral(CONTEXT ctx, Stack<Expr> stack, TimestampLiteral expr) throws PlanningException;
+ RESULT visitIntervalLiteral(CONTEXT ctx, Stack<Expr> stack, IntervalLiteral expr) throws PlanningException;
RESULT visitTimeLiteral(CONTEXT ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException;
RESULT visitDateLiteral(CONTEXT ctx, Stack<Expr> stack, DateLiteral expr) throws PlanningException;
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
index b8f3311..841ea22 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
@@ -249,7 +249,9 @@ public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTE
case TimestampLiteral:
current = visitTimestampLiteral(ctx, stack, (TimestampLiteral) expr);
break;
-
+ case IntervalLiteral:
+ current = visitIntervalLiteral(ctx, stack, (IntervalLiteral) expr);
+ break;
default:
@@ -728,6 +730,11 @@ public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTE
}
@Override
+ public RESULT visitIntervalLiteral(CONTEXT ctx, Stack<Expr> stack, IntervalLiteral expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
public RESULT visitTimeLiteral(CONTEXT ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
return null;
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
index 1b57b98..be0259b 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
@@ -550,6 +550,11 @@ public class ExprAnnotator extends BaseAlgebraVisitor<ExprAnnotator.Context, Eva
}
@Override
+ public EvalNode visitIntervalLiteral(Context ctx, Stack<Expr> stack, IntervalLiteral expr) throws PlanningException {
+ return new ConstEval(new IntervalDatum(expr.getExprStr()));
+ }
+
+ @Override
public EvalNode visitTimeLiteral(Context ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
TimeValue timeValue = expr.getTime();
int [] times = timeToIntArray(timeValue.getHours(),
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
index 9030629..b87665a 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
@@ -188,17 +188,19 @@ class ExprNormalizer extends SimpleAlgebraVisitor<ExprNormalizer.ExprNormalizedR
stack.push(expr);
Expr param;
- for (int i = 0; i < expr.getParams().length; i++) {
- param = expr.getParams()[i];
- visit(ctx, stack, param);
-
- if (OpType.isAggregationFunction(param.getType())) {
- String referenceName = ctx.plan.generateUniqueColumnName(param);
- ctx.aggExprs.add(new NamedExpr(param, referenceName));
- expr.getParams()[i] = new ColumnReferenceExpr(referenceName);
+ Expr[] paramExprs = expr.getParams();
+ if (paramExprs != null) {
+ for (int i = 0; i < paramExprs.length; i++) {
+ param = paramExprs[i];
+ visit(ctx, stack, param);
+
+ if (OpType.isAggregationFunction(param.getType())) {
+ String referenceName = ctx.plan.generateUniqueColumnName(param);
+ ctx.aggExprs.add(new NamedExpr(param, referenceName));
+ expr.getParams()[i] = new ColumnReferenceExpr(referenceName);
+ }
}
}
-
stack.pop();
return expr;
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
index 551393c..4cb4776 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
@@ -141,7 +141,37 @@ public class ExprsVerifier extends BasicEvalNodeVisitor<VerificationState, EvalN
private static void checkArithmeticOperand(VerificationState state, BinaryEval evalNode) {
EvalNode leftExpr = evalNode.getLeftExpr();
EvalNode rightExpr = evalNode.getRightExpr();
- if (!(checkNumericType(leftExpr.getValueType()) && checkNumericType(rightExpr.getValueType()))) {
+
+ DataType leftDataType = leftExpr.getValueType();
+ DataType rightDataType = rightExpr.getValueType();
+
+ Type leftType = leftDataType.getType();
+ Type rightType = rightDataType.getType();
+
+ if (leftType == Type.DATE &&
+ (checkIntType(rightDataType) ||
+ rightType == Type.DATE || rightType == Type.INTERVAL || rightType == Type.TIME)) {
+ return;
+ }
+
+ if (leftType == Type.INTERVAL &&
+ (checkNumericType(rightDataType) ||
+ rightType == Type.DATE || rightType == Type.INTERVAL || rightType == Type.TIME ||
+ rightType == Type.TIMESTAMP)) {
+ return;
+ }
+
+ if (leftType == Type.TIME &&
+ (rightType == Type.DATE || rightType == Type.INTERVAL || rightType == Type.TIME)) {
+ return;
+ }
+
+ if (leftType == Type.TIMESTAMP &&
+ (rightType == Type.TIMESTAMP || rightType == Type.INTERVAL || rightType == Type.TIME)) {
+ return;
+ }
+
+ if (!(checkNumericType(leftDataType) && checkNumericType(rightDataType))) {
state.addVerification("No operator matches the given name and argument type(s): " + evalNode.toString());
}
}
@@ -150,6 +180,11 @@ public class ExprsVerifier extends BasicEvalNodeVisitor<VerificationState, EvalN
return dataType.getType() == Type.INET4 || dataType.getType() == Type.INET6;
}
+ private static boolean checkIntType(DataType dataType) {
+ int typeNumber = dataType.getType().getNumber();
+ return Type.INT1.getNumber() < typeNumber && typeNumber <= Type.INT8.getNumber();
+ }
+
private static boolean checkNumericType(DataType dataType) {
int typeNumber = dataType.getType().getNumber();
return Type.INT1.getNumber() < typeNumber && typeNumber <= Type.NUMERIC.getNumber();
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java b/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java
new file mode 100644
index 0000000..7648d1f
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java
@@ -0,0 +1,96 @@
+/**
+ * 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.tajo.engine.eval;
+
+import org.apache.tajo.datum.exception.InvalidOperationException;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.fail;
+
+public class TestIntervalType extends ExprTestBase {
+ @Test
+ public void testIntervalPostgresqlCase() throws IOException {
+ // http://www.postgresql.org/docs/8.2/static/functions-datetime.html
+ testSimpleEval("select date '2001-09-28' + 7", new String[]{"2001-10-05"});
+ testSimpleEval("select date '2001-09-28' + interval '1 hour'", new String[]{"2001-09-28 01:00:00"});
+
+ testSimpleEval("select date '2001-09-28' + time '03:00'", new String[]{"2001-09-28 03:00:00"});
+ testSimpleEval("select interval '1 day' + interval '1 hour'", new String[]{"1 day 01:00:00"});
+
+ testSimpleEval("select timestamp '2001-09-28 01:00' + interval '23 hours'", new String[]{"2001-09-29 00:00:00"});
+
+ testSimpleEval("select time '01:00' + interval '3 hours'", new String[]{"04:00:00"});
+
+ testSimpleEval("select date '2001-10-01' - date '2001-09-28'", new String[]{"3"});
+ testSimpleEval("select date '2001-10-01' - 7", new String[]{"2001-09-24"});
+ testSimpleEval("select date '2001-09-28' - interval '1 hour'", new String[]{"2001-09-27 23:00:00"});
+
+ testSimpleEval("select time '05:00' - time '03:00'", new String[]{"02:00:00"});
+ testSimpleEval("select time '05:00' - interval '2 hours'", new String[]{"03:00:00"});
+ testSimpleEval("select timestamp '2001-09-28 23:00' - interval '23 hours'", new String[]{"2001-09-28 00:00:00"});
+
+ testSimpleEval("select interval '1 day' - interval '1 hour'", new String[]{"23:00:00"});
+
+ testSimpleEval("select timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00'", new String[]{"1 day 15:00:00"});
+ testSimpleEval("select 900 * interval '1 second'", new String[]{"00:15:00"});
+ testSimpleEval("select 21 * interval '1 day'", new String[]{"21 days"});
+ testSimpleEval("select 3.5 * interval '1 hour'", new String[]{"03:30:00"});
+ testSimpleEval("select interval '1 hour' / 1.5", new String[]{"00:40:00"});
+ }
+
+ @Test
+ public void testCaseByCase() throws Exception {
+ testSimpleEval("select date '2001-08-28' + interval '10 day 1 hour'", new String[]{"2001-09-07 01:00:00"});
+ testSimpleEval("select interval '10 day 01:00:00' + date '2001-08-28'", new String[]{"2001-09-07 01:00:00"});
+ testSimpleEval("select time '10:20:30' + interval '1 day 01:00:00'", new String[]{"11:20:30"});
+ testSimpleEval("select interval '1 day 01:00:00' + time '10:20:30'", new String[]{"11:20:30"});
+ testSimpleEval("select time '10:20:30' - interval '1 day 01:00:00'", new String[]{"09:20:30"});
+
+ testSimpleEval("select (interval '1 month 20 day' + interval '50 day')", new String[]{"1 month 70 days"});
+ testSimpleEval("select date '2013-01-01' + interval '1 month 70 day'", new String[]{"2013-04-12 00:00:00"});
+ testSimpleEval("select date '2013-01-01' + (interval '1 month 20 day' + interval '50 day')", new String[]{"2013-04-12 00:00:00"});
+ testSimpleEval("select interval '1 month 70 day' + date '2013-01-01'", new String[]{"2013-04-12 00:00:00"});
+ testSimpleEval("select date '2013-01-01' - interval '1 month 70 day'", new String[]{"2012-09-22 00:00:00"});
+
+ testSimpleEval("select timestamp '2001-09-28 23:00' - interval '1 month 2 day 10:20:30'", new String[]{"2001-08-26 12:39:30"});
+ testSimpleEval("select timestamp '2001-09-28 23:00' + interval '1 month 2 day 10:20:30'", new String[]{"2001-10-31 09:20:30"});
+ testSimpleEval("select interval '1 month 2 day 10:20:30' + timestamp '2001-09-28 23:00'", new String[]{"2001-10-31 09:20:30"});
+
+
+ testSimpleEval("select interval '5 month' / 3", new String[]{"1 month 20 days"});
+
+ // Notice: Different from postgresql result(13 days 01:02:36.4992) because of double type precision.
+ testSimpleEval("select interval '1 month' / 2.3", new String[]{"13 days 01:02:36.522"});
+
+ testSimpleEval("select interval '1 month' * 2.3", new String[]{"2 months 9 days"});
+ testSimpleEval("select interval '3 year 5 month 1 hour' / 1.5", new String[]{"2 years 3 months 10 days 00:40:00"});
+ }
+
+ @Test
+ public void testWrongFormatLiteral() throws Exception {
+ try {
+ testSimpleEval("select interval '1 month 2 day 23 hours 10:20:30'", new String[]{"DUMMY"});
+ fail("hour and time format can not be used at the same time in interval.");
+ } catch (InvalidOperationException e) {
+ //success
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java b/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
index ac7a2b8..8641548 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
@@ -250,4 +250,10 @@ public class TestDateTimeFunctions extends ExprTestBase {
testSimpleEval("select utc_usec_to('year' ,1274259481071200);", new String[]{1262304000000000L+""});
testSimpleEval("select utc_usec_to('week' ,1207929480000000, 2);", new String[]{1207612800000000L+""});
}
+
+ @Test
+ public void testToDate() throws IOException {
+ testSimpleEval("select to_date('2014-01-04', 'yyyy-MM-dd')", new String[]{"2014-01-04"});
+ testSimpleEval("select to_date('2014-01-04', 'yyyy-MM-dd') + interval '1 day'", new String[]{"2014-01-05 00:00:00"});
+ }
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
----------------------------------------------------------------------
diff --git a/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java b/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
index b64bfe1..33b2ff3 100644
--- a/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
+++ b/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
@@ -22,6 +22,7 @@ import org.apache.tajo.catalog.Column;
import org.apache.tajo.catalog.Schema;
import org.apache.tajo.common.TajoDataTypes;
import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.datum.IntervalDatum;
import org.apache.tajo.util.BitArray;
import java.nio.ByteBuffer;
@@ -116,6 +117,12 @@ public class RowStoreUtil {
tuple.put(i, DatumFactory.createFromInt8(type, l));
break;
+ case INTERVAL:
+ int month = bb.getInt();
+ long milliseconds = bb.getLong();
+ tuple.put(i, new IntervalDatum(month, milliseconds));
+ break;
+
case FLOAT4:
float f = bb.getFloat();
tuple.put(i, DatumFactory.createFloat4(f));
@@ -197,6 +204,11 @@ public class RowStoreUtil {
case TIMESTAMP:
bb.putLong(tuple.get(i).asInt8());
break;
+ case INTERVAL:
+ IntervalDatum interval = (IntervalDatum) tuple.get(i);
+ bb.putInt(interval.getMonths());
+ bb.putLong(interval.getMilliSeconds());
+ break;
case BLOB:
byte [] bytes = tuple.get(i).asByteArray();
bb.putInt(bytes.length);
http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
----------------------------------------------------------------------
diff --git a/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java b/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
index de73a3a..6113357 100644
--- a/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
+++ b/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
@@ -79,6 +79,7 @@ public class TextSerializerDeserializer implements SerializerDeserializer {
case DATE:
case TIME:
case TIMESTAMP:
+ case INTERVAL:
bytes = datum.asTextBytes();
length = bytes.length;
out.write(bytes);
@@ -158,6 +159,10 @@ public class TextSerializerDeserializer implements SerializerDeserializer {
datum = isNull(bytes, offset, length, nullCharacters) ? NullDatum.get()
: DatumFactory.createTimeStamp(new String(bytes, offset, length));
break;
+ case INTERVAL:
+ datum = isNull(bytes, offset, length, nullCharacters) ? NullDatum.get()
+ : DatumFactory.createInterval(new String(bytes, offset, length));
+ break;
case PROTOBUF: {
if (isNull(bytes, offset, length, nullCharacters)) {
datum = NullDatum.get();