diff --git a/pom.xml b/pom.xml index 204189c8..a359aad6 100644 --- a/pom.xml +++ b/pom.xml @@ -64,9 +64,11 @@ 11 11 - 2.5.0 4.0.5 + 2.7.1 + debug + 2.0.9 5.10.0 @@ -98,6 +100,7 @@ org.hsqldb hsqldb ${dep.hsqldb.version} + ${dep.hsqldb.classifier} com.healthmarketscience.jackcess diff --git a/src/main/java/net/ucanaccess/jdbc/UcanaccessDriver.java b/src/main/java/net/ucanaccess/jdbc/UcanaccessDriver.java index 412baab0..f6d3fcd5 100644 --- a/src/main/java/net/ucanaccess/jdbc/UcanaccessDriver.java +++ b/src/main/java/net/ucanaccess/jdbc/UcanaccessDriver.java @@ -24,11 +24,15 @@ public final class UcanaccessDriver implements Driver { DriverManager.registerDriver(new UcanaccessDriver()); Class.forName("org.hsqldb.jdbc.JDBCDriver"); - } catch (ClassNotFoundException e) { + // Set property with semicolon-separated list (including wildcards) of Java classes + // that can be used for routines based on Java static methods + System.setProperty("hsqldb.method_class_names", "net.ucanaccess.converters.*"); + + } catch (ClassNotFoundException _ex) { Logger.logWarning(LoggerMessageEnum.HSQLDB_DRIVER_NOT_FOUND); - throw new RuntimeException(e.getMessage()); - } catch (SQLException e) { - throw new RuntimeException(e.getMessage()); + throw new RuntimeException(_ex.getMessage()); + } catch (SQLException _ex) { + throw new RuntimeException(_ex.getMessage()); } } @@ -173,7 +177,11 @@ public Connection connect(String _url, Properties _props) throws SQLException { SQLWarning sqlw = null; if (!alreadyLoaded) { boolean toBeLoaded = !dbRef.loadedFromKeptMirror(session); - LoadJet la = new LoadJet(dbRef.getHSQLDBConnection(session), dbRef.getDbIO()); + Connection conn = dbRef.getHSQLDBConnection(session); + // from version 2.7 hsqldb translates timestamps stored without timezone in the database + // into the default timezone. MS Access however does not know timezones, therefore assume timestamps are UTC + conn.createStatement().executeQuery("SET TIME ZONE 'UTC'"); + LoadJet la = new LoadJet(conn, dbRef.getDbIO()); Logger.turnOffJackcessLog(); if (_props.containsKey("sysschema")) { boolean sysSchema = Boolean.parseBoolean(_props.getProperty("sysschema")); diff --git a/src/test/java/net/ucanaccess/converters/AddFunctions.java b/src/test/java/net/ucanaccess/converters/AddFunctions.java new file mode 100644 index 00000000..f590013a --- /dev/null +++ b/src/test/java/net/ucanaccess/converters/AddFunctions.java @@ -0,0 +1,23 @@ +package net.ucanaccess.converters; + +import net.ucanaccess.converters.TypesMap.AccessType; +import net.ucanaccess.ext.FunctionType; + +import java.sql.Timestamp; + +public final class AddFunctions { + + private AddFunctions() { + } + + @FunctionType(functionName = "pluto", argumentTypes = {AccessType.TEXT, AccessType.TEXT, AccessType.DATETIME}, returnType = AccessType.TEXT) + public static String example(String s1, String s2, Timestamp dt) { + return s1 + s2 + dt; + } + + @FunctionType(functionName = "concat", argumentTypes = {AccessType.TEXT, AccessType.TEXT}, returnType = AccessType.TEXT) + public static String concat(String s1, String s2) { + return s1 + s2; + } + +} diff --git a/src/test/java/net/ucanaccess/test/integration/AddFunctionTest.java b/src/test/java/net/ucanaccess/test/integration/AddFunctionTest.java index b59cea33..8c5f8819 100644 --- a/src/test/java/net/ucanaccess/test/integration/AddFunctionTest.java +++ b/src/test/java/net/ucanaccess/test/integration/AddFunctionTest.java @@ -1,7 +1,7 @@ package net.ucanaccess.test.integration; +import net.ucanaccess.converters.AddFunctions; import net.ucanaccess.test.util.AccessVersion; -import net.ucanaccess.test.util.AddFunctionClass; import net.ucanaccess.test.util.UcanaccessBaseTest; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -31,9 +31,9 @@ void testAddFunction(AccessVersion _accessVersion) throws Exception { st.execute("INSERT INTO t_add_function (id) VALUES(1)"); } - ucanaccess.addFunctions(AddFunctionClass.class); - dumpQueryResult("SELECT pluto('hello',' world ', NOW()) FROM t_add_function"); - checkQuery("SELECT CONCAT('Hello World, ','Ucanaccess') FROM t_add_function", "Hello World, Ucanaccess"); + ucanaccess.addFunctions(AddFunctions.class); + dumpQueryResult("SELECT pluto('hello', ' world ', NOW()) FROM t_add_function"); + checkQuery("SELECT CONCAT('Hello World, ', 'Ucanaccess') FROM t_add_function", "Hello World, Ucanaccess"); dropTable("t_add_function"); } diff --git a/src/test/java/net/ucanaccess/test/integration/CorruptedTest.java b/src/test/java/net/ucanaccess/test/integration/CorruptedTest.java index 50ae56b9..0e57fe2d 100644 --- a/src/test/java/net/ucanaccess/test/integration/CorruptedTest.java +++ b/src/test/java/net/ucanaccess/test/integration/CorruptedTest.java @@ -17,12 +17,12 @@ class CorruptedTest extends UcanaccessBaseTest { private static final ByteArrayOutputStream ERR_CONTENT = new ByteArrayOutputStream(); @BeforeAll - static void setUpStreams() { + static void setStdErr() { System.setErr(new PrintStream(ERR_CONTENT)); } @AfterAll - static void cleanUpStreams() { + static void resetStdErr() { System.setErr(System.err); } @@ -39,10 +39,10 @@ void testCorrupted(AccessVersion _accessVersion) throws SQLException { getLogger().info("UcanaccessConnection: {}", ucanaccess); String nl = System.lineSeparator(); String err = - "WARNING: integrity constraint violation: foreign key no parent; BABY_DADDYBABY table: BABY value: 34" + nl + "WARNING: integrity constraint violation: foreign key no parent ; BABY_DADDYBABY table: BABY value: 34" + nl + "WARNING: Detected Foreign Key constraint breach, table Baby, record Row[162:1][{ID=2,fk1=34}]: making the table Baby read-only" + nl + "WARNING: Detected Not Null constraint breach, table NotNull, record Row[140:0][{ID=1,notnull=,vvv=gg,fk1=34}]: making the table NotNull read-only" + nl - + "WARNING: integrity constraint violation: foreign key no parent; NOTNULL_DADDYNOTNULL table: NOTNULL value: 34" + nl + + "WARNING: integrity constraint violation: foreign key no parent ; NOTNULL_DADDYNOTNULL table: NOTNULL value: 34" + nl + "WARNING: Detected Foreign Key constraint breach, table NotNull, record Row[140:3][{ID=4,notnull=t,vvv=t,fk1=2}]: making the table NotNull read-only" + nl + "WARNING: Detected Unique constraint breach, table UK, record Row[181:1][{ID=2,uk=1}]: making the table UK read-only"; assertEquals(err, ERR_CONTENT.toString().trim()); diff --git a/src/test/java/net/ucanaccess/test/integration/SummerTimeLostHourTest.java b/src/test/java/net/ucanaccess/test/integration/UtcTimezoneTest.java similarity index 84% rename from src/test/java/net/ucanaccess/test/integration/SummerTimeLostHourTest.java rename to src/test/java/net/ucanaccess/test/integration/UtcTimezoneTest.java index 1f094049..ca68cac0 100644 --- a/src/test/java/net/ucanaccess/test/integration/SummerTimeLostHourTest.java +++ b/src/test/java/net/ucanaccess/test/integration/UtcTimezoneTest.java @@ -18,31 +18,26 @@ import java.sql.SQLException; import java.sql.Statement; import java.time.LocalDateTime; -import java.util.Locale; import java.util.TimeZone; -class SummerTimeLostHourTest extends UcanaccessBaseTest { +class UtcTimezoneTest extends UcanaccessBaseTest { - private static Locale prevLocale; private static TimeZone prevTimeZone; @BeforeAll static void setLocalAndTimezone() { - prevLocale = Locale.getDefault(); prevTimeZone = TimeZone.getDefault(); - Locale.setDefault(Locale.ITALY); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Rome")); } @AfterAll static void resetLocalAndTimezone() { - Locale.setDefault(prevLocale); TimeZone.setDefault(prevTimeZone); } @Override protected String getAccessPath() { - return TEST_DB_DIR + "summerTimeLostHour.accdb"; // Access 2007 + return TEST_DB_DIR + "utcTimezoneTest.accdb"; // Access 2007 } @ParameterizedTest(name = "[{index}] {0}") @@ -56,9 +51,10 @@ void testForLostHour(AccessVersion _accessVersion) throws SQLException, IOExcept */ Connection hsqldbConn = ucanaccess.getHSQLDBConnection(); Statement hsqldbStmt = hsqldbConn.createStatement(); - ResultSet rs = hsqldbStmt.executeQuery("SELECT CAST(f_datetime AS VARCHAR(26)) AS str FROM t_datetime WHERE id=1"); + ResultSet rs = hsqldbStmt.executeQuery("SELECT f_datetime, CAST(f_datetime AS VARCHAR(26)) AS str FROM t_datetime WHERE id=1"); rs.next(); - assertEquals("2017-03-26 02:00:00.000000", rs.getString(1)); + assertEquals("2017-03-26 02:00:00.000000", rs.getString("f_datetime")); + assertEquals("2017-03-26 02:00:00.000000", rs.getString("str")); /* * also ensure that 02:00:00 -> 01:00:00 doesn't happen when writing back to Access diff --git a/src/test/java/net/ucanaccess/test/util/AddFunctionClass.java b/src/test/java/net/ucanaccess/test/util/AddFunctionClass.java deleted file mode 100644 index 9ad3015c..00000000 --- a/src/test/java/net/ucanaccess/test/util/AddFunctionClass.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.ucanaccess.test.util; - -import net.ucanaccess.converters.TypesMap.AccessType; -import net.ucanaccess.ext.FunctionType; - -import java.sql.Timestamp; - -public final class AddFunctionClass { - - private AddFunctionClass() { - } - - @FunctionType(functionName = "pluto", argumentTypes = { AccessType.TEXT, AccessType.TEXT, - AccessType.DATETIME }, returnType = AccessType.TEXT) - public static String example(String s1, String s2, Timestamp dt) { - return s1 + s2 + dt; - } - - @FunctionType(functionName = "concat", argumentTypes = { AccessType.TEXT, - AccessType.TEXT }, returnType = AccessType.TEXT) - public static String concat(String s1, String s2) { - return s1 + s2; - } - -} diff --git a/src/test/java/net/ucanaccess/test/util/UcanaccessBaseTest.java b/src/test/java/net/ucanaccess/test/util/UcanaccessBaseTest.java index f62720d4..542843d9 100644 --- a/src/test/java/net/ucanaccess/test/util/UcanaccessBaseTest.java +++ b/src/test/java/net/ucanaccess/test/util/UcanaccessBaseTest.java @@ -22,26 +22,26 @@ public abstract class UcanaccessBaseTest extends AbstractBaseTest { protected static final String TEST_DB_DIR = "testdbs/"; - private static final File TEST_TEMP_DIR = createTempDir("ucanaccess-test"); + private static final File TEST_TEMP_DIR = createTempDir("ucanaccess-test"); static { Main.setBatchMode(true); } - private File fileAccDb; - private String password = ""; - private AccessVersion accessVersion; + private File fileAccDb; + private String password = ""; + private AccessVersion accessVersion; // CHECKSTYLE:OFF protected UcanaccessConnection ucanaccess; // CHECKSTYLE:ON - private String user = "ucanaccess"; - private Connection verifyConnection; - private Boolean ignoreCase; + private String user = "ucanaccess"; + private Connection verifyConnection; + private Boolean ignoreCase; - private long inactivityTimeout = -1; - private String columnOrder; - private String append2JdbcURL = ""; - private Boolean showSchema; + private long inactivityTimeout = -1; + private String columnOrder; + private String append2JdbcURL = ""; + private Boolean showSchema; protected UcanaccessBaseTest() { } @@ -378,10 +378,8 @@ protected UcanaccessConnection getUcanaccessConnection(String _dbPath) throws SQ } private UcanaccessConnection getUcanaccessConnection(String _urlPrefix, String _dbPath) throws SQLException { - if (_dbPath == null) { - _dbPath = getAccessTempPath(); - } - String url = _urlPrefix + _dbPath; + String dbPath = Optional.ofNullable(_dbPath).orElseGet(this::getAccessTempPath); + String url = _urlPrefix + dbPath; if (ignoreCase != null) { url += ";ignoreCase=" + ignoreCase; } diff --git a/src/test/resources/testdbs/summerTimeLostHour.accdb b/src/test/resources/testdbs/utcTimezoneTest.accdb similarity index 100% rename from src/test/resources/testdbs/summerTimeLostHour.accdb rename to src/test/resources/testdbs/utcTimezoneTest.accdb