From fc04e7551d0a1edd72c809f8432c7c5cc67b646e Mon Sep 17 00:00:00 2001 From: quaso Date: Mon, 20 Feb 2023 14:16:38 +0100 Subject: [PATCH] fixes #354 different behaviour of jTDS MSSQL driver --- .../jdbc/AutodetectJdbcCustomization.java | 7 +++- .../scheduler/jdbc/JdbcCustomization.java | 4 +++ .../scheduler/jdbc/JdbcTaskRepository.java | 2 +- .../jdbc/MssqlJdbcCustomization.java | 36 ++++++++++++++++--- .../scheduler/task/schedule/CronSchedule.java | 2 ++ 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/AutodetectJdbcCustomization.java b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/AutodetectJdbcCustomization.java index a739a4de..f3ea61c4 100644 --- a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/AutodetectJdbcCustomization.java +++ b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/AutodetectJdbcCustomization.java @@ -44,7 +44,7 @@ public AutodetectJdbcCustomization(DataSource dataSource) { if (databaseProductName.equals(MICROSOFT_SQL_SERVER)) { LOG.info("Using MSSQL jdbc-overrides."); - detectedCustomization = new MssqlJdbcCustomization(); + detectedCustomization = new MssqlJdbcCustomization(c.getMetaData().getDriverName()); } else if (databaseProductName.equals(POSTGRESQL)) { LOG.info("Using PostgreSQL jdbc-overrides."); detectedCustomization = new PostgreSqlJdbcCustomization(); @@ -67,6 +67,11 @@ public Instant getInstant(ResultSet rs, String columnName) throws SQLException { return jdbcCustomization.getInstant(rs, columnName); } + @Override + public byte[] getBytes(ResultSet rs, String columnName) throws SQLException { + return jdbcCustomization.getBytes(rs, columnName); + } + @Override public boolean supportsExplicitQueryLimitPart() { return jdbcCustomization.supportsExplicitQueryLimitPart(); diff --git a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcCustomization.java b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcCustomization.java index e4596055..d87c4f5b 100644 --- a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcCustomization.java +++ b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcCustomization.java @@ -29,6 +29,10 @@ public interface JdbcCustomization { void setInstant(PreparedStatement p, int index, Instant value) throws SQLException; Instant getInstant(ResultSet rs, String columnName) throws SQLException; + default byte[] getBytes(ResultSet rs, String columnName) throws SQLException { + return rs.getBytes(columnName); + } + boolean supportsExplicitQueryLimitPart(); String getQueryLimitPart(int limit); diff --git a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcTaskRepository.java b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcTaskRepository.java index ae48b6d5..cc15545a 100644 --- a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcTaskRepository.java +++ b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/JdbcTaskRepository.java @@ -470,7 +470,7 @@ public Void map(ResultSet rs) throws SQLException { } String instanceId = rs.getString("task_instance"); - byte[] data = rs.getBytes("task_data"); + byte[] data = jdbcCustomization.getBytes(rs,"task_data"); Instant executionTime = jdbcCustomization.getInstant(rs, "execution_time"); diff --git a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/MssqlJdbcCustomization.java b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/MssqlJdbcCustomization.java index b1d5ff6b..ea59d0f6 100644 --- a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/MssqlJdbcCustomization.java +++ b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/jdbc/MssqlJdbcCustomization.java @@ -17,10 +17,10 @@ import com.github.kagkarlsson.scheduler.task.Execution; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.sql.*; import java.time.Instant; import java.util.Calendar; import java.util.List; @@ -29,6 +29,13 @@ public class MssqlJdbcCustomization implements JdbcCustomization { + private final String driverName; + + public MssqlJdbcCustomization(String driverName) { + this.driverName = driverName; + } + + @Override public String getName() { return "MSSQL"; @@ -44,6 +51,27 @@ public Instant getInstant(ResultSet rs, String columnName) throws SQLException { return Optional.ofNullable(rs.getTimestamp(columnName)).map(Timestamp::toInstant).orElse(null); } + @Override + public byte[] getBytes(ResultSet rs, String columnName) throws SQLException { + byte[] result; + if (driverName.contains("jTDS")) { + try (Reader reader = ((Clob) rs.getObject(columnName)).getCharacterStream()) { + char[] charArray = new char[8 * 1024]; + StringBuilder builder = new StringBuilder(); + int numCharsRead; + while ((numCharsRead = reader.read(charArray, 0, charArray.length)) != -1) { + builder.append(charArray, 0, numCharsRead); + } + result = builder.toString().getBytes(StandardCharsets.UTF_16LE); + } catch (IOException e) { + throw new SQLException(e); + } + } else { + result = rs.getBytes(columnName); + } + return result; + } + @Override public boolean supportsExplicitQueryLimitPart() { return false; diff --git a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/task/schedule/CronSchedule.java b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/task/schedule/CronSchedule.java index b722d0a6..5175036b 100644 --- a/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/task/schedule/CronSchedule.java +++ b/db-scheduler/src/main/java/com/github/kagkarlsson/scheduler/task/schedule/CronSchedule.java @@ -39,6 +39,8 @@ public class CronSchedule implements Schedule, Serializable { private static final String DISABLED = "-"; private static final Logger LOG = LoggerFactory.getLogger(CronSchedule.class); + + private static final long serialVersionUID = 6577185347687546367L; private final String pattern; private final ZoneId zoneId; private transient ExecutionTime cronExecutionTime; // lazily initialized