Skip to content

Commit

Permalink
Support LocalDateTime and OffsetDateTime in CallableStatement (#1393)
Browse files Browse the repository at this point in the history
  • Loading branch information
CasualSuperman committed Sep 1, 2020
1 parent 059ab70 commit 65c9c3d
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -740,6 +741,34 @@ public <T> T getObject(int index, Class<T> 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 == java.time.OffsetDateTime.class) {
microsoft.sql.DateTimeOffset dateTimeOffset = getDateTimeOffset(index);
if (dateTimeOffset == null) {
returnValue = null;
} else {
returnValue = dateTimeOffset.getOffsetDateTime();
}
} else if (type == java.time.OffsetTime.class) {
microsoft.sql.DateTimeOffset dateTimeOffset = getDateTimeOffset(index);
if (dateTimeOffset == null) {
returnValue = null;
} else {
returnValue = dateTimeOffset.getOffsetDateTime().toOffsetTime();
}
} else if (type == microsoft.sql.DateTimeOffset.class) {
returnValue = getDateTimeOffset(index);
} else if (type == UUID.class) {
Expand Down Expand Up @@ -893,6 +922,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))
Expand Down
@@ -1,15 +1,23 @@
package com.microsoft.sqlserver.jdbc.callablestatement;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.text.MessageFormat;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
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;
Expand Down Expand Up @@ -39,6 +47,8 @@ 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");
private static String getObjectOffsetDateTimeProcedureName = RandomUtil.getIdentifier("CallableStatementTest_getObjectOffsetDateTime_SP");

/**
* Setup before test
Expand All @@ -53,11 +63,15 @@ 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);
TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(getObjectOffsetDateTimeProcedureName), stmt);

createGUIDTable(stmt);
createGUIDStoredProcedure(stmt);
createSetNullProcedure(stmt);
createInputParamsProcedure(stmt);
createGetObjectLocalDateTimeProcedure(stmt);
createGetObjectOffsetDateTimeProcedure(stmt);
}
}

Expand Down Expand Up @@ -123,6 +137,74 @@ public void getSetNullWithTypeVarchar() throws SQLException {
}
}


/**
* Tests getObject(n, java.time.LocalDateTime.class).
*
* @throws SQLException
*/
@Test
public void testGetObjectAsLocalDateTime() 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);
}
}
}

/**
* Tests getObject(n, java.time.OffsetDateTime.class) and getObject(n, java.time.OffsetTime.class).
*
* @throws SQLException
*/
@Test
@Tag(Constants.xAzureSQLDW)
public void testGetObjectAsOffsetDateTime() throws SQLException {
String sql = "{CALL " + AbstractSQLGenerator.escapeIdentifier(getObjectOffsetDateTimeProcedureName) + " (?, ?)}";
try (Connection con = DriverManager.getConnection(connectionString); CallableStatement cs = con.prepareCall(sql)) {
cs.registerOutParameter(1, Types.TIMESTAMP_WITH_TIMEZONE);
cs.registerOutParameter(2, Types.TIMESTAMP_WITH_TIMEZONE);

final String testValue = "2018-01-02T11:22:33.123456700+12:34";

cs.execute();

OffsetDateTime expected = OffsetDateTime.parse(testValue);
OffsetDateTime actual = cs.getObject(1, OffsetDateTime.class);
assertEquals(expected, actual);
assertNull(cs.getObject(2, OffsetDateTime.class));

OffsetTime expectedTime = OffsetTime.parse(testValue.split("T")[1]);
OffsetTime actualTime = cs.getObject(1, OffsetTime.class);
assertEquals(expectedTime, actualTime);
assertNull(cs.getObject(2, OffsetTime.class));
}
}

/**
* recognize parameter names with and without leading '@'
*
Expand Down Expand Up @@ -179,6 +261,8 @@ public static void cleanup() 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);
TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(getObjectOffsetDateTimeProcedureName), stmt);
}
}

Expand Down Expand Up @@ -207,4 +291,18 @@ 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 @p1 = '2018-03-11T02:00:00.1234567'";
stmt.execute(sql);
}

private static void createGetObjectOffsetDateTimeProcedure(Statement stmt) throws SQLException {
String sql = "CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(getObjectOffsetDateTimeProcedureName)
+ "(@p1 DATETIMEOFFSET OUTPUT, @p2 DATETIMEOFFSET OUTPUT) AS "
+ "SELECT @p1 = '2018-01-02T11:22:33.123456700+12:34', @p2 = NULL";
stmt.execute(sql);
}
}

0 comments on commit 65c9c3d

Please sign in to comment.