changeset 820:4c35009cd59c

In ResultSet.getObject() method added support for retrieving TIMESTAMP WITH TIME ZONE data as java.time.OffsetDateTime object and TIME WITH TIME ZONE as java.time.OffsetTime object. Also methods ResultSetMetaData.getColumnClassName() and ParameterMetaData.getParameterClassName() now return java.time.OffsetDateTime.class for columns of type TIMESTAMP WITH TIME ZONE and java.time.OffsetTime.class for columns of type TIME WITH TIME ZONE.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 14 Dec 2023 14:58:21 +0100 (16 months ago)
parents 726a1d7b168c
children 8053a633f548
files ChangeLog SQLSTATEs src/main/java/org/monetdb/jdbc/MonetDriver.java src/main/java/org/monetdb/jdbc/MonetResultSet.java tests/JDBC_API_Tester.java
diffstat 5 files changed, 144 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,15 @@
 # ChangeLog file for monetdb-java
 # This file is updated with Maddlog
 
+* Thu Dec 14 2023 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
+- In ResultSet.getObject() method added support for retrieving
+  TIMESTAMP WITH TIME ZONE data as java.time.OffsetDateTime object
+  and TIME WITH TIME ZONE as java.time.OffsetTime object.
+  Also methods ResultSetMetaData.getColumnClassName() and
+  ParameterMetaData.getParameterClassName() now return
+  java.time.OffsetDateTime.class for columns of type TIMESTAMP WITH TIME ZONE
+  and java.time.OffsetTime.class for columns of type TIME WITH TIME ZONE.
+
 * Thu Oct 12 2023 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
 - Improved DatabaseMetaData.getSystemFunctions(). It now also returns
   functions: current_sessionid, greatest, ifnull, least, sql_max, sql_min.
--- a/SQLSTATEs
+++ b/SQLSTATEs
@@ -27,6 +27,11 @@ 22M29 invalid inet format
 22M30 invalid URL format
 22M31 invalid UUID format
 22M32 invalid JSON format
+22M33 invalid Date format
+22M34 invalid Time format
+22M35 invalid Timestamp format
+22M36 invalid Time with time zone format
+22M37 invalid Timestamp with time zone format
 
 2BM37 dependent objects still exist
 2DM30 autocommit mode active
--- a/src/main/java/org/monetdb/jdbc/MonetDriver.java
+++ b/src/main/java/org/monetdb/jdbc/MonetDriver.java
@@ -433,11 +433,13 @@ public final class MonetDriver implement
 			case Types.DATE:
 				return java.sql.Date.class;
 			case Types.TIME:
+				return java.sql.Time.class;
 			case Types.TIME_WITH_TIMEZONE:
-				return java.sql.Time.class;
+				return java.time.OffsetTime.class;
 			case Types.TIMESTAMP:
+				return java.sql.Timestamp.class;
 			case Types.TIMESTAMP_WITH_TIMEZONE:
-				return java.sql.Timestamp.class;
+				return java.time.OffsetDateTime.class;
 			case Types.CLOB:
 				return java.sql.Clob.class;
 			case Types.BLOB:
--- a/src/main/java/org/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/org/monetdb/jdbc/MonetResultSet.java
@@ -38,6 +38,8 @@ import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
 import java.util.Calendar;
 import java.util.Map;
 import java.util.TimeZone;
@@ -1426,11 +1428,13 @@ public class MonetResultSet
 			case Types.DATE:
 				return getDate(columnIndex, null);
 			case Types.TIME:
+				return getTime(columnIndex, null);
 			case Types.TIME_WITH_TIMEZONE:
-				return getTime(columnIndex, null);
+				return getOffsetTime(columnIndex);
 			case Types.TIMESTAMP:
+				return getTimestamp(columnIndex, null);
 			case Types.TIMESTAMP_WITH_TIMEZONE:
-				return getTimestamp(columnIndex, null);
+				return getOffsetDateTime(columnIndex);
 			case Types.BINARY:
 			case Types.VARBINARY:
 		/*	case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY */
@@ -1511,33 +1515,53 @@ public class MonetResultSet
 
 		if (type == null || type == String.class) {
 			return val;
-		} else if (type == BigDecimal.class) {
+		}
+		if (type == BigDecimal.class) {
 			return getBigDecimal(columnIndex);
-		} else if (type == Boolean.class) {
+		}
+		if (type == Boolean.class) {
 			return Boolean.valueOf(getBoolean(columnIndex));
-		} else if (type == Short.class) {
+		}
+		if (type == Integer.class) {
+			return Integer.valueOf(getInt(columnIndex));
+		}
+		if (type == Long.class) {
+			return Long.valueOf(getLong(columnIndex));
+		}
+		if (type == Short.class) {
 			return Short.valueOf(getShort(columnIndex));
-		} else if (type == Integer.class) {
-			return Integer.valueOf(getInt(columnIndex));
-		} else if (type == Long.class) {
-			return Long.valueOf(getLong(columnIndex));
-		} else if (type == Float.class) {
-			return Float.valueOf(getFloat(columnIndex));
-		} else if (type == Double.class) {
+		}
+		if (type == Double.class) {
 			return Double.valueOf(getDouble(columnIndex));
-		} else if (type == byte[].class) {
-			return getBytes(columnIndex);
-		} else if (type == Date.class) {
+		}
+		if (type == Float.class) {
+			return Float.valueOf(getFloat(columnIndex));
+		}
+		if (type == Date.class) {
 			return getDate(columnIndex, null);
-		} else if (type == Time.class) {
+		}
+		if (type == Time.class) {
 			return getTime(columnIndex, null);
-		} else if (type == Timestamp.class) {
+		}
+		if (type == Timestamp.class) {
 			return getTimestamp(columnIndex, null);
-		} else if (type == Clob.class) {
+		}
+		if (type == OffsetTime.class) {
+			return getOffsetTime(columnIndex);
+		}
+		if (type == OffsetDateTime.class) {
+			return getOffsetDateTime(columnIndex);
+		}
+		if (type == Clob.class) {
 			return getClob(columnIndex);
-		} else if (type == Blob.class) {
+		}
+		if (type == Blob.class) {
 			return getBlob(columnIndex);
-		} else if (classImplementsSQLData(type)) {
+		}
+		if (type == byte[].class) {
+			return getBytes(columnIndex);
+		}
+		if (classImplementsSQLData(type)) {
 			final SQLData x;
 			try {
 				final java.lang.reflect.Constructor<? extends SQLData> ctor =
@@ -1694,9 +1718,8 @@ public class MonetResultSet
 			};
 			x.readSQL(input, MonetDBtype);
 			return x;
-		} else {
-			return val;
 		}
+		return val;
 	}
 
 	/**
@@ -3254,6 +3277,12 @@ public class MonetResultSet
 		if (type == Timestamp.class) {
 			return type.cast(getTimestamp(columnIndex, null));
 		}
+		if (type == OffsetTime.class) {
+			return type.cast(getOffsetTime(columnIndex));
+		}
+		if (type == OffsetDateTime.class) {
+			return type.cast(getOffsetDateTime(columnIndex));
+		}
 		if (type == java.util.Date.class) {
 		        final Timestamp timestamp = getTimestamp(columnIndex, null);
 		        return type.cast(new java.util.Date(timestamp.getTime()));
@@ -3362,6 +3391,67 @@ public class MonetResultSet
 	}
 
 	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object and will convert to OffsetTime.
+	 * If the conversion is not supported a SQLException is thrown.
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return OffsetTime object or null
+	 * @throws SQLException if conversion is not supported
+	 */
+	private OffsetTime getOffsetTime(final int columnIndex) throws SQLException {
+		final String val;
+		try {
+			val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			return OffsetTime.parse(val, java.time.format.DateTimeFormatter.ISO_TIME);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		} catch (java.time.format.DateTimeParseException e) {
+			throw new SQLException("Failed to convert to OffsetTime: " + e.getMessage(), "22M36");
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object and will convert to OffsetDateTime.
+	 * If the conversion is not supported a SQLException is thrown.
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return OffsetDateTime object or null
+	 * @throws SQLException if conversion is not supported
+	 */
+	private OffsetDateTime getOffsetDateTime(final int columnIndex) throws SQLException {
+		final String val;
+		try {
+			val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+
+			// ISO_OFFSET_DATE_TIME format expects a 'T' instead of a space between date and time parts
+			// replace the space between date and time parts with 'T'
+			String val_new = val;
+			final int space = val.indexOf(' ', 4);
+			if (space > 4 && space < 16) {
+				val_new = val.substring(0, space) + "T" + val.substring(space + 1);
+			}
+			// System.out.println("getOffsetDateTime() changed " + val + " into " + val_new);
+			return OffsetDateTime.parse(val_new, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		} catch (java.time.format.DateTimeParseException e) {
+			throw new SQLException("Failed to convert to OffsetDateTime: " + e.getMessage(), "22M37");
+		}
+	}
+
+	/**
 	 * Small helper method that formats the "Invalid Column Index number ..." message
 	 * and creates a new SQLDataException object whose SQLState is set
 	 * to "22010": invalid indicator parameter value.
--- a/tests/JDBC_API_Tester.java
+++ b/tests/JDBC_API_Tester.java
@@ -3071,6 +3071,12 @@ final public class JDBC_API_Tester {
 				.append(rs.getTime("t", c)).append(" | ")
 				.append(rs.getTime("tz", c)).append("\n");
 
+				sb.append("getObject:\n")
+				.append(rs.getObject("ts")).append(" | ")
+				.append(rs.getObject("tsz")).append(" | ")
+				.append(rs.getObject("t")).append(" | ")
+				.append(rs.getObject("tz")).append("\n");
+
 				SQLWarning w = rs.getWarnings();
 				while (w != null) {
 					sb.append(w.getMessage()).append("\n");
@@ -3109,6 +3115,8 @@ final public class JDBC_API_Tester {
 			"1970-01-01 08:00:00.0 | 1970-01-01 00:00:00.0 | 08:00:00 | 00:00:00\n" +
 			"Africa/Windhoek:\n" +
 			"1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" +
+			"getObject:\n" +
+			"1970-01-01 00:00:00.0 | 1970-01-01T01:00+01:00 | 00:00:00 | 01:00+01:00\n" +
 			"retrieved row (String):\n" +
 // old output		"1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" +
 			"1970-01-01 00:00:00.000000 | 00:00:00 | \n" +
@@ -3118,6 +3126,8 @@ final public class JDBC_API_Tester {
 			"1970-01-01 08:00:00.0 | 1970-01-01 00:00:00.0 | 08:00:00 | 00:00:00\n" +
 			"Africa/Windhoek:\n" +
 			"1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" +
+			"getObject:\n" +
+			"1970-01-01 00:00:00.0 | 1970-01-01T01:00+01:00 | 00:00:00 | 01:00+01:00\n" +
 			"retrieved row (String):\n" +
 // old output		"1969-12-31 16:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 16:00:00 | 01:00:00+01:00\n" +
 			"1969-12-31 16:00:00.000000 | 16:00:00 | \n" +
@@ -3127,6 +3137,8 @@ final public class JDBC_API_Tester {
 			"1970-01-01 00:00:00.0 | 1970-01-01 00:00:00.0 | 00:00:00 | 00:00:00\n" +
 			"Africa/Windhoek:\n" +
 			"1969-12-31 14:00:00.0 | 1970-01-01 00:00:00.0 | 14:00:00 | 00:00:00\n" +
+			"getObject:\n" +
+			"1969-12-31 16:00:00.0 | 1970-01-01T01:00+01:00 | 16:00:00 | 01:00+01:00\n" +
 			"retrieved row (String):\n" +
 // old output		"1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" +
 			"1970-01-01 00:00:00.000000 | 00:00:00 | \n" +
@@ -3136,6 +3148,8 @@ final public class JDBC_API_Tester {
 			"1970-01-01 08:00:00.0 | 1970-01-01 00:00:00.0 | 08:00:00 | 00:00:00\n" +
 			"Africa/Windhoek:\n" +
 			"1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" +
+			"getObject:\n" +
+			"1970-01-01 00:00:00.0 | 1970-01-01T01:00+01:00 | 00:00:00 | 01:00+01:00\n" +
 			"0. true	true\n");
 	}