Skip to content

Commit

Permalink
[CALCITE-6138] Parser does not accept TIMESTAMP WITH TIME ZONE as a d…
Browse files Browse the repository at this point in the history
…ata type

Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
  • Loading branch information
mihaibudiu authored and snuyanzin committed Mar 12, 2024
1 parent fe0da06 commit 6d3a81d
Show file tree
Hide file tree
Showing 35 changed files with 614 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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.checkerframework.checker.nullness.qual.Nullable;
Expand Down Expand Up @@ -192,6 +193,9 @@ private String translateMatch(RexNode condition) {
private static Object literalValue(RexLiteral literal) {
Comparable<?> value = RexLiteral.value(literal);
switch (literal.getTypeName()) {
case TIMESTAMP_TZ:
assert value instanceof TimestampWithTimeZoneString;
return value.toString();
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
assert value instanceof TimestampString;
Expand Down
46 changes: 21 additions & 25 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -4787,6 +4787,7 @@ SqlLiteral DateTimeLiteral() :
{
final String p;
final Span s;
boolean local = false;
}
{
<LBRACE_D> <QUOTED_STRING> {
Expand Down Expand Up @@ -4818,6 +4819,7 @@ SqlLiteral DateTimeLiteral() :
return SqlLiteral.createUnknown("DATETIME", p, s.end(this));
}
|
LOOKAHEAD(2)
<TIME> { s = span(); } p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("TIME", p, s.end(this));
}
Expand All @@ -4827,8 +4829,14 @@ SqlLiteral DateTimeLiteral() :
return SqlLiteral.createUnknown("TIMESTAMP", p, s.end(this));
}
|
<TIMESTAMP> { s = span(); } <WITH> <LOCAL> <TIME> <ZONE> p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("TIMESTAMP WITH LOCAL TIME ZONE", p, s.end(this));
LOOKAHEAD(2)
<TIME> { s = span(); } <WITH> ( <LOCAL> { local = true; } )? <TIME> <ZONE> p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("TIME WITH " + (local ? "LOCAL " : "") + "TIME ZONE", p, s.end(this));
}
|
LOOKAHEAD(2)
<TIMESTAMP> { s = span(); } <WITH> ( <LOCAL> { local = true; } )? <TIME> <ZONE> p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("TIMESTAMP WITH " + (local ? "LOCAL " : "") + "TIME ZONE", p, s.end(this));
}
}

Expand Down Expand Up @@ -6112,7 +6120,6 @@ SqlTypeNameSpec DateTimeTypeName() :
{
int precision = -1;
SqlTypeName typeName;
boolean withLocalTimeZone = false;
final Span s;
}
{
Expand All @@ -6124,25 +6131,15 @@ SqlTypeNameSpec DateTimeTypeName() :
LOOKAHEAD(2)
<TIME> { s = span(); }
precision = PrecisionOpt()
withLocalTimeZone = TimeZoneOpt()
typeName = TimeZoneOpt(true)
{
if (withLocalTimeZone) {
typeName = SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE;
} else {
typeName = SqlTypeName.TIME;
}
return new SqlBasicTypeNameSpec(typeName, precision, s.end(this));
}
|
<TIMESTAMP> { s = span(); }
precision = PrecisionOpt()
withLocalTimeZone = TimeZoneOpt()
typeName = TimeZoneOpt(false)
{
if (withLocalTimeZone) {
typeName = SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE;
} else {
typeName = SqlTypeName.TIMESTAMP;
}
return new SqlBasicTypeNameSpec(typeName, precision, s.end(this));
}
}
Expand All @@ -6163,23 +6160,22 @@ int PrecisionOpt() :

/**
* Parse a time zone suffix for DateTime types. According to SQL-2011,
* "with time zone" and "without time zone" belong to standard SQL but we
* only implement the "without time zone".
*
* <p>We also support "with local time zone".
*
* @return true if this is "with local time zone".
* "with time zone" and "without time zone" belong to standard SQL.
* We also support "with local time zone".
*/
boolean TimeZoneOpt() :
SqlTypeName TimeZoneOpt(boolean timeType) :
{
boolean local = false;
}
{
LOOKAHEAD(3)
<WITHOUT> <TIME> <ZONE> { return false; }
<WITHOUT> <TIME> <ZONE> { return timeType ? SqlTypeName.TIME : SqlTypeName.TIMESTAMP; }
|
<WITH> <LOCAL> <TIME> <ZONE> { return true; }
<WITH> ( <LOCAL> { local = true; } )? <TIME> <ZONE> {
return timeType ? (local ? SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE : SqlTypeName.TIME_TZ)
: (local ? SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE : SqlTypeName.TIMESTAMP_TZ); }
|
{ return false; }
{ return timeType ? SqlTypeName.TIME : SqlTypeName.TIMESTAMP; }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2451,6 +2451,7 @@ private static class ContainsSubstrImplementor extends AbstractRexCallImplemento
// Search does not include TZ so this conversion is okay
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
expr = Expressions.call(BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method, expr);
break;
default:
Expand Down Expand Up @@ -2633,6 +2634,7 @@ private static class FloorImplementor extends MethodImplementor {
translator.getRoot()));
// fall through
case TIMESTAMP:
case TIMESTAMP_TZ:
type = long.class;
floorMethod = custom ? customTimestampMethod : timestampMethod;
preFloor = true;
Expand Down Expand Up @@ -2713,6 +2715,7 @@ private static class TimestampAddImplementor
final Expression operand2 = argValueList.get(2);
switch (call.getType().getSqlTypeName()) {
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
case TIMESTAMP:
return Expressions.call(customTimestampMethod, translator.getRoot(),
operand0, operand1, operand2);
Expand Down Expand Up @@ -2745,6 +2748,7 @@ private static class TimestampDiffImplementor
private Method getMethod(RexCall call) {
switch (call.operands.get(1).getType().getSqlTypeName()) {
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
case TIMESTAMP:
return customTimestampMethod;
default:
Expand Down Expand Up @@ -3106,6 +3110,7 @@ private static class ExtractImplementor extends AbstractRexCallImplementor {
Expressions.call(BuiltInMethod.TIME_ZONE.method,
translator.getRoot()));
// fall through
case TIMESTAMP_TZ:
case TIMESTAMP:
operand =
Expressions.call(BuiltInMethod.FLOOR_DIV.method, operand,
Expand Down Expand Up @@ -3136,6 +3141,7 @@ private static class ExtractImplementor extends AbstractRexCallImplementor {
Expressions.constant(TimeUnit.DAY.multiplier.longValue()));
// fall through
case TIMESTAMP:
case TIMESTAMP_TZ:
// convert to seconds
return Expressions.divide(operand,
Expressions.constant(TimeUnit.SECOND.multiplier.longValue()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,15 @@ private static Type fieldType(Field field) {
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
case TIME_TZ:
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 TIMESTAMP_TZ:
case BIGINT:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ protected RelMdSize() {}
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
case TIME_TZ:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
Expand All @@ -311,6 +312,7 @@ protected RelMdSize() {}
case FLOAT: // sic
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
Expand Down Expand Up @@ -364,6 +366,7 @@ public double typeValueSize(RelDataType type, @Nullable Comparable value) {
case DATE:
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
case TIME_TZ:
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
Expand All @@ -373,6 +376,7 @@ public double typeValueSize(RelDataType type, @Nullable Comparable value) {
case DOUBLE:
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,14 @@ private RexLiteral dateTimeLiteral(RexBuilder rexBuilder, Calendar calendar,
ts = TimestampString.fromCalendarFields(calendar);
p = operand.getType().getPrecision();
return rexBuilder.makeTimestampLiteral(ts, p);
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ: {
ts = TimestampString.fromCalendarFields(calendar);
final TimeZone tz = calendar.getTimeZone();
final TimestampWithTimeZoneString localTs = new TimestampWithTimeZoneString(ts, tz);
p = operand.getType().getPrecision();
return rexBuilder.makeTimestampTzLiteral(localTs, p);
}
case TIMESTAMP_WITH_LOCAL_TIME_ZONE: {
ts = TimestampString.fromCalendarFields(calendar);
final TimeZone tz = TimeZone.getTimeZone(this.timeZone);
final TimestampString localTs =
Expand All @@ -584,6 +591,7 @@ private RexLiteral dateTimeLiteral(RexBuilder rexBuilder, Calendar calendar,
.getLocalTimestampString();
p = operand.getType().getPrecision();
return rexBuilder.makeTimestampWithLocalTimeZoneLiteral(localTs, p);
}
case DATE:
final DateString d = DateString.fromCalendarFields(calendar);
return rexBuilder.makeDateLiteral(d);
Expand Down Expand Up @@ -646,6 +654,12 @@ private RexNode compareFloorCeil(SqlKind comparison, RexNode operand,

private Calendar timestampValue(RexLiteral timeLiteral) {
switch (timeLiteral.getTypeName()) {
case TIMESTAMP_TZ:
TimestampWithTimeZoneString value =
requireNonNull(timeLiteral.getValueAs(TimestampWithTimeZoneString.class));
return Util.calendar(
value.getLocalTimestampString().getMillisSinceEpoch(),
value.getTimeZone());
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
final TimeZone tz = TimeZone.getTimeZone(this.timeZone);
return Util.calendar(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,8 @@ private static int find(RelNode rel, int ref) {
exprsLineage.put(expr, i);
SqlTypeName sqlTypeName = expr.getType().getSqlTypeName();
if (sqlTypeName == SqlTypeName.TIMESTAMP
|| sqlTypeName == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
|| sqlTypeName == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE
|| sqlTypeName == SqlTypeName.TIMESTAMP_TZ) {
timestampExprs.add(expr);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
return 15;
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
case TIME_TZ:
case DATE:
return 0; // SQL99 part 2 section 6.1 syntax rule 30
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
// farrago supports only 0 (see
// SqlTypeName.getDefaultPrecision), but it should be 6
// (microseconds) per SQL99 part 2 section 6.1 syntax rule 30.
Expand All @@ -126,8 +128,10 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
return 65536;
case TIME:
case TIME_WITH_LOCAL_TIME_ZONE:
case TIME_TZ:
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
case TIMESTAMP_TZ:
return SqlTypeName.MAX_DATETIME_PRECISION;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
Expand Down Expand Up @@ -168,6 +172,8 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
return isPrefix ? "TIMESTAMP '" : "'";
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return isPrefix ? "TIMESTAMP WITH LOCAL TIME ZONE '" : "'";
case TIMESTAMP_TZ:
return isPrefix ? "TIMESTAMP WITH TIME ZONE '" : "'";
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
Expand All @@ -187,6 +193,8 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
return isPrefix ? "TIME '" : "'";
case TIME_WITH_LOCAL_TIME_ZONE:
return isPrefix ? "TIME WITH LOCAL TIME ZONE '" : "'";
case TIME_TZ:
return isPrefix ? "TIME WITH TIME ZONE '" : "'";
case DATE:
return isPrefix ? "DATE '" : "'";
case ARRAY:
Expand Down
Loading

0 comments on commit 6d3a81d

Please sign in to comment.