Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support LocalDateTime and OffsetDateTime in CallableStatement #1393

Merged
merged 9 commits into from
Sep 1, 2020
Original file line number Diff line number Diff line change
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();
}
}
CasualSuperman marked this conversation as resolved.
Show resolved Hide resolved
} 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
Original file line number Diff line number Diff line change
@@ -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 {
CasualSuperman marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}