Skip to content

Commit

Permalink
Add support for interval datatypes
Browse files Browse the repository at this point in the history
  • Loading branch information
bhvkshah committed Nov 8, 2023
1 parent a5f234c commit 6b3f897
Show file tree
Hide file tree
Showing 10 changed files with 575 additions and 25 deletions.
4 changes: 4 additions & 0 deletions src/main/java/com/amazon/redshift/core/Oid.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public class Oid {
public static final int VOID = 2278;
public static final int INTERVAL = 1186;
public static final int INTERVAL_ARRAY = 1187;
public static final int INTERVALY2M = 1188;
public static final int INTERVALY2M_ARRAY = 1189;
public static final int INTERVALD2S = 1190;
public static final int INTERVALD2S_ARRAY = 1191;
public static final int CHAR = 18; // This is not char(N), this is "char" a single byte type.
public static final int CHAR_ARRAY = 1002;
public static final int VARBIT = 1562;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ public String toString(int index, boolean standardConformingStrings) {
p.append("::date");
} else if (paramType == Oid.INTERVAL) {
p.append("::interval");
} else if (paramType == Oid.INTERVALY2M) {
p.append("::interval year to month");
} else if (paramType == Oid.INTERVALD2S) {
p.append("::interval day to second");
} else if (paramType == Oid.NUMERIC) {
p.append("::numeric");
}
Expand Down
31 changes: 7 additions & 24 deletions src/main/java/com/amazon/redshift/jdbc/RedshiftConnectionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import com.amazon.redshift.util.RedshiftObject;
import com.amazon.redshift.util.RedshiftException;
import com.amazon.redshift.util.RedshiftInterval;
import com.amazon.redshift.util.RedshiftIntervalYearToMonth;
import com.amazon.redshift.util.RedshiftIntervalDayToSecond;
import com.amazon.redshift.util.RedshiftState;
import com.amazon.redshift.util.RedshiftProperties;

Expand Down Expand Up @@ -486,6 +488,8 @@ private static Set<Integer> getSupportedBinaryOids() {
Oid.TIMETZ,
Oid.TIMESTAMP,
Oid.TIMESTAMPTZ,
Oid.INTERVALY2M,
Oid.INTERVALD2S,
Oid.INT2_ARRAY,
Oid.INT4_ARRAY,
Oid.INT8_ARRAY,
Expand Down Expand Up @@ -824,31 +828,8 @@ else if (byteValue != null && obj instanceof RedshiftInterval) {
// Binary format is 8 bytes time and 4 byes months
long time = ByteConverter.int8(byteValue, 0);
int month = ByteConverter.int4(byteValue, 8);
int tm_year;
int tm_mon;

if(month != 0)
{
tm_year = month / 12;
tm_mon = month % 12;
}
else
{
tm_year = 0;
tm_mon = 0;
}

int tm_mday = (int)(time / 86400000000L);
time -= (tm_mday * 86400000000L);
int tm_hour = (int)(time / 3600000000L);
time -= (tm_hour * 3600000000L);
int tm_min = (int)(time / 60000000L);
time -= (tm_min * 60000000L);
int tm_sec = (int)(time / 1000000L);
int fsec = (int)(time - (tm_sec * 1000000));
double sec = tm_sec + (fsec/1000000.0);

intervalObj.setValue(tm_year, tm_mon, tm_mday, tm_hour, tm_min, sec);
intervalObj.setValue(month, time);

// intervalObj.setValue(new String(byteValue));
}
Expand Down Expand Up @@ -909,6 +890,8 @@ private void initObjectTypes(Properties info) throws SQLException {
addDataType("polygon", com.amazon.redshift.geometric.RedshiftPolygon.class);
addDataType("money", com.amazon.redshift.util.RedshiftMoney.class);
addDataType("interval", com.amazon.redshift.util.RedshiftInterval.class);
// intervaly2m and intervald2s are not object types rather they are
// binary types native to Redshift, hence they are added in TypeInfoCache.

Enumeration<?> e = info.propertyNames();
while (e.hasMoreElements()) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.amazon.redshift.util.RedshiftBinaryObject;
import com.amazon.redshift.util.RedshiftTime;
import com.amazon.redshift.util.RedshiftTimestamp;
import com.amazon.redshift.util.RedshiftIntervalYearToMonth;
import com.amazon.redshift.util.RedshiftIntervalDayToSecond;
import com.amazon.redshift.util.RedshiftObject;
import com.amazon.redshift.util.RedshiftException;
import com.amazon.redshift.util.RedshiftState;
Expand Down Expand Up @@ -508,6 +510,44 @@ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
setTimestamp(parameterIndex, x, null);
}

public void setIntervalYearToMonth(int parameterIndex, RedshiftIntervalYearToMonth x) throws SQLException {
if (RedshiftLogger.isEnable())
connection.getLogger().logFunction(true, parameterIndex, x);

if (x == null) {
setNull(parameterIndex, Types.OTHER);
return;
}

if (connection.binaryTransferSend(Oid.INTERVALY2M)) {
byte[] bytes = new byte[4];
ByteConverter.int4(bytes, 0, (int) x.totalMonths());
preparedParameters.setBinaryParameter(parameterIndex, bytes, Oid.INTERVALY2M);
return;
}

bindString(parameterIndex, x.getValue(), Oid.UNSPECIFIED);
}

public void setIntervalDayToSecond(int parameterIndex, RedshiftIntervalDayToSecond x) throws SQLException {
if (RedshiftLogger.isEnable())
connection.getLogger().logFunction(true, parameterIndex, x);

if (x == null) {
setNull(parameterIndex, Types.OTHER);
return;
}

if (connection.binaryTransferSend(Oid.INTERVALD2S)) {
byte[] bytes = new byte[8];
ByteConverter.int8(bytes, 0, (long) x.totalMicroseconds());
preparedParameters.setBinaryParameter(parameterIndex, bytes, Oid.INTERVALD2S);
return;
}

bindString(parameterIndex, x.getValue(), Oid.UNSPECIFIED);
}

private void setCharacterStreamPost71(int parameterIndex, InputStream x, int length,
String encoding) throws SQLException {

Expand Down Expand Up @@ -1092,6 +1132,10 @@ public void setObject(int parameterIndex, Object x) throws SQLException {
setTime(parameterIndex, (Time) x);
} else if (x instanceof Timestamp) {
setTimestamp(parameterIndex, (Timestamp) x);
} else if (x instanceof RedshiftIntervalYearToMonth) {
setIntervalYearToMonth(parameterIndex, (RedshiftIntervalYearToMonth) x);
} else if (x instanceof RedshiftIntervalDayToSecond) {
setIntervalDayToSecond(parameterIndex, (RedshiftIntervalDayToSecond) x);
} else if (x instanceof Boolean) {
setBoolean(parameterIndex, (Boolean) x);
} else if (x instanceof Byte) {
Expand Down
69 changes: 68 additions & 1 deletion src/main/java/com/amazon/redshift/jdbc/RedshiftResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import com.amazon.redshift.util.RedshiftException;
import com.amazon.redshift.util.RedshiftGeography;
import com.amazon.redshift.util.RedshiftGeometry;
import com.amazon.redshift.util.RedshiftIntervalYearToMonth;
import com.amazon.redshift.util.RedshiftIntervalDayToSecond;
import com.amazon.redshift.util.RedshiftState;

import java.io.ByteArrayInputStream;
Expand Down Expand Up @@ -247,6 +249,11 @@ protected Object internalGetObject(int columnIndex, Field field) throws SQLExcep
return getClob(columnIndex);
case Types.BLOB:
return getBlob(columnIndex);
case Types.OTHER:
if (field.getOID() == Oid.INTERVALY2M)
return getIntervalYearToMonth(columnIndex);
if (field.getOID() == Oid.INTERVALD2S)
return getIntervalDayToSecond(columnIndex);

default:
String type = getRSType(columnIndex);
Expand Down Expand Up @@ -2244,7 +2251,9 @@ private boolean isCharType(int columnIndex) throws SQLException {
|| colType == Types.NCHAR
|| colType == Types.LONGNVARCHAR
|| colType == Types.REF_CURSOR
|| (colType == Types.OTHER && !isInterval(columnIndex)));
|| (colType == Types.OTHER && !isInterval(columnIndex) &&
!isIntervalYearToMonth(columnIndex) &&
!isIntervalDayToSecond(columnIndex)));
}

@Override
Expand Down Expand Up @@ -2279,6 +2288,14 @@ public String getString(int columnIndex) throws SQLException {
return connection.getTimestampUtils().timeToString((java.util.Date) obj,
oid == Oid.TIMESTAMPTZ || oid == Oid.TIMETZ);
}
if (obj instanceof RedshiftIntervalYearToMonth) {
RedshiftIntervalYearToMonth ym = (RedshiftIntervalYearToMonth) obj;
return ym.getValue();
}
if (obj instanceof RedshiftIntervalDayToSecond) {
RedshiftIntervalDayToSecond ds = (RedshiftIntervalDayToSecond) obj;
return ds.getValue();
}
if ("hstore".equals(getRSType(columnIndex))) {
return HStoreConverter.toString((Map<?, ?>) obj);
}
Expand Down Expand Up @@ -2896,6 +2913,40 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException {
return getTimestamp(columnIndex, null);
}

public RedshiftIntervalYearToMonth getIntervalYearToMonth(int columnIndex) throws SQLException{
if (RedshiftLogger.isEnable())
connection.getLogger().log(LogLevel.DEBUG, " getIntervalYearToMonth columnIndex: {0}", columnIndex);

checkResultSet(columnIndex);
if (wasNullFlag) {
return null;
}

if (isBinary(columnIndex)) {
return new RedshiftIntervalYearToMonth(ByteConverter.int4(thisRow.get(columnIndex - 1), 0));
}

String str = getString(columnIndex);
return new RedshiftIntervalYearToMonth(getString(columnIndex));
}

public RedshiftIntervalDayToSecond getIntervalDayToSecond(int columnIndex) throws SQLException{
if (RedshiftLogger.isEnable())
connection.getLogger().log(LogLevel.DEBUG, " getIntervalDayToSecond columnIndex: {0}", columnIndex);

checkResultSet(columnIndex);
if (wasNullFlag) {
return null;
}

if (isBinary(columnIndex)) {
return new RedshiftIntervalDayToSecond(ByteConverter.int8(thisRow.get(columnIndex - 1), 0));
}

String str = getString(columnIndex);
return new RedshiftIntervalDayToSecond(getString(columnIndex));
}

public InputStream getAsciiStream(int columnIndex) throws SQLException {
if (RedshiftLogger.isEnable())
connection.getLogger().log(LogLevel.DEBUG, " getAsciiStream columnIndex: {0}", columnIndex);
Expand Down Expand Up @@ -3015,6 +3066,14 @@ public Timestamp getTimestamp(String columnName) throws SQLException {
return getTimestamp(findColumn(columnName), null);
}

public RedshiftIntervalYearToMonth getIntervalYearToMonth(String columnName) throws SQLException {
return getIntervalYearToMonth(findColumn(columnName));
}

public RedshiftIntervalDayToSecond getIntervalDayToSecond(String columnName) throws SQLException {
return getIntervalDayToSecond(findColumn(columnName));
}

public InputStream getAsciiStream(String columnName) throws SQLException {
return getAsciiStream(findColumn(columnName));
}
Expand Down Expand Up @@ -3309,6 +3368,14 @@ protected boolean isGeography(int column) {
protected boolean isInterval(int column) {
return (fields[column - 1].getOID() == Oid.INTERVAL);
}

protected boolean isIntervalYearToMonth(int column) {
return (fields[column - 1].getOID() == Oid.INTERVALY2M);
}

protected boolean isIntervalDayToSecond(int column) {
return (fields[column - 1].getOID() == Oid.INTERVALD2S);
}


// ----------------- Formatting Methods -------------------
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/amazon/redshift/jdbc/TypeInfoCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ public class TypeInfoCache implements TypeInfo {
{"timetz", Oid.TIMETZ, Types.TIME, "java.sql.Time", Oid.TIMETZ_ARRAY},
{"timestamp", Oid.TIMESTAMP, Types.TIMESTAMP, "java.sql.Timestamp", Oid.TIMESTAMP_ARRAY},
{"timestamptz", Oid.TIMESTAMPTZ, Types.TIMESTAMP, "java.sql.Timestamp", Oid.TIMESTAMPTZ_ARRAY},
{"intervaly2m", Oid.INTERVALY2M, Types.OTHER, "com.amazon.redshift.util.RedshiftIntervalYearToMonth", Oid.INTERVALY2M_ARRAY},
{"intervald2s", Oid.INTERVALD2S, Types.OTHER, "com.amazon.redshift.util.RedshiftIntervalDayToSecond", Oid.INTERVALD2S_ARRAY},
//JCP! if mvn.project.property.redshift.jdbc.spec >= "JDBC4.2"
{"refcursor", Oid.REF_CURSOR, Types.REF_CURSOR, "java.sql.ResultSet", Oid.REF_CURSOR_ARRAY},
//JCP! endif
Expand Down Expand Up @@ -745,6 +747,8 @@ public int getPrecision(int oid, int typmod) {
case Oid.TIME:
case Oid.TIMETZ:
case Oid.INTERVAL:
case Oid.INTERVALY2M:
case Oid.INTERVALD2S:
case Oid.TIMESTAMP:
case Oid.TIMESTAMPTZ:
return getDisplaySize(oid, typmod);
Expand Down Expand Up @@ -817,6 +821,8 @@ public boolean isCaseSensitive(int oid) {
case Oid.TIMESTAMP:
case Oid.TIMESTAMPTZ:
case Oid.INTERVAL:
case Oid.INTERVALY2M:
case Oid.INTERVALD2S:
case Oid.GEOGRAPHY:
return false;
default:
Expand Down Expand Up @@ -902,6 +908,8 @@ public int getDisplaySize(int oid, int typmod) {
return 13 + 1 + 8 + secondSize + 6;
}
case Oid.INTERVAL:
case Oid.INTERVALY2M:
case Oid.INTERVALD2S:
// SELECT LENGTH('-123456789 years 11 months 33 days 23 hours 10.123456 seconds'::interval);
return 49;
case Oid.VARCHAR:
Expand Down Expand Up @@ -955,6 +963,7 @@ public int getMaximumPrecision(int oid) {
case Oid.TIMESTAMP:
case Oid.TIMESTAMPTZ:
case Oid.INTERVAL:
case Oid.INTERVALD2S:
return 6;
case Oid.BPCHAR:
case Oid.VARCHAR:
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/com/amazon/redshift/util/RedshiftInterval.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,37 @@ public void setValue(int years, int months, int days, int hours, int minutes, do
setSeconds(seconds);
}

/**
* Set all values of this interval using just two specified values.
*
* @param month Total number of months (assuming 12 months in a year)
* @param time Total number of microseconds (assuming 1day = 24hrs = 1440mins = 86400secs = 8.64e10microsecs)
*/
public void setValue(int month, long time) {
int tm_year;
int tm_mon;

if (month != 0) {
tm_year = month / 12;
tm_mon = month % 12;
} else {
tm_year = 0;
tm_mon = 0;
}

int tm_mday = (int)(time / 86400000000L);
time -= (tm_mday * 86400000000L);
int tm_hour = (int)(time / 3600000000L);
time -= (tm_hour * 3600000000L);
int tm_min = (int)(time / 60000000L);
time -= (tm_min * 60000000L);
int tm_sec = (int)(time / 1000000L);
int fsec = (int)(time - (tm_sec * 1000000));
double sec = tm_sec + (fsec/1000000.0);

setValue(tm_year, tm_mon, tm_mday, tm_hour, tm_min, sec);
}

/**
* Returns the stored interval information as a string.
*
Expand Down Expand Up @@ -437,6 +468,25 @@ public void scale(int factor) {
setSeconds(factor * getSeconds());
}

/**
* Converts the month-year part of interval to the total number of months using 1year = 12months.
*
* @return Total number of months.
*/
public int totalMonths() {
return 12 * years + months;
}

/**
* Converts the day-time part of interval to the total number of microseconds using
* 1day = 24hrs = 1440mins = 86400secs = 8.64e10microsecs.
*
* @return Total number of microseconds.
*/
public long totalMicroseconds() {
return ((long) (((days * 24 + hours) * 60 + minutes) * 60 + wholeSeconds)) * 1000000 + microSeconds;
}

/**
* Returns integer value of value or 0 if value is null.
*
Expand Down

0 comments on commit 6b3f897

Please sign in to comment.