From 03dcfeefdc673d9bc75b400e766290d0907f5df1 Mon Sep 17 00:00:00 2001 From: Bobby Wertman Date: Mon, 27 Jul 2020 23:31:54 -0400 Subject: [PATCH] Support LocalDateTime in CallableStatement This PR adds support for the requested functionality in #1392, but does not address the mentioned code duplication. The added library code was copied verbatim from SQLServerResultSet. --- .../jdbc/SQLServerCallableStatement.java | 23 +++++++ .../CallableStatementTest.java | 60 +++++++++++++++++-- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index d864f2fa6b..84324bda44 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -24,6 +24,7 @@ import java.sql.Time; import java.sql.Timestamp; import java.text.MessageFormat; +import java.time.LocalDateTime; import java.util.Calendar; import java.util.HashMap; import java.util.TreeMap; @@ -740,6 +741,20 @@ public T getObject(int index, Class type) throws SQLException { returnValue = getTime(index); } else if (type == java.sql.Timestamp.class) { returnValue = getTimestamp(index); + } else if (type == java.time.LocalDateTime.class || type == java.time.LocalDate.class + || type == java.time.LocalTime.class) { + java.time.LocalDateTime ldt = getLocalDateTime(index); + if (null == ldt) { + returnValue = null; + } else { + if (type == java.time.LocalDateTime.class) { + returnValue = ldt; + } else if (type == java.time.LocalDate.class) { + returnValue = ldt.toLocalDate(); + } else { + returnValue = ldt.toLocalTime(); + } + } } else if (type == microsoft.sql.DateTimeOffset.class) { returnValue = getDateTimeOffset(index); } else if (type == UUID.class) { @@ -893,6 +908,14 @@ public Timestamp getTimestamp(String name, Calendar cal) throws SQLServerExcepti return value; } + LocalDateTime getLocalDateTime(int columnIndex) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "getLocalDateTime", columnIndex); + checkClosed(); + LocalDateTime value = (LocalDateTime) getValue(columnIndex, JDBCType.LOCALDATETIME); + loggerExternal.exiting(getClassNameLogging(), "getLocalDateTime", value); + return value; + } + @Override public Timestamp getDateTime(int index) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java index 139b8670aa..d7e137f30a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -3,13 +3,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; +import java.sql.*; import java.text.MessageFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.TimeZone; import java.util.UUID; import org.junit.jupiter.api.AfterAll; @@ -39,6 +38,7 @@ public class CallableStatementTest extends AbstractTest { private static String outputProcedureNameGUID = RandomUtil.getIdentifier("uniqueidentifier_SP"); private static String setNullProcedureName = RandomUtil.getIdentifier("CallableStatementTest_setNull_SP"); private static String inputParamsProcedureName = RandomUtil.getIdentifier("CallableStatementTest_inputParams_SP"); + private static String getObjectLocalDateTimeProcedureName = RandomUtil.getIdentifier("CallableStatementTest_getObjectLocalDateTime_SP"); /** * Setup before test @@ -53,11 +53,13 @@ public static void setupTest() throws SQLException { TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(outputProcedureNameGUID), stmt); TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(setNullProcedureName), stmt); TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(inputParamsProcedureName), stmt); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(getObjectLocalDateTimeProcedureName), stmt); createGUIDTable(stmt); createGUIDStoredProcedure(stmt); createSetNullProcedure(stmt); createInputParamsProcedure(stmt); + createGetObjectLocalDateTimeProcedure(stmt); } } @@ -123,6 +125,45 @@ public void getSetNullWithTypeVarchar() throws SQLException { } } + + /** + * Tests getObject(n, java.time.LocalDateTime.class). + * + * @throws SQLException + */ + @Test + public void getObjectAsLocalDateTime() throws SQLException { + String sql = "{CALL " + AbstractSQLGenerator.escapeIdentifier(getObjectLocalDateTimeProcedureName) + " (?)}"; + try (Connection con = DriverManager.getConnection(connectionString); CallableStatement cs = con.prepareCall(sql)) { + cs.registerOutParameter(1, Types.TIMESTAMP); + TimeZone prevTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("America/Edmonton")); + + // a local date/time that does not actually exist because of Daylight Saving Time + final String testValueDate = "2018-03-11"; + final String testValueTime = "02:00:00.1234567"; + final String testValueDateTime = testValueDate + "T" + testValueTime; + + try { + cs.execute(); + + LocalDateTime expectedLocalDateTime = LocalDateTime.parse(testValueDateTime); + LocalDateTime actualLocalDateTime = cs.getObject(1, LocalDateTime.class); + assertEquals(expectedLocalDateTime, actualLocalDateTime); + + LocalDate expectedLocalDate = LocalDate.parse(testValueDate); + LocalDate actualLocalDate = cs.getObject(1, LocalDate.class); + assertEquals(expectedLocalDate, actualLocalDate); + + LocalTime expectedLocalTime = LocalTime.parse(testValueTime); + LocalTime actualLocalTime = cs.getObject(1, LocalTime.class); + assertEquals(expectedLocalTime, actualLocalTime); + } finally { + TimeZone.setDefault(prevTimeZone); + } + } + } + /** * recognize parameter names with and without leading '@' * @@ -207,4 +248,11 @@ private static void createInputParamsProcedure(Statement stmt) throws SQLExcepti stmt.execute(sql); } + + private static void createGetObjectLocalDateTimeProcedure(Statement stmt) throws SQLException { + String sql = "CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(getObjectLocalDateTimeProcedureName) + + "(@p1 datetime2(7) OUTPUT) AS " + + "SELECT '2018-03-11T02:00:00.1234567' = GETDATE()"; + stmt.execute(sql); + } }