Skip to content

Commit

Permalink
Add support for interval types to JDBC driver
Browse files Browse the repository at this point in the history
  • Loading branch information
dain committed Apr 22, 2014
1 parent a48e081 commit 5cb37df
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Licensed 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:https://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 com.facebook.presto.jdbc;

import java.util.Objects;

public class PrestoIntervalDayTime
{
private static final long MILLIS_IN_SECOND = 1000;
private static final long MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND;
private static final long MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE;
private static final long MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;

private final long milliSeconds;

public PrestoIntervalDayTime(long milliSeconds)
{
this.milliSeconds = milliSeconds;
}

public PrestoIntervalDayTime(int day, int hour, int minute, int second, int millis)
{
milliSeconds = toMillis(day, hour, minute, second, millis);
}

public long getMilliSeconds()
{
return milliSeconds;
}

@Override
public int hashCode()
{
return Objects.hash(milliSeconds);
}

@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
PrestoIntervalDayTime other = (PrestoIntervalDayTime) obj;
return Objects.equals(this.milliSeconds, other.milliSeconds);
}

@Override
public String toString()
{
return formatMillis(milliSeconds);
}

private static long toMillis(int day, int hour, int minute, int second, int millis)
{
return (day * MILLIS_IN_DAY) +
(hour * MILLIS_IN_HOUR) +
(minute * MILLIS_IN_MINUTE) +
(second * MILLIS_IN_SECOND) +
millis;
}

private static String formatMillis(long millis)
{
long day = millis / MILLIS_IN_DAY;
millis %= MILLIS_IN_DAY;
long hour = millis / MILLIS_IN_HOUR;
millis %= MILLIS_IN_HOUR;
long minute = millis / MILLIS_IN_MINUTE;
millis %= MILLIS_IN_MINUTE;
long second = millis / MILLIS_IN_SECOND;
millis %= MILLIS_IN_SECOND;

return String.format("%d %02d:%02d:%02d.%03d", day, hour, minute, second, millis);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Licensed 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:https://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 com.facebook.presto.jdbc;

import java.util.Objects;

public class PrestoIntervalYearMonth
{
private final long months;

public PrestoIntervalYearMonth(long months)
{
this.months = months;
}

public PrestoIntervalYearMonth(int year, int months)
{
this.months = (12 * year) + months;
}

public long getMonths()
{
return months;
}

@Override
public int hashCode()
{
return Objects.hash(months);
}

@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
PrestoIntervalYearMonth other = (PrestoIntervalYearMonth) obj;
return Objects.equals(this.months, other.months);
}

@Override
public String toString()
{
return formatMonths(months);
}

private static String formatMonths(long months)
{
return (months / 12) + "-" + (months % 12);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.joda.time.DateTimeZone;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;
import org.joda.time.format.ISODateTimeFormat;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;

import java.io.InputStream;
import java.io.Reader;
Expand Down Expand Up @@ -89,9 +92,36 @@ public class PrestoResultSet
.toFormatter()
.withOffsetParsed();

private static final PeriodFormatter INTERVAL_YEAR_TO_MONTH_FORMATTER = new PeriodFormatterBuilder()
.appendYears()
.appendLiteral("-")
.appendMonths()
.toFormatter();

private static final PeriodFormatter INTERVAL_DAY_TO_SECOND_FORMATTER = new PeriodFormatterBuilder()
.appendDays()
.appendLiteral(" ")
.appendHours()
.appendLiteral(":")
.appendMinutes()
.appendLiteral(":")
.appendSecondsWithOptionalMillis()
.toFormatter();

private static final int YEAR_FIELD = 0;
private static final int MONTH_FIELD = 1;
private static final int DAY_FIELD = 3;
private static final int HOUR_FIELD = 4;
private static final int MINUTE_FIELD = 5;
private static final int SECOND_FIELD = 6;
private static final int MILLIS_FIELD = 7;

private static final int VARCHAR_MAX = 1024 * 1024 * 1024;
private static final int TIME_ZONE_MAX = 40; // current longest time zone is 32
private static final int TIME_MAX = "HH:mm:ss.SSS".length();
private static final int TIME_WITH_TIME_ZONE_MAX = TIME_MAX + TIME_ZONE_MAX;
private static final int TIMESTAMP_MAX = "yyyy-MM-dd HH:mm:ss.SSS".length();
private static final int TIMESTAMP_WITH_TIME_ZONE_MAX = TIMESTAMP_MAX + TIME_ZONE_MAX;
private static final int DATE_MAX = "yyyy-MM-dd".length();

private final StatementClient client;
Expand Down Expand Up @@ -507,10 +537,48 @@ public Object getObject(int columnIndex)
return getTime(columnIndex);
case Types.TIMESTAMP:
return getTimestamp(columnIndex);
case Types.JAVA_OBJECT:
if (columnInfo.getColumnTypeName().equalsIgnoreCase("interval year to month")) {
return getIntervalYearMonth(columnIndex);
}
else if (columnInfo.getColumnTypeName().equalsIgnoreCase("interval day to second")) {
return getIntervalDayTime(columnIndex);
}
}
return column(columnIndex);
}

private PrestoIntervalYearMonth getIntervalYearMonth(int columnIndex)
throws SQLException
{
Object value = column(columnIndex);
if (value == null) {
return null;
}

Period period = INTERVAL_YEAR_TO_MONTH_FORMATTER.parsePeriod(String.valueOf(value));
return new PrestoIntervalYearMonth(
period.getValue(YEAR_FIELD),
period.getValue(MONTH_FIELD));
}

private PrestoIntervalDayTime getIntervalDayTime(int columnIndex)
throws SQLException
{
Object value = column(columnIndex);
if (value == null) {
return null;
}

Period period = INTERVAL_DAY_TO_SECOND_FORMATTER.parsePeriod(String.valueOf(value));
return new PrestoIntervalDayTime(
period.getValue(DAY_FIELD),
period.getValue(HOUR_FIELD),
period.getValue(MINUTE_FIELD),
period.getValue(SECOND_FIELD),
period.getValue(MILLIS_FIELD));
}

@Override
public Object getObject(String columnLabel)
throws SQLException
Expand Down Expand Up @@ -1748,27 +1816,47 @@ private static void setTypeInfo(ColumnInfo.Builder builder, String type)
builder.setColumnDisplaySize(VARCHAR_MAX);
break;
case "time":
case "time with time zone":
builder.setColumnType(Types.TIME);
builder.setSigned(true);
builder.setPrecision(3);
builder.setScale(0);
builder.setColumnDisplaySize(TIME_MAX);
break;
case "time with time zone":
builder.setColumnType(Types.TIME);
builder.setSigned(true);
builder.setPrecision(3);
builder.setScale(0);
builder.setColumnDisplaySize(TIME_WITH_TIME_ZONE_MAX);
break;
case "timestamp":
case "timestamp with time zone":
builder.setColumnType(Types.TIMESTAMP);
builder.setSigned(true);
builder.setPrecision(3);
builder.setScale(0);
builder.setColumnDisplaySize(TIMESTAMP_MAX);
break;
case "timestamp with time zone":
builder.setColumnType(Types.TIMESTAMP);
builder.setSigned(true);
builder.setPrecision(3);
builder.setScale(0);
builder.setColumnDisplaySize(TIMESTAMP_WITH_TIME_ZONE_MAX);
break;
case "date":
builder.setColumnType(Types.DATE);
builder.setSigned(true);
builder.setScale(0);
builder.setColumnDisplaySize(DATE_MAX);
break;
case "interval year to month":
builder.setColumnType(Types.JAVA_OBJECT);
builder.setColumnDisplaySize(TIMESTAMP_MAX);
break;
case "interval day to second":
builder.setColumnType(Types.JAVA_OBJECT);
builder.setColumnDisplaySize(TIMESTAMP_MAX);
break;
default:
throw new AssertionError("unimplemented type: " + type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ public void testTypes()
", TIMESTAMP '2004-05-06 6:07:08 +06:17' as e" +
", TIMESTAMP '2007-08-09 9:10:11 Europe/Berlin' as f" +
", DATE '2013-03-22' as g" +
", INTERVAL '123-11' YEAR TO MONTH as h" +
", INTERVAL '11 22:33:44.555' DAY TO SECOND as i" +
"")) {
assertTrue(rs.next());

Expand Down Expand Up @@ -162,6 +164,11 @@ public void testTypes()
assertEquals(rs.getDate("g", ASIA_ORAL_CALENDAR), new Date(new DateTime(2013, 3, 22, 0, 0, ASIA_ORAL_ZONE).getMillis()));
assertEquals(rs.getObject("g"), new Date(new DateTime(2013, 3, 22, 0, 0).getMillis()));

assertEquals(rs.getObject(8), new PrestoIntervalYearMonth(123, 11));
assertEquals(rs.getObject("h"), new PrestoIntervalYearMonth(123, 11));
assertEquals(rs.getObject(9), new PrestoIntervalDayTime(11, 22, 33, 44, 555));
assertEquals(rs.getObject("i"), new PrestoIntervalDayTime(11, 22, 33, 44, 555));

assertFalse(rs.next());
}
}
Expand Down

0 comments on commit 5cb37df

Please sign in to comment.