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
|
||||
name = database
|
||||
version = 0.0.5
|
||||
version = 1.0.0
|
||||
|
||||
org.gradle.warning.mode = ALL
|
||||
|
|
|
@ -20,4 +20,5 @@ test {
|
|||
"${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
|
||||
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
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -407,15 +407,17 @@ public class Pool implements BagStateListener {
|
|||
|
||||
private void initializeDataSource()
|
||||
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
String jdbcUrl = config.getProperties().getProperty("url");
|
||||
String dsClassName = config.getDataSourceClassName();
|
||||
String url = config.getUrl();
|
||||
DataSource ds = config.getDataSource();
|
||||
if (ds == null) {
|
||||
String dsClassName = config.getDataSourceClassName();
|
||||
if (dsClassName != null) {
|
||||
Class<?> clazz = Class.forName(dsClassName, true, ClassLoader.getSystemClassLoader());
|
||||
ds = (DataSource) clazz.getDeclaredConstructor().newInstance();
|
||||
} else if (jdbcUrl != null) {
|
||||
ds = new DriverDataSource(jdbcUrl, config.getDriverClassName(), config.getProperties(), config.getUsername(), config.getPassword());
|
||||
} else if (url != null) {
|
||||
ds = new DriverDataSource(url, config.getDriverClassName(), config.getProperties(), config.getUsername(), config.getPassword());
|
||||
} else {
|
||||
throw new IllegalStateException("no dataSource configured?");
|
||||
}
|
||||
}
|
||||
setTargetFromProperties(ds, config.getProperties());
|
||||
|
|
|
@ -56,7 +56,7 @@ public class PoolConfig {
|
|||
|
||||
private String driverClassName;
|
||||
|
||||
private String jdbcUrl;
|
||||
private String url;
|
||||
|
||||
private String poolName;
|
||||
|
||||
|
@ -106,11 +106,18 @@ public class PoolConfig {
|
|||
this.idleTimeout = IDLE_TIMEOUT;
|
||||
this.initializationFailTimeout = -1;
|
||||
this.isAutoCommit = true;
|
||||
this.jdbcUrl = properties.getProperty("url");
|
||||
this.aliveBypassWindowMs = TimeUnit.MILLISECONDS.toMillis(500);
|
||||
this.housekeepingPeriodMs = TimeUnit.SECONDS.toMillis(30);
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getCatalog() {
|
||||
return catalog;
|
||||
}
|
||||
|
@ -588,7 +595,7 @@ public class PoolConfig {
|
|||
transactionIsolationName = getNullIfEmpty(transactionIsolationName);
|
||||
dataSourceClassName = getNullIfEmpty(dataSourceClassName);
|
||||
driverClassName = getNullIfEmpty(driverClassName);
|
||||
jdbcUrl = getNullIfEmpty(jdbcUrl);
|
||||
url = getNullIfEmpty(url);
|
||||
if (dataSource != null) {
|
||||
if (dataSourceClassName != null) {
|
||||
logger.log(Level.WARNING, "using dataSource and ignoring dataSourceClassName: " + poolName);
|
||||
|
@ -597,17 +604,17 @@ public class PoolConfig {
|
|||
if (driverClassName != null) {
|
||||
logger.log(Level.SEVERE, "cannot use driverClassName and dataSourceClassName together: " + poolName);
|
||||
throw new IllegalStateException("cannot use driverClassName and dataSourceClassName together.");
|
||||
} else if (jdbcUrl != null) {
|
||||
logger.log(Level.WARNING, "using dataSourceClassName and ignoring jdbcUrl: " + poolName);
|
||||
} else if (url != null) {
|
||||
logger.log(Level.WARNING, "using dataSourceClassName and ignoring url: " + poolName);
|
||||
}
|
||||
} else if (jdbcUrl != null) {
|
||||
} else if (url != null) {
|
||||
// ok
|
||||
} else if (driverClassName != null) {
|
||||
logger.log(Level.SEVERE, "jdbcUrl is required with driverClassName: " + poolName);
|
||||
throw new IllegalArgumentException("jdbcUrl is required with driverClassName.");
|
||||
logger.log(Level.SEVERE, "url is required with driverClassName: " + poolName);
|
||||
throw new IllegalArgumentException("url is required with driverClassName");
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "dataSource or dataSourceClassName or jdbcUrl is required: " + poolName);
|
||||
throw new IllegalArgumentException("dataSource or dataSourceClassName or jdbcUrl is required.");
|
||||
logger.log(Level.SEVERE, "dataSource or dataSourceClassName or url is required: " + poolName);
|
||||
throw new IllegalArgumentException("dataSource or dataSourceClassName or url is required");
|
||||
}
|
||||
validateNumerics();
|
||||
}
|
||||
|
|
|
@ -481,7 +481,7 @@ public class ProxyResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public long getLong(int columnIndex) throws SQLException {
|
||||
return delegate.getInt(columnIndex);
|
||||
return delegate.getLong(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -136,12 +136,21 @@ public class DriverDataSource implements DataSource {
|
|||
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) {
|
||||
DriverManager.setLoginTimeout(seconds);
|
||||
try {
|
||||
DriverManager.setLoginTimeout(seconds);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "setLoginTimeout failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() {
|
||||
return DriverManager.getLoginTimeout();
|
||||
try {
|
||||
return DriverManager.getLoginTimeout();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "getLoginTimeout failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,7 +63,6 @@ public class BagTest {
|
|||
bag.remove(inuse);
|
||||
bag.remove(inuse);
|
||||
assertTrue(bag.getLastMessage().contains("not borrowed or reserved"));
|
||||
bag.close();
|
||||
try {
|
||||
PoolEntry bagEntry = pool.newPoolEntry();
|
||||
bag.add(bagEntry);
|
||||
|
|
|
@ -29,12 +29,11 @@ public class ConnectionStateTest {
|
|||
config.setConnectionTestQuery("VALUES 1");
|
||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||
try (Connection connection = ds.getConnection()) {
|
||||
Connection unwrap = connection.unwrap(Connection.class);
|
||||
unwrap.setAutoCommit(false);
|
||||
connection.close();
|
||||
assertFalse(unwrap.getAutoCommit());
|
||||
}
|
||||
Connection connection = ds.getConnection();
|
||||
Connection unwrap = connection.unwrap(Connection.class);
|
||||
unwrap.setAutoCommit(false);
|
||||
connection.close();
|
||||
assertFalse(unwrap.getAutoCommit());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,12 +47,11 @@ public class ConnectionStateTest {
|
|||
config.setConnectionTestQuery("VALUES 1");
|
||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||
try (Connection connection = ds.getConnection()) {
|
||||
Connection unwrap = connection.unwrap(Connection.class);
|
||||
unwrap.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
|
||||
connection.close();
|
||||
assertEquals(Connection.TRANSACTION_READ_UNCOMMITTED, unwrap.getTransactionIsolation());
|
||||
}
|
||||
Connection connection = ds.getConnection();
|
||||
Connection unwrap = connection.unwrap(Connection.class);
|
||||
unwrap.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
|
||||
connection.close();
|
||||
assertEquals(Connection.TRANSACTION_READ_UNCOMMITTED, unwrap.getTransactionIsolation());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,12 +74,11 @@ public class ConnectionStateTest {
|
|||
config.setConnectionTestQuery("VALUES 1");
|
||||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||
try (Connection connection = ds.getConnection()) {
|
||||
Connection unwrap = connection.unwrap(Connection.class);
|
||||
connection.setReadOnly(true);
|
||||
connection.close();
|
||||
assertFalse(unwrap.isReadOnly());
|
||||
}
|
||||
Connection connection = ds.getConnection();
|
||||
Connection unwrap = connection.unwrap(Connection.class);
|
||||
connection.setReadOnly(true);
|
||||
connection.close();
|
||||
assertFalse(unwrap.isReadOnly());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -453,6 +453,7 @@ public class ConnectionTest {
|
|||
config.setDataSource(stubDataSource);
|
||||
try (PoolDataSource ds = new PoolDataSource(config);
|
||||
Connection ignored = ds.getConnection()) {
|
||||
assertNotNull(ignored);
|
||||
fail("Initialization should have failed");
|
||||
} catch (SQLTransientConnectionException e) {
|
||||
// passed
|
||||
|
@ -472,6 +473,7 @@ public class ConnectionTest {
|
|||
config.setDataSource(stubDataSource);
|
||||
try (PoolDataSource ds = new PoolDataSource(config)) {
|
||||
try (Connection ignored = ds.getConnection()) {
|
||||
assertNotNull(ignored);
|
||||
stubDataSource.setErrorOnConnection(true);
|
||||
fail("SQLException should occur!");
|
||||
} catch (Exception e) {
|
||||
|
@ -520,6 +522,7 @@ public class ConnectionTest {
|
|||
config.setDataSourceClassName("org.xbib.io.pool.jdbc.mock.StubDataSource");
|
||||
try (PoolDataSource ds = new PoolDataSource(config);
|
||||
Connection ignored = ds.getConnection()) {
|
||||
assertNotNull(ignored);
|
||||
// passed
|
||||
} catch (SQLTransientConnectionException sqle) {
|
||||
fail("Failed to obtain connection");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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.assertTrue;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -38,6 +39,7 @@ public class JdbcDriverTest {
|
|||
assertNotNull(unwrap);
|
||||
try (Connection connection = ds.getConnection()) {
|
||||
// test that getConnection() succeeds
|
||||
assertFalse(connection.isClosed());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
dependencies {
|
||||
api project(":jdbc-connection-pool")
|
||||
testImplementation libs.derby
|
||||
testImplementation libs.hsqldb
|
||||
testImplementation libs.testcontainers
|
||||
testImplementation libs.testcontainers.junit.jupiter
|
||||
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 {
|
||||
uses Flavor;
|
||||
requires transitive org.xbib.jdbc.connection.pool;
|
||||
exports org.xbib.jdbc.query;
|
||||
provides Flavor with Derby, Hsql, Oracle, Postgresql, SqlServer;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.xbib.jdbc.query;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
|
@ -47,7 +47,7 @@ public interface ConfigFrom extends Supplier<Config> {
|
|||
* defaultPropertyFiles("properties", "conf/app.properties", "local.properties", "sample.properties")
|
||||
* </pre>
|
||||
*/
|
||||
ConfigFrom defaultPropertyFiles();
|
||||
ConfigFrom defaultProperties();
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* </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.
|
||||
|
@ -67,15 +67,15 @@ public interface ConfigFrom extends Supplier<Config> {
|
|||
* .split(File.pathSeparator));
|
||||
* </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);
|
||||
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
package org.xbib.jdbc.query;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -20,6 +26,10 @@ import java.util.regex.Pattern;
|
|||
*/
|
||||
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<>();
|
||||
|
||||
public ConfigFromImpl() {
|
||||
|
@ -72,64 +82,59 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom defaultPropertyFiles() {
|
||||
return defaultPropertyFiles("properties", "conf/app.properties", "local.properties", "sample.properties");
|
||||
public ConfigFrom defaultProperties() {
|
||||
return defaultProperties("properties", "conf/app.properties", "local.properties", "sample.properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom defaultPropertyFiles(String systemPropertyKey, String... filenames) {
|
||||
return defaultPropertyFiles(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames);
|
||||
public ConfigFrom defaultProperties(String systemPropertyKey, String... filenames) {
|
||||
return defaultProperties(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom defaultPropertyFiles(String systemPropertyKey, CharsetDecoder decoder, String... filenames) {
|
||||
String properties = System.getProperty(systemPropertyKey, String.join(File.pathSeparator, filenames));
|
||||
return propertyFile(Charset.defaultCharset().newDecoder(), properties.split(File.pathSeparator));
|
||||
public ConfigFrom defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames) {
|
||||
String properties = System.getProperty(systemPropertyKey, String.join(SEPARATOR, filenames));
|
||||
return properties(Charset.defaultCharset().newDecoder(), properties.split(SEPARATOR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom propertyFile(String... filenames) {
|
||||
return propertyFile(Charset.defaultCharset().newDecoder(), filenames);
|
||||
public ConfigFrom properties(String... filenames) {
|
||||
return properties(Charset.defaultCharset().newDecoder(), filenames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom propertyFile(CharsetDecoder decoder, String... filenames) {
|
||||
public ConfigFrom properties(CharsetDecoder decoder, String... filenames) {
|
||||
for (String filename : filenames) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom propertyFile(File... files) {
|
||||
return propertyFile(Charset.defaultCharset().newDecoder(), files);
|
||||
public ConfigFrom properties(InputStream... inputStreams) {
|
||||
return properties(Charset.defaultCharset().newDecoder(), inputStreams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom propertyFile(CharsetDecoder decoder, File... files) {
|
||||
for (File file : files) {
|
||||
if (file != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
try (
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
InputStreamReader reader = new InputStreamReader(fis, decoder)
|
||||
) {
|
||||
properties.load(reader);
|
||||
}
|
||||
searchPath.add(new ConfigImpl(properties::getProperty, "propertyFile(" + file.getAbsolutePath() + ")"));
|
||||
} catch (Exception e) {
|
||||
// Put a "fake" provider in so we can see it failed
|
||||
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());
|
||||
}
|
||||
public ConfigFrom properties(CharsetDecoder decoder, InputStream... inputStreams) {
|
||||
for (InputStream inputStream : inputStreams) {
|
||||
Properties properties = new Properties();
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream, decoder)) {
|
||||
properties.load(reader);
|
||||
searchPath.add(new ConfigImpl(properties::getProperty, "propertyFile(" + inputStream + ")"));
|
||||
} catch (Exception e) {
|
||||
custom(k -> null, "Ignored: propertyFile(" + inputStream + ") " + e.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
|
|
@ -170,14 +170,12 @@ public class ConfigImpl implements Config {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
|
||||
BigDecimal value = getBigDecimal(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimalOrThrow(String key) {
|
||||
return nonnull(key, getBigDecimal(key));
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package org.xbib.jdbc.query;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
@ -216,4 +218,27 @@ public interface Database extends Supplier<Database> {
|
|||
* with millisToWarn=10000 and millisToError=30000.
|
||||
*/
|
||||
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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
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 {
|
||||
|
||||
private static final Logger log = Logger.getLogger(Database.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(Database.class.getName());
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
|
@ -88,7 +91,7 @@ public class DatabaseImpl implements Database {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Long nextSequenceValue(/*@Untainted*/ String sequenceName) {
|
||||
public Long nextSequenceValue(String sequenceName) {
|
||||
return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull();
|
||||
}
|
||||
|
||||
|
@ -97,9 +100,10 @@ public class DatabaseImpl implements Database {
|
|||
return options.currentDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitNow() {
|
||||
if (options.ignoreTransactionControl()) {
|
||||
log.fine("Ignoring call to commitNow()");
|
||||
logger.fine("Ignoring call to commitNow()");
|
||||
return;
|
||||
}
|
||||
if (!options.allowTransactionControl()) {
|
||||
|
@ -112,9 +116,10 @@ public class DatabaseImpl implements Database {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollbackNow() {
|
||||
if (options.ignoreTransactionControl()) {
|
||||
log.fine("Ignoring call to rollbackNow()");
|
||||
logger.fine("Ignoring call to rollbackNow()");
|
||||
return;
|
||||
}
|
||||
if (!options.allowTransactionControl()) {
|
||||
|
@ -153,53 +158,22 @@ public class DatabaseImpl implements Database {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void dropSequenceQuietly(/*@Untainted*/ String sequenceName) {
|
||||
public void dropSequenceQuietly(String sequenceName) {
|
||||
ddl(flavor().sequenceDrop(sequenceName)).executeQuietly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropTableQuietly(/*@Untainted*/ String tableName) {
|
||||
if (flavor() == Flavor.postgresql || flavor() == Flavor.hsqldb) {
|
||||
ddl("drop table if exists " + tableName).executeQuietly();
|
||||
} else {
|
||||
ddl("drop table " + tableName).executeQuietly();
|
||||
}
|
||||
public void dropTableQuietly(String tableName) {
|
||||
ddl(flavor().tableDrop(tableName)).executeQuietly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tableExists(String tableName) throws DatabaseException {
|
||||
|
||||
String schemaName = null;
|
||||
Method getSchema = null;
|
||||
|
||||
try {
|
||||
// Use reflections to see if connection.getSchema API exists. It should exist for any JDBC7 or later implementation
|
||||
// We still support Oracle 11 with odbc6, however, so we can't assume it's there.
|
||||
getSchema = connection.getClass().getDeclaredMethod("getSchema");
|
||||
} catch (NoSuchMethodException noMethodExc) {
|
||||
// Expected if method does not exist - just let it go
|
||||
return tableExists(tableName, connection.getSchema());
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Unable to getSchema()", e);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -230,24 +204,24 @@ public class DatabaseImpl implements Database {
|
|||
try {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
String normalizedTable = normalizeTableName(tableName);
|
||||
ResultSet rs = metaData.getColumns(null, null, normalizedTable, "%");
|
||||
ResultSetMetaData rsmd = rs.getMetaData();
|
||||
ResultSet resultSet = metaData.getColumns(null, null, normalizedTable, "%");
|
||||
ResultSetMetaData rsmd = resultSet.getMetaData();
|
||||
int cols = rsmd.getColumnCount();
|
||||
while (rs.next()) {
|
||||
while (resultSet.next()) {
|
||||
String name = "";
|
||||
int size = 0;
|
||||
for (int i = 1; i <= cols; i++) {
|
||||
String label = rsmd.getColumnName(i);
|
||||
if ("COLUMN_NAME".equalsIgnoreCase(label)) {
|
||||
name = rs.getString(i);
|
||||
name = resultSet.getString(i);
|
||||
}
|
||||
if ("COLUMN_SIZE".equalsIgnoreCase(label)) {
|
||||
size = rs.getBigDecimal(i).intValue();
|
||||
size = resultSet.getBigDecimal(i).intValue();
|
||||
}
|
||||
}
|
||||
map.put(name, size);
|
||||
}
|
||||
rs.close();
|
||||
resultSet.close();
|
||||
} catch (SQLException 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) {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
// If user gave us a quoted string, leave it alone for look up
|
||||
if (tableName.length() > 2) {
|
||||
if (tableName.startsWith("\"") && tableName.endsWith("\"")) {
|
||||
|
@ -268,11 +241,9 @@ public class DatabaseImpl implements Database {
|
|||
return tableName.substring(1, tableName.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (flavor().isNormalizedUpperCase()) {
|
||||
return tableName.toUpperCase();
|
||||
}
|
||||
|
||||
return tableName.toLowerCase();
|
||||
}
|
||||
|
||||
|
@ -288,18 +259,18 @@ public class DatabaseImpl implements Database {
|
|||
Duration duration = Duration.between(appDate, dbDate).abs();
|
||||
if (duration.getSeconds() > 3600) {
|
||||
throw new DatabaseException("App and db time are over an hour apart (check your timezones) app: "
|
||||
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
|
||||
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
|
||||
+ appDate + " db: "
|
||||
+ dbDate);
|
||||
}
|
||||
if (duration.getSeconds() * 1000 > millisToError) {
|
||||
throw new DatabaseException("App and db time over " + millisToError + " millis apart (check your clocks) app: "
|
||||
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
|
||||
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
|
||||
+ appDate + " db: "
|
||||
+ dbDate);
|
||||
}
|
||||
if (duration.getSeconds() * 1000 > millisToWarn) {
|
||||
log.warning("App and db time are over " + millisToWarn + " millis apart (check your clocks) app: "
|
||||
+ DateTimeFormatter.ISO_INSTANT.format(appDate) + " db: "
|
||||
+ DateTimeFormatter.ISO_INSTANT.format(dbDate));
|
||||
logger.warning("App and db time are over " + millisToWarn + " millis apart (check your clocks) app: "
|
||||
+ appDate + " db: "
|
||||
+ dbDate);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
@ -309,4 +280,83 @@ public class DatabaseImpl implements Database {
|
|||
public void assertTimeSynchronized() {
|
||||
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.driver.class The driver to initialize with Class.forName(). This will
|
||||
* be guessed from the database.url if not provided.
|
||||
* database.flavor One of the enumerated values in {@link Flavor}. If this
|
||||
* is not provided the flavor will be guessed based on the
|
||||
* database.flavor A {@link Flavor}. If this is not provided the flavor will be guessed based on the
|
||||
* value for database.url, if possible.
|
||||
* </pre>
|
||||
*
|
||||
|
@ -167,10 +166,12 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
try {
|
||||
DriverManager.getDriver(url);
|
||||
} catch (SQLException e) {
|
||||
try {
|
||||
Class.forName(Flavor.driverForJdbcUrl(url));
|
||||
} catch (ClassNotFoundException e1) {
|
||||
throw new DatabaseException("couldn't locate JDBC driver - try setting -Djdbc.drivers=some.Driver", e1);
|
||||
if (flavor.driverClass() != null) {
|
||||
try {
|
||||
Class.forName(flavor.driverClass());
|
||||
} catch (ClassNotFoundException e1) {
|
||||
throw new DatabaseException("couldn't locate JDBC driver", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new BuilderImpl(null, () -> {
|
||||
|
@ -531,7 +532,6 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
if (propertyPrefix == null) {
|
||||
propertyPrefix = "";
|
||||
}
|
||||
|
||||
String driver;
|
||||
String flavorStr;
|
||||
String url;
|
||||
|
@ -561,11 +561,9 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
user = properties.getProperty(propertyPrefix + "database.user");
|
||||
password = properties.getProperty(propertyPrefix + "database.password");
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
throw new DatabaseException("You must use -D" + propertyPrefix + "database.url=...");
|
||||
}
|
||||
|
||||
if (user != null && password == null) {
|
||||
System.out.println("Enter database password for user " + user + ":");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Flavor flavor;
|
||||
if (flavorStr != null) {
|
||||
flavor = Flavor.valueOf(flavorStr);
|
||||
} else {
|
||||
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) {
|
||||
try {
|
||||
Class.forName(driver).getDeclaredConstructor().newInstance();
|
||||
} 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) {
|
||||
|
@ -616,7 +608,12 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
}
|
||||
PoolConfig poolConfig = new PoolConfig();
|
||||
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.setPassword(config.getString("database.password"));
|
||||
poolConfig.setMaximumPoolSize(config.getInteger("database.pool.size", 8));
|
||||
|
@ -774,7 +771,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
try {
|
||||
// JDBC specifies that autoCommit is the default for all new connections.
|
||||
// Don't try to be clever about clearing it conditionally.
|
||||
if (!options.flavor().autoCommitOnly()) {
|
||||
if (!options.flavor().isAutoCommitOnly()) {
|
||||
connection.setAutoCommit(false);
|
||||
metric.checkpoint("setAutoCommit");
|
||||
}
|
||||
|
@ -868,7 +865,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
}
|
||||
if (connection != null) {
|
||||
try {
|
||||
if (!options.flavor().autoCommitOnly()) {
|
||||
if (!options.flavor().isAutoCommitOnly()) {
|
||||
connection.commit();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -884,7 +881,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
}
|
||||
if (connection != null) {
|
||||
try {
|
||||
if (!options.flavor().autoCommitOnly()) {
|
||||
if (!options.flavor().isAutoCommitOnly()) {
|
||||
connection.commit();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -902,7 +899,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
|
||||
if (connection != null) {
|
||||
try {
|
||||
if (!options.flavor().autoCommitOnly()) {
|
||||
if (!options.flavor().isAutoCommitOnly()) {
|
||||
connection.rollback();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -919,7 +916,7 @@ public final class DatabaseProvider implements Supplier<Database> {
|
|||
|
||||
if (connection != null) {
|
||||
try {
|
||||
if (!options.flavor().autoCommitOnly()) {
|
||||
if (!options.flavor().isAutoCommitOnly()) {
|
||||
connection.rollback();
|
||||
}
|
||||
} 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
|
||||
* 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();
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.xbib.jdbc.query;
|
||||
|
||||
import org.xbib.jdbc.query.flavor.Postgresql;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Calendar;
|
||||
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.
|
||||
*/
|
||||
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.Index;
|
||||
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.SQLException;
|
||||
|
@ -274,15 +276,13 @@ public class Schema {
|
|||
sql.append(check.expression);
|
||||
sql.append(")");
|
||||
}
|
||||
|
||||
sql.append("\n)");
|
||||
if (table.customClauses.containsKey(flavor)) {
|
||||
sql.append(" ").append(table.customClauses.get(flavor));
|
||||
}
|
||||
executeOrPrint(sql, db, script);
|
||||
sql = new Sql();
|
||||
|
||||
if (flavor == Flavor.oracle || flavor == Flavor.postgresql) {
|
||||
if (flavor instanceof Oracle || flavor instanceof Postgresql) {
|
||||
if (table.comment != null) {
|
||||
sql.append("comment on table ");
|
||||
sql.append(table.name);
|
||||
|
@ -292,7 +292,6 @@ public class Schema {
|
|||
executeOrPrint(sql, db, script);
|
||||
sql = new Sql();
|
||||
}
|
||||
|
||||
for (Column c : table.columns) {
|
||||
if (c.comment != null) {
|
||||
sql.append("comment on column ");
|
||||
|
@ -308,7 +307,6 @@ public class Schema {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Table table : tables) {
|
||||
for (ForeignKey fk : table.foreignKeys) {
|
||||
Sql sql = new Sql();
|
||||
|
@ -329,7 +327,6 @@ public class Schema {
|
|||
executeOrPrint(sql, db, script);
|
||||
}
|
||||
}
|
||||
|
||||
for (Table table : tables) {
|
||||
for (Index index : table.indexes) {
|
||||
Sql sql = new Sql();
|
||||
|
|
|
@ -22,18 +22,17 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
public Sql() {
|
||||
}
|
||||
|
||||
public Sql(/*@Untainted*/ String sql) {
|
||||
public Sql(String 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));
|
||||
}
|
||||
|
||||
public static Sql insert(/*@Untainted*/ String table, List<SqlArgs> args) {
|
||||
public static Sql insert(String table, List<SqlArgs> args) {
|
||||
Sql sql = null;
|
||||
List<String> expectedColumns = null;
|
||||
|
||||
for (SqlArgs arg : args) {
|
||||
if (arg.positionalCount() > 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
public Sql append(/*@Untainted*/ String sql) {
|
||||
public Sql append(String sql) {
|
||||
this.sql.append(sql);
|
||||
return this;
|
||||
}
|
||||
|
@ -133,12 +132,12 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
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);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Sql insert(int offset, /*@Untainted*/ String str) {
|
||||
public Sql insert(int offset, String str) {
|
||||
this.sql.insert(offset, str);
|
||||
return this;
|
||||
}
|
||||
|
@ -189,7 +188,7 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
* <p>
|
||||
* 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);
|
||||
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,
|
||||
* and notes that the list is not empty.
|
||||
*/
|
||||
public Sql listSeparator(/*@Untainted*/ String sql) {
|
||||
public Sql listSeparator(String sql) {
|
||||
if (listFirstItem.peek()) {
|
||||
listFirstItem.pop();
|
||||
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();
|
||||
return append(sql);
|
||||
}
|
||||
|
||||
/*@Untainted*/
|
||||
public String sql() {
|
||||
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.
|
||||
*/
|
||||
/*@Untainted*/
|
||||
public String toString() {
|
||||
return sql();
|
||||
}
|
||||
|
@ -232,80 +229,67 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argBoolean( String argName, Boolean arg) {
|
||||
sqlArgs.argBoolean(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argInteger(Integer arg) {
|
||||
sqlArgs.argInteger(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argInteger( String argName, Integer arg) {
|
||||
sqlArgs.argInteger(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argLong(Long arg) {
|
||||
sqlArgs.argLong(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argLong(String argName, Long arg) {
|
||||
sqlArgs.argLong(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argFloat(Float arg) {
|
||||
sqlArgs.argFloat(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argFloat( String argName, Float arg) {
|
||||
sqlArgs.argFloat(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDouble(Double arg) {
|
||||
sqlArgs.argDouble(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDouble( String argName, Double arg) {
|
||||
sqlArgs.argDouble(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argBigDecimal(BigDecimal arg) {
|
||||
sqlArgs.argBigDecimal(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argBigDecimal( String argName, BigDecimal arg) {
|
||||
sqlArgs.argBigDecimal(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argString(String arg) {
|
||||
sqlArgs.argString(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argString( String argName, String arg) {
|
||||
public Sql argString(String argName, String arg) {
|
||||
sqlArgs.argString(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
@ -315,32 +299,27 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDate( String argName, LocalDateTime arg) {
|
||||
public Sql argDate(String argName, LocalDateTime arg) {
|
||||
sqlArgs.argDate(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDateNowPerApp() {
|
||||
sqlArgs.argDateNowPerApp();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDateNowPerApp( String argName) {
|
||||
public Sql argDateNowPerApp(String argName) {
|
||||
sqlArgs.argDateNowPerApp(argName);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDateNowPerDb() {
|
||||
sqlArgs.argDateNowPerDb();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argDateNowPerDb( String argName) {
|
||||
public Sql argDateNowPerDb(String argName) {
|
||||
sqlArgs.argDateNowPerDb(argName);
|
||||
return this;
|
||||
}
|
||||
|
@ -352,36 +331,31 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
}
|
||||
|
||||
|
||||
public Sql argBlobBytes( String argName, byte[] arg) {
|
||||
public Sql argBlobBytes(String argName, byte[] arg) {
|
||||
sqlArgs.argBlobBytes(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argBlobInputStream(InputStream arg) {
|
||||
sqlArgs.argBlobInputStream(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argBlobInputStream( String argName, InputStream arg) {
|
||||
sqlArgs.argBlobInputStream(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argClobString(String arg) {
|
||||
sqlArgs.argClobString(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argClobString( String argName, String arg) {
|
||||
sqlArgs.argClobString(argName, arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Sql argClobReader(Reader arg) {
|
||||
sqlArgs.argClobReader(arg);
|
||||
return this;
|
||||
|
@ -419,7 +393,6 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
|
|||
if (batched != null) {
|
||||
throw new DatabaseException("Batch not supported for 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,
|
||||
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
|
||||
* and type inferred from the metadata
|
||||
*/
|
||||
|
||||
public static SqlArgs readRow(Row r) {
|
||||
return new Builder(r).read(r);
|
||||
}
|
||||
|
@ -119,44 +118,37 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argDouble( String argName, Double arg) {
|
||||
invocations.add(new Invocation(ColumnType.Double, argName, arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argBigDecimal(BigDecimal arg) {
|
||||
invocations.add(new Invocation(ColumnType.BigDecimal, null, arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argBigDecimal( String argName, BigDecimal arg) {
|
||||
invocations.add(new Invocation(ColumnType.BigDecimal, argName, arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argString(String arg) {
|
||||
invocations.add(new Invocation(ColumnType.String, null, arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argString( String argName, String arg) {
|
||||
invocations.add(new Invocation(ColumnType.String, argName, arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argDate(LocalDateTime arg) {
|
||||
// date argument with a time on it
|
||||
invocations.add(new Invocation(ColumnType.Date, null, arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SqlArgs argDate( String argName, LocalDateTime arg) {
|
||||
// date argument with a time on it
|
||||
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) {
|
||||
SqlArgs args = new SqlArgs();
|
||||
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
switch (types[i]) {
|
||||
case Types.SMALLINT:
|
||||
|
@ -705,22 +695,24 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
|
|||
args.argInteger(names[i], r.getIntegerOrNull());
|
||||
break;
|
||||
case Types.BIGINT:
|
||||
args.argLong(names[i], r.getLongOrNull());
|
||||
if (precision[i] <= 64 && scale[i] == 0) {
|
||||
args.argLong(names[i], r.getLongOrNull());
|
||||
} else {
|
||||
args.argBigDecimal(names[i], r.getBigDecimalOrNull());
|
||||
}
|
||||
break;
|
||||
case Types.REAL:
|
||||
case 100: // Oracle proprietary it seems
|
||||
case 100: // Oracle binary float
|
||||
args.argFloat(names[i], r.getFloatOrNull());
|
||||
break;
|
||||
case Types.DOUBLE:
|
||||
case 101: // Oracle proprietary it seems
|
||||
case 101: // Oracle binary double
|
||||
args.argDouble(names[i], r.getDoubleOrNull());
|
||||
break;
|
||||
case Types.NUMERIC:
|
||||
if (precision[i] == 10 && scale[i] == 0) {
|
||||
// Oracle reports integer as numeric
|
||||
if (precision[i] <= 10 && scale[i] == 0) {
|
||||
args.argInteger(names[i], r.getIntegerOrNull());
|
||||
} else if (precision[i] == 19 && scale[i] == 0) {
|
||||
// Oracle reports long as numeric
|
||||
} else if (precision[i] <= 19 && scale[i] == 0) {
|
||||
args.argLong(names[i], r.getLongOrNull());
|
||||
} else {
|
||||
args.argBigDecimal(names[i], r.getBigDecimalOrNull());
|
||||
|
|
|
@ -288,7 +288,6 @@ public class SqlInsertImpl implements SqlInsert {
|
|||
if (!hasPk()) {
|
||||
throw new DatabaseException("Call argPkSeq() before insertReturningPkSeq()");
|
||||
}
|
||||
|
||||
if (options.flavor().supportsInsertReturning()) {
|
||||
return updateInternal(1, primaryKeyColumnName);
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
|
@ -16,28 +21,28 @@ public class When {
|
|||
}
|
||||
|
||||
public When oracle(String sql) {
|
||||
if (actualFlavor == Flavor.oracle) {
|
||||
if (actualFlavor instanceof Oracle) {
|
||||
chosen = sql;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public When derby(String sql) {
|
||||
if (actualFlavor == Flavor.derby) {
|
||||
if (actualFlavor instanceof Derby) {
|
||||
chosen = sql;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public When postgres(String sql) {
|
||||
if (actualFlavor == Flavor.postgresql) {
|
||||
if (actualFlavor instanceof Postgresql) {
|
||||
chosen = sql;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public When sqlserver(String sql) {
|
||||
if (actualFlavor == Flavor.sqlserver) {
|
||||
if (actualFlavor instanceof SqlServer) {
|
||||
chosen = sql;
|
||||
}
|
||||
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.sql.ResultSetMetaData;
|
||||
import java.sql.Timestamp;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
|
@ -54,7 +52,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
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 {
|
||||
|
||||
|
@ -62,10 +60,6 @@ public abstract class CommonTest {
|
|||
|
||||
final static String TEST_TABLE_NAME = "dbtest";
|
||||
|
||||
/**
|
||||
* Enable retrying failed tests if they have the @Retry annotation.
|
||||
*/
|
||||
|
||||
protected DatabaseProvider dbp;
|
||||
|
||||
protected Database db;
|
||||
|
|
|
@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test;
|
|||
import org.xbib.jdbc.query.Config;
|
||||
import org.xbib.jdbc.query.ConfigFrom;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Properties;
|
||||
|
@ -54,7 +53,7 @@ public class ConfigTest {
|
|||
properties.store(new FileWriter(filename2), null);
|
||||
|
||||
// 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(-2), config.getInteger("foo2"));
|
||||
|
@ -62,12 +61,12 @@ public class ConfigTest {
|
|||
assertEquals(5, config.getInteger("unknown", 5));
|
||||
|
||||
// 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("foo2"));
|
||||
|
||||
// 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(-2), config.getInteger("foo2"));
|
||||
|
@ -75,7 +74,7 @@ public class ConfigTest {
|
|||
assertEquals(5, config.getInteger("unknown", 5));
|
||||
|
||||
// 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("foo2"));
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.logging.Logger;
|
|||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* Exercise Database functionality with a real database (Derby).
|
||||
*/
|
||||
|
@ -79,46 +78,6 @@ public class DerbyTest extends CommonTest {
|
|||
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")
|
||||
@Test
|
||||
|
@ -143,4 +102,41 @@ public class DerbyTest extends CommonTest {
|
|||
public void 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;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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.
|
||||
*/
|
||||
@Disabled
|
||||
public class HsqldbTest extends CommonTest {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HsqldbTest.class.getName());
|
||||
|
||||
@Override
|
||||
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
|
||||
String propertiesFile = System.getProperty("local.properties", "local.properties");
|
||||
String propertiesFile = "hsqldb.properties";
|
||||
Config config = ConfigFrom.firstOf()
|
||||
.systemProperties()
|
||||
.propertyFile(propertiesFile)
|
||||
.excludePrefix("database.")
|
||||
.removePrefix("hsqldb.").get();
|
||||
logger.log(Level.INFO, "config = " + config);
|
||||
.properties(getClass().getResourceAsStream(propertiesFile))
|
||||
.get();
|
||||
return DatabaseProvider.builder(config)
|
||||
.withSqlParameterLogging()
|
||||
.withSqlInExceptionMessages()
|
||||
.withOptions(options).build();
|
||||
.withOptions(options)
|
||||
.build();
|
||||
}
|
||||
|
||||
@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.")
|
||||
@Test
|
||||
public void argLocalDateTimeZones() {
|
||||
// <2000-01-01> but was: <1999-12-31>
|
||||
// See bug: https://bugs.documentfoundation.org/show_bug.cgi?id=63566
|
||||
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,"
|
||||
+ " 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)
|
||||
.argDouble(Double.MAX_VALUE).argBigDecimal(new BigDecimal("123.456"))
|
||||
.argString("hello").argString("Z").argClobString("hello again")
|
||||
.argBlobBytes(new byte[]{'1', '2'}).argBoolean(true)
|
||||
.argDateNowPerApp().argLocalDate(localDateNow).insert(1);
|
||||
.argInteger(Integer.MAX_VALUE)
|
||||
.argLong(Long.MAX_VALUE)
|
||||
.argDouble((double) Float.MAX_VALUE)
|
||||
.argDouble(Double.MAX_VALUE)
|
||||
.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,"
|
||||
+ " str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date) values (?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.argInteger(Integer.MIN_VALUE).argLong(Long.MIN_VALUE).argDouble(0.000001d)
|
||||
.argDouble(Double.MIN_VALUE).argBigDecimal(new BigDecimal("-123.456"))
|
||||
.argString("goodbye").argString("A").argClobString("bye again")
|
||||
.argBlobBytes(new byte[]{'3', '4'}).argBoolean(false)
|
||||
.argDateNowPerApp().argLocalDate(localDateNow).insert(1);
|
||||
.argInteger(Integer.MIN_VALUE)
|
||||
.argLong(Long.MIN_VALUE)
|
||||
.argDouble(0.000001d)
|
||||
.argDouble(Double.MIN_VALUE)
|
||||
.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")
|
||||
.addColumn("nbr_integer").asInteger().table()
|
||||
|
|
|
@ -20,9 +20,10 @@ public class SqlServerTest extends CommonTest {
|
|||
String propertiesFile = System.getProperty("local.properties", "local.properties");
|
||||
Config config = ConfigFrom.firstOf()
|
||||
.systemProperties()
|
||||
.propertyFile(propertiesFile)
|
||||
.properties(propertiesFile)
|
||||
.excludePrefix("database.")
|
||||
.removePrefix("sqlserver.").get();
|
||||
.removePrefix("sqlserver.")
|
||||
.get();
|
||||
return DatabaseProvider.builder(config)
|
||||
.withSqlParameterLogging()
|
||||
.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 {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
version('gradle', '7.5')
|
||||
version('gradle', '7.5.1')
|
||||
version('junit', '5.8.2')
|
||||
version('testcontainers', '1.17.3')
|
||||
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('junit4', 'junit', 'junit').version('4.13.2')
|
||||
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('testcontainers', 'org.testcontainers', 'testcontainers').versionRef('testcontainers')
|
||||
library('testcontainers-junit-jupiter', 'org.testcontainers', 'junit-jupiter').versionRef('testcontainers')
|
||||
|
|
Loading…
Reference in a new issue