change flavor from enum to interface, fix int/long bug in JDBC pool, add queue implementation
This commit is contained in:
parent
7198e766c3
commit
1047be8456
42 changed files with 1667 additions and 1274 deletions
|
@ -1,5 +1,5 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = database
|
name = database
|
||||||
version = 0.0.5
|
version = 1.0.0
|
||||||
|
|
||||||
org.gradle.warning.mode = ALL
|
org.gradle.warning.mode = ALL
|
||||||
|
|
|
@ -20,4 +20,5 @@ test {
|
||||||
"${result.skippedTestCount} skipped"
|
"${result.skippedTestCount} skipped"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
|
||||||
}
|
}
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -407,15 +407,17 @@ public class Pool implements BagStateListener {
|
||||||
|
|
||||||
private void initializeDataSource()
|
private void initializeDataSource()
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||||
String jdbcUrl = config.getProperties().getProperty("url");
|
String url = config.getUrl();
|
||||||
String dsClassName = config.getDataSourceClassName();
|
|
||||||
DataSource ds = config.getDataSource();
|
DataSource ds = config.getDataSource();
|
||||||
if (ds == null) {
|
if (ds == null) {
|
||||||
|
String dsClassName = config.getDataSourceClassName();
|
||||||
if (dsClassName != null) {
|
if (dsClassName != null) {
|
||||||
Class<?> clazz = Class.forName(dsClassName, true, ClassLoader.getSystemClassLoader());
|
Class<?> clazz = Class.forName(dsClassName, true, ClassLoader.getSystemClassLoader());
|
||||||
ds = (DataSource) clazz.getDeclaredConstructor().newInstance();
|
ds = (DataSource) clazz.getDeclaredConstructor().newInstance();
|
||||||
} else if (jdbcUrl != null) {
|
} else if (url != null) {
|
||||||
ds = new DriverDataSource(jdbcUrl, config.getDriverClassName(), config.getProperties(), config.getUsername(), config.getPassword());
|
ds = new DriverDataSource(url, config.getDriverClassName(), config.getProperties(), config.getUsername(), config.getPassword());
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("no dataSource configured?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTargetFromProperties(ds, config.getProperties());
|
setTargetFromProperties(ds, config.getProperties());
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class PoolConfig {
|
||||||
|
|
||||||
private String driverClassName;
|
private String driverClassName;
|
||||||
|
|
||||||
private String jdbcUrl;
|
private String url;
|
||||||
|
|
||||||
private String poolName;
|
private String poolName;
|
||||||
|
|
||||||
|
@ -106,11 +106,18 @@ public class PoolConfig {
|
||||||
this.idleTimeout = IDLE_TIMEOUT;
|
this.idleTimeout = IDLE_TIMEOUT;
|
||||||
this.initializationFailTimeout = -1;
|
this.initializationFailTimeout = -1;
|
||||||
this.isAutoCommit = true;
|
this.isAutoCommit = true;
|
||||||
this.jdbcUrl = properties.getProperty("url");
|
|
||||||
this.aliveBypassWindowMs = TimeUnit.MILLISECONDS.toMillis(500);
|
this.aliveBypassWindowMs = TimeUnit.MILLISECONDS.toMillis(500);
|
||||||
this.housekeepingPeriodMs = TimeUnit.SECONDS.toMillis(30);
|
this.housekeepingPeriodMs = TimeUnit.SECONDS.toMillis(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCatalog() {
|
public String getCatalog() {
|
||||||
return catalog;
|
return catalog;
|
||||||
}
|
}
|
||||||
|
@ -588,7 +595,7 @@ public class PoolConfig {
|
||||||
transactionIsolationName = getNullIfEmpty(transactionIsolationName);
|
transactionIsolationName = getNullIfEmpty(transactionIsolationName);
|
||||||
dataSourceClassName = getNullIfEmpty(dataSourceClassName);
|
dataSourceClassName = getNullIfEmpty(dataSourceClassName);
|
||||||
driverClassName = getNullIfEmpty(driverClassName);
|
driverClassName = getNullIfEmpty(driverClassName);
|
||||||
jdbcUrl = getNullIfEmpty(jdbcUrl);
|
url = getNullIfEmpty(url);
|
||||||
if (dataSource != null) {
|
if (dataSource != null) {
|
||||||
if (dataSourceClassName != null) {
|
if (dataSourceClassName != null) {
|
||||||
logger.log(Level.WARNING, "using dataSource and ignoring dataSourceClassName: " + poolName);
|
logger.log(Level.WARNING, "using dataSource and ignoring dataSourceClassName: " + poolName);
|
||||||
|
@ -597,17 +604,17 @@ public class PoolConfig {
|
||||||
if (driverClassName != null) {
|
if (driverClassName != null) {
|
||||||
logger.log(Level.SEVERE, "cannot use driverClassName and dataSourceClassName together: " + poolName);
|
logger.log(Level.SEVERE, "cannot use driverClassName and dataSourceClassName together: " + poolName);
|
||||||
throw new IllegalStateException("cannot use driverClassName and dataSourceClassName together.");
|
throw new IllegalStateException("cannot use driverClassName and dataSourceClassName together.");
|
||||||
} else if (jdbcUrl != null) {
|
} else if (url != null) {
|
||||||
logger.log(Level.WARNING, "using dataSourceClassName and ignoring jdbcUrl: " + poolName);
|
logger.log(Level.WARNING, "using dataSourceClassName and ignoring url: " + poolName);
|
||||||
}
|
}
|
||||||
} else if (jdbcUrl != null) {
|
} else if (url != null) {
|
||||||
// ok
|
// ok
|
||||||
} else if (driverClassName != null) {
|
} else if (driverClassName != null) {
|
||||||
logger.log(Level.SEVERE, "jdbcUrl is required with driverClassName: " + poolName);
|
logger.log(Level.SEVERE, "url is required with driverClassName: " + poolName);
|
||||||
throw new IllegalArgumentException("jdbcUrl is required with driverClassName.");
|
throw new IllegalArgumentException("url is required with driverClassName");
|
||||||
} else {
|
} else {
|
||||||
logger.log(Level.SEVERE, "dataSource or dataSourceClassName or jdbcUrl is required: " + poolName);
|
logger.log(Level.SEVERE, "dataSource or dataSourceClassName or url is required: " + poolName);
|
||||||
throw new IllegalArgumentException("dataSource or dataSourceClassName or jdbcUrl is required.");
|
throw new IllegalArgumentException("dataSource or dataSourceClassName or url is required");
|
||||||
}
|
}
|
||||||
validateNumerics();
|
validateNumerics();
|
||||||
}
|
}
|
||||||
|
|
|
@ -481,7 +481,7 @@ public class ProxyResultSet implements ResultSet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getLong(int columnIndex) throws SQLException {
|
public long getLong(int columnIndex) throws SQLException {
|
||||||
return delegate.getInt(columnIndex);
|
return delegate.getLong(columnIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -136,12 +136,21 @@ public class DriverDataSource implements DataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLoginTimeout(int seconds) {
|
public void setLoginTimeout(int seconds) {
|
||||||
|
try {
|
||||||
DriverManager.setLoginTimeout(seconds);
|
DriverManager.setLoginTimeout(seconds);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "setLoginTimeout failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getLoginTimeout() {
|
public int getLoginTimeout() {
|
||||||
|
try {
|
||||||
return DriverManager.getLoginTimeout();
|
return DriverManager.getLoginTimeout();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "getLoginTimeout failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -63,7 +63,6 @@ public class BagTest {
|
||||||
bag.remove(inuse);
|
bag.remove(inuse);
|
||||||
bag.remove(inuse);
|
bag.remove(inuse);
|
||||||
assertTrue(bag.getLastMessage().contains("not borrowed or reserved"));
|
assertTrue(bag.getLastMessage().contains("not borrowed or reserved"));
|
||||||
bag.close();
|
|
||||||
try {
|
try {
|
||||||
PoolEntry bagEntry = pool.newPoolEntry();
|
PoolEntry bagEntry = pool.newPoolEntry();
|
||||||
bag.add(bagEntry);
|
bag.add(bagEntry);
|
||||||
|
|
|
@ -29,14 +29,13 @@ public class ConnectionStateTest {
|
||||||
config.setConnectionTestQuery("VALUES 1");
|
config.setConnectionTestQuery("VALUES 1");
|
||||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||||
try (Connection connection = ds.getConnection()) {
|
Connection connection = ds.getConnection();
|
||||||
Connection unwrap = connection.unwrap(Connection.class);
|
Connection unwrap = connection.unwrap(Connection.class);
|
||||||
unwrap.setAutoCommit(false);
|
unwrap.setAutoCommit(false);
|
||||||
connection.close();
|
connection.close();
|
||||||
assertFalse(unwrap.getAutoCommit());
|
assertFalse(unwrap.getAutoCommit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionIsolation() throws Exception {
|
public void testTransactionIsolation() throws Exception {
|
||||||
|
@ -48,14 +47,13 @@ public class ConnectionStateTest {
|
||||||
config.setConnectionTestQuery("VALUES 1");
|
config.setConnectionTestQuery("VALUES 1");
|
||||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||||
try (Connection connection = ds.getConnection()) {
|
Connection connection = ds.getConnection();
|
||||||
Connection unwrap = connection.unwrap(Connection.class);
|
Connection unwrap = connection.unwrap(Connection.class);
|
||||||
unwrap.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
|
unwrap.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
|
||||||
connection.close();
|
connection.close();
|
||||||
assertEquals(Connection.TRANSACTION_READ_UNCOMMITTED, unwrap.getTransactionIsolation());
|
assertEquals(Connection.TRANSACTION_READ_UNCOMMITTED, unwrap.getTransactionIsolation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsolation() {
|
public void testIsolation() {
|
||||||
|
@ -76,14 +74,13 @@ public class ConnectionStateTest {
|
||||||
config.setConnectionTestQuery("VALUES 1");
|
config.setConnectionTestQuery("VALUES 1");
|
||||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||||
try (Connection connection = ds.getConnection()) {
|
Connection connection = ds.getConnection();
|
||||||
Connection unwrap = connection.unwrap(Connection.class);
|
Connection unwrap = connection.unwrap(Connection.class);
|
||||||
connection.setReadOnly(true);
|
connection.setReadOnly(true);
|
||||||
connection.close();
|
connection.close();
|
||||||
assertFalse(unwrap.isReadOnly());
|
assertFalse(unwrap.isReadOnly());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCatalog() throws Exception {
|
public void testCatalog() throws Exception {
|
||||||
|
|
|
@ -453,6 +453,7 @@ public class ConnectionTest {
|
||||||
config.setDataSource(stubDataSource);
|
config.setDataSource(stubDataSource);
|
||||||
try (PoolDataSource ds = new PoolDataSource(config);
|
try (PoolDataSource ds = new PoolDataSource(config);
|
||||||
Connection ignored = ds.getConnection()) {
|
Connection ignored = ds.getConnection()) {
|
||||||
|
assertNotNull(ignored);
|
||||||
fail("Initialization should have failed");
|
fail("Initialization should have failed");
|
||||||
} catch (SQLTransientConnectionException e) {
|
} catch (SQLTransientConnectionException e) {
|
||||||
// passed
|
// passed
|
||||||
|
@ -472,6 +473,7 @@ public class ConnectionTest {
|
||||||
config.setDataSource(stubDataSource);
|
config.setDataSource(stubDataSource);
|
||||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||||
try (Connection ignored = ds.getConnection()) {
|
try (Connection ignored = ds.getConnection()) {
|
||||||
|
assertNotNull(ignored);
|
||||||
stubDataSource.setErrorOnConnection(true);
|
stubDataSource.setErrorOnConnection(true);
|
||||||
fail("SQLException should occur!");
|
fail("SQLException should occur!");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -520,6 +522,7 @@ public class ConnectionTest {
|
||||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||||
try (PoolDataSource ds = new PoolDataSource(config);
|
try (PoolDataSource ds = new PoolDataSource(config);
|
||||||
Connection ignored = ds.getConnection()) {
|
Connection ignored = ds.getConnection()) {
|
||||||
|
assertNotNull(ignored);
|
||||||
// passed
|
// passed
|
||||||
} catch (SQLTransientConnectionException sqle) {
|
} catch (SQLTransientConnectionException sqle) {
|
||||||
fail("Failed to obtain connection");
|
fail("Failed to obtain connection");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.io.pool.jdbc;
|
package org.xbib.io.pool.jdbc;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
@ -38,6 +39,7 @@ public class JdbcDriverTest {
|
||||||
assertNotNull(unwrap);
|
assertNotNull(unwrap);
|
||||||
try (Connection connection = ds.getConnection()) {
|
try (Connection connection = ds.getConnection()) {
|
||||||
// test that getConnection() succeeds
|
// test that getConnection() succeeds
|
||||||
|
assertFalse(connection.isClosed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":jdbc-connection-pool")
|
api project(":jdbc-connection-pool")
|
||||||
testImplementation libs.derby
|
testImplementation libs.derby
|
||||||
|
testImplementation libs.hsqldb
|
||||||
testImplementation libs.testcontainers
|
testImplementation libs.testcontainers
|
||||||
testImplementation libs.testcontainers.junit.jupiter
|
testImplementation libs.testcontainers.junit.jupiter
|
||||||
testImplementation libs.testcontainers.oracle.xe
|
testImplementation libs.testcontainers.oracle.xe
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
import org.xbib.jdbc.query.flavor.Derby;
|
||||||
|
import org.xbib.jdbc.query.flavor.Hsql;
|
||||||
|
import org.xbib.jdbc.query.flavor.Oracle;
|
||||||
|
import org.xbib.jdbc.query.flavor.Postgresql;
|
||||||
|
import org.xbib.jdbc.query.flavor.SqlServer;
|
||||||
|
|
||||||
module org.xbib.jdbc.query {
|
module org.xbib.jdbc.query {
|
||||||
|
uses Flavor;
|
||||||
requires transitive org.xbib.jdbc.connection.pool;
|
requires transitive org.xbib.jdbc.connection.pool;
|
||||||
exports org.xbib.jdbc.query;
|
exports org.xbib.jdbc.query;
|
||||||
|
provides Flavor with Derby, Hsql, Oracle, Postgresql, SqlServer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.xbib.jdbc.query;
|
package org.xbib.jdbc.query;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.CharsetDecoder;
|
import java.nio.charset.CharsetDecoder;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -47,7 +47,7 @@ public interface ConfigFrom extends Supplier<Config> {
|
||||||
* defaultPropertyFiles("properties", "conf/app.properties", "local.properties", "sample.properties")
|
* defaultPropertyFiles("properties", "conf/app.properties", "local.properties", "sample.properties")
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
ConfigFrom defaultPropertyFiles();
|
ConfigFrom defaultProperties();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a set of properties files to read from, which can be overridden by a specified system property.
|
* Adds a set of properties files to read from, which can be overridden by a specified system property.
|
||||||
|
@ -56,7 +56,7 @@ public interface ConfigFrom extends Supplier<Config> {
|
||||||
* defaultPropertyFiles(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames)
|
* defaultPropertyFiles(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames)
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
ConfigFrom defaultPropertyFiles(String systemPropertyKey, String... filenames);
|
ConfigFrom defaultProperties(String systemPropertyKey, String... filenames);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a set of properties files to read from, which can be overridden by a specified system property.
|
* Adds a set of properties files to read from, which can be overridden by a specified system property.
|
||||||
|
@ -67,15 +67,15 @@ public interface ConfigFrom extends Supplier<Config> {
|
||||||
* .split(File.pathSeparator));
|
* .split(File.pathSeparator));
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
ConfigFrom defaultPropertyFiles(String systemPropertyKey, CharsetDecoder decoder, String... filenames);
|
ConfigFrom defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames);
|
||||||
|
|
||||||
ConfigFrom propertyFile(String... filenames);
|
ConfigFrom properties(String... filenames);
|
||||||
|
|
||||||
ConfigFrom propertyFile(CharsetDecoder decoder, String... filenames);
|
ConfigFrom properties(CharsetDecoder decoder, String... filenames);
|
||||||
|
|
||||||
ConfigFrom propertyFile(File... files);
|
ConfigFrom properties(InputStream... inputStreams);
|
||||||
|
|
||||||
ConfigFrom propertyFile(CharsetDecoder decoder, File... files);
|
ConfigFrom properties(CharsetDecoder decoder, InputStream... inputStreams);
|
||||||
|
|
||||||
ConfigFrom rename(String key, String newKey);
|
ConfigFrom rename(String key, String newKey);
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
package org.xbib.jdbc.query;
|
package org.xbib.jdbc.query;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.IOException;
|
||||||
import java.io.FileInputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.CharsetDecoder;
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -20,6 +26,10 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public class ConfigFromImpl implements ConfigFrom {
|
public class ConfigFromImpl implements ConfigFrom {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ConfigFromImpl.class.getName());
|
||||||
|
|
||||||
|
private static final String SEPARATOR = FileSystems.getDefault().getSeparator();
|
||||||
|
|
||||||
private final List<Config> searchPath = new ArrayList<>();
|
private final List<Config> searchPath = new ArrayList<>();
|
||||||
|
|
||||||
public ConfigFromImpl() {
|
public ConfigFromImpl() {
|
||||||
|
@ -72,64 +82,59 @@ public class ConfigFromImpl implements ConfigFrom {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom defaultPropertyFiles() {
|
public ConfigFrom defaultProperties() {
|
||||||
return defaultPropertyFiles("properties", "conf/app.properties", "local.properties", "sample.properties");
|
return defaultProperties("properties", "conf/app.properties", "local.properties", "sample.properties");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom defaultPropertyFiles(String systemPropertyKey, String... filenames) {
|
public ConfigFrom defaultProperties(String systemPropertyKey, String... filenames) {
|
||||||
return defaultPropertyFiles(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames);
|
return defaultProperties(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom defaultPropertyFiles(String systemPropertyKey, CharsetDecoder decoder, String... filenames) {
|
public ConfigFrom defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames) {
|
||||||
String properties = System.getProperty(systemPropertyKey, String.join(File.pathSeparator, filenames));
|
String properties = System.getProperty(systemPropertyKey, String.join(SEPARATOR, filenames));
|
||||||
return propertyFile(Charset.defaultCharset().newDecoder(), properties.split(File.pathSeparator));
|
return properties(Charset.defaultCharset().newDecoder(), properties.split(SEPARATOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom propertyFile(String... filenames) {
|
public ConfigFrom properties(String... filenames) {
|
||||||
return propertyFile(Charset.defaultCharset().newDecoder(), filenames);
|
return properties(Charset.defaultCharset().newDecoder(), filenames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom propertyFile(CharsetDecoder decoder, String... filenames) {
|
public ConfigFrom properties(CharsetDecoder decoder, String... filenames) {
|
||||||
for (String filename : filenames) {
|
for (String filename : filenames) {
|
||||||
if (filename != null) {
|
if (filename != null) {
|
||||||
propertyFile(decoder, new File(filename));
|
Path path = Paths.get(filename);
|
||||||
|
if (Files.exists(path)) {
|
||||||
|
try (InputStream inputStream = Files.newInputStream(path)) {
|
||||||
|
properties(decoder, inputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
custom(k -> null, "Ignored: propertyFile(" + filename + ") " + e.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
properties(decoder, getClass().getResourceAsStream(filename));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom propertyFile(File... files) {
|
public ConfigFrom properties(InputStream... inputStreams) {
|
||||||
return propertyFile(Charset.defaultCharset().newDecoder(), files);
|
return properties(Charset.defaultCharset().newDecoder(), inputStreams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigFrom propertyFile(CharsetDecoder decoder, File... files) {
|
public ConfigFrom properties(CharsetDecoder decoder, InputStream... inputStreams) {
|
||||||
for (File file : files) {
|
for (InputStream inputStream : inputStreams) {
|
||||||
if (file != null) {
|
|
||||||
try {
|
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
try (
|
try (InputStreamReader reader = new InputStreamReader(inputStream, decoder)) {
|
||||||
FileInputStream fis = new FileInputStream(file);
|
|
||||||
InputStreamReader reader = new InputStreamReader(fis, decoder)
|
|
||||||
) {
|
|
||||||
properties.load(reader);
|
properties.load(reader);
|
||||||
}
|
searchPath.add(new ConfigImpl(properties::getProperty, "propertyFile(" + inputStream + ")"));
|
||||||
searchPath.add(new ConfigImpl(properties::getProperty, "propertyFile(" + file.getAbsolutePath() + ")"));
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Put a "fake" provider in so we can see it failed
|
custom(k -> null, "Ignored: propertyFile(" + inputStream + ") " + e.getClass().getSimpleName());
|
||||||
String fileName = file.getName();
|
|
||||||
try {
|
|
||||||
fileName = file.getAbsolutePath();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
// Fall back to relative name
|
|
||||||
}
|
|
||||||
custom(k -> null, "Ignored: propertyFile(" + fileName + ") " + e.getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -170,14 +170,12 @@ public class ConfigImpl implements Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
|
public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
|
||||||
BigDecimal value = getBigDecimal(key);
|
BigDecimal value = getBigDecimal(key);
|
||||||
return value == null ? defaultValue : value;
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal getBigDecimalOrThrow(String key) {
|
public BigDecimal getBigDecimalOrThrow(String key) {
|
||||||
return nonnull(key, getBigDecimal(key));
|
return nonnull(key, getBigDecimal(key));
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package org.xbib.jdbc.query;
|
package org.xbib.jdbc.query;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,4 +218,27 @@ public interface Database extends Supplier<Database> {
|
||||||
* with millisToWarn=10000 and millisToError=30000.
|
* with millisToWarn=10000 and millisToError=30000.
|
||||||
*/
|
*/
|
||||||
void assertTimeSynchronized();
|
void assertTimeSynchronized();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data into a queue table, select a channel, and consume the returned primary keys.
|
||||||
|
*
|
||||||
|
* @param table the queue table
|
||||||
|
* @param channel the queue table channel
|
||||||
|
* @param data the data for the queue table
|
||||||
|
* @param consumer the consumer for the primary keys
|
||||||
|
* @throws SQLException if writing fails
|
||||||
|
*/
|
||||||
|
void writeQueue(String table, String channel, String data, Consumer<Long> consumer) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from a queue table and lock rows in a transaction until processing by a consumer
|
||||||
|
* has succeeded or failed.
|
||||||
|
* Commit only if processing was successful, otherwise roll back the transaction.
|
||||||
|
* The connection is switched into manual commit and afterwards, auto-commit is enabled again.
|
||||||
|
* @param table the queue table
|
||||||
|
* @param channel the queue table channel. A queue table can have many channels
|
||||||
|
* @param consumer a consumer for the queue table data column or null
|
||||||
|
* @throws SQLException if rollback fails
|
||||||
|
*/
|
||||||
|
public void readQueue(String table, String channel, Consumer<String> consumer) throws SQLException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,27 @@
|
||||||
package org.xbib.jdbc.query;
|
package org.xbib.jdbc.query;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.sql.Timestamp;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary class for accessing a relational (SQL) database.
|
* Primary class for accessing a relational database.
|
||||||
*/
|
*/
|
||||||
public class DatabaseImpl implements Database {
|
public class DatabaseImpl implements Database {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(Database.class.getName());
|
private static final Logger logger = Logger.getLogger(Database.class.getName());
|
||||||
|
|
||||||
private final Connection connection;
|
private final Connection connection;
|
||||||
|
|
||||||
|
@ -88,7 +91,7 @@ public class DatabaseImpl implements Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long nextSequenceValue(/*@Untainted*/ String sequenceName) {
|
public Long nextSequenceValue(String sequenceName) {
|
||||||
return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull();
|
return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,9 +100,10 @@ public class DatabaseImpl implements Database {
|
||||||
return options.currentDate();
|
return options.currentDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void commitNow() {
|
public void commitNow() {
|
||||||
if (options.ignoreTransactionControl()) {
|
if (options.ignoreTransactionControl()) {
|
||||||
log.fine("Ignoring call to commitNow()");
|
logger.fine("Ignoring call to commitNow()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!options.allowTransactionControl()) {
|
if (!options.allowTransactionControl()) {
|
||||||
|
@ -112,9 +116,10 @@ public class DatabaseImpl implements Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void rollbackNow() {
|
public void rollbackNow() {
|
||||||
if (options.ignoreTransactionControl()) {
|
if (options.ignoreTransactionControl()) {
|
||||||
log.fine("Ignoring call to rollbackNow()");
|
logger.fine("Ignoring call to rollbackNow()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!options.allowTransactionControl()) {
|
if (!options.allowTransactionControl()) {
|
||||||
|
@ -153,53 +158,22 @@ public class DatabaseImpl implements Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dropSequenceQuietly(/*@Untainted*/ String sequenceName) {
|
public void dropSequenceQuietly(String sequenceName) {
|
||||||
ddl(flavor().sequenceDrop(sequenceName)).executeQuietly();
|
ddl(flavor().sequenceDrop(sequenceName)).executeQuietly();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dropTableQuietly(/*@Untainted*/ String tableName) {
|
public void dropTableQuietly(String tableName) {
|
||||||
if (flavor() == Flavor.postgresql || flavor() == Flavor.hsqldb) {
|
ddl(flavor().tableDrop(tableName)).executeQuietly();
|
||||||
ddl("drop table if exists " + tableName).executeQuietly();
|
|
||||||
} else {
|
|
||||||
ddl("drop table " + tableName).executeQuietly();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tableExists(String tableName) throws DatabaseException {
|
public boolean tableExists(String tableName) throws DatabaseException {
|
||||||
|
|
||||||
String schemaName = null;
|
|
||||||
Method getSchema = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use reflections to see if connection.getSchema API exists. It should exist for any JDBC7 or later implementation
|
return tableExists(tableName, connection.getSchema());
|
||||||
// We still support Oracle 11 with odbc6, however, so we can't assume it's there.
|
} catch (SQLException e) {
|
||||||
getSchema = connection.getClass().getDeclaredMethod("getSchema");
|
throw new DatabaseException("Unable to getSchema()", e);
|
||||||
} catch (NoSuchMethodException noMethodExc) {
|
|
||||||
// Expected if method does not exist - just let it go
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
if (getSchema != null) {
|
|
||||||
schemaName = ((String) getSchema.invoke(connection, new Object[0]));
|
|
||||||
} else if (flavor() == Flavor.oracle) {
|
|
||||||
// Oracle defaults to user name schema - use that.
|
|
||||||
log.warning("Connection getSchema API was not found. Defaulting to Oracle user name schema." +
|
|
||||||
"If this is not appropriate, please use tableExists(tableName, schemaName) API or upgrade to ojdbc7 or later");
|
|
||||||
schemaName = connection.getMetaData().getUserName();
|
|
||||||
}
|
|
||||||
if (schemaName == null) {
|
|
||||||
// connection.getSchema API was supported starting at JDK1.7. Method should not be null.
|
|
||||||
throw new NullPointerException("Unable to retrieve schema name.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception exc) {
|
|
||||||
throw new DatabaseException("Unable to determine the schema. " +
|
|
||||||
"Please use tableExists(tableName, schemaName API) or upgrade to a JDBC7 driver or later.", exc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableExists(tableName, schemaName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -230,24 +204,24 @@ public class DatabaseImpl implements Database {
|
||||||
try {
|
try {
|
||||||
DatabaseMetaData metaData = connection.getMetaData();
|
DatabaseMetaData metaData = connection.getMetaData();
|
||||||
String normalizedTable = normalizeTableName(tableName);
|
String normalizedTable = normalizeTableName(tableName);
|
||||||
ResultSet rs = metaData.getColumns(null, null, normalizedTable, "%");
|
ResultSet resultSet = metaData.getColumns(null, null, normalizedTable, "%");
|
||||||
ResultSetMetaData rsmd = rs.getMetaData();
|
ResultSetMetaData rsmd = resultSet.getMetaData();
|
||||||
int cols = rsmd.getColumnCount();
|
int cols = rsmd.getColumnCount();
|
||||||
while (rs.next()) {
|
while (resultSet.next()) {
|
||||||
String name = "";
|
String name = "";
|
||||||
int size = 0;
|
int size = 0;
|
||||||
for (int i = 1; i <= cols; i++) {
|
for (int i = 1; i <= cols; i++) {
|
||||||
String label = rsmd.getColumnName(i);
|
String label = rsmd.getColumnName(i);
|
||||||
if ("COLUMN_NAME".equalsIgnoreCase(label)) {
|
if ("COLUMN_NAME".equalsIgnoreCase(label)) {
|
||||||
name = rs.getString(i);
|
name = resultSet.getString(i);
|
||||||
}
|
}
|
||||||
if ("COLUMN_SIZE".equalsIgnoreCase(label)) {
|
if ("COLUMN_SIZE".equalsIgnoreCase(label)) {
|
||||||
size = rs.getBigDecimal(i).intValue();
|
size = resultSet.getBigDecimal(i).intValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map.put(name, size);
|
map.put(name, size);
|
||||||
}
|
}
|
||||||
rs.close();
|
resultSet.close();
|
||||||
} catch (SQLException exc) {
|
} catch (SQLException exc) {
|
||||||
throw new DatabaseException("Unable to look up table " + tableName + " : " + exc.getMessage(), exc);
|
throw new DatabaseException("Unable to look up table " + tableName + " : " + exc.getMessage(), exc);
|
||||||
}
|
}
|
||||||
|
@ -260,7 +234,6 @@ public class DatabaseImpl implements Database {
|
||||||
if (tableName == null) {
|
if (tableName == null) {
|
||||||
return tableName;
|
return tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user gave us a quoted string, leave it alone for look up
|
// If user gave us a quoted string, leave it alone for look up
|
||||||
if (tableName.length() > 2) {
|
if (tableName.length() > 2) {
|
||||||
if (tableName.startsWith("\"") && tableName.endsWith("\"")) {
|
if (tableName.startsWith("\"") && tableName.endsWith("\"")) {
|
||||||
|
@ -268,11 +241,9 @@ public class DatabaseImpl implements Database {
|
||||||
return tableName.substring(1, tableName.length() - 1);
|
return tableName.substring(1, tableName.length() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flavor().isNormalizedUpperCase()) {
|
if (flavor().isNormalizedUpperCase()) {
|
||||||
return tableName.toUpperCase();
|
return tableName.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
return tableName.toLowerCase();
|
return tableName.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,18 +259,18 @@ public class DatabaseImpl implements Database {
|
||||||
Duration duration = Duration.between(appDate, dbDate).abs();
|
Duration duration = Duration.between(appDate, dbDate).abs();
|
||||||
if (duration.getSeconds() > 3600) {
|
if (duration.getSeconds() > 3600) {
|
||||||
throw new DatabaseException("App and db time are over an hour apart (check your timezones) app: "
|
throw new DatabaseException("App and db time are over an hour apart (check your timezones) app: "
|
||||||
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
|
+ appDate + " db: "
|
||||||
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
|
+ dbDate);
|
||||||
}
|
}
|
||||||
if (duration.getSeconds() * 1000 > millisToError) {
|
if (duration.getSeconds() * 1000 > millisToError) {
|
||||||
throw new DatabaseException("App and db time over " + millisToError + " millis apart (check your clocks) app: "
|
throw new DatabaseException("App and db time over " + millisToError + " millis apart (check your clocks) app: "
|
||||||
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
|
+ appDate + " db: "
|
||||||
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
|
+ dbDate);
|
||||||
}
|
}
|
||||||
if (duration.getSeconds() * 1000 > millisToWarn) {
|
if (duration.getSeconds() * 1000 > millisToWarn) {
|
||||||
log.warning("App and db time are over " + millisToWarn + " millis apart (check your clocks) app: "
|
logger.warning("App and db time are over " + millisToWarn + " millis apart (check your clocks) app: "
|
||||||
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
|
+ appDate + " db: "
|
||||||
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
|
+ dbDate);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -309,4 +280,83 @@ public class DatabaseImpl implements Database {
|
||||||
public void assertTimeSynchronized() {
|
public void assertTimeSynchronized() {
|
||||||
assertTimeSynchronized(10000, 30000);
|
assertTimeSynchronized(10000, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeQueue(String table, String channel, String data, Consumer<Long> consumer) throws SQLException {
|
||||||
|
writeNextIntoQueue(connection, table, channel, data, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readQueue(String table, String channel, Consumer<String> consumer) throws SQLException {
|
||||||
|
try {
|
||||||
|
connection.setAutoCommit(false);
|
||||||
|
while (true) {
|
||||||
|
try (ResultSet resultSet = readNextFromQueue(connection, table, channel)) {
|
||||||
|
if (resultSet.next()) {
|
||||||
|
Long key = resultSet.getLong("key");
|
||||||
|
try {
|
||||||
|
if (consumer != null) {
|
||||||
|
consumer.accept(resultSet.getString("data"));
|
||||||
|
}
|
||||||
|
succeedInQueue(connection, table, key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
failInQueue(connection, table, key);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
connection.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
connection.rollback();
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
connection.setAutoCommit(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeNextIntoQueue(Connection connection,
|
||||||
|
String table,
|
||||||
|
String channel,
|
||||||
|
String data,
|
||||||
|
Consumer<Long> consumer) throws SQLException {
|
||||||
|
try (PreparedStatement preparedStatement = connection.prepareStatement(flavor().writeNextIntoQueue(table),
|
||||||
|
Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
preparedStatement.setString(1, channel);
|
||||||
|
preparedStatement.setString(2, data);
|
||||||
|
preparedStatement.setTimestamp(3, Timestamp.valueOf(LocalDateTime.now()));
|
||||||
|
int rows = preparedStatement.executeUpdate();
|
||||||
|
if (rows > 0) {
|
||||||
|
try (ResultSet resultSet = preparedStatement.getGeneratedKeys()) {
|
||||||
|
if (resultSet.next()) {
|
||||||
|
if (consumer != null) {
|
||||||
|
consumer.accept(resultSet.getLong(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultSet readNextFromQueue(Connection connection, String table, String channel) throws SQLException {
|
||||||
|
PreparedStatement preparedStatement = connection.prepareStatement(flavor().readNextFromQueue(table));
|
||||||
|
preparedStatement.setString(1, channel);
|
||||||
|
preparedStatement.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now()));
|
||||||
|
return preparedStatement.executeQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void succeedInQueue(Connection connection, String table, Long key) throws SQLException {
|
||||||
|
try (PreparedStatement preparedStatement = connection.prepareStatement(flavor().succeedInQueue(table))) {
|
||||||
|
preparedStatement.setLong(1, key);
|
||||||
|
preparedStatement.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void failInQueue(Connection connection, String table, Long key) throws SQLException {
|
||||||
|
try (PreparedStatement preparedStatement = connection.prepareStatement(flavor().succeedInQueue(table))) {
|
||||||
|
preparedStatement.setLong(1, key);
|
||||||
|
preparedStatement.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
* database.pool.size=... How many connections in the connection pool (default 10).
|
* database.pool.size=... How many connections in the connection pool (default 10).
|
||||||
* database.driver.class The driver to initialize with Class.forName(). This will
|
* database.driver.class The driver to initialize with Class.forName(). This will
|
||||||
* be guessed from the database.url if not provided.
|
* be guessed from the database.url if not provided.
|
||||||
* database.flavor One of the enumerated values in {@link Flavor}. If this
|
* database.flavor A {@link Flavor}. If this is not provided the flavor will be guessed based on the
|
||||||
* is not provided the flavor will be guessed based on the
|
|
||||||
* value for database.url, if possible.
|
* value for database.url, if possible.
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -167,10 +166,12 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
try {
|
try {
|
||||||
DriverManager.getDriver(url);
|
DriverManager.getDriver(url);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
if (flavor.driverClass() != null) {
|
||||||
try {
|
try {
|
||||||
Class.forName(Flavor.driverForJdbcUrl(url));
|
Class.forName(flavor.driverClass());
|
||||||
} catch (ClassNotFoundException e1) {
|
} catch (ClassNotFoundException e1) {
|
||||||
throw new DatabaseException("couldn't locate JDBC driver - try setting -Djdbc.drivers=some.Driver", e1);
|
throw new DatabaseException("couldn't locate JDBC driver", e1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new BuilderImpl(null, () -> {
|
return new BuilderImpl(null, () -> {
|
||||||
|
@ -531,7 +532,6 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
if (propertyPrefix == null) {
|
if (propertyPrefix == null) {
|
||||||
propertyPrefix = "";
|
propertyPrefix = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String driver;
|
String driver;
|
||||||
String flavorStr;
|
String flavorStr;
|
||||||
String url;
|
String url;
|
||||||
|
@ -561,11 +561,9 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
user = properties.getProperty(propertyPrefix + "database.user");
|
user = properties.getProperty(propertyPrefix + "database.user");
|
||||||
password = properties.getProperty(propertyPrefix + "database.password");
|
password = properties.getProperty(propertyPrefix + "database.password");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
throw new DatabaseException("You must use -D" + propertyPrefix + "database.url=...");
|
throw new DatabaseException("You must use -D" + propertyPrefix + "database.url=...");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user != null && password == null) {
|
if (user != null && password == null) {
|
||||||
System.out.println("Enter database password for user " + user + ":");
|
System.out.println("Enter database password for user " + user + ":");
|
||||||
byte[] input = new byte[256];
|
byte[] input = new byte[256];
|
||||||
|
@ -576,28 +574,22 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
throw new DatabaseException("Error reading password from standard input", e);
|
throw new DatabaseException("Error reading password from standard input", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Flavor flavor;
|
Flavor flavor;
|
||||||
if (flavorStr != null) {
|
if (flavorStr != null) {
|
||||||
flavor = Flavor.valueOf(flavorStr);
|
flavor = Flavor.valueOf(flavorStr);
|
||||||
} else {
|
} else {
|
||||||
flavor = Flavor.fromJdbcUrl(url);
|
flavor = Flavor.fromJdbcUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver == null) {
|
|
||||||
if (flavor == Flavor.oracle) {
|
|
||||||
driver = "oracle.jdbc.OracleDriver";
|
|
||||||
} else if (flavor == Flavor.postgresql) {
|
|
||||||
driver = "org.postgresql.Driver";
|
|
||||||
} else if (flavor == Flavor.derby) {
|
|
||||||
driver = "org.apache.derby.jdbc.EmbeddedDriver";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (driver != null) {
|
if (driver != null) {
|
||||||
try {
|
try {
|
||||||
Class.forName(driver).getDeclaredConstructor().newInstance();
|
Class.forName(driver).getDeclaredConstructor().newInstance();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new DatabaseException("Unable to load JDBC driver: " + driver, e);
|
driver = flavor.driverClass();
|
||||||
|
try {
|
||||||
|
Class.forName(driver).getDeclaredConstructor().newInstance();
|
||||||
|
} catch (Exception ee) {
|
||||||
|
throw new DatabaseException("Unable to load JDBC driver: " + driver, ee);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
|
@ -616,7 +608,12 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
}
|
}
|
||||||
PoolConfig poolConfig = new PoolConfig();
|
PoolConfig poolConfig = new PoolConfig();
|
||||||
poolConfig.setPoolName(config.getString("database.pool.name", "database-pool-" + poolNameCounter.getAndAdd(1)));
|
poolConfig.setPoolName(config.getString("database.pool.name", "database-pool-" + poolNameCounter.getAndAdd(1)));
|
||||||
poolConfig.setDriverClassName(config.getString("database.driver.class", Flavor.driverForJdbcUrl(url)));
|
poolConfig.setUrl(config.getString("database.url"));
|
||||||
|
// provide driver class only if in config
|
||||||
|
String driverClassName = config.getString("database.driver.class");
|
||||||
|
if (driverClassName != null) {
|
||||||
|
poolConfig.setDriverClassName(config.getString("database.driver.class"));
|
||||||
|
}
|
||||||
poolConfig.setUsername(config.getString("database.user"));
|
poolConfig.setUsername(config.getString("database.user"));
|
||||||
poolConfig.setPassword(config.getString("database.password"));
|
poolConfig.setPassword(config.getString("database.password"));
|
||||||
poolConfig.setMaximumPoolSize(config.getInteger("database.pool.size", 8));
|
poolConfig.setMaximumPoolSize(config.getInteger("database.pool.size", 8));
|
||||||
|
@ -774,7 +771,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
try {
|
try {
|
||||||
// JDBC specifies that autoCommit is the default for all new connections.
|
// JDBC specifies that autoCommit is the default for all new connections.
|
||||||
// Don't try to be clever about clearing it conditionally.
|
// Don't try to be clever about clearing it conditionally.
|
||||||
if (!options.flavor().autoCommitOnly()) {
|
if (!options.flavor().isAutoCommitOnly()) {
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
metric.checkpoint("setAutoCommit");
|
metric.checkpoint("setAutoCommit");
|
||||||
}
|
}
|
||||||
|
@ -868,7 +865,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
}
|
}
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
try {
|
try {
|
||||||
if (!options.flavor().autoCommitOnly()) {
|
if (!options.flavor().isAutoCommitOnly()) {
|
||||||
connection.commit();
|
connection.commit();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -884,7 +881,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
}
|
}
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
try {
|
try {
|
||||||
if (!options.flavor().autoCommitOnly()) {
|
if (!options.flavor().isAutoCommitOnly()) {
|
||||||
connection.commit();
|
connection.commit();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -902,7 +899,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
|
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
try {
|
try {
|
||||||
if (!options.flavor().autoCommitOnly()) {
|
if (!options.flavor().isAutoCommitOnly()) {
|
||||||
connection.rollback();
|
connection.rollback();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -919,7 +916,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
||||||
|
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
try {
|
try {
|
||||||
if (!options.flavor().autoCommitOnly()) {
|
if (!options.flavor().isAutoCommitOnly()) {
|
||||||
connection.rollback();
|
connection.rollback();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -114,9 +114,6 @@ public interface Options {
|
||||||
*
|
*
|
||||||
* <p>It is strongly recommended to always run your database in GMT timezone, and
|
* <p>It is strongly recommended to always run your database in GMT timezone, and
|
||||||
* leave this set to the default.</p>
|
* leave this set to the default.</p>
|
||||||
*
|
|
||||||
* <p>Behavior in releases 1.3 and prior was to use the JVM default TimeZone, and
|
|
||||||
* this was not configurable.</p>
|
|
||||||
*/
|
*/
|
||||||
Calendar calendarForTimestamps();
|
Calendar calendarForTimestamps();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.xbib.jdbc.query;
|
package org.xbib.jdbc.query;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.flavor.Postgresql;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -23,7 +25,7 @@ public class OptionsOverride implements Options {
|
||||||
* Defer to OptionsDefault for anything that is not specified, and use postgresql flavor.
|
* Defer to OptionsDefault for anything that is not specified, and use postgresql flavor.
|
||||||
*/
|
*/
|
||||||
public OptionsOverride() {
|
public OptionsOverride() {
|
||||||
parent = new OptionsDefault(Flavor.postgresql);
|
parent = new OptionsDefault(new Postgresql());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,6 +5,8 @@ import org.xbib.jdbc.query.Schema.Table.Column;
|
||||||
import org.xbib.jdbc.query.Schema.Table.ForeignKey;
|
import org.xbib.jdbc.query.Schema.Table.ForeignKey;
|
||||||
import org.xbib.jdbc.query.Schema.Table.Index;
|
import org.xbib.jdbc.query.Schema.Table.Index;
|
||||||
import org.xbib.jdbc.query.Schema.Table.Unique;
|
import org.xbib.jdbc.query.Schema.Table.Unique;
|
||||||
|
import org.xbib.jdbc.query.flavor.Oracle;
|
||||||
|
import org.xbib.jdbc.query.flavor.Postgresql;
|
||||||
|
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
@ -274,15 +276,13 @@ public class Schema {
|
||||||
sql.append(check.expression);
|
sql.append(check.expression);
|
||||||
sql.append(")");
|
sql.append(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
sql.append("\n)");
|
sql.append("\n)");
|
||||||
if (table.customClauses.containsKey(flavor)) {
|
if (table.customClauses.containsKey(flavor)) {
|
||||||
sql.append(" ").append(table.customClauses.get(flavor));
|
sql.append(" ").append(table.customClauses.get(flavor));
|
||||||
}
|
}
|
||||||
executeOrPrint(sql, db, script);
|
executeOrPrint(sql, db, script);
|
||||||
sql = new Sql();
|
sql = new Sql();
|
||||||
|
if (flavor instanceof Oracle || flavor instanceof Postgresql) {
|
||||||
if (flavor == Flavor.oracle || flavor == Flavor.postgresql) {
|
|
||||||
if (table.comment != null) {
|
if (table.comment != null) {
|
||||||
sql.append("comment on table ");
|
sql.append("comment on table ");
|
||||||
sql.append(table.name);
|
sql.append(table.name);
|
||||||
|
@ -292,7 +292,6 @@ public class Schema {
|
||||||
executeOrPrint(sql, db, script);
|
executeOrPrint(sql, db, script);
|
||||||
sql = new Sql();
|
sql = new Sql();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Column c : table.columns) {
|
for (Column c : table.columns) {
|
||||||
if (c.comment != null) {
|
if (c.comment != null) {
|
||||||
sql.append("comment on column ");
|
sql.append("comment on column ");
|
||||||
|
@ -308,7 +307,6 @@ public class Schema {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Table table : tables) {
|
for (Table table : tables) {
|
||||||
for (ForeignKey fk : table.foreignKeys) {
|
for (ForeignKey fk : table.foreignKeys) {
|
||||||
Sql sql = new Sql();
|
Sql sql = new Sql();
|
||||||
|
@ -329,7 +327,6 @@ public class Schema {
|
||||||
executeOrPrint(sql, db, script);
|
executeOrPrint(sql, db, script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Table table : tables) {
|
for (Table table : tables) {
|
||||||
for (Index index : table.indexes) {
|
for (Index index : table.indexes) {
|
||||||
Sql sql = new Sql();
|
Sql sql = new Sql();
|
||||||
|
|
|
@ -22,18 +22,17 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
public Sql() {
|
public Sql() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sql(/*@Untainted*/ String sql) {
|
public Sql(String sql) {
|
||||||
this.sql.append(sql);
|
this.sql.append(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Sql insert(/*@Untainted*/ String table, SqlArgs args) {
|
public static Sql insert(String table, SqlArgs args) {
|
||||||
return insert(table, Collections.singletonList(args));
|
return insert(table, Collections.singletonList(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Sql insert(/*@Untainted*/ String table, List<SqlArgs> args) {
|
public static Sql insert(String table, List<SqlArgs> args) {
|
||||||
Sql sql = null;
|
Sql sql = null;
|
||||||
List<String> expectedColumns = null;
|
List<String> expectedColumns = null;
|
||||||
|
|
||||||
for (SqlArgs arg : args) {
|
for (SqlArgs arg : args) {
|
||||||
if (arg.positionalCount() > 0) {
|
if (arg.positionalCount() > 0) {
|
||||||
throw new DatabaseException("The SqlArgs must all be named to do this");
|
throw new DatabaseException("The SqlArgs must all be named to do this");
|
||||||
|
@ -98,7 +97,7 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sql append(/*@Untainted*/ String sql) {
|
public Sql append(String sql) {
|
||||||
this.sql.append(sql);
|
this.sql.append(sql);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -133,12 +132,12 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sql replace(int start, int end, /*@Untainted*/ String str) {
|
public Sql replace(int start, int end, String str) {
|
||||||
this.sql.replace(start, end, str);
|
this.sql.replace(start, end, str);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sql insert(int offset, /*@Untainted*/ String str) {
|
public Sql insert(int offset, String str) {
|
||||||
this.sql.insert(offset, str);
|
this.sql.insert(offset, str);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -189,7 +188,7 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
* <p>
|
* <p>
|
||||||
* Each list started must have be ended. "Lists" are only to support using listSeparator(sep)
|
* Each list started must have be ended. "Lists" are only to support using listSeparator(sep)
|
||||||
*/
|
*/
|
||||||
public Sql listStart(/*@Untainted*/ String sql) {
|
public Sql listStart(String sql) {
|
||||||
listFirstItem.push(true);
|
listFirstItem.push(true);
|
||||||
return append(sql);
|
return append(sql);
|
||||||
}
|
}
|
||||||
|
@ -198,7 +197,7 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
* Appends the passed bit of sql only if a previous item has already been appended,
|
* Appends the passed bit of sql only if a previous item has already been appended,
|
||||||
* and notes that the list is not empty.
|
* and notes that the list is not empty.
|
||||||
*/
|
*/
|
||||||
public Sql listSeparator(/*@Untainted*/ String sql) {
|
public Sql listSeparator(String sql) {
|
||||||
if (listFirstItem.peek()) {
|
if (listFirstItem.peek()) {
|
||||||
listFirstItem.pop();
|
listFirstItem.pop();
|
||||||
listFirstItem.push(false);
|
listFirstItem.push(false);
|
||||||
|
@ -209,12 +208,11 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql listEnd(/*@Untainted*/ String sql) {
|
public Sql listEnd(String sql) {
|
||||||
listFirstItem.pop();
|
listFirstItem.pop();
|
||||||
return append(sql);
|
return append(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@Untainted*/
|
|
||||||
public String sql() {
|
public String sql() {
|
||||||
return sql.toString();
|
return sql.toString();
|
||||||
}
|
}
|
||||||
|
@ -222,7 +220,6 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
/**
|
/**
|
||||||
* Same as sql(), provided for drop-in compatibility with StringBuilder.
|
* Same as sql(), provided for drop-in compatibility with StringBuilder.
|
||||||
*/
|
*/
|
||||||
/*@Untainted*/
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return sql();
|
return sql();
|
||||||
}
|
}
|
||||||
|
@ -232,79 +229,66 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argBoolean( String argName, Boolean arg) {
|
public Sql argBoolean( String argName, Boolean arg) {
|
||||||
sqlArgs.argBoolean(argName, arg);
|
sqlArgs.argBoolean(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argInteger(Integer arg) {
|
public Sql argInteger(Integer arg) {
|
||||||
sqlArgs.argInteger(arg);
|
sqlArgs.argInteger(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argInteger( String argName, Integer arg) {
|
public Sql argInteger( String argName, Integer arg) {
|
||||||
sqlArgs.argInteger(argName, arg);
|
sqlArgs.argInteger(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argLong(Long arg) {
|
public Sql argLong(Long arg) {
|
||||||
sqlArgs.argLong(arg);
|
sqlArgs.argLong(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argLong(String argName, Long arg) {
|
public Sql argLong(String argName, Long arg) {
|
||||||
sqlArgs.argLong(argName, arg);
|
sqlArgs.argLong(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argFloat(Float arg) {
|
public Sql argFloat(Float arg) {
|
||||||
sqlArgs.argFloat(arg);
|
sqlArgs.argFloat(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argFloat( String argName, Float arg) {
|
public Sql argFloat( String argName, Float arg) {
|
||||||
sqlArgs.argFloat(argName, arg);
|
sqlArgs.argFloat(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDouble(Double arg) {
|
public Sql argDouble(Double arg) {
|
||||||
sqlArgs.argDouble(arg);
|
sqlArgs.argDouble(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDouble( String argName, Double arg) {
|
public Sql argDouble( String argName, Double arg) {
|
||||||
sqlArgs.argDouble(argName, arg);
|
sqlArgs.argDouble(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argBigDecimal(BigDecimal arg) {
|
public Sql argBigDecimal(BigDecimal arg) {
|
||||||
sqlArgs.argBigDecimal(arg);
|
sqlArgs.argBigDecimal(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argBigDecimal( String argName, BigDecimal arg) {
|
public Sql argBigDecimal( String argName, BigDecimal arg) {
|
||||||
sqlArgs.argBigDecimal(argName, arg);
|
sqlArgs.argBigDecimal(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argString(String arg) {
|
public Sql argString(String arg) {
|
||||||
sqlArgs.argString(arg);
|
sqlArgs.argString(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argString(String argName, String arg) {
|
public Sql argString(String argName, String arg) {
|
||||||
sqlArgs.argString(argName, arg);
|
sqlArgs.argString(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
|
@ -315,31 +299,26 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDate(String argName, LocalDateTime arg) {
|
public Sql argDate(String argName, LocalDateTime arg) {
|
||||||
sqlArgs.argDate(argName, arg);
|
sqlArgs.argDate(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDateNowPerApp() {
|
public Sql argDateNowPerApp() {
|
||||||
sqlArgs.argDateNowPerApp();
|
sqlArgs.argDateNowPerApp();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDateNowPerApp(String argName) {
|
public Sql argDateNowPerApp(String argName) {
|
||||||
sqlArgs.argDateNowPerApp(argName);
|
sqlArgs.argDateNowPerApp(argName);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDateNowPerDb() {
|
public Sql argDateNowPerDb() {
|
||||||
sqlArgs.argDateNowPerDb();
|
sqlArgs.argDateNowPerDb();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argDateNowPerDb(String argName) {
|
public Sql argDateNowPerDb(String argName) {
|
||||||
sqlArgs.argDateNowPerDb(argName);
|
sqlArgs.argDateNowPerDb(argName);
|
||||||
return this;
|
return this;
|
||||||
|
@ -357,31 +336,26 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argBlobInputStream(InputStream arg) {
|
public Sql argBlobInputStream(InputStream arg) {
|
||||||
sqlArgs.argBlobInputStream(arg);
|
sqlArgs.argBlobInputStream(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argBlobInputStream( String argName, InputStream arg) {
|
public Sql argBlobInputStream( String argName, InputStream arg) {
|
||||||
sqlArgs.argBlobInputStream(argName, arg);
|
sqlArgs.argBlobInputStream(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argClobString(String arg) {
|
public Sql argClobString(String arg) {
|
||||||
sqlArgs.argClobString(arg);
|
sqlArgs.argClobString(arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argClobString( String argName, String arg) {
|
public Sql argClobString( String argName, String arg) {
|
||||||
sqlArgs.argClobString(argName, arg);
|
sqlArgs.argClobString(argName, arg);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sql argClobReader(Reader arg) {
|
public Sql argClobReader(Reader arg) {
|
||||||
sqlArgs.argClobReader(arg);
|
sqlArgs.argClobReader(arg);
|
||||||
return this;
|
return this;
|
||||||
|
@ -419,7 +393,6 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
if (batched != null) {
|
if (batched != null) {
|
||||||
throw new DatabaseException("Batch not supported for update");
|
throw new DatabaseException("Batch not supported for update");
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlArgs.apply(update);
|
sqlArgs.apply(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,16 +400,4 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
||||||
Integer, Long, Float, Double, BigDecimal, String, ClobString, ClobStream,
|
Integer, Long, Float, Double, BigDecimal, String, ClobString, ClobStream,
|
||||||
BlobBytes, BlobStream, Date, DateNowPerApp, DateNowPerDb, Boolean
|
BlobBytes, BlobStream, Date, DateNowPerApp, DateNowPerDb, Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Invocation {
|
|
||||||
ColumnType columnType;
|
|
||||||
String argName;
|
|
||||||
Object arg;
|
|
||||||
|
|
||||||
Invocation(ColumnType columnType, String argName, Object arg) {
|
|
||||||
this.columnType = columnType;
|
|
||||||
this.argName = argName;
|
|
||||||
this.arg = arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
|
||||||
* @return a SqlArgs with one invocation for each column in the Row, with name
|
* @return a SqlArgs with one invocation for each column in the Row, with name
|
||||||
* and type inferred from the metadata
|
* and type inferred from the metadata
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static SqlArgs readRow(Row r) {
|
public static SqlArgs readRow(Row r) {
|
||||||
return new Builder(r).read(r);
|
return new Builder(r).read(r);
|
||||||
}
|
}
|
||||||
|
@ -119,44 +118,37 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argDouble( String argName, Double arg) {
|
public SqlArgs argDouble( String argName, Double arg) {
|
||||||
invocations.add(new Invocation(ColumnType.Double, argName, arg));
|
invocations.add(new Invocation(ColumnType.Double, argName, arg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argBigDecimal(BigDecimal arg) {
|
public SqlArgs argBigDecimal(BigDecimal arg) {
|
||||||
invocations.add(new Invocation(ColumnType.BigDecimal, null, arg));
|
invocations.add(new Invocation(ColumnType.BigDecimal, null, arg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argBigDecimal( String argName, BigDecimal arg) {
|
public SqlArgs argBigDecimal( String argName, BigDecimal arg) {
|
||||||
invocations.add(new Invocation(ColumnType.BigDecimal, argName, arg));
|
invocations.add(new Invocation(ColumnType.BigDecimal, argName, arg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argString(String arg) {
|
public SqlArgs argString(String arg) {
|
||||||
invocations.add(new Invocation(ColumnType.String, null, arg));
|
invocations.add(new Invocation(ColumnType.String, null, arg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argString( String argName, String arg) {
|
public SqlArgs argString( String argName, String arg) {
|
||||||
invocations.add(new Invocation(ColumnType.String, argName, arg));
|
invocations.add(new Invocation(ColumnType.String, argName, arg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argDate(LocalDateTime arg) {
|
public SqlArgs argDate(LocalDateTime arg) {
|
||||||
// date argument with a time on it
|
// date argument with a time on it
|
||||||
invocations.add(new Invocation(ColumnType.Date, null, arg));
|
invocations.add(new Invocation(ColumnType.Date, null, arg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs argDate( String argName, LocalDateTime arg) {
|
public SqlArgs argDate( String argName, LocalDateTime arg) {
|
||||||
// date argument with a time on it
|
// date argument with a time on it
|
||||||
invocations.add(new Invocation(ColumnType.Date, argName, arg));
|
invocations.add(new Invocation(ColumnType.Date, argName, arg));
|
||||||
|
@ -694,10 +686,8 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SqlArgs read(Row r) {
|
public SqlArgs read(Row r) {
|
||||||
SqlArgs args = new SqlArgs();
|
SqlArgs args = new SqlArgs();
|
||||||
|
|
||||||
for (int i = 0; i < names.length; i++) {
|
for (int i = 0; i < names.length; i++) {
|
||||||
switch (types[i]) {
|
switch (types[i]) {
|
||||||
case Types.SMALLINT:
|
case Types.SMALLINT:
|
||||||
|
@ -705,22 +695,24 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
|
||||||
args.argInteger(names[i], r.getIntegerOrNull());
|
args.argInteger(names[i], r.getIntegerOrNull());
|
||||||
break;
|
break;
|
||||||
case Types.BIGINT:
|
case Types.BIGINT:
|
||||||
|
if (precision[i] <= 64 && scale[i] == 0) {
|
||||||
args.argLong(names[i], r.getLongOrNull());
|
args.argLong(names[i], r.getLongOrNull());
|
||||||
|
} else {
|
||||||
|
args.argBigDecimal(names[i], r.getBigDecimalOrNull());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Types.REAL:
|
case Types.REAL:
|
||||||
case 100: // Oracle proprietary it seems
|
case 100: // Oracle binary float
|
||||||
args.argFloat(names[i], r.getFloatOrNull());
|
args.argFloat(names[i], r.getFloatOrNull());
|
||||||
break;
|
break;
|
||||||
case Types.DOUBLE:
|
case Types.DOUBLE:
|
||||||
case 101: // Oracle proprietary it seems
|
case 101: // Oracle binary double
|
||||||
args.argDouble(names[i], r.getDoubleOrNull());
|
args.argDouble(names[i], r.getDoubleOrNull());
|
||||||
break;
|
break;
|
||||||
case Types.NUMERIC:
|
case Types.NUMERIC:
|
||||||
if (precision[i] == 10 && scale[i] == 0) {
|
if (precision[i] <= 10 && scale[i] == 0) {
|
||||||
// Oracle reports integer as numeric
|
|
||||||
args.argInteger(names[i], r.getIntegerOrNull());
|
args.argInteger(names[i], r.getIntegerOrNull());
|
||||||
} else if (precision[i] == 19 && scale[i] == 0) {
|
} else if (precision[i] <= 19 && scale[i] == 0) {
|
||||||
// Oracle reports long as numeric
|
|
||||||
args.argLong(names[i], r.getLongOrNull());
|
args.argLong(names[i], r.getLongOrNull());
|
||||||
} else {
|
} else {
|
||||||
args.argBigDecimal(names[i], r.getBigDecimalOrNull());
|
args.argBigDecimal(names[i], r.getBigDecimalOrNull());
|
||||||
|
|
|
@ -288,7 +288,6 @@ public class SqlInsertImpl implements SqlInsert {
|
||||||
if (!hasPk()) {
|
if (!hasPk()) {
|
||||||
throw new DatabaseException("Call argPkSeq() before insertReturningPkSeq()");
|
throw new DatabaseException("Call argPkSeq() before insertReturningPkSeq()");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.flavor().supportsInsertReturning()) {
|
if (options.flavor().supportsInsertReturning()) {
|
||||||
return updateInternal(1, primaryKeyColumnName);
|
return updateInternal(1, primaryKeyColumnName);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package org.xbib.jdbc.query;
|
package org.xbib.jdbc.query;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.flavor.Derby;
|
||||||
|
import org.xbib.jdbc.query.flavor.Oracle;
|
||||||
|
import org.xbib.jdbc.query.flavor.Postgresql;
|
||||||
|
import org.xbib.jdbc.query.flavor.SqlServer;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,28 +21,28 @@ public class When {
|
||||||
}
|
}
|
||||||
|
|
||||||
public When oracle(String sql) {
|
public When oracle(String sql) {
|
||||||
if (actualFlavor == Flavor.oracle) {
|
if (actualFlavor instanceof Oracle) {
|
||||||
chosen = sql;
|
chosen = sql;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public When derby(String sql) {
|
public When derby(String sql) {
|
||||||
if (actualFlavor == Flavor.derby) {
|
if (actualFlavor instanceof Derby) {
|
||||||
chosen = sql;
|
chosen = sql;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public When postgres(String sql) {
|
public When postgres(String sql) {
|
||||||
if (actualFlavor == Flavor.postgresql) {
|
if (actualFlavor instanceof Postgresql) {
|
||||||
chosen = sql;
|
chosen = sql;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public When sqlserver(String sql) {
|
public When sqlserver(String sql) {
|
||||||
if (actualFlavor == Flavor.sqlserver) {
|
if (actualFlavor instanceof SqlServer) {
|
||||||
chosen = sql;
|
chosen = sql;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
package org.xbib.jdbc.query.flavor;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class BigQuery implements Flavor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "bigQuery";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String url) {
|
||||||
|
return url.startsWith("jdbc:bigquery:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverClass() {
|
||||||
|
return "com.simba.googlebigquery.jdbc42.Driver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNormalizedUpperCase() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeInteger() {
|
||||||
|
return "int64";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBoolean() {
|
||||||
|
// BigQuery has a native boolean type, but we're not trying to use it
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLong() {
|
||||||
|
return "int64";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeFloat() {
|
||||||
|
return "float64";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDouble() {
|
||||||
|
return "float64";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBigDecimal(int size, int precision) {
|
||||||
|
return "numeric";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringVar(int length) {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringFixed(int length) {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeClob() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBlob() {
|
||||||
|
return "bytes";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDate() {
|
||||||
|
return "datetime";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLocalDate() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStringForClob() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useBytesForBlob() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceNextVal(String sequenceName) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceSelectNextVal(String sequenceName) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceDrop(String dbtestSeq) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tableDrop(String table) {
|
||||||
|
return "drop table " + table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturning() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dbTimeMillis() {
|
||||||
|
return "current_timestamp()";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCacheClause(int nbrValuesToCache) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOrderClause(boolean order) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCycleClause(boolean cycle) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromAny() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
|
||||||
|
// Construct a datetime literal
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
|
||||||
|
dateFormat.setCalendar(calendar);
|
||||||
|
return String.format("datetime '%s'", dateFormat.format(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String localDateAsSqlFunction(Date date) {
|
||||||
|
// Construct a datetime literal
|
||||||
|
return String.format("datetime '%s'", date.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOptions() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoCommitOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String writeNextIntoQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readNextFromQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String succeedInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String failInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
194
jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java
Normal file
194
jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Derby.java
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package org.xbib.jdbc.query.flavor;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class Derby implements Flavor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "derby";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String url) {
|
||||||
|
return url.startsWith("jdbc:derby:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverClass() {
|
||||||
|
return "org.apache.derby.jdbc.EmbeddedDriver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNormalizedUpperCase() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeInteger() {
|
||||||
|
return "integer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBoolean() {
|
||||||
|
return "char(1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLong() {
|
||||||
|
return "bigint";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeFloat() {
|
||||||
|
return "real";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDouble() {
|
||||||
|
return "double";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBigDecimal(int size, int precision) {
|
||||||
|
return "numeric(" + size + "," + precision + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringVar(int length) {
|
||||||
|
return "varchar(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringFixed(int length) {
|
||||||
|
return "char(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeClob() {
|
||||||
|
return "clob";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBlob() {
|
||||||
|
return "blob";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDate() {
|
||||||
|
return "timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLocalDate() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStringForClob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useBytesForBlob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceNextVal(String sequenceName) {
|
||||||
|
return "next value for " + sequenceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceSelectNextVal(String sequenceName) {
|
||||||
|
return "values next value for " + sequenceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceDrop(String sequenceName) {
|
||||||
|
return "drop sequence " + sequenceName + " restrict";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tableDrop(String table) {
|
||||||
|
return "drop table " + table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturning() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCacheClause(int nbrValuesToCache) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOrderClause(boolean order) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCycleClause(boolean cycle) {
|
||||||
|
return cycle ? " cycle" : " no cycle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dbTimeMillis() {
|
||||||
|
return "current_timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromAny() {
|
||||||
|
return " from sysibm.sysdummy1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||||
|
dateFormat.setCalendar(calendar);
|
||||||
|
return "timestamp('" + dateFormat.format(date) + "')";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String localDateAsSqlFunction(Date date) {
|
||||||
|
return "'" + date.toString() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOptions() {
|
||||||
|
return " as bigint";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoCommitOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String writeNextIntoQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readNextFromQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String succeedInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String failInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
192
jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java
Normal file
192
jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Hsql.java
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package org.xbib.jdbc.query.flavor;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class Hsql implements Flavor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "hsqldb";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String url) {
|
||||||
|
return url.startsWith("jdbc:hsqldb:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverClass() {
|
||||||
|
return "org.hsqldb.jdbc.JDBCDriver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNormalizedUpperCase() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeInteger() {
|
||||||
|
return "integer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBoolean() {
|
||||||
|
return "char(1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLong() {
|
||||||
|
return "bigint";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeFloat() {
|
||||||
|
return "double";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDouble() {
|
||||||
|
return "double";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBigDecimal(int size, int precision) {
|
||||||
|
return "numeric(" + size + "," + precision + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringVar(int length) {
|
||||||
|
return "varchar(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringFixed(int length) {
|
||||||
|
return "char(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeClob() {
|
||||||
|
return "clob(2G)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBlob() {
|
||||||
|
return "blob(2G)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDate() {
|
||||||
|
return "timestamp(3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLocalDate() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStringForClob() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useBytesForBlob() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceNextVal(String sequenceName) {
|
||||||
|
return "next value for " + sequenceName + "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceSelectNextVal(String sequenceName) {
|
||||||
|
return "select " + sequenceNextVal(sequenceName) + fromAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceDrop(String dbtestSeq) {
|
||||||
|
return "drop sequence if exists " + dbtestSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tableDrop(String table) {
|
||||||
|
return "drop table if exists " + table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOrderClause(boolean order) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCycleClause(boolean cycle) {
|
||||||
|
return cycle ? " cycle" : " no cycle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromAny() {
|
||||||
|
return " from (values(0))";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturning() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dbTimeMillis() {
|
||||||
|
return "localtimestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCacheClause(int nbrValuesToCache) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000XXX");
|
||||||
|
dateFormat.setCalendar(calendar);
|
||||||
|
return "cast(timestamp '" + dateFormat.format(date) + "' as timestamp without time zone)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String localDateAsSqlFunction(Date date) {
|
||||||
|
return "'" + date.toString() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOptions() {
|
||||||
|
return " as bigint";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoCommitOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String writeNextIntoQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readNextFromQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String succeedInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String failInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
200
jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Oracle.java
Normal file
200
jdbc-query/src/main/java/org/xbib/jdbc/query/flavor/Oracle.java
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
package org.xbib.jdbc.query.flavor;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class Oracle implements Flavor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "oracle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String url) {
|
||||||
|
return url.startsWith("jdbc:oracle:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverClass() {
|
||||||
|
return "oracle.jdbc.OracleDriver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNormalizedUpperCase() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeFloat() {
|
||||||
|
return "binary_float";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDouble() {
|
||||||
|
return "binary_double";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBigDecimal(int size, int precision) {
|
||||||
|
return "numeric(" + size + "," + precision + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeInteger() {
|
||||||
|
return "numeric(10)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBoolean() {
|
||||||
|
return "char(1 char)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLong() {
|
||||||
|
return "numeric(19)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDate() {
|
||||||
|
return "timestamp(3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLocalDate() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStringForClob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useBytesForBlob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceNextVal(String sequenceName) {
|
||||||
|
return sequenceName + ".nextval";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceSelectNextVal(String sequenceName) {
|
||||||
|
return "select " + sequenceName + ".nextval from dual";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceDrop(String sequenceName) {
|
||||||
|
return "drop sequence " + sequenceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tableDrop(String table) {
|
||||||
|
return "drop table " + table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringVar(int length) {
|
||||||
|
return "varchar2(" + length + " char)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringFixed(int length) {
|
||||||
|
return "char(" + length + " char)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeClob() {
|
||||||
|
return "clob";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBlob() {
|
||||||
|
return "blob";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOrderClause(boolean order) {
|
||||||
|
return order ? " order" : " noorder";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCycleClause(boolean cycle) {
|
||||||
|
return cycle ? " cycle" : " nocycle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturning() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dbTimeMillis() {
|
||||||
|
return "systimestamp(3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCacheClause(int nbrValuesToCache) {
|
||||||
|
if (nbrValuesToCache < 2) {
|
||||||
|
return " nocache";
|
||||||
|
}
|
||||||
|
return " cache " + nbrValuesToCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromAny() {
|
||||||
|
return " from dual";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
|
||||||
|
dateFormat.setCalendar(calendar);
|
||||||
|
return "timestamp '" + dateFormat.format(date) + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String localDateAsSqlFunction(Date date) {
|
||||||
|
return "to_date('" + date.toString() + "', 'yyyy-mm-dd')";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOptions() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoCommitOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String writeNextIntoQueue(String table) {
|
||||||
|
return "insert into " + table
|
||||||
|
+ " (channel, data, state, delay, modified) "
|
||||||
|
+ "values (?, ?, null, 0, ?)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readNextFromQueue(String table) {
|
||||||
|
return "select id, data from " + table +
|
||||||
|
" where channel = ? and state is NULL and modified <= (CAST (? as TIMESTAMP) - (INTERVAL '1 minute' * delay))" +
|
||||||
|
" order by id asc limit 1 for update skip lock";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String succeedInQueue(String table) {
|
||||||
|
return "update" + table + " set state = 'OK', modified = " + dbTimeMillis() + " where id = ?";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String failInQueue(String table) {
|
||||||
|
return "update" + table + " set state = 'FAIL', modified = " + dbTimeMillis() + " where id = ?";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
package org.xbib.jdbc.query.flavor;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class Postgresql implements Flavor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "postgresql";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String url) {
|
||||||
|
return url.startsWith("jdbc:postgresql:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverClass() {
|
||||||
|
return "org.postgresql.Driver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNormalizedUpperCase() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeInteger() {
|
||||||
|
return "integer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBoolean() {
|
||||||
|
return "char(1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLong() {
|
||||||
|
return "bigint";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeFloat() {
|
||||||
|
return "real";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDouble() {
|
||||||
|
return "double precision";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBigDecimal(int size, int precision) {
|
||||||
|
return "numeric(" + size + "," + precision + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringVar(int length) {
|
||||||
|
return "varchar(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringFixed(int length) {
|
||||||
|
return "char(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeClob() {
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBlob() {
|
||||||
|
return "bytea";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDate() {
|
||||||
|
return "timestamp(3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLocalDate() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStringForClob() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useBytesForBlob() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceNextVal(String sequenceName) {
|
||||||
|
return "nextval('" + sequenceName + "')";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceSelectNextVal(String sequenceName) {
|
||||||
|
return "select nextval('" + sequenceName + "')";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceDrop(String dbtestSeq) {
|
||||||
|
return "drop sequence " + dbtestSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tableDrop(String table) {
|
||||||
|
return "drop table if exists " + table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOrderClause(boolean order) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCycleClause(boolean cycle) {
|
||||||
|
return cycle ? " cycle" : " no cycle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromAny() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturning() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dbTimeMillis() {
|
||||||
|
return "date_trunc('milliseconds',localtimestamp)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCacheClause(int nbrValuesToCache) {
|
||||||
|
return " cache " + nbrValuesToCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
|
||||||
|
dateFormat.setCalendar(calendar);
|
||||||
|
return "'" + dateFormat.format(date) + " GMT'::timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String localDateAsSqlFunction(Date date) {
|
||||||
|
return "'" + date.toString() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOptions() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoCommitOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String writeNextIntoQueue(String table) {
|
||||||
|
return "insert into " + table
|
||||||
|
+ " (channel, data, state, delay, modified) "
|
||||||
|
+ "values (?, ?, null, 0, ?)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readNextFromQueue(String table) {
|
||||||
|
return "select id, data from " + table +
|
||||||
|
" where channel = ? and state is NULL and pushed_at <= (CAST (? as TIMESTAMP) - (INTERVAL '1 minute' * delay))" +
|
||||||
|
" order by id asc limit 1 for update skip lock";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String succeedInQueue(String table) {
|
||||||
|
return "update" + table + " set state = 'OK', modified = " + dbTimeMillis() + " where id = ?";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String failInQueue(String table) {
|
||||||
|
return "update" + table + " set state = 'FAIL', modified = " + dbTimeMillis() + " where id = ?";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
package org.xbib.jdbc.query.flavor;
|
||||||
|
|
||||||
|
import org.xbib.jdbc.query.Flavor;
|
||||||
|
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class SqlServer implements Flavor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sqlServer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String url) {
|
||||||
|
return url.startsWith("jdbc:sqlserver:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String driverClass() {
|
||||||
|
return "com.microsoft.sqlserver.jdbc.SQLServerDriver";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNormalizedUpperCase() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeFloat() {
|
||||||
|
return "float(24)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDouble() {
|
||||||
|
return "float(53)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBigDecimal(int size, int precision) {
|
||||||
|
return "numeric(" + size + "," + precision + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeInteger() {
|
||||||
|
return "numeric(10)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBoolean() {
|
||||||
|
return "char(1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLong() {
|
||||||
|
return "numeric(19)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeDate() {
|
||||||
|
return "datetime2(3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeLocalDate() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStringForClob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useBytesForBlob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceNextVal(String sequenceName) {
|
||||||
|
return "next value for " + sequenceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceSelectNextVal(String sequenceName) {
|
||||||
|
return "select next value for " + sequenceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceDrop(String dbtestSeq) {
|
||||||
|
return "drop sequence " + dbtestSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String tableDrop(String table) {
|
||||||
|
return "drop table " + table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringVar(int length) {
|
||||||
|
return "varchar(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeStringFixed(int length) {
|
||||||
|
return "char(" + length + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeClob() {
|
||||||
|
return "varchar(max)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String typeBlob() {
|
||||||
|
return "varbinary(max)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOrderClause(boolean order) {
|
||||||
|
// Not supported
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCycleClause(boolean cycle) {
|
||||||
|
return cycle ? " cycle" : " no cycle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturning() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dbTimeMillis() {
|
||||||
|
return "current_timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceCacheClause(int nbrValuesToCache) {
|
||||||
|
if (nbrValuesToCache < 2) {
|
||||||
|
return " no cache";
|
||||||
|
}
|
||||||
|
return " cache " + nbrValuesToCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromAny() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
|
||||||
|
dateFormat.setCalendar(calendar);
|
||||||
|
return "cast('" + dateFormat.format(date) + "' as datetime2(3))";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String localDateAsSqlFunction(Date date) {
|
||||||
|
return "'" + date.toString() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sequenceOptions() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoCommitOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String writeNextIntoQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readNextFromQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String succeedInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String failInQueue(String table) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
org.xbib.jdbc.query.flavor.BigQuery
|
||||||
|
org.xbib.jdbc.query.flavor.Derby
|
||||||
|
org.xbib.jdbc.query.flavor.Hsql
|
||||||
|
org.xbib.jdbc.query.flavor.Oracle
|
||||||
|
org.xbib.jdbc.query.flavor.Postgresql
|
||||||
|
org.xbib.jdbc.query.flavor.SqlServer
|
|
@ -26,14 +26,12 @@ import java.io.StringReader;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.Month;
|
import java.time.Month;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -54,7 +52,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exercise Database functionality with a real databases.
|
* Exercise Database functionality with a real database.
|
||||||
*/
|
*/
|
||||||
public abstract class CommonTest {
|
public abstract class CommonTest {
|
||||||
|
|
||||||
|
@ -62,10 +60,6 @@ public abstract class CommonTest {
|
||||||
|
|
||||||
final static String TEST_TABLE_NAME = "dbtest";
|
final static String TEST_TABLE_NAME = "dbtest";
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable retrying failed tests if they have the @Retry annotation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected DatabaseProvider dbp;
|
protected DatabaseProvider dbp;
|
||||||
|
|
||||||
protected Database db;
|
protected Database db;
|
||||||
|
|
|
@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.xbib.jdbc.query.Config;
|
import org.xbib.jdbc.query.Config;
|
||||||
import org.xbib.jdbc.query.ConfigFrom;
|
import org.xbib.jdbc.query.ConfigFrom;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -54,7 +53,7 @@ public class ConfigTest {
|
||||||
properties.store(new FileWriter(filename2), null);
|
properties.store(new FileWriter(filename2), null);
|
||||||
|
|
||||||
// Throw a null in here just to make sure it doesn't blow up
|
// Throw a null in here just to make sure it doesn't blow up
|
||||||
Config config = ConfigFrom.firstOf().propertyFile(filename1, null, filename2).get();
|
Config config = ConfigFrom.firstOf().properties(filename1, null, filename2).get();
|
||||||
|
|
||||||
assertEquals(Integer.valueOf(1), config.getInteger("foo"));
|
assertEquals(Integer.valueOf(1), config.getInteger("foo"));
|
||||||
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
||||||
|
@ -62,12 +61,12 @@ public class ConfigTest {
|
||||||
assertEquals(5, config.getInteger("unknown", 5));
|
assertEquals(5, config.getInteger("unknown", 5));
|
||||||
|
|
||||||
// Now flip the order and verify precedence works
|
// Now flip the order and verify precedence works
|
||||||
config = ConfigFrom.firstOf().propertyFile(filename2, null, filename1).get();
|
config = ConfigFrom.firstOf().properties(filename2, null, filename1).get();
|
||||||
assertEquals(Integer.valueOf(2), config.getInteger("foo"));
|
assertEquals(Integer.valueOf(2), config.getInteger("foo"));
|
||||||
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
||||||
|
|
||||||
// Same as above tests, but using File version rather than filename String
|
// Same as above tests, but using File version rather than filename String
|
||||||
config = ConfigFrom.firstOf().propertyFile(new File(filename1), new File("does not exist"), new File(filename2)).get();
|
config = ConfigFrom.firstOf().properties(filename1, "does not exist", filename2).get();
|
||||||
|
|
||||||
assertEquals(Integer.valueOf(1), config.getInteger("foo"));
|
assertEquals(Integer.valueOf(1), config.getInteger("foo"));
|
||||||
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
||||||
|
@ -75,7 +74,7 @@ public class ConfigTest {
|
||||||
assertEquals(5, config.getInteger("unknown", 5));
|
assertEquals(5, config.getInteger("unknown", 5));
|
||||||
|
|
||||||
// Now flip the order and verify precedence works
|
// Now flip the order and verify precedence works
|
||||||
config = ConfigFrom.firstOf().propertyFile(new File(filename2), null, new File(filename1)).get();
|
config = ConfigFrom.firstOf().properties(filename2, null, filename1).get();
|
||||||
assertEquals(Integer.valueOf(2), config.getInteger("foo"));
|
assertEquals(Integer.valueOf(2), config.getInteger("foo"));
|
||||||
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
assertEquals(Integer.valueOf(-2), config.getInteger("foo2"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exercise Database functionality with a real database (Derby).
|
* Exercise Database functionality with a real database (Derby).
|
||||||
*/
|
*/
|
||||||
|
@ -79,46 +78,6 @@ public class DerbyTest extends CommonTest {
|
||||||
super.intervals();
|
super.intervals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void argBigDecimal31Precision0() {
|
|
||||||
db.dropTableQuietly("dbtest");
|
|
||||||
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 0).schema().execute(db);
|
|
||||||
BigDecimal value = new BigDecimal("9999999999999999999999999999999"); // 31 digits
|
|
||||||
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
|
||||||
assertEquals(value,
|
|
||||||
db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void argBigDecimal31Precision1() {
|
|
||||||
db.dropTableQuietly("dbtest");
|
|
||||||
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 1).schema().execute(db);
|
|
||||||
BigDecimal value = new BigDecimal("999999999999999999999999999999.9"); // 31 digits
|
|
||||||
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
|
||||||
assertEquals(value,
|
|
||||||
db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void argBigDecimal31Precision30() {
|
|
||||||
db.dropTableQuietly("dbtest");
|
|
||||||
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 30).schema().execute(db);
|
|
||||||
BigDecimal value = new BigDecimal("9.999999999999999999999999999999"); // 31 digits
|
|
||||||
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
|
||||||
assertEquals(value,
|
|
||||||
db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void argBigDecimal31Precision31() {
|
|
||||||
db.dropTableQuietly("dbtest");
|
|
||||||
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 31).schema().execute(db);
|
|
||||||
BigDecimal value = new BigDecimal("0.9999999999999999999999999999999"); // 31 digits
|
|
||||||
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
|
||||||
logger.log(Level.INFO, db.toSelect("select i from dbtest").queryBigDecimalOrNull().toString());
|
|
||||||
assertEquals(value,
|
|
||||||
db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Disabled("Derby limits out at precision 31")
|
@Disabled("Derby limits out at precision 31")
|
||||||
@Test
|
@Test
|
||||||
|
@ -143,4 +102,41 @@ public class DerbyTest extends CommonTest {
|
||||||
public void argBigDecimal38Precision38() {
|
public void argBigDecimal38Precision38() {
|
||||||
super.argBigDecimal38Precision38();
|
super.argBigDecimal38Precision38();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void argBigDecimal31Precision0() {
|
||||||
|
db.dropTableQuietly("dbtest");
|
||||||
|
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 0).schema().execute(db);
|
||||||
|
BigDecimal value = new BigDecimal("9999999999999999999999999999999"); // 31 digits
|
||||||
|
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
||||||
|
assertEquals(value, db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void argBigDecimal31Precision1() {
|
||||||
|
db.dropTableQuietly("dbtest");
|
||||||
|
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 1).schema().execute(db);
|
||||||
|
BigDecimal value = new BigDecimal("999999999999999999999999999999.9"); // 31 digits
|
||||||
|
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
||||||
|
assertEquals(value, db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void argBigDecimal31Precision30() {
|
||||||
|
db.dropTableQuietly("dbtest");
|
||||||
|
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 30).schema().execute(db);
|
||||||
|
BigDecimal value = new BigDecimal("9.999999999999999999999999999999"); // 31 digits
|
||||||
|
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
||||||
|
assertEquals(value, db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void argBigDecimal31Precision31() {
|
||||||
|
db.dropTableQuietly("dbtest");
|
||||||
|
new Schema().addTable("dbtest").addColumn("i").asBigDecimal(31, 31).schema().execute(db);
|
||||||
|
BigDecimal value = new BigDecimal("0.9999999999999999999999999999999"); // 31 digits
|
||||||
|
db.toInsert("insert into dbtest (i) values (?)").argBigDecimal(value).insert(1);
|
||||||
|
logger.log(Level.INFO, db.toSelect("select i from dbtest").queryBigDecimalOrNull().toString());
|
||||||
|
assertEquals(value, db.toSelect("select i from dbtest where i=?").argBigDecimal(value).queryBigDecimalOrNull());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.xbib.jdbc.query.test;
|
package org.xbib.jdbc.query.test;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -22,24 +21,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
/**
|
/**
|
||||||
* Exercise database functionality with a real HyperSQL database.
|
* Exercise database functionality with a real HyperSQL database.
|
||||||
*/
|
*/
|
||||||
@Disabled
|
|
||||||
public class HsqldbTest extends CommonTest {
|
public class HsqldbTest extends CommonTest {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(HsqldbTest.class.getName());
|
private static final Logger logger = Logger.getLogger(HsqldbTest.class.getName());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
|
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
|
||||||
String propertiesFile = System.getProperty("local.properties", "local.properties");
|
String propertiesFile = "hsqldb.properties";
|
||||||
Config config = ConfigFrom.firstOf()
|
Config config = ConfigFrom.firstOf()
|
||||||
.systemProperties()
|
.properties(getClass().getResourceAsStream(propertiesFile))
|
||||||
.propertyFile(propertiesFile)
|
.get();
|
||||||
.excludePrefix("database.")
|
|
||||||
.removePrefix("hsqldb.").get();
|
|
||||||
logger.log(Level.INFO, "config = " + config);
|
|
||||||
return DatabaseProvider.builder(config)
|
return DatabaseProvider.builder(config)
|
||||||
.withSqlParameterLogging()
|
.withSqlParameterLogging()
|
||||||
.withSqlInExceptionMessages()
|
.withSqlInExceptionMessages()
|
||||||
.withOptions(options).build();
|
.withOptions(options)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -61,9 +57,16 @@ public class HsqldbTest extends CommonTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Disabled("HSQLDB uses always static GMT timezone")
|
||||||
|
@Test
|
||||||
|
public void clockSync() {
|
||||||
|
db.assertTimeSynchronized();
|
||||||
|
}
|
||||||
|
|
||||||
@Disabled("LocalDate implementations should be TimeZone agnostic, but HSQLDB implementation has a bug.")
|
@Disabled("LocalDate implementations should be TimeZone agnostic, but HSQLDB implementation has a bug.")
|
||||||
@Test
|
@Test
|
||||||
public void argLocalDateTimeZones() {
|
public void argLocalDateTimeZones() {
|
||||||
|
// <2000-01-01> but was: <1999-12-31>
|
||||||
// See bug: https://bugs.documentfoundation.org/show_bug.cgi?id=63566
|
// See bug: https://bugs.documentfoundation.org/show_bug.cgi?id=63566
|
||||||
super.argLocalDateTimeZones();
|
super.argLocalDateTimeZones();
|
||||||
}
|
}
|
||||||
|
@ -91,19 +94,35 @@ public class HsqldbTest extends CommonTest {
|
||||||
|
|
||||||
db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar,"
|
db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar,"
|
||||||
+ " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)")
|
+ " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||||
.argInteger(Integer.MAX_VALUE).argLong(Long.MAX_VALUE).argDouble((double) Float.MAX_VALUE)
|
.argInteger(Integer.MAX_VALUE)
|
||||||
.argDouble(Double.MAX_VALUE).argBigDecimal(new BigDecimal("123.456"))
|
.argLong(Long.MAX_VALUE)
|
||||||
.argString("hello").argString("Z").argClobString("hello again")
|
.argDouble((double) Float.MAX_VALUE)
|
||||||
.argBlobBytes(new byte[]{'1', '2'}).argBoolean(true)
|
.argDouble(Double.MAX_VALUE)
|
||||||
.argDateNowPerApp().argLocalDate(localDateNow).insert(1);
|
.argBigDecimal(new BigDecimal("123.456"))
|
||||||
|
.argString("hello")
|
||||||
|
.argString("Z")
|
||||||
|
.argClobString("hello again")
|
||||||
|
.argBlobBytes(new byte[]{'1', '2'})
|
||||||
|
.argBoolean(true)
|
||||||
|
.argDateNowPerApp()
|
||||||
|
.argLocalDate(localDateNow)
|
||||||
|
.insert(1);
|
||||||
|
|
||||||
db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar,"
|
db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar,"
|
||||||
+ " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)")
|
+ " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||||
.argInteger(Integer.MIN_VALUE).argLong(Long.MIN_VALUE).argDouble(0.000001d)
|
.argInteger(Integer.MIN_VALUE)
|
||||||
.argDouble(Double.MIN_VALUE).argBigDecimal(new BigDecimal("-123.456"))
|
.argLong(Long.MIN_VALUE)
|
||||||
.argString("goodbye").argString("A").argClobString("bye again")
|
.argDouble(0.000001d)
|
||||||
.argBlobBytes(new byte[]{'3', '4'}).argBoolean(false)
|
.argDouble(Double.MIN_VALUE)
|
||||||
.argDateNowPerApp().argLocalDate(localDateNow).insert(1);
|
.argBigDecimal(new BigDecimal("-123.456"))
|
||||||
|
.argString("goodbye")
|
||||||
|
.argString("A")
|
||||||
|
.argClobString("bye again")
|
||||||
|
.argBlobBytes(new byte[]{'3', '4'})
|
||||||
|
.argBoolean(false)
|
||||||
|
.argDateNowPerApp()
|
||||||
|
.argLocalDate(localDateNow)
|
||||||
|
.insert(1);
|
||||||
|
|
||||||
String expectedSchema = new Schema().addTable("dbtest2")
|
String expectedSchema = new Schema().addTable("dbtest2")
|
||||||
.addColumn("nbr_integer").asInteger().table()
|
.addColumn("nbr_integer").asInteger().table()
|
||||||
|
|
|
@ -20,9 +20,10 @@ public class SqlServerTest extends CommonTest {
|
||||||
String propertiesFile = System.getProperty("local.properties", "local.properties");
|
String propertiesFile = System.getProperty("local.properties", "local.properties");
|
||||||
Config config = ConfigFrom.firstOf()
|
Config config = ConfigFrom.firstOf()
|
||||||
.systemProperties()
|
.systemProperties()
|
||||||
.propertyFile(propertiesFile)
|
.properties(propertiesFile)
|
||||||
.excludePrefix("database.")
|
.excludePrefix("database.")
|
||||||
.removePrefix("sqlserver.").get();
|
.removePrefix("sqlserver.")
|
||||||
|
.get();
|
||||||
return DatabaseProvider.builder(config)
|
return DatabaseProvider.builder(config)
|
||||||
.withSqlParameterLogging()
|
.withSqlParameterLogging()
|
||||||
.withSqlInExceptionMessages()
|
.withSqlInExceptionMessages()
|
||||||
|
|
9
jdbc-query/src/test/resources/logging.properties
Normal file
9
jdbc-query/src/test/resources/logging.properties
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
|
||||||
|
.level=ALL
|
||||||
|
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] %5$s %6$s%n
|
||||||
|
java.util.logging.ConsoleHandler.level=ALL
|
||||||
|
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
||||||
|
java.util.logging.FileHandler.level=ALL
|
||||||
|
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
|
||||||
|
java.util.logging.FileHandler.pattern=build/marc.log
|
||||||
|
jdk.event.security.level=INFO
|
|
@ -0,0 +1,3 @@
|
||||||
|
database.url=jdbc:hsqldb:file:build/hsqldb;shutdown=true
|
||||||
|
database.user=SA
|
||||||
|
database.password=
|
|
@ -1,7 +1,7 @@
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
libs {
|
libs {
|
||||||
version('gradle', '7.5')
|
version('gradle', '7.5.1')
|
||||||
version('junit', '5.8.2')
|
version('junit', '5.8.2')
|
||||||
version('testcontainers', '1.17.3')
|
version('testcontainers', '1.17.3')
|
||||||
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
|
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
|
||||||
|
@ -10,6 +10,7 @@ dependencyResolutionManagement {
|
||||||
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
|
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
|
||||||
library('junit4', 'junit', 'junit').version('4.13.2')
|
library('junit4', 'junit', 'junit').version('4.13.2')
|
||||||
library('derby', 'org.apache.derby', 'derby').version('10.15.2.0')
|
library('derby', 'org.apache.derby', 'derby').version('10.15.2.0')
|
||||||
|
library('hsqldb', 'org.hsqldb', 'hsqldb').version('2.7.1')
|
||||||
library('h2', 'com.h2database', 'h2').version('1.4.200')
|
library('h2', 'com.h2database', 'h2').version('1.4.200')
|
||||||
library('testcontainers', 'org.testcontainers', 'testcontainers').versionRef('testcontainers')
|
library('testcontainers', 'org.testcontainers', 'testcontainers').versionRef('testcontainers')
|
||||||
library('testcontainers-junit-jupiter', 'org.testcontainers', 'junit-jupiter').versionRef('testcontainers')
|
library('testcontainers-junit-jupiter', 'org.testcontainers', 'junit-jupiter').versionRef('testcontainers')
|
||||||
|
|
Loading…
Reference in a new issue