changeset 832:7eb05cbf67dc

In ResultSet.getObject(column, Class<T> type) and ResultSet.getObject(column, Map<String,Class<?>>) methods added support to return objects for classes: java.time.LocalDate, java.time.LocalDateTime.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 28 Dec 2023 17:12:56 +0100 (16 months ago)
parents 5f3e19ef251a
children e890195256ac
files ChangeLog src/main/java/org/monetdb/jdbc/MonetResultSet.java tests/JDBC_API_Tester.java
diffstat 3 files changed, 168 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,12 @@
 # ChangeLog file for monetdb-java
 # This file is updated with Maddlog
 
+* Thu Dec 28 2023 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
+- In ResultSet.getObject(column, Class<T> type) and
+  ResultSet.getObject(column, Map<String,Class<?>>) methods added support
+  to return objects for classes: java.time.LocalDate, java.time.LocalDateTime
+  and java.time.LocalTime.
+
 * Wed Dec 20 2023 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
 - Enhanced DatabaseMetaData.getTables() method by adding support for SQL
   table type names: 'BASE TABLE', 'GLOBAL TEMPORARY' and 'LOCAL TEMPORARY'
--- a/src/main/java/org/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/org/monetdb/jdbc/MonetResultSet.java
@@ -38,8 +38,12 @@ import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Map;
 import java.util.TimeZone;
@@ -1546,12 +1550,21 @@ public class MonetResultSet
 		if (type == Timestamp.class) {
 			return getTimestamp(columnIndex, null);
 		}
-		if (type == OffsetTime.class) {
-			return getOffsetTime(columnIndex);
+		if (type == LocalDate.class) {
+			return getLocalDate(columnIndex);
+		}
+		if (type == LocalDateTime.class) {
+			return getLocalDateTime(columnIndex);
+		}
+		if (type == LocalTime.class) {
+			return getLocalTime(columnIndex);
 		}
 		if (type == OffsetDateTime.class) {
 			return getOffsetDateTime(columnIndex);
 		}
+		if (type == OffsetTime.class) {
+			return getOffsetTime(columnIndex);
+		}
 		if (type == Clob.class) {
 			return getClob(columnIndex);
 		}
@@ -3277,12 +3290,21 @@ public class MonetResultSet
 		if (type == Timestamp.class) {
 			return type.cast(getTimestamp(columnIndex, null));
 		}
-		if (type == OffsetTime.class) {
-			return type.cast(getOffsetTime(columnIndex));
+		if (type == LocalDate.class) {
+			return type.cast(getLocalDate(columnIndex));
+		}
+		if (type == LocalDateTime.class) {
+			return type.cast(getLocalDateTime(columnIndex));
+		}
+		if (type == LocalTime.class) {
+			return type.cast(getLocalTime(columnIndex));
 		}
 		if (type == OffsetDateTime.class) {
 			return type.cast(getOffsetDateTime(columnIndex));
 		}
+		if (type == OffsetTime.class) {
+			return type.cast(getOffsetTime(columnIndex));
+		}
 		if (type == java.util.Date.class) {
 		        final Timestamp timestamp = getTimestamp(columnIndex, null);
 		        return type.cast(new java.util.Date(timestamp.getTime()));
@@ -3392,14 +3414,14 @@ public class MonetResultSet
 
 	/**
 	 * Retrieves the value of the designated column in the current row
-	 * of this ResultSet object and will convert to OffsetTime.
+	 * of this ResultSet object and will convert to LocalDate.
 	 * 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
+	 * @return LocalDate object or null
 	 * @throws SQLException if conversion is not supported
 	 */
-	private OffsetTime getOffsetTime(final int columnIndex) throws SQLException {
+	private LocalDate getLocalDate(final int columnIndex) throws SQLException {
 		final String val;
 		try {
 			val = tlp.values[columnIndex - 1];
@@ -3408,11 +3430,78 @@ public class MonetResultSet
 				return null;
 			}
 			lastReadWasNull = false;
-			return OffsetTime.parse(val, java.time.format.DateTimeFormatter.ISO_TIME);
+
+			// Note: ISO_LOCAL_DATE format requires the year to have 4 (or more) digits else parse will fail
+			// This means years -999 to 999 will fail to parse. They should have been zero padded, so -0999 to 0999.
+			return LocalDate.parse(val, DateTimeFormatter.ISO_LOCAL_DATE);
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		} catch (java.time.format.DateTimeParseException e) {
-			throw new SQLException("Failed to convert to OffsetTime: " + e.getMessage(), "22M36");
+			throw new SQLException("Failed to convert to LocalDate: " + e.getMessage(), "22M33");
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object and will convert to LocalDateTime.
+	 * If the conversion is not supported a SQLException is thrown.
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return LocalDateTime object or null
+	 * @throws SQLException if conversion is not supported
+	 */
+	private LocalDateTime getLocalDateTime(final int columnIndex) throws SQLException {
+		final String val;
+		try {
+			val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+
+			// ISO_LOCAL_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("getLocalDateTime() changed " + val + " into " + val_new);
+			}
+
+			// Note: ISO_LOCAL_DATE_TIME format requires the year to have 4 (or more) digits else parse will fail
+			// This means years -999 to 999 will fail to parse. They should have been zero padded, so -0999 to 0999.
+			return LocalDateTime.parse(val_new, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		} catch (java.time.format.DateTimeParseException e) {
+			throw new SQLException("Failed to convert to LocalDateTime: " + e.getMessage(), "22M35");
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object and will convert to LocalTime.
+	 * If the conversion is not supported a SQLException is thrown.
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return LocalTime object or null
+	 * @throws SQLException if conversion is not supported
+	 */
+	private LocalTime getLocalTime(final int columnIndex) throws SQLException {
+		final String val;
+		try {
+			val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			return LocalTime.parse(val, DateTimeFormatter.ISO_LOCAL_TIME);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		} catch (java.time.format.DateTimeParseException e) {
+			throw new SQLException("Failed to convert to LocalTime: " + e.getMessage(), "22M34");
 		}
 	}
 
@@ -3441,9 +3530,12 @@ public class MonetResultSet
 			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);
 			}
-			// System.out.println("getOffsetDateTime() changed " + val + " into " + val_new);
-			return OffsetDateTime.parse(val_new, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+
+			// Note: ISO_OFFSET_DATE_TIME format requires the year to have 4 (or more) digits else parse will fail
+			// This means years -999 to 999 will fail to parse. They should have been zero padded, so -0999 to 0999.
+			return OffsetDateTime.parse(val_new, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		} catch (java.time.format.DateTimeParseException e) {
@@ -3452,6 +3544,32 @@ 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, 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");
+		}
+	}
+
+	/**
 	 * 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
@@ -39,7 +39,7 @@ import org.monetdb.jdbc.types.URL;
  */
 final public class JDBC_API_Tester {
 	StringBuilder sb;	// buffer to collect the test output
-	final static int sbInitLen = 3712;
+	final static int sbInitLen = 5224;
 	Connection con;	// main connection shared by all tests
 	int dbmsMajorVersion;
 	int dbmsMinorVersion;
@@ -4296,12 +4296,15 @@ final public class JDBC_API_Tester {
 			"1. ts 2004-04-24 11:43:53.123000 to ts: 2004-04-24 11:43:53.123\n" +
 			"1. ts 2004-04-24 11:43:53.123000 to tm: 11:43:53\n" +
 			"1. ts 2004-04-24 11:43:53.123000 to dt: 2004-04-24\n" +
+			"1. ts 2004-04-24 11:43:53.123000 to LocalDateTime: 2004-04-24T11:43:53.123\n" +
 			"2. t 11:43:53 to ts: 1970-01-01 11:43:53.0\n" +
 			"2. t 11:43:53 to tm: 11:43:53\n" +
 			"2. t 11:43:53 to dt: 1970-01-01\n" +
+			"2. t 11:43:53 to LocalTime: 11:43:53\n" +
 			"3. d 2004-04-24 to ts: 2004-04-24 00:00:00.0\n" +
 			"3. d 2004-04-24 to tm: 00:00:00\n" +
 			"3. d 2004-04-24 to dt: 2004-04-24\n" +
+			"3. d 2004-04-24 to LocalDate: 2004-04-24\n" +
 			"4. vc 2004-04-24 11:43:53.654321 to ts: 2004-04-24 11:43:53.654321\n" +
 			"4. vc 2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 5 found: '-' in '2004-04-24 11:43:53.654321'\n" +
 			"4. vc 2004-04-24 11:43:53.654321 to dt: 2004-04-24\n" +
@@ -4314,21 +4317,27 @@ final public class JDBC_API_Tester {
 			"11. ts 904-04-24 11:43:53.567000 to ts: 0904-04-24 11:43:53.567\n" +
 			"11. ts 904-04-24 11:43:53.567000 to tm: 11:43:53\n" +
 			"11. ts 904-04-24 11:43:53.567000 to dt: 0904-04-24\n" +
+			"11. ts 904-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '904-04-24T11:43:53.567000' could not be parsed at index 0\n" +
 			"12. ts 74-04-24 11:43:53.567000 to ts: 0074-04-24 11:43:53.567\n" +
 			"12. ts 74-04-24 11:43:53.567000 to tm: 11:43:53\n" +
 			"12. ts 74-04-24 11:43:53.567000 to dt: 0074-04-24\n" +
+			"12. ts 74-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '74-04-24T11:43:53.567000' could not be parsed at index 0\n" +
 			"13. ts 4-04-24 11:43:53.567000 to ts: 0004-04-24 11:43:53.567\n" +
 			"13. ts 4-04-24 11:43:53.567000 to tm: 11:43:53\n" +
 			"13. ts 4-04-24 11:43:53.567000 to dt: 0004-04-24\n" +
+			"13. ts 4-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '4-04-24T11:43:53.567000' could not be parsed at index 0\n" +
 			"14. d 904-04-24 to ts: 0904-04-24 00:00:00.0\n" +
 			"14. d 904-04-24 to tm: 00:00:00\n" +
 			"14. d 904-04-24 to dt: 0904-04-24\n" +
+			"14. d 904-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '904-04-24' could not be parsed at index 0\n" +
 			"15. d 74-04-24 to ts: 0074-04-24 00:00:00.0\n" +
 			"15. d 74-04-24 to tm: 00:00:00\n" +
 			"15. d 74-04-24 to dt: 0074-04-24\n" +
+			"15. d 74-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '74-04-24' could not be parsed at index 0\n" +
 			"16. d 4-04-24 to ts: 0004-04-24 00:00:00.0\n" +
 			"16. d 4-04-24 to tm: 00:00:00\n" +
 			"16. d 4-04-24 to dt: 0004-04-24\n" +
+			"16. d 4-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '4-04-24' could not be parsed at index 0\n" +
 			"17. vc 904-04-24 11:43:53.567 to ts: 0904-04-24 11:43:53.567\n" +
 			"17. vc 904-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 4 found: '-' in '904-04-24 11:43:53.567'\n" +
 			"17. vc 904-04-24 11:43:53.567 to dt: 0904-04-24\n" +
@@ -4341,15 +4350,19 @@ final public class JDBC_API_Tester {
 			"21. ts -4-04-24 11:43:53.567000 to ts: 0004-04-24 11:43:53.567\n" +
 			"21. ts -4-04-24 11:43:53.567000 to tm: 11:43:53\n" +
 			"21. ts -4-04-24 11:43:53.567000 to dt: 0004-04-24\n" +
+			"21. ts -4-04-24 11:43:53.567000 to LocalDateTime: rs.getObject(colnm, class<T>): Failed to convert to LocalDateTime: Text '-4-04-24T11:43:53.567000' could not be parsed at index 1\n" +
 			"22. ts -2004-04-24 11:43:53.567000 to ts: 2004-04-24 11:43:53.567\n" +
 			"22. ts -2004-04-24 11:43:53.567000 to tm: 11:43:53\n" +
 			"22. ts -2004-04-24 11:43:53.567000 to dt: 2004-04-24\n" +
+			"22. ts -2004-04-24 11:43:53.567000 to LocalDateTime: -2004-04-24T11:43:53.567\n" +
 			"23. d -4-04-24 to ts: 0004-04-24 00:00:00.0\n" +
 			"23. d -4-04-24 to tm: 00:00:00\n" +
 			"23. d -4-04-24 to dt: 0004-04-24\n" +
+			"23. d -4-04-24 to LocalDate: rs.getObject(colnm, class<T>): Failed to convert to LocalDate: Text '-4-04-24' could not be parsed at index 1\n" +
 			"24. d -3004-04-24 to ts: 3004-04-24 00:00:00.0\n" +
 			"24. d -3004-04-24 to tm: 00:00:00\n" +
 			"24. d -3004-04-24 to dt: 3004-04-24\n" +
+			"24. d -3004-04-24 to LocalDate: -3004-04-24\n" +
 			"25. vc -2004-04-24 11:43:53.654321 to ts: 2004-04-24 11:43:53.654321\n" +
 			"25. vc -2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 6 found: '-' in '-2004-04-24 11:43:53.654321'\n" +
 			"25. vc -2004-04-24 11:43:53.654321 to dt: 2004-04-24\n" +
@@ -4394,6 +4407,25 @@ final public class JDBC_API_Tester {
 		}
 		readWarnings(rs.getWarnings());
 		rs.clearWarnings();
+
+		// getObject(colnm, class<T>) may raise a conversion error
+		try {
+			switch(colnm) {
+			case "d":
+				sb.append(data).append("LocalDate: ").append(rs.getObject(colnm, java.time.LocalDate.class)).append("\n");
+				break;
+			case "ts":
+				sb.append(data).append("LocalDateTime: ").append(rs.getObject(colnm, java.time.LocalDateTime.class)).append("\n");
+				break;
+			case "t":
+				sb.append(data).append("LocalTime: ").append(rs.getObject(colnm, java.time.LocalTime.class)).append("\n");
+				break;
+			}
+		} catch (SQLException e) {
+			sb.append("rs.getObject(colnm, class<T>): ").append(e.getMessage()).append("\n");
+		}
+		readWarnings(rs.getWarnings());
+		rs.clearWarnings();
 	}
 
 	private void Test_Sbatching() {