move java.util.Date to java.time

main
Jörg Prante 2 years ago
parent 1047be8456
commit 625c8999a3

@ -2,7 +2,6 @@ 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;
@ -90,11 +89,6 @@ public interface Database extends Supplier<Database> {
*/
Long nextSequenceValue( String sequenceName);
/**
* Get the value that would be used if you specify an argNowPerApp() parameter.
*/
LocalDateTime nowPerApp();
/**
* Cause the underlying connection to commit its transaction immediately. This
* must be explicitly enabled (see {@link Options},

@ -10,7 +10,6 @@ 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;
@ -95,11 +94,6 @@ public class DatabaseImpl implements Database {
return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull();
}
@Override
public LocalDateTime nowPerApp() {
return options.currentDate();
}
@Override
public void commitNow() {
if (options.ignoreTransactionControl()) {
@ -250,8 +244,9 @@ public class DatabaseImpl implements Database {
@Override
public void assertTimeSynchronized(long millisToWarn, long millisToError) {
toSelect("select ?" + flavor().fromAny())
.argDateNowPerDb().queryFirstOrNull(r -> {
LocalDateTime appDate = nowPerApp();
.argLocalDateTimeNowPerDb()
.queryFirstOrNull(r -> {
LocalDateTime appDate = LocalDateTime.now();
LocalDateTime dbDate = r.getLocalDateTimeOrNull();
if (dbDate == null) {
throw new DatabaseException("Expecting a date in the result");

@ -1106,7 +1106,7 @@ public final class DatabaseProvider implements Supplier<Database> {
public Builder withDatePerAppOnly() {
return new BuilderImpl(ds, connectionProvider, new OptionsOverride() {
@Override
public boolean useDatePerAppOnly() {
public boolean useLocalDateTimeOnly() {
return true;
}
}.withParent(this.options));

@ -30,13 +30,11 @@ public class DdlImpl implements Ddl {
private void updateInternal(boolean quiet) {
CallableStatement ps = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
boolean isSuccess = false;
String errorCode = null;
Exception logEx = null;
try {
ps = connection.prepareCall(sql);
metric.checkpoint("prep");
ps.execute();
metric.checkpoint("exec");

@ -1,8 +1,5 @@
package org.xbib.jdbc.query;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.ServiceLoader;
public interface Flavor {
@ -39,7 +36,7 @@ public interface Flavor {
String typeBlob();
String typeDate();
String typeLocalDateTime();
String typeLocalDate();
@ -72,17 +69,6 @@ public interface Flavor {
*/
String fromAny();
/**
* Return a SQL function representing the specified date. For example, in PostgreSQL this
* looks like "'1970-01-02 02:17:36.789000 GMT'::timestamp".
*/
String dateAsSqlFunction(Timestamp timestamp, Calendar calendar);
/**
* Return a SQL function representing the specified date without time.
*/
String localDateAsSqlFunction(Date date);
String sequenceOptions();
/**

@ -1,8 +1,5 @@
package org.xbib.jdbc.query;
import java.time.LocalDateTime;
import java.util.Calendar;
/**
* Control various optional behavior for the database interactions.
*/
@ -95,27 +92,10 @@ public interface Options {
Flavor flavor();
/**
* The value returned by this method will be used for argDateNowPerApp() calls. It
* may also be used for argDateNowPerDb() calls if you have enabled that.
*/
LocalDateTime currentDate();
/**
* Wherever argDateNowPerDb() is specified, use argDateNowPerApp() instead. This is
* useful for testing purposes as you can use OptionsOverride to provide your
* own system clock that will be used for time travel.
*/
boolean useDatePerAppOnly();
/**
* This calendar will be used for conversions when storing and retrieving timestamps
* from the database. By default this is the JVM default with TimeZone explicitly set
* to GMT (so timestamps will be stored in the database as GMT).
*
* <p>It is strongly recommended to always run your database in GMT timezone, and
* leave this set to the default.</p>
* This is useful for testing purposes as you can use OptionsOverride to provide your
* own clock that will be used.
*/
Calendar calendarForTimestamps();
boolean useLocalDateTimeOnly();
/**
* The maximum number of characters to print in debug SQL for a given String type

@ -1,10 +1,6 @@
package org.xbib.jdbc.query;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* Control various optional behavior for the database interactions.
@ -44,8 +40,7 @@ public class OptionsDefault implements Options {
@Override
public String generateErrorCode() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:H:m:s");
return sdf.format(new Date()) + "-" + Math.round(Math.random() * 1000000);
return LocalDateTime.now() + "-" + Math.round(Math.random() * 1000000);
}
@Override
@ -64,20 +59,10 @@ public class OptionsDefault implements Options {
}
@Override
public LocalDateTime currentDate() {
return LocalDateTime.now();
}
@Override
public boolean useDatePerAppOnly() {
public boolean useLocalDateTimeOnly() {
return false;
}
@Override
public Calendar calendarForTimestamps() {
return Calendar.getInstance(TimeZone.getDefault());
}
@Override
public int maxStringLengthParam() {
return 4000;

@ -2,9 +2,6 @@ package org.xbib.jdbc.query;
import org.xbib.jdbc.query.flavor.Postgresql;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
/**
* Base class for selectively overriding another Options object.
@ -90,18 +87,8 @@ public class OptionsOverride implements Options {
}
@Override
public LocalDateTime currentDate() {
return parent.currentDate();
}
@Override
public boolean useDatePerAppOnly() {
return parent.useDatePerAppOnly();
}
@Override
public Calendar calendarForTimestamps() {
return parent.calendarForTimestamps();
public boolean useLocalDateTimeOnly() {
return parent.useLocalDateTimeOnly();
}
@Override

@ -6,6 +6,7 @@ import java.math.BigDecimal;
import java.sql.ResultSetMetaData;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* Interface for reading results from a database query.
@ -228,173 +229,142 @@ public interface Row {
* representation. Some databases will pad the number out to "full precision". This
* method tries to reduce scale if there is zero padding to the right of the decimal.
*/
BigDecimal getBigDecimalOrNull();
BigDecimal getBigDecimalOrNull(int columnOneBased);
BigDecimal getBigDecimalOrNull(String columnName);
BigDecimal getBigDecimalOrZero();
BigDecimal getBigDecimalOrZero(int columnOneBased);
BigDecimal getBigDecimalOrZero(String columnName);
/**
* @return the value, or null if it is SQL null; never returns the empty string
*/
String getStringOrNull();
/**
* @return the value, or null if it is SQL null; never returns the empty string
*/
String getStringOrNull(int columnOneBased);
/**
* @return the value, or null if it is SQL null; never returns the empty string
*/
String getStringOrNull(String columnName);
/**
* @return the value, or the empty string if it is SQL null; never returns null
*/
String getStringOrEmpty();
/**
* @return the value, or the empty string if it is SQL null; never returns null
*/
String getStringOrEmpty(int columnOneBased);
/**
* @return the value, or the empty string if it is SQL null; never returns null
*/
String getStringOrEmpty(String columnName);
/**
* @return the value, or null if it is SQL null; never returns the empty string
*/
String getClobStringOrNull();
/**
* @return the value, or null if it is SQL null; never returns the empty string
*/
String getClobStringOrNull(int columnOneBased);
/**
* @return the value, or null if it is SQL null; never returns the empty string
*/
String getClobStringOrNull(String columnName);
/**
* @return the value, or the empty string if it is SQL null; never returns null
*/
String getClobStringOrEmpty();
/**
* @return the value, or the empty string if it is SQL null; never returns null
*/
String getClobStringOrEmpty(int columnOneBased);
/**
* @return the value, or the empty string if it is SQL null; never returns null
*/
String getClobStringOrEmpty(String columnName);
/**
* @return the value, or null if it is SQL null
*/
Reader getClobReaderOrNull();
/**
* @return the value, or null if it is SQL null
*/
Reader getClobReaderOrNull(int columnOneBased);
/**
* @return the value, or null if it is SQL null
*/
Reader getClobReaderOrNull(String columnName);
/**
* @return the value, or a StringReader containing the empty string if it is SQL null
*/
Reader getClobReaderOrEmpty();
/**
* @return the value, or a StringReader containing the empty string if it is SQL null
*/
Reader getClobReaderOrEmpty(int columnOneBased);
/**
* @return the value, or a StringReader containing the empty string if it is SQL null
*/
Reader getClobReaderOrEmpty(String columnName);
byte[] getBlobBytesOrNull();
byte[] getBlobBytesOrNull(int columnOneBased);
byte[] getBlobBytesOrNull(String columnName);
byte[] getBlobBytesOrZeroLen();
byte[] getBlobBytesOrZeroLen(int columnOneBased);
byte[] getBlobBytesOrZeroLen(String columnName);
InputStream getBlobInputStreamOrNull();
InputStream getBlobInputStreamOrNull(int columnOneBased);
InputStream getBlobInputStreamOrNull(String columnName);
InputStream getBlobInputStreamOrEmpty();
InputStream getBlobInputStreamOrEmpty(int columnOneBased);
InputStream getBlobInputStreamOrEmpty(String columnName);
LocalDateTime getLocalDateTimeOrNull();
LocalDateTime getLocalDateTimeOrNull(int columnOneBased);
LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId);
LocalDateTime getLocalDateTimeOrNull(String columnName);
LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId);
/**
* Retrieve column as LocalDate, .i.e, date with no time.
*

@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* Safely wrap a ResultSet and provide access to the data it contains.
@ -38,7 +39,6 @@ class RowsAdaptor implements Rows {
}
}
@Override
public String[] getColumnLabels() {
try {
@ -661,22 +661,49 @@ class RowsAdaptor implements Rows {
public LocalDateTime getLocalDateTimeOrNull() {
return getLocalDateTimeOrNull(column++);
}
@Override
public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) {
try {
column = columnOneBased + 1;
return toLocalDateTime(rs, columnOneBased);
return rs.getObject(columnOneBased, LocalDateTime.class);
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
@Override
public LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId) {
try {
column = columnOneBased + 1;
LocalDateTime localDateTime = rs.getObject(columnOneBased, LocalDateTime.class);
Timestamp timestamp = rs.getTimestamp(columnOneBased);
if (zoneId != null) {
return localDateTime != null ? localDateTime.atZone(zoneId).toLocalDateTime() : null;
} else {
return localDateTime;
}
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
@Override
public LocalDateTime getLocalDateTimeOrNull(String columnName) {
try {
column = rs.findColumn(columnName) + 1;
return toLocalDateTime(rs, columnName);
return rs.getObject(columnName, LocalDateTime.class);
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
@Override
public LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId) {
try {
column = rs.findColumn(columnName) + 1;
LocalDateTime localDateTime = rs.getObject(columnName, LocalDateTime.class);
return localDateTime != null ? localDateTime.atZone(zoneId).toLocalDateTime() : null;
} catch (SQLException e) {
throw new DatabaseException(e);
}
@ -686,7 +713,6 @@ class RowsAdaptor implements Rows {
public LocalDate getLocalDateOrNull() {
return getLocalDateOrNull(column++);
}
@Override
public LocalDate getLocalDateOrNull(int columnOneBased) {
@ -697,7 +723,6 @@ class RowsAdaptor implements Rows {
throw new DatabaseException(e);
}
}
@Override
public LocalDate getLocalDateOrNull(String columnName) {
@ -719,16 +744,6 @@ class RowsAdaptor implements Rows {
}
}
private LocalDateTime toLocalDateTime(ResultSet rs, int col) throws SQLException {
Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps());
return val == null ? null : val.toLocalDateTime();
}
private LocalDateTime toLocalDateTime(ResultSet rs, String col) throws SQLException {
Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps());
return val == null ? null : val.toLocalDateTime();
}
private LocalDate toLocalDate(ResultSet rs, int col) throws SQLException {
java.sql.Date val = rs.getDate(col);
return val == null ? null : val.toLocalDate();

@ -5,5 +5,6 @@ package org.xbib.jdbc.query;
*/
@FunctionalInterface
public interface RowsHandler<T> {
T process(Rows rs) throws Exception;
}

@ -76,17 +76,14 @@ public class Schema {
Table table = addTable(tableName);
try {
ResultSetMetaData metadata = r.getMetadata();
int columnCount = metadata.getColumnCount();
String[] names = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
names[i] = metadata.getColumnName(i + 1);
}
names = SqlArgs.tidyColumnNames(names);
for (int i = 0; i < columnCount; i++) {
int type = metadata.getColumnType(i + 1);
switch (type) {
case Types.SMALLINT:
case Types.INTEGER:
@ -142,13 +139,14 @@ public class Schema {
// This is the type dates and times with time and time zone associated.
// Note that Oracle dates are always really Timestamps.
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
// Check if we really have a LocalDate implemented by the DB as a timestamp
if (metadata.getScale(i + 1) == 0) {
// If the scale is 0, this is a LocalDate (no time/timezone).
// Anything with a time/timezone will have a non-zero scale
table.addColumn(names[i]).asLocalDate();
} else {
table.addColumn(names[i]).asDate();
table.addColumn(names[i]).asLocalDateTime();
}
break;
@ -229,8 +227,8 @@ public class Schema {
case StringFixed:
sql.append(flavor.typeStringFixed(column.scale));
break;
case Date:
sql.append(flavor.typeDate()); // Append a date with time
case LocalDateTime:
sql.append(flavor.typeLocalDateTime()); // Append a date with time
break;
case LocalDate:
sql.append(flavor.typeLocalDate()); // Append a true date - no time
@ -400,7 +398,7 @@ public class Schema {
}
public enum ColumnType {
Integer, Long, Float, Double, BigDecimal, StringVar, StringFixed, Clob, Blob, Date, LocalDate, Boolean
Integer, Long, Float, Double, BigDecimal, StringVar, StringFixed, Clob, Blob, LocalDateTime, LocalDate, Boolean
}
public class Sequence {
@ -525,13 +523,13 @@ public class Schema {
public Schema schema() {
if (createTracking) {
addColumn("create_time").asDate().table();
addColumn("create_time").asLocalDateTime().table();
}
if (createTrackingFkName != null) {
addColumn("create_user").foreignKey(createTrackingFkName).references(createTrackingFkTable).table();
}
if (updateTracking || updateSequence) {
addColumn("update_time").asDate().table();
addColumn("update_time").asLocalDateTime().table();
}
if (updateTrackingFkName != null) {
addColumn("update_user").foreignKey(updateTrackingFkName).references(updateTrackingFkTable).table();
@ -868,8 +866,8 @@ public class Schema {
}
// This type is for dates that have time associated
public Column asDate() {
return asType(ColumnType.Date);
public Column asLocalDateTime() {
return asType(ColumnType.LocalDateTime);
}
// This type is for true dates with no time associated

@ -295,32 +295,22 @@ public class Sql implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Apply {
}
public Sql argDate(LocalDateTime arg) {
sqlArgs.argDate(arg);
sqlArgs.argLocalDateTime(arg);
return this;
}
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) {
sqlArgs.argDateNowPerApp(argName);
sqlArgs.argLocalDateTime(argName, arg);
return this;
}
public Sql argDateNowPerDb() {
sqlArgs.argDateNowPerDb();
sqlArgs.argLocalDateTimeNowPerDb();
return this;
}
public Sql argDateNowPerDb(String argName) {
sqlArgs.argDateNowPerDb(argName);
sqlArgs.argLocalDateTimeNowPerDb(argName);
return this;
}

@ -63,62 +63,53 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
}
return uniqueNames.toArray(new String[uniqueNames.size()]);
}
public SqlArgs argBoolean(Boolean arg) {
invocations.add(new Invocation(ColumnType.Boolean, null, arg));
return this;
}
public SqlArgs argBoolean( String argName, Boolean arg) {
public SqlArgs argBoolean(String argName, Boolean arg) {
invocations.add(new Invocation(ColumnType.Boolean, argName, arg));
return this;
}
public SqlArgs argInteger(Integer arg) {
invocations.add(new Invocation(ColumnType.Integer, null, arg));
return this;
}
public SqlArgs argInteger( String argName, Integer arg) {
public SqlArgs argInteger(String argName, Integer arg) {
invocations.add(new Invocation(ColumnType.Integer, argName, arg));
return this;
}
public SqlArgs argLong(Long arg) {
invocations.add(new Invocation(ColumnType.Long, null, arg));
return this;
}
public SqlArgs argLong( String argName, Long arg) {
public SqlArgs argLong(String argName, Long arg) {
invocations.add(new Invocation(ColumnType.Long, argName, arg));
return this;
}
public SqlArgs argFloat(Float arg) {
invocations.add(new Invocation(ColumnType.Float, null, arg));
return this;
}
public SqlArgs argFloat( String argName, Float arg) {
public SqlArgs argFloat(String argName, Float arg) {
invocations.add(new Invocation(ColumnType.Float, argName, arg));
return this;
}
public SqlArgs argDouble(Double arg) {
invocations.add(new Invocation(ColumnType.Double, null, arg));
return this;
}
public SqlArgs argDouble( String argName, Double arg) {
public SqlArgs argDouble(String argName, Double arg) {
invocations.add(new Invocation(ColumnType.Double, argName, arg));
return this;
}
@ -128,7 +119,7 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
return this;
}
public SqlArgs argBigDecimal( String argName, BigDecimal arg) {
public SqlArgs argBigDecimal(String argName, BigDecimal arg) {
invocations.add(new Invocation(ColumnType.BigDecimal, argName, arg));
return this;
}
@ -138,109 +129,80 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
return this;
}
public SqlArgs argString( String argName, String arg) {
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));
public SqlArgs argLocalDateTime(LocalDateTime arg) {
invocations.add(new Invocation(ColumnType.LocalDateTime, 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));
public SqlArgs argLocalDateTime(String argName, LocalDateTime arg) {
invocations.add(new Invocation(ColumnType.LocalDateTime, argName, arg));
return this;
}
public SqlArgs argLocalDate(LocalDate arg) {
// date argument with no time on it
invocations.add(new Invocation(ColumnType.LocalDate, null, arg));
return this;
}
public SqlArgs argLocalDate( String argName, LocalDate arg) {
// date argument with no time on it
public SqlArgs argLocalDate(String argName, LocalDate arg) {
invocations.add(new Invocation(ColumnType.LocalDate, argName, arg));
return this;
}
public SqlArgs argDateNowPerApp() {
invocations.add(new Invocation(ColumnType.DateNowPerApp, null, null));
public SqlArgs argLocalDateTimeNowPerDb() {
invocations.add(new Invocation(ColumnType.LocalDateTimeNowPerDb, null, null));
return this;
}
public SqlArgs argDateNowPerApp( String argName) {
invocations.add(new Invocation(ColumnType.DateNowPerApp, argName, null));
public SqlArgs argLocalDateTimeNowPerDb(String argName) {
invocations.add(new Invocation(ColumnType.LocalDateTimeNowPerDb, argName, null));
return this;
}
public SqlArgs argDateNowPerDb() {
invocations.add(new Invocation(ColumnType.DateNowPerDb, null, null));
return this;
}
public SqlArgs argDateNowPerDb( String argName) {
invocations.add(new Invocation(ColumnType.DateNowPerDb, argName, null));
return this;
}
public SqlArgs argBlobBytes(byte[] arg) {
invocations.add(new Invocation(ColumnType.BlobBytes, null, arg));
return this;
}
public SqlArgs argBlobBytes( String argName, byte[] arg) {
public SqlArgs argBlobBytes(String argName, byte[] arg) {
invocations.add(new Invocation(ColumnType.BlobBytes, argName, arg));
return this;
}
public SqlArgs argBlobInputStream(InputStream arg) {
invocations.add(new Invocation(ColumnType.BlobStream, null, arg));
return this;
}
public SqlArgs argBlobInputStream( String argName, InputStream arg) {
public SqlArgs argBlobInputStream(String argName, InputStream arg) {
invocations.add(new Invocation(ColumnType.BlobStream, argName, arg));
return this;
}
public SqlArgs argClobString(String arg) {
invocations.add(new Invocation(ColumnType.ClobString, null, arg));
return this;
}
public SqlArgs argClobString( String argName, String arg) {
public SqlArgs argClobString(String argName, String arg) {
invocations.add(new Invocation(ColumnType.ClobString, argName, arg));
return this;
}
public SqlArgs argClobReader(Reader arg) {
invocations.add(new Invocation(ColumnType.ClobStream, null, arg));
return this;
}
public SqlArgs argClobReader( String argName, Reader arg) {
public SqlArgs argClobReader(String argName, Reader arg) {
invocations.add(new Invocation(ColumnType.ClobStream, argName, arg));
return this;
}
public SqlArgs makePositional() {
for (Invocation invocation : invocations) {
@ -248,7 +210,6 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
}
return this;
}
public List<String> names() {
List<String> names = new ArrayList<>();
@ -348,25 +309,18 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
select.argLocalDate(i.argName, (LocalDate) i.arg);
}
break;
case Date:
case LocalDateTime:
if (i.argName == null) {
select.argDate((LocalDateTime) i.arg);
select.argLocalDateTime((LocalDateTime) i.arg);
} else {
select.argDate(i.argName, (LocalDateTime) i.arg);
select.argLocalDateTime(i.argName, (LocalDateTime) i.arg);
}
break;
case DateNowPerApp:
case LocalDateTimeNowPerDb:
if (i.argName == null) {
select.argDateNowPerApp();
select.argLocalDateTimeNowPerDb();
} else {
select.argDateNowPerApp(i.argName);
}
break;
case DateNowPerDb:
if (i.argName == null) {
select.argDateNowPerDb();
} else {
select.argDateNowPerDb(i.argName);
select.argLocalDateTimeNowPerDb(i.argName);
}
break;
}
@ -456,33 +410,24 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
}
break;
case LocalDate:
// date argument with no time on it
if (i.argName == null) {
insert.argLocalDate((LocalDate) i.arg);
} else {
insert.argLocalDate(i.argName, (LocalDate) i.arg);
}
break;
case Date:
// date argument with a time on it
if (i.argName == null) {
insert.argDate((LocalDateTime) i.arg);
} else {
insert.argDate(i.argName, (LocalDateTime) i.arg);
}
break;
case DateNowPerApp:
case LocalDateTime:
if (i.argName == null) {
insert.argDateNowPerApp();
insert.argLocalDateTime((LocalDateTime) i.arg);
} else {
insert.argDateNowPerApp(i.argName);
insert.argLocalDateTime(i.argName, (LocalDateTime) i.arg);
}
break;
case DateNowPerDb:
case LocalDateTimeNowPerDb:
if (i.argName == null) {
insert.argDateNowPerDb();
insert.argLocalDateTimeNowPerDb();
} else {
insert.argDateNowPerDb(i.argName);
insert.argLocalDateTimeNowPerDb(i.argName);
}
break;
}
@ -578,25 +523,18 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
update.argLocalDate(i.argName, (LocalDate) i.arg);
}
break;
case Date:
if (i.argName == null) {
update.argDate((LocalDateTime) i.arg);
} else {
update.argDate(i.argName, (LocalDateTime) i.arg);
}
break;
case DateNowPerApp:
case LocalDateTime:
if (i.argName == null) {
update.argDateNowPerApp();
update.argLocalDateTime((LocalDateTime) i.arg);
} else {
update.argDateNowPerApp(i.argName);
update.argLocalDateTime(i.argName, (LocalDateTime) i.arg);
}
break;
case DateNowPerDb:
case LocalDateTimeNowPerDb:
if (i.argName == null) {
update.argDateNowPerDb();
update.argLocalDateTimeNowPerDb();
} else {
update.argDateNowPerDb(i.argName);
update.argLocalDateTimeNowPerDb(i.argName);
}
break;
}
@ -622,14 +560,29 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
}
public enum ColumnType {
Integer, Long, Float, Double, BigDecimal, String, ClobString, ClobStream,
BlobBytes, BlobStream, Date, LocalDate, DateNowPerApp, DateNowPerDb, Boolean
String,
Boolean,
Integer,
Long,
Float,
Double,
BigDecimal,
ClobString,
ClobStream,
BlobBytes,
BlobStream,
LocalDate,
LocalDateTime,
LocalDateTimeNowPerDb
}
private static class Invocation {
ColumnType columnType;
String argName;
Object arg;
private final ColumnType columnType;
private String argName;
private final Object arg;
Invocation(ColumnType columnType, String argName, Object arg) {
this.columnType = columnType;
@ -639,8 +592,12 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Invocation that = (Invocation) o;
return columnType == that.columnType &&
Objects.equals(argName, that.argName) &&
@ -659,9 +616,13 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
}
public static class Builder {
private final int[] types;
private final int[] precision;
private final int[] scale;
private String[] names;
public Builder(Row r) {
@ -672,14 +633,12 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
types = new int[columnCount];
precision = new int[columnCount];
scale = new int[columnCount];
for (int i = 0; i < columnCount; i++) {
names[i] = metadata.getColumnLabel(i + 1);
types[i] = metadata.getColumnType(i + 1);
precision[i] = metadata.getPrecision(i + 1);
scale[i] = metadata.getScale(i + 1);
}
names = tidyColumnNames(names);
} catch (SQLException e) {
throw new DatabaseException("Unable to retrieve metadata from ResultSet", e);
@ -727,22 +686,19 @@ public class SqlArgs implements SqlInsert.Apply, SqlUpdate.Apply, SqlSelect.Appl
case Types.NCLOB:
args.argClobString(names[i], r.getClobStringOrNull());
break;
// Check Date before TimeStamp because SQL dates are also timestamps
case Types.DATE:
args.argLocalDate(names[i], r.getLocalDateOrNull());
break;
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
if (this.scale[i] == 0) {
// If the scale is 0, this is a LocalDate (no time/timezone).
// Anything with a time will have a non-zero scale
args.argLocalDate(names[i], r.getLocalDateOrNull());
} else {
args.argDate(names[i], r.getLocalDateTimeOrNull());
args.argLocalDateTime(names[i], r.getLocalDateTimeOrNull());
}
break;
case Types.NVARCHAR:
case Types.VARCHAR:
case Types.CHAR:

@ -5,6 +5,7 @@ import java.io.Reader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* Interface for configuring (setting parameters) and executing a chunk of SQL.
@ -39,21 +40,19 @@ public interface SqlInsert {
SqlInsert argString( String argName, String arg);
SqlInsert argDate(LocalDateTime arg); // date with time
SqlInsert argLocalDateTime(LocalDateTime arg);
SqlInsert argDate(String argName, LocalDateTime arg); // date with time
SqlInsert argLocalDateTime(LocalDateTime arg, ZoneId zoneId);
SqlInsert argLocalDate(LocalDate arg); // date only - no timestamp
SqlInsert argLocalDateTime(String argName, LocalDateTime arg);
SqlInsert argLocalDate( String argName, LocalDate arg); // date only - no timestamp
SqlInsert argLocalDate(LocalDate arg);
SqlInsert argDateNowPerApp();
SqlInsert argLocalDate( String argName, LocalDate arg);
SqlInsert argDateNowPerApp(String argName);
SqlInsert argLocalDateTimeNowPerDb();
SqlInsert argDateNowPerDb();
SqlInsert argDateNowPerDb(String argName);
SqlInsert argLocalDateTimeNowPerDb(String argName);
SqlInsert argBlobBytes(byte[] arg);
@ -126,7 +125,6 @@ public interface SqlInsert {
* <p>This version of insert expects exactly one row to be inserted, and will throw
* a DatabaseException if that isn't the case.</p>
*/
Long insertReturningPkSeq(String primaryKeyColumnName);
<T> T insertReturning(String tableName, String primaryKeyColumnName, RowsHandler<T> rowsHandler,

@ -15,6 +15,7 @@ import java.sql.ResultSet;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -40,9 +41,9 @@ public class SqlInsertImpl implements SqlInsert {
private List<Batch> batched;
private List<Object> parameterList; // !null ==> traditional ? args
private List<Object> parameterList;
private Map<String, Object> parameterMap; // !null ==> named :abc args
private Map<String, Object> parameterMap;
private String pkArgName;
@ -141,13 +142,18 @@ public class SqlInsertImpl implements SqlInsert {
}
@Override
public SqlInsert argDate(LocalDateTime arg) {
return positionalArg(adaptor.nullDate(arg));
public SqlInsert argLocalDateTime(LocalDateTime arg) {
return positionalArg(adaptor.nullLocalDateTime(arg));
}
@Override
public SqlInsert argDate( String argName, LocalDateTime arg) {
return namedArg(argName, adaptor.nullDate(arg));
public SqlInsert argLocalDateTime(LocalDateTime arg, ZoneId zoneId) {
return positionalArg(adaptor.nullLocalDateTime(arg, zoneId));
}
@Override
public SqlInsert argLocalDateTime(String argName, LocalDateTime arg) {
return namedArg(argName, adaptor.nullLocalDateTime(arg));
}
@Override
@ -161,27 +167,17 @@ public class SqlInsertImpl implements SqlInsert {
}
@Override
public SqlInsert argDateNowPerApp() {
return positionalArg(adaptor.nullDate(options.currentDate()));
}
@Override
public SqlInsert argDateNowPerApp( String argName) {
return namedArg(argName, adaptor.nullDate(options.currentDate()));
}
@Override
public SqlInsert argDateNowPerDb() {
if (options.useDatePerAppOnly()) {
return positionalArg(adaptor.nullDate(options.currentDate()));
public SqlInsert argLocalDateTimeNowPerDb() {
if (options.useLocalDateTimeOnly()) {
return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now()));
}
return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
}
@Override
public SqlInsert argDateNowPerDb( String argName) {
if (options.useDatePerAppOnly()) {
return namedArg(argName, adaptor.nullDate(options.currentDate()));
public SqlInsert argLocalDateTimeNowPerDb(String argName) {
if (options.useLocalDateTimeOnly()) {
return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now()));
}
return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
}
@ -235,7 +231,6 @@ public class SqlInsertImpl implements SqlInsert {
public SqlInsert withArgs(SqlArgs args) {
return apply(args);
}
@Override
public SqlInsert apply(Apply apply) {
@ -312,7 +307,6 @@ public class SqlInsertImpl implements SqlInsert {
if (!hasPk()) {
throw new DatabaseException("Identify a primary key with argPk*() before insertReturning()");
}
if (options.flavor().supportsInsertReturning()) {
return updateInternal(1, primaryKeyColumnName, handler, otherColumnNames);
} else if (pkSeqName != null) {
@ -367,7 +361,6 @@ public class SqlInsertImpl implements SqlInsert {
}
@Override
public SqlInsert argPkSeq( String argName, String sequenceName) {
if (hasPk() && batched == null) {
throw new DatabaseException("Only call one argPk*() method");
@ -381,7 +374,6 @@ public class SqlInsertImpl implements SqlInsert {
}
@Override
public SqlInsert argPkLong(String argName, Long arg) {
if (hasPk() && batched == null) {
throw new DatabaseException("Only call one argPk*() method");
@ -395,7 +387,6 @@ public class SqlInsertImpl implements SqlInsert {
}
@Override
public SqlInsert argPkLong(Long arg) {
if (hasPk() && batched == null) {
throw new DatabaseException("Only call one argPk*() method");
@ -415,18 +406,14 @@ public class SqlInsertImpl implements SqlInsert {
private int[] insertBatchInternal() {
batch();
if (batched == null || batched.size() == 0) {
throw new DatabaseException("Batch insert requires parameters");
}
PreparedStatement ps = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
String executeSql = sql;
Object[] firstRowParameters = null;
List<Object[]> parameters = new ArrayList<>();
boolean isSuccess = false;
String errorCode = null;
Exception logEx = null;
@ -444,7 +431,6 @@ public class SqlInsertImpl implements SqlInsert {
}
parameters.add(mpSql.getArgs());
}
if (connection != null) {
ps = connection.prepareStatement(executeSql);
@ -452,7 +438,6 @@ public class SqlInsertImpl implements SqlInsert {
adaptor.addParameters(ps, params);
ps.addBatch();
}
metric.checkpoint("prep");
int[] numAffectedRows = ps.executeBatch();
metric.checkpoint("execBatch", parameters.size());
@ -482,13 +467,10 @@ public class SqlInsertImpl implements SqlInsert {
if (batched != null) {
throw new DatabaseException("Call insertBatch() if you are using the batch() feature");
}
PreparedStatement ps = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
String executeSql = sql;
Object[] parameters = null;
boolean isSuccess = false;
String errorCode = null;
Exception logEx = null;
@ -496,10 +478,8 @@ public class SqlInsertImpl implements SqlInsert {
MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap);
executeSql = mpSql.getSqlToExecute();
parameters = mpSql.getArgs();
if (connection != null) {
ps = connection.prepareStatement(executeSql);
adaptor.addParameters(ps, parameters);
metric.checkpoint("prep");
int numAffectedRows = ps.executeUpdate();
@ -536,14 +516,11 @@ public class SqlInsertImpl implements SqlInsert {
if (batched != null) {
throw new DatabaseException("Call insertBatch() if you are using the batch() feature");
}
PreparedStatement ps = null;
ResultSet rs = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
String executeSql = sql;
Object[] parameters = null;
boolean isSuccess = false;
String errorCode = null;
Exception logEx = null;
@ -551,10 +528,8 @@ public class SqlInsertImpl implements SqlInsert {
MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap);
executeSql = mpSql.getSqlToExecute();
parameters = mpSql.getArgs();
if (connection != null) {
ps = connection.prepareStatement(executeSql, new String[]{pkToReturn});
adaptor.addParameters(ps, parameters);
metric.checkpoint("prep");
int numAffectedRows = ps.executeUpdate();
@ -598,14 +573,11 @@ public class SqlInsertImpl implements SqlInsert {
if (batched != null) {
throw new DatabaseException("Call insertBatch() if you are using the batch() feature");
}
PreparedStatement ps = null;
ResultSet rs = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
String executeSql = sql;
Object[] parameters = null;
boolean isSuccess = false;
String errorCode = null;
Exception logEx = null;
@ -613,14 +585,11 @@ public class SqlInsertImpl implements SqlInsert {
MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap);
executeSql = mpSql.getSqlToExecute();
parameters = mpSql.getArgs();
String[] returnCols = new String[otherCols.length + 1];
returnCols[0] = pkToReturn;
System.arraycopy(otherCols, 0, returnCols, 1, otherCols.length);
if (connection != null) {
ps = connection.prepareStatement(executeSql, returnCols);
adaptor.addParameters(ps, parameters);
metric.checkpoint("prep");
int numAffectedRows = ps.executeUpdate();
@ -682,8 +651,8 @@ public class SqlInsertImpl implements SqlInsert {
}
private static class Batch {
private final List<Object> parameterList; // !null ==> traditional ? args
private final Map<String, Object> parameterMap; // !null ==> named :abc args
private final List<Object> parameterList;
private final Map<String, Object> parameterMap;
public Batch(List<Object> parameterList, Map<String, Object> parameterMap) {
this.parameterList = parameterList;

@ -3,6 +3,7 @@ package org.xbib.jdbc.query;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
/**
@ -10,126 +11,64 @@ import java.util.List;
*/
public interface SqlSelect {
SqlSelect argBoolean(Boolean arg);
SqlSelect argBoolean( String argName, Boolean arg);
SqlSelect argInteger(Integer arg);
SqlSelect argInteger( String argName, Integer arg);
SqlSelect argLong(Long arg);
SqlSelect argLong( String argName, Long arg);
SqlSelect argFloat(Float arg);
SqlSelect argFloat( String argName, Float arg);
SqlSelect argDouble(Double arg);
SqlSelect argDouble( String argName, Double arg);
SqlSelect argBigDecimal(BigDecimal arg);
SqlSelect argBigDecimal( String argName, BigDecimal arg);
SqlSelect argString(String arg);
SqlSelect argString( String argName, String arg);
SqlSelect argDate(LocalDateTime arg); // Date with time
SqlSelect argDate(String argName, LocalDateTime arg); // Date with time
SqlSelect argLocalDate(LocalDate arg); // Date without time
SqlSelect argLocalDateTime(LocalDateTime arg);
SqlSelect argLocalDate( String argName, LocalDate arg); // Date without time
SqlSelect argLocalDateTime(String argName, LocalDateTime arg);
SqlSelect argDateNowPerApp();
SqlSelect argLocalDate(LocalDate arg);
SqlSelect argDateNowPerApp( String argName);
SqlSelect argLocalDate( String argName, LocalDate arg);
SqlSelect argDateNowPerDb();
SqlSelect argLocalDateTimeNowPerDb();
SqlSelect argDateNowPerDb( String argName);
SqlSelect argLocalDateTimeNowPerDb(String argName);
SqlSelect withTimeoutSeconds(int seconds);
SqlSelect withMaxRows(int rows);
SqlSelect withArgs(SqlArgs args);
SqlSelect apply(Apply apply);
SqlSelect fetchSize(int fetchSize);
Boolean queryBooleanOrNull();
boolean queryBooleanOrFalse();
boolean queryBooleanOrTrue();
Long queryLongOrNull();
long queryLongOrZero();
/**
@ -137,61 +76,34 @@ public interface SqlSelect {
*
* @return the first column values, omitting any that were null
*/
List<Long> queryLongs();
Integer queryIntegerOrNull();
int queryIntegerOrZero();
List<Integer> queryIntegers();
Float queryFloatOrNull();
float queryFloatOrZero();
List<Float> queryFloats();
Double queryDoubleOrNull();
double queryDoubleOrZero();
List<Double> queryDoubles();
BigDecimal queryBigDecimalOrNull();
BigDecimal queryBigDecimalOrZero();
List<BigDecimal> queryBigDecimals();
String queryStringOrNull();
String queryStringOrEmpty();
/**
@ -199,25 +111,19 @@ public interface SqlSelect {
*
* @return the first column values, omitting any that were null
*/
List<String> queryStrings();
LocalDateTime queryDateOrNull(); // Date with time
LocalDateTime queryLocalDateTimeOrNull();
List<LocalDateTime> queryDates(); // Date with time
LocalDateTime queryLocalDateTimeOrNull(ZoneId zoneId);
LocalDate queryLocalDateOrNull(); // Date without time
List<LocalDateTime> queryLocalDateTimes();
List<LocalDateTime> queryLocalDateTimes(ZoneId zoneId);
LocalDate queryLocalDateOrNull();
List<LocalDate> queryLocalDates(); // Date without time
List<LocalDate> queryLocalDates();
/**
* This is the most generic and low-level way to iterate the query results.

@ -11,6 +11,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -23,7 +24,7 @@ import java.util.logging.Logger;
*/
public class SqlSelectImpl implements SqlSelect {
private static final Logger log = Logger.getLogger(Database.class.getName());
private static final Logger logger = Logger.getLogger(Database.class.getName());
private final Connection connection;
@ -114,115 +115,84 @@ public class SqlSelectImpl implements SqlSelect {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argBigDecimal(BigDecimal arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argBigDecimal( String argName, BigDecimal arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argString(String arg) {
return positionalArg(adaptor.nullString(arg));
}
@Override
public SqlSelect argString( String argName, String arg) {
public SqlSelect argString(String argName, String arg) {
return namedArg(argName, adaptor.nullString(arg));
}
@Override
public SqlSelect argDate(LocalDateTime arg) {
// Date with time
return positionalArg(adaptor.nullDate(arg));
public SqlSelect argLocalDateTime(LocalDateTime arg) {
return positionalArg(adaptor.nullLocalDateTime(arg));
}
@Override
public SqlSelect argDate( String argName, LocalDateTime arg) {
// Date with time
return namedArg(argName, adaptor.nullDate(arg));
public SqlSelect argLocalDateTime(String argName, LocalDateTime arg) {
return namedArg(argName, adaptor.nullLocalDateTime(arg));
}
@Override
public SqlSelect argLocalDate(LocalDate arg) {
// Date with no time
return positionalArg(adaptor.nullLocalDate(arg));
}
@Override
public SqlSelect argLocalDate( String argName, LocalDate arg) {
// Date with no time
return namedArg(argName, adaptor.nullLocalDate(arg));
}
@Override
public SqlSelect argDateNowPerApp() {
return positionalArg(adaptor.nullDate(options.currentDate()));
}
@Override
public SqlSelect argDateNowPerApp( String argName) {
return namedArg(argName, adaptor.nullDate(options.currentDate()));
}
@Override
public SqlSelect argDateNowPerDb() {
if (options.useDatePerAppOnly()) {
return positionalArg(adaptor.nullDate(options.currentDate()));
public SqlSelect argLocalDateTimeNowPerDb() {
if (options.useLocalDateTimeOnly()) {
return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now()));
}
return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
}
@Override
public SqlSelect argDateNowPerDb( String argName) {
if (options.useDatePerAppOnly()) {
return namedArg(argName, adaptor.nullDate(options.currentDate()));
public SqlSelect argLocalDateTimeNowPerDb(String argName) {
if (options.useLocalDateTimeOnly()) {
return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now()));
}
return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
}
@Override
public SqlSelect withTimeoutSeconds(int seconds) {
timeoutSeconds = seconds;
return this;
}
@Override
public SqlSelect withMaxRows(int rows) {
maxRows = rows;
return this;
}
@Override
public SqlSelect withArgs(SqlArgs args) {
return apply(args);
}
@Override
public SqlSelect apply(Apply apply) {
apply.apply(this);
return this;
}
@Override
public SqlSelect fetchSize(int rows) {
@ -253,7 +223,6 @@ public class SqlSelectImpl implements SqlSelect {
}
@Override
public Long queryLongOrNull() {
return queryWithTimeout(rs -> {
if (rs.next()) {
@ -287,7 +256,6 @@ public class SqlSelectImpl implements SqlSelect {
return result;
});
}
@Override
public Integer queryIntegerOrNull() {
@ -323,7 +291,6 @@ public class SqlSelectImpl implements SqlSelect {
return result;
});
}
@Override
public Float queryFloatOrNull() {
@ -404,7 +371,6 @@ public class SqlSelectImpl implements SqlSelect {
return null;
});
}
@Override
public BigDecimal queryBigDecimalOrZero() {
@ -415,7 +381,6 @@ public class SqlSelectImpl implements SqlSelect {
return new BigDecimal(0);
});
}
@Override
public List<BigDecimal> queryBigDecimals() {
@ -440,7 +405,6 @@ public class SqlSelectImpl implements SqlSelect {
return null;
});
}
@Override
public String queryStringOrEmpty() {
@ -467,21 +431,31 @@ public class SqlSelectImpl implements SqlSelect {
}
@Override
public LocalDateTime queryDateOrNull() {
public LocalDateTime queryLocalDateTimeOrNull() {
return queryLocalDateTimeOrNull(null);
}
@Override
public LocalDateTime queryLocalDateTimeOrNull(ZoneId zoneId) {
return queryWithTimeout(rs -> {
if (rs.next()) {
return rs.getLocalDateTimeOrNull(1);
return rs.getLocalDateTimeOrNull(1, zoneId);
}
return null;
});
}
@Override
public List<LocalDateTime> queryDates() {
public List<LocalDateTime> queryLocalDateTimes() {
return queryLocalDateTimes(null);
}
@Override
public List<LocalDateTime> queryLocalDateTimes(ZoneId zoneId) {
return queryWithTimeout(rs -> {
List<LocalDateTime> result = new ArrayList<>();
while (rs.next()) {
LocalDateTime value = rs.getLocalDateTimeOrNull(1);
LocalDateTime value = rs.getLocalDateTimeOrNull(1, zoneId);
if (value != null) {
result.add(value);
}
@ -489,7 +463,7 @@ public class SqlSelectImpl implements SqlSelect {
return result;
});
}
@Override
public LocalDate queryLocalDateOrNull() {
// Date without time
@ -603,11 +577,9 @@ public class SqlSelectImpl implements SqlSelect {
private <T> T queryWithTimeout(RowsHandler<T> handler) {
assert ps == null;
ResultSet rs = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
Metric metric = new Metric(logger.isLoggable(Level.FINE));
String executeSql = sql;
Object[] parameters = null;
boolean isWarn = false;
boolean isSuccess = false;
String errorCode = null;
@ -616,26 +588,21 @@ public class SqlSelectImpl implements SqlSelect {
MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap);
executeSql = mpSql.getSqlToExecute();
parameters = mpSql.getArgs();
if (connection != null) {
synchronized (cancelLock) {
ps = connection.prepareStatement(executeSql,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
}
if (timeoutSeconds >= 0) {
ps.setQueryTimeout(timeoutSeconds);
}
if (maxRows > 0) {
ps.setMaxRows(maxRows);
}
if (fetchSize >= 0) {
ps.setFetchSize(fetchSize);
}
adaptor.addParameters(ps, parameters);
metric.checkpoint("prep");
rs = ps.executeQuery();
@ -662,18 +629,18 @@ public class SqlSelectImpl implements SqlSelect {
logEx = e;
throw DatabaseException.wrap(DebugSql.exceptionMessage(executeSql, parameters, errorCode, options), e);
} finally {
adaptor.closeQuietly(rs, log);
adaptor.closeQuietly(ps, log);
adaptor.closeQuietly(rs, logger);
adaptor.closeQuietly(ps, logger);
synchronized (cancelLock) {
ps = null;
}
metric.done("close");
if (isSuccess) {
DebugSql.logSuccess("Query", log, metric, executeSql, parameters, options);
DebugSql.logSuccess("Query", logger, metric, executeSql, parameters, options);
} else if (isWarn) {
DebugSql.logWarning("Query", log, metric, "QueryTimedOutException", executeSql, parameters, options, null);
DebugSql.logWarning("Query", logger, metric, "QueryTimedOutException", executeSql, parameters, options, null);
} else {
DebugSql.logError("Query", log, metric, errorCode, executeSql, parameters, options, logEx);
DebugSql.logError("Query", logger, metric, errorCode, executeSql, parameters, options, logEx);
}
}
}

@ -39,21 +39,17 @@ public interface SqlUpdate {
SqlUpdate argString(String argName, String arg);
SqlUpdate argDate(LocalDateTime arg);
SqlUpdate argLocalDateTime(LocalDateTime arg);
SqlUpdate argDate(String argName, LocalDateTime arg);
SqlUpdate argLocalDateTime(String argName, LocalDateTime arg);
SqlUpdate argLocalDate(LocalDate arg);
SqlUpdate argLocalDate(String argName, LocalDate arg);
SqlUpdate argDateNowPerApp();
SqlUpdate argDateNowPerApp(String argName);
SqlUpdate argDateNowPerDb();
SqlUpdate argLocalDateTimeNowPerDb();
SqlUpdate argDateNowPerDb(String argName);
SqlUpdate argLocalDateTimeNowPerDb(String argName);
SqlUpdate argBlobBytes(byte[] arg);

@ -49,187 +49,149 @@ public class SqlUpdateImpl implements SqlUpdate {
this.options = options;
adaptor = new StatementAdaptor(options);
}
@Override
public SqlUpdate argBoolean(Boolean arg) {
return positionalArg(adaptor.nullString(booleanToString(arg)));
}
@Override
public SqlUpdate argBoolean(String argName, Boolean arg) {
return namedArg(argName, adaptor.nullString(booleanToString(arg)));
}
@Override
public SqlUpdate argInteger(Integer arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argInteger(String argName, Integer arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argLong(Long arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argLong(String argName, Long arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argFloat(Float arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argFloat(String argName, Float arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argDouble(Double arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argDouble(String argName, Double arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argBigDecimal(BigDecimal arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argBigDecimal(String argName, BigDecimal arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlUpdate argString(String arg) {
return positionalArg(adaptor.nullString(arg));
}
@Override
public SqlUpdate argString(String argName, String arg) {
return namedArg(argName, adaptor.nullString(arg));
}
@Override
public SqlUpdate argDate(LocalDateTime arg) {
return positionalArg(adaptor.nullDate(arg));
public SqlUpdate argLocalDateTime(LocalDateTime arg) {
return positionalArg(adaptor.nullLocalDateTime(arg));
}
@Override
public SqlUpdate argDate(String argName, LocalDateTime arg) {
return namedArg(argName, adaptor.nullDate(arg));
public SqlUpdate argLocalDateTime(String argName, LocalDateTime arg) {
return namedArg(argName, adaptor.nullLocalDateTime(arg));
}
@Override
public SqlUpdate argLocalDate(LocalDate arg) {
return positionalArg(adaptor.nullLocalDate(arg));
}
@Override
public SqlUpdate argLocalDate(String argName, LocalDate arg) {
return namedArg(argName, adaptor.nullLocalDate(arg));
}
@Override
public SqlUpdate argDateNowPerApp() {
return positionalArg(adaptor.nullDate(options.currentDate()));
}
@Override
public SqlUpdate argDateNowPerApp(String argName) {
return namedArg(argName, adaptor.nullDate(options.currentDate()));
}
@Override
public SqlUpdate argDateNowPerDb() {
if (options.useDatePerAppOnly()) {
return positionalArg(adaptor.nullDate(options.currentDate()));
public SqlUpdate argLocalDateTimeNowPerDb() {
if (options.useLocalDateTimeOnly()) {
return positionalArg(adaptor.nullLocalDateTime(LocalDateTime.now()));
}
return positionalArg(new RewriteArg(options.flavor().dbTimeMillis()));
}
@Override
public SqlUpdate argDateNowPerDb(String argName) {
if (options.useDatePerAppOnly()) {
return namedArg(argName, adaptor.nullDate(options.currentDate()));
public SqlUpdate argLocalDateTimeNowPerDb(String argName) {
if (options.useLocalDateTimeOnly()) {
return namedArg(argName, adaptor.nullLocalDateTime(LocalDateTime.now()));
}
return namedArg(argName, new RewriteArg(options.flavor().dbTimeMillis()));
}
@Override
public SqlUpdate argBlobBytes(byte[] arg) {
return positionalArg(adaptor.nullBytes(arg));
}
@Override
public SqlUpdate argBlobBytes(String argName, byte[] arg) {
return namedArg(argName, adaptor.nullBytes(arg));
}
@Override
public SqlUpdate argBlobStream(InputStream arg) {
return positionalArg(adaptor.nullInputStream(arg));
}
@Override
public SqlUpdate argBlobStream(String argName, InputStream arg) {
return namedArg(argName, adaptor.nullInputStream(arg));
}
@Override
public SqlUpdate argClobString(String arg) {
return positionalArg(adaptor.nullClobReader(arg == null ? null : new InternalStringReader(arg)));
}
@Override
public SqlUpdate argClobString(String argName, String arg) {
return namedArg(argName, adaptor.nullClobReader(arg == null ? null : new InternalStringReader(arg)));
}
@Override
public SqlUpdate argClobReader(Reader arg) {
return positionalArg(adaptor.nullClobReader(arg));
}
@Override
public SqlUpdate argClobReader(String argName, Reader arg) {
return namedArg(argName, adaptor.nullClobReader(arg));
}
@ -244,7 +206,6 @@ public class SqlUpdateImpl implements SqlUpdate {
return apply(args);
}
@Override
public SqlUpdate apply(Apply apply) {
apply.apply(this);
@ -419,8 +380,8 @@ public class SqlUpdateImpl implements SqlUpdate {
}
private static class Batch {
private final List<Object> parameterList; // !null ==> traditional ? args
private final Map<String, Object> parameterMap; // !null ==> named :abc args
private final List<Object> parameterList;
private final Map<String, Object> parameterMap;
public Batch(List<Object> parameterList, Map<String, Object> parameterMap) {
this.parameterList = parameterList;

@ -1,6 +1,6 @@
package org.xbib.jdbc.query;
import java.io.ByteArrayOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
@ -13,75 +13,32 @@ import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Deal with mapping parameters into prepared statements.
*/
public class StatementAdaptor {
private static final Logger logger = Logger.getLogger(StatementAdaptor.class.getName());
private final Options options;
public StatementAdaptor(Options options) {
this.options = options;
}
private static String readerToString(Reader r) {
Scanner s = new Scanner(r).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
private static byte[] streamToBytes(InputStream is) throws SQLException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
try {
while ((length = is.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
} catch (IOException e) {
throw new SQLException("Unable to convert InputStream parameter to bytes", e);
}
return out.toByteArray();
}
/**
* Converts the java.util.Date into a java.sql.Timestamp, following the nanos/millis canonicalization
* required by the spec. If a java.sql.Timestamp is passed in (since it extends java.util.Date),
* it will be checked and canonicalized only if not already correct.
*/
private static Timestamp toSqlTimestamp(Date date) {
long millis = date.getTime();
int fractionalSecondMillis = (int) (millis % 1000); // guaranteed < 1000
if (fractionalSecondMillis == 0) { // this means it's already correct by the spec
if (date instanceof Timestamp) {
return (Timestamp) date;
} else {
return new Timestamp(millis);
}
} else { // the millis are invalid and need to be corrected
int tsNanos = fractionalSecondMillis * 1000000;
long tsMillis = millis - fractionalSecondMillis;
Timestamp timestamp = new Timestamp(tsMillis);
timestamp.setNanos(tsNanos);
return timestamp;
}
}
public void addParameters(PreparedStatement ps, Object[] parameters) throws SQLException {
for (int i = 0; i < parameters.length; i++) {
Object parameter = parameters[i];
// Unwrap secret args here so we can use them
if (parameter instanceof SecretArg) {
parameter = ((SecretArg) parameter).getArg();
}
if (parameter == null) {
ParameterMetaData metaData;
int parameterType;
@ -106,57 +63,52 @@ public class StatementAdaptor {
} else if (parameter instanceof java.sql.Date) {
ps.setDate(i + 1, (java.sql.Date) parameter);
} else if (parameter instanceof Date) {
Date date = (Date) parameter;
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
ps.setTimestamp(i + 1, Timestamp.valueOf(localDateTime));
// this will correct the millis and nanos according to the JDBC spec
// if a correct Timestamp is passed in, this will detect that and leave it alone
ps.setTimestamp(i + 1, toSqlTimestamp((Date) parameter), options.calendarForTimestamps());
//ps.setTimestamp(i + 1, toSqlTimestamp((Date) parameter), options.calendarForTimestamps());
} else if (parameter instanceof Reader) {
if (options.useStringForClob()) {
ps.setString(i + 1, readerToString((Reader) parameter));
try (BufferedReader reader = new BufferedReader((Reader) parameter)) {
ps.setString(i + 1, reader.lines().collect(Collectors.joining()));
} catch (IOException e) {
throw new SQLException(e);
}
} else {
ps.setCharacterStream(i + 1, (Reader) parameter);
}
} else if (parameter instanceof InputStream) {
if (options.useBytesForBlob()) {
ps.setBytes(i + 1, streamToBytes((InputStream) parameter));
try {
ps.setBytes(i + 1, ((InputStream) parameter).readAllBytes());
} catch (IOException e) {
throw new SQLException(e);
}
} else {
ps.setBinaryStream(i + 1, (InputStream) parameter);
}
} else if (parameter instanceof Float) {
//if (options.flavor() == Flavor.oracle && ps.isWrapperFor(OraclePreparedStatement.class)) {
// The Oracle 11 driver setDouble() first converts the double to NUMBER, causing underflow
// for small values so we need to use the proprietary mechanism
//ps.unwrap(OraclePreparedStatement.class).setBinaryFloat(i + 1, (Float) parameter);
//} else {
ps.setFloat(i + 1, (Float) parameter);
//}
ps.setFloat(i + 1, (Float) parameter);
} else if (parameter instanceof Double) {
//if (options.flavor() == Flavor.oracle && ps.isWrapperFor(OraclePreparedStatement.class)) {
// The Oracle 11 driver setDouble() first converts the double to NUMBER, causing underflow
// for small values so we need to use the proprietary mechanism
//ps.unwrap(OraclePreparedStatement.class).setBinaryDouble(i + 1, (Double) parameter);
//} else {
ps.setDouble(i + 1, (Double) parameter);
//}
ps.setDouble(i + 1, (Double) parameter);
} else {
ps.setObject(i + 1, parameter);
}
}
}
public Object nullDate(LocalDateTime arg) {
if (arg == null) {
return new SqlNull(Types.TIMESTAMP);
}
return Timestamp.valueOf(arg);
public Object nullLocalDateTime(LocalDateTime arg) {
return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg);
}
// Processes a true date without time information.
public Object nullLocalDate(LocalDate arg) {
if (arg == null) {
return new SqlNull(Types.DATE);
}
public Object nullLocalDateTime(LocalDateTime arg, ZoneId zoneId) {
return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.atZone(zoneId).toLocalDateTime());
}
return java.sql.Date.valueOf(arg);
public Object nullLocalDate(LocalDate arg) {
return arg == null ? new SqlNull(Types.DATE) : java.sql.Date.valueOf(arg);
}
public Object nullNumeric(Number arg) {
@ -217,5 +169,4 @@ public class StatementAdaptor {
}
}
}
}

@ -2,11 +2,6 @@ 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
@ -81,7 +76,7 @@ public class BigQuery implements Flavor {
}
@Override
public String typeDate() {
public String typeLocalDateTime() {
return "datetime";
}
@ -150,20 +145,6 @@ public class BigQuery implements Flavor {
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();

@ -2,11 +2,6 @@ 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
@ -80,7 +75,7 @@ public class Derby implements Flavor {
}
@Override
public String typeDate() {
public String typeLocalDateTime() {
return "timestamp";
}
@ -149,18 +144,6 @@ public class Derby implements Flavor {
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";

@ -0,0 +1,175 @@
package org.xbib.jdbc.query.flavor;
import org.xbib.jdbc.query.Flavor;
public class H2 implements Flavor {
@Override
public String getName() {
return "h2";
}
@Override
public boolean supports(String url) {
return url.startsWith("jdbc:h2:");
}
@Override
public String driverClass() {
return "org.h2.Driver";
}
@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 typeLocalDateTime() {
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 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();
}
}

@ -2,11 +2,6 @@ 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
@ -80,8 +75,8 @@ public class Hsql implements Flavor {
}
@Override
public String typeDate() {
return "timestamp(3)";
public String typeLocalDateTime() {
return "timestamp with time zone";
}
@Override
@ -149,18 +144,6 @@ public class Hsql implements Flavor {
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";

@ -0,0 +1,177 @@
package org.xbib.jdbc.query.flavor;
import org.xbib.jdbc.query.Flavor;
public class MySQL implements Flavor {
@Override
public String getName() {
return "mysql";
}
@Override
public boolean supports(String url) {
return url.startsWith("jdbc:mysql:");
}
@Override
public String driverClass() {
return "com.mysql.jdbc.Driver";
}
@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 typeLocalDateTime() {
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 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();
}
}

@ -2,11 +2,6 @@ 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
@ -60,7 +55,7 @@ public class Oracle implements Flavor {
}
@Override
public String typeDate() {
public String typeLocalDateTime() {
return "timestamp(3)";
}
@ -152,18 +147,6 @@ public class Oracle implements Flavor {
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 "";

@ -2,11 +2,6 @@ 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
@ -80,7 +75,7 @@ public class Postgresql implements Flavor {
}
@Override
public String typeDate() {
public String typeLocalDateTime() {
return "timestamp(3)";
}
@ -149,18 +144,6 @@ public class Postgresql implements Flavor {
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 "";

@ -2,11 +2,6 @@ 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
@ -60,7 +55,7 @@ public class SqlServer implements Flavor {
}
@Override
public String typeDate() {
public String typeLocalDateTime() {
return "datetime2(3)";
}
@ -153,18 +148,6 @@ public class SqlServer implements Flavor {
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 "";

@ -6,7 +6,8 @@ import org.xbib.jdbc.query.SqlNull;
import java.io.InputStream;
import java.io.Reader;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -77,9 +78,15 @@ public class DebugSql {
} else if (argToPrint instanceof SqlNull || argToPrint == null) {
buf.append("null");
} else if (argToPrint instanceof java.sql.Timestamp) {
buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps()));
java.sql.Timestamp timestamp = (java.sql.Timestamp) argToPrint;
LocalDateTime localDateTime = timestamp.toLocalDateTime();
buf.append(localDateTime.toString());
//buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps()));
} else if (argToPrint instanceof java.sql.Date) {
buf.append(options.flavor().localDateAsSqlFunction((java.sql.Date) argToPrint));
java.sql.Date date = (java.sql.Date) argToPrint;
LocalDate localDate = date.toLocalDate();
buf.append(localDate.toString());
//buf.append(options.flavor().localDateAsSqlFunction((java.sql.Date) argToPrint));
} else if (argToPrint instanceof Number) {
buf.append(argToPrint);
} else if (argToPrint instanceof Boolean) {

@ -1,6 +1,8 @@
org.xbib.jdbc.query.flavor.BigQuery
org.xbib.jdbc.query.flavor.Derby
org.xbib.jdbc.query.flavor.Hsql
org.xbib.jdbc.query.flavor.H2
org.xbib.jdbc.query.flavor.MySQL
org.xbib.jdbc.query.flavor.Oracle
org.xbib.jdbc.query.flavor.Postgresql
org.xbib.jdbc.query.flavor.SqlServer

@ -25,7 +25,6 @@ import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.ResultSetMetaData;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -36,9 +35,9 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.hamcrest.MatcherAssert.assertThat;
@ -64,22 +63,15 @@ public abstract class CommonTest {
protected Database db;
protected LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
protected LocalDateTime now;
protected LocalDate localDateNow = LocalDate.now();
protected LocalDate localDateNow;
@BeforeEach
public void setupJdbc() throws Exception {
now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
localDateNow = LocalDate.now();
dbp = createDatabaseProvider(new OptionsOverride() {
@Override
public LocalDateTime currentDate() {
return now;
}
@Override
public Calendar calendarForTimestamps() {
return Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));
}
});
db = dbp.get();
db.dropTableQuietly(TEST_TABLE_NAME);
@ -153,13 +145,13 @@ public abstract class CommonTest {
.addColumn("str_fixed").asStringFixed(1).table()
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().table().schema().execute(db);
BigDecimal bigDecimal = new BigDecimal("5.3");
db.toInsert("insert into dbtest values (?,?,?,?,?,?,?,?,?,?,?)").argInteger(1).argLong(2L).argFloat(3.2f).argDouble(4.2)
.argBigDecimal(bigDecimal).argString("Hello").argString("T").argClobString("World")
.argBlobBytes("More".getBytes()).argDate(now).argLocalDate(localDateNow).insert(1);
.argBlobBytes("More".getBytes()).argLocalDateTime(now).argLocalDate(localDateNow).insert(1);
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
+ "bin_blob, date_millis, local_date from dbtest")
@ -286,14 +278,14 @@ public abstract class CommonTest {
.argBigDecimal("bd", bigDecimal)
.argString("s", "Hello")
.argString("sf", "T")
.argDate("date", now)
.argLocalDateTime("date", now)
.argLocalDate("local_date", localDateNow)
.queryLongOrNull());
List<Long> result = db.toSelect("select count(*) from dbtest where nbr_integer=:i and nbr_long=:l and "
+ "abs(nbr_float-:f)<0.01 and abs(nbr_double-:d)<0.01 and nbr_big_decimal=:bd and str_varchar=:s "
+ "and str_fixed=:sf and date_millis=:date and local_date=:local_date").argInteger("i", 1).argLong("l", 2L).argFloat("f", 3.2f)
.argDouble("d", 4.2).argBigDecimal("bd", bigDecimal).argString("s", "Hello").argString("sf", "T")
.argDate("date", now).argLocalDate("local_date", localDateNow).queryLongs();
.argLocalDateTime("date", now).argLocalDate("local_date", localDateNow).queryLongs();
assertEquals(1, result.size());
assertEquals(Long.valueOf(1), result.get(0));
}
@ -312,7 +304,7 @@ public abstract class CommonTest {
.addColumn("str_fixed").asStringFixed(1).table()
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().table().schema().execute(db);
BigDecimal bigDecimal = new BigDecimal("5.3");
@ -327,12 +319,12 @@ public abstract class CommonTest {
.argString("T")
.argClobString("World")
.argBlobBytes("More".getBytes())
.argDate(now)
.argLocalDateTime(now)
.argLocalDate(localDateNow).insert());
db.toUpdate("update dbtest set nbr_integer=?, nbr_long=?, nbr_float=?, nbr_double=?, nbr_big_decimal=?, "
+ "str_varchar=?, str_fixed=?, str_lob=?, bin_blob=?, date_millis=?, local_date=?").argInteger(null).argLong(null)
.argFloat(null).argDouble(null).argBigDecimal(null).argString(null).argString(null).argClobString(null)
.argBlobBytes(null).argDate(null).argLocalDate(null).update(1);
.argBlobBytes(null).argLocalDateTime(null).argLocalDate(null).update(1);
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
+ "bin_blob, date_millis, local_date from dbtest").query((RowsHandler<Void>) rs -> {
assertTrue(rs.next());
@ -363,7 +355,7 @@ public abstract class CommonTest {
assertEquals(1, db.toUpdate("update dbtest set nbr_integer=?, nbr_long=?, nbr_float=?, nbr_double=?, "
+ "nbr_big_decimal=?, str_varchar=?, str_fixed=?, str_lob=?, bin_blob=?, date_millis=?, local_date=?").argInteger(1)
.argLong(2L).argFloat(3.2f).argDouble(4.2).argBigDecimal(bigDecimal).argString("Hello").argString("T")
.argClobString("World").argBlobBytes("More".getBytes()).argDate(now).argLocalDate(localDateNow).update());
.argClobString("World").argBlobBytes("More".getBytes()).argLocalDateTime(now).argLocalDate(localDateNow).update());
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
+ "bin_blob, date_millis, local_date from dbtest").query((RowsHandler<Void>) rs -> {
assertTrue(rs.next());
@ -426,20 +418,20 @@ public abstract class CommonTest {
.addColumn("str_fixed").asStringFixed(1).table()
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().table().schema().execute(db);
BigDecimal bigDecimal = new BigDecimal("5.3");
db.toInsert("insert into dbtest values (:pk,:a,:b,:c,:d,:e,:f,:sf,:g,:h,:i,:j)").argLong(":pk", 1L).argInteger(":a", 1)
.argLong(":b", 2L).argFloat(":c", 3.2f).argDouble(":d", 4.2).argBigDecimal(":e", bigDecimal)
.argString(":f", "Hello").argString(":sf", "T")
.argClobString(":g", "World").argBlobBytes(":h", "More".getBytes())
.argDate(":i", now).argLocalDate(":j", localDateNow).insert(1);
.argLocalDateTime(":i", now).argLocalDate(":j", localDateNow).insert(1);
db.toUpdate("update dbtest set nbr_integer=:a, nbr_long=:b, nbr_float=:c, nbr_double=:d, nbr_big_decimal=:e, "
+ "str_varchar=:f, str_fixed=:sf, str_lob=:g, bin_blob=:h, date_millis=:i, local_date=:j").argInteger(":a", null)
.argLong(":b", null).argFloat(":c", null).argDouble(":d", null).argBigDecimal(":e", null)
.argString(":f", null).argString(":sf", null)
.argClobString(":g", null).argBlobBytes(":h", null)
.argDate(":i", null).argLocalDate(":j", null).update(1);
.argLocalDateTime(":i", null).argLocalDate(":j", null).update(1);
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
+ "bin_blob, date_millis, local_date from dbtest").query((RowsHandler<Void>) rs -> {
assertTrue(rs.next());
@ -472,7 +464,7 @@ public abstract class CommonTest {
.argLong(":b", 2L).argFloat(":c", 3.2f).argDouble(":d", 4.2).argBigDecimal(":e", bigDecimal)
.argString(":f", "Hello").argString(":sf", "T")
.argClobString(":g", "World").argBlobBytes(":h", "More".getBytes())
.argDate(":i", now).argLocalDate(":j", localDateNow).update(1);
.argLocalDateTime(":i", now).argLocalDate(":j", localDateNow).update(1);
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
+ "bin_blob, date_millis, local_date from dbtest")
.query((RowsHandler<Void>) rs -> {
@ -537,11 +529,11 @@ public abstract class CommonTest {
.addColumn("str_fixed").asStringFixed(1).table()
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().table().schema().execute(db);
db.toInsert("insert into dbtest values (?,?,?,?,?,?,?,?,?,?,?,?)").argLong(1L).argInteger(null).argLong(null)
.argFloat(null).argDouble(null).argBigDecimal(null).argString(null).argString(null).argClobString(null)
.argBlobBytes(null).argDate(null).argLocalDate(null).insert(1);
.argBlobBytes(null).argLocalDateTime(null).argLocalDate(null).insert(1);
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar, str_fixed, str_lob, "
+ "bin_blob, date_millis, local_date from dbtest")
.query((RowsHandler<Void>) rs -> {
@ -604,7 +596,7 @@ public abstract class CommonTest {
String dateColumnName = "local_date";
new Schema()
.addTable("dbtest")
.addColumn(timestampColumnName).asDate().table()
.addColumn(timestampColumnName).asLocalDateTime().table()
.addColumn(dateColumnName).asLocalDate().table().schema().execute(db);
db.toSelect("select * from dbtest").query((RowsHandler<Void>) rs -> {
ResultSetMetaData metadata = rs.getMetadata();
@ -612,8 +604,10 @@ public abstract class CommonTest {
String columnName = metadata.getColumnName(i);
String columnType = metadata.getColumnTypeName(i);
if (columnName.equalsIgnoreCase(timestampColumnName)) {
if ("sqlserver".equals(db.flavor().toString())) {
if ("sqlserver".equals(db.flavor().getName())) {
assertEquals("DATETIME2", columnType.toUpperCase());
} else if ("hsqldb".equals(db.flavor().getName())) {
assertEquals("TIMESTAMP WITH TIME ZONE", columnType.toUpperCase());
} else {
assertEquals("TIMESTAMP", columnType.toUpperCase());
}
@ -629,11 +623,11 @@ public abstract class CommonTest {
@Test
public void intervals() {
new Schema().addTable("dbtest").addColumn("d").asDate().schema().execute(db);
db.toInsert("insert into dbtest (d) values (?)").argDate(now).insert(1);
new Schema().addTable("dbtest").addColumn("d").asLocalDateTime().schema().execute(db);
db.toInsert("insert into dbtest (d) values (?)").argLocalDateTime(now).insert(1);
assertEquals(1, db.toSelect("select count(1) from dbtest where d - interval '1' hour * ? < ?")
.argInteger(2)
.argDate(now)
.argLocalDateTime(now)
.queryIntegerOrZero());
}
@ -650,7 +644,7 @@ public abstract class CommonTest {
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("boolean_flag").asBoolean().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().schema().execute(db);
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 (?,?,?,?,?,?,?,?,?,?,?,?)")
@ -658,14 +652,16 @@ public abstract class CommonTest {
.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);
.argLocalDateTime(now)
.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).argFloat(0.000001f)
.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);
.argLocalDateTime(now)
.argLocalDate(localDateNow).insert(1);
String expectedSchema = new Schema().addTable("dbtest2")
.addColumn("nbr_integer").asInteger().table()
.addColumn("nbr_long").asLong().table()
@ -677,7 +673,7 @@ public abstract class CommonTest {
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("boolean_flag").asBoolean().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().schema().print(db.flavor());
List<SqlArgs> args = db.toSelect("select 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 from dbtest")
@ -719,7 +715,7 @@ public abstract class CommonTest {
.argClobString("str_lob", "bye again")
.argBlobBytes("bin_blob", new byte[]{'3', '4'})
.argString("boolean_flag", "N")//.argBoolean("boolean_flag", false)
.argDate("date_millis", now)
.argLocalDateTime("date_millis", now)
.argLocalDate("local_date", localDateNow),
new SqlArgs().argInteger("nbr_integer", Integer.MAX_VALUE)
.argLong("nbr_long", Long.MAX_VALUE)
@ -731,7 +727,7 @@ public abstract class CommonTest {
.argClobString("str_lob", "hello again")
.argBlobBytes("bin_blob", new byte[]{'1', '2'})
.argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true)
.argDate("date_millis", now)
.argLocalDateTime("date_millis", now)
.argLocalDate("local_date", localDateNow)),
db.toSelect("select 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 from dbtest2 order by 1")
@ -1368,25 +1364,26 @@ public abstract class CommonTest {
@Test
public void insertReturningAppDate() {
db.dropSequenceQuietly("dbtest_seq");
new Schema()
.addTable("dbtest")
.addColumn("pk").primaryKey().table()
.addColumn("d").asDate().table().schema()
.addColumn("d")
.asLocalDateTime()
.table()
.schema()
.addSequence("dbtest_seq").schema()
.execute(db);
db.toInsert("insert into dbtest (pk, d) values (:seq, :d)")
.argPkSeq(":seq", "dbtest_seq")
.argDateNowPerApp(":d")
.argLocalDateTime(":d", now)
.insertReturning("dbtest", "pk", rs -> {
assertTrue(rs.next());
assertEquals(Long.valueOf(1L), rs.getLongOrNull(1));
assertThat(rs.getLocalDateTimeOrNull(2), equalTo(now));
assertThat(rs.getLocalDateTimeOrNull(2, ZoneId.systemDefault()), equalTo(now));
assertFalse(rs.next());
return null;
}, "d");
assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argDate(now).queryLongOrNull());
assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argLocalDateTime(now).queryLongOrNull());
}
@Test
@ -1394,18 +1391,20 @@ public abstract class CommonTest {
new Schema()
.addTable("dbtest")
.addColumn("pk").primaryKey().table()
.addColumn("d").asDate().table()
.addColumn("d2").asDate().table()
.addColumn("d").asLocalDateTime().table()
.addColumn("d2").asLocalDateTime().table()
.addColumn("d3").asLocalDate().table()
.addColumn("d4").asLocalDate().table()
.addColumn("s").asString(5).table()
.addColumn("s2").asString(5).table()
.addColumn("i").asInteger().table().schema()
.execute(db);
db.toInsert("insert into dbtest (pk, d, d3, s) values (?,?,?,?)")
.argLong(1L).argDateNowPerApp().argLocalDate(localDateNow).argString("foo").insert(1);
.argLong(1L)
.argLocalDateTime(now)
.argLocalDate(localDateNow)
.argString("foo")
.insert(1);
assertEquals(Long.valueOf(1L), db.toSelect("select pk from dbtest").queryLongOrNull());
assertNull(db.toSelect("select pk from dbtest where 1=0").queryLongOrNull());
assertNull(db.toSelect("select i from dbtest").queryLongOrNull());
@ -1415,7 +1414,6 @@ public abstract class CommonTest {
assertEquals(1L, (long) db.toSelect("select pk from dbtest").queryLongs().get(0));
assertTrue(db.toSelect("select pk from dbtest where 1=0").queryLongs().isEmpty());
assertTrue(db.toSelect("select i from dbtest").queryLongs().isEmpty());
assertEquals(Integer.valueOf(1), db.toSelect("select pk from dbtest").queryIntegerOrNull());
assertNull(db.toSelect("select pk from dbtest where 1=0").queryIntegerOrNull());
assertNull(db.toSelect("select i from dbtest").queryIntegerOrNull());
@ -1425,7 +1423,6 @@ public abstract class CommonTest {
assertEquals(1L, (int) db.toSelect("select pk from dbtest").queryIntegers().get(0));
assertTrue(db.toSelect("select pk from dbtest where 1=0").queryIntegers().isEmpty());
assertTrue(db.toSelect("select i from dbtest").queryIntegers().isEmpty());
assertEquals("foo", db.toSelect("select s from dbtest").queryStringOrNull());
assertNull(db.toSelect("select s from dbtest where 1=0").queryStringOrNull());
assertNull(db.toSelect("select s2 from dbtest").queryStringOrNull());
@ -1435,13 +1432,12 @@ public abstract class CommonTest {
assertEquals("foo", db.toSelect("select s from dbtest").queryStrings().get(0));
assertTrue(db.toSelect("select s from dbtest where 1=0").queryStrings().isEmpty());
assertTrue(db.toSelect("select s2 from dbtest").queryStrings().isEmpty());
assertEquals(now, db.toSelect("select d from dbtest").queryDateOrNull());
assertNull(db.toSelect("select d from dbtest where 1=0").queryDateOrNull());
assertNull(db.toSelect("select d2 from dbtest").queryDateOrNull());
assertEquals(db.toSelect("select d from dbtest").queryDates().get(0), now);
assertTrue(db.toSelect("select d from dbtest where 1=0").queryDates().isEmpty());
assertTrue(db.toSelect("select d2 from dbtest").queryDates().isEmpty());
assertEquals(now, db.toSelect("select d from dbtest").queryLocalDateTimeOrNull());
assertNull(db.toSelect("select d from dbtest where 1=0").queryLocalDateTimeOrNull());
assertNull(db.toSelect("select d2 from dbtest").queryLocalDateTimeOrNull());
assertEquals(db.toSelect("select d from dbtest").queryLocalDateTimes().get(0), now);
assertTrue(db.toSelect("select d from dbtest where 1=0").queryLocalDateTimes().isEmpty());
assertTrue(db.toSelect("select d2 from dbtest").queryLocalDateTimes().isEmpty());
assertEquals(localDateNow, db.toSelect("select d3 from dbtest").queryLocalDateOrNull());
assertNull(db.toSelect("select d3 from dbtest where 1=0").queryLocalDateOrNull());
@ -1511,12 +1507,12 @@ public abstract class CommonTest {
new Schema()
.addTable("dbtest")
.addColumn("pk").primaryKey().table()
.addColumn("d").asDate().table().schema()
.addColumn("d").asLocalDateTime().table().schema()
.addSequence("dbtest_seq").schema()
.execute(db);
LocalDateTime dbNow = db.toInsert("insert into dbtest (pk, d) values (:seq, :d)")
.argPkSeq(":seq", "dbtest_seq")
.argDateNowPerDb(":d")
.argLocalDateTimeNowPerDb(":d")
.insertReturning("dbtest", "pk", rs -> {
assertTrue(rs.next());
assertEquals(Long.valueOf(1L), rs.getLongOrNull(1));
@ -1524,7 +1520,8 @@ public abstract class CommonTest {
assertFalse(rs.next());
return dbDate;
}, "d");
assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d=?").argDate(dbNow).queryLongOrNull());
assertEquals(Long.valueOf(1L),
db.toSelect("select count(*) from dbtest where d = ?").argLocalDateTime(dbNow).queryLongOrNull());
}
@Test
@ -1581,12 +1578,12 @@ public abstract class CommonTest {
for (int attempts = 1; attempts <= 10; attempts++) {
new Schema()
.addTable("dbtest")
.addColumn("d").asDate().table().schema()
.addColumn("d").asLocalDateTime().table().schema()
.execute(db);
db.toInsert("insert into dbtest (d) values (?)")
.argDateNowPerDb()
.argLocalDateTimeNowPerDb()
.insert(1);
LocalDateTime dbNow = db.toSelect("select d from dbtest").queryDateOrNull();
LocalDateTime dbNow = db.toSelect("select d from dbtest").queryLocalDateTimeOrNull();
if (dbNow != null && dbNow.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() % 10 != 0) {
return;
}
@ -1600,19 +1597,17 @@ public abstract class CommonTest {
public void dateRoundTrip() {
new Schema()
.addTable("dbtest")
.addColumn("d1").asDate().table()
.addColumn("d2").asDate().table().schema()
.addColumn("d1").asLocalDateTime().table()
.addColumn("d2").asLocalDateTime().table()
.schema()
.execute(db);
// Store current time as per the database
db.toInsert("insert into dbtest (d1) values (?)")
.argDateNowPerDb()
.argLocalDateTimeNowPerDb()
.insert(1);
// Now pull it out, put it back in, and verify it matches in the database
LocalDateTime dbNow = db.toSelect("select d1 from dbtest").queryDateOrNull();
db.toUpdate("update dbtest set d2=?")
.argDate(dbNow)
LocalDateTime dbNow = db.toSelect("select d1 from dbtest").queryLocalDateTimeOrNull();
db.toUpdate("update dbtest set d2 = ?")
.argLocalDateTime(dbNow)
.update(1);
assertEquals(Long.valueOf(1L), db.toSelect("select count(*) from dbtest where d1=d2").queryLongOrNull());
}
@ -1620,66 +1615,75 @@ public abstract class CommonTest {
public void dateRoundTripTimezones() {
new Schema()
.addTable("dbtest")
.addColumn("d").asDate().table().schema()
.addColumn("d")
.asLocalDateTime()
.table()
.schema()
.execute(db);
Instant instant = Instant.ofEpochMilli(166656789L);
LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)));
LocalDateTime datePlus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4)));
TimeZone defaultTZ = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00"));
db.toInsert("insert into dbtest (d) values (?)").argDate(dateMinus).insert(1);
LocalDateTime localDateTimeMinus = db.toSelect("select d from dbtest").queryDateOrNull();
assertEquals(dateMinus, localDateTimeMinus);
assertEquals("1970-01-02 18:17:36.789", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimeMinus));
db.toDelete("delete from dbtest where d=?").argDate(dateMinus).update(1);
TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00"));
db.toInsert("insert into dbtest (d) values (?)").argDate(datePlus).insert(1);
LocalDateTime localDateTimePlus = db.toSelect("select d from dbtest").queryDateOrNull();
assertEquals(datePlus, localDateTimePlus);
assertEquals("1970-01-03 02:17:36.789", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimePlus));
db.toDelete("delete from dbtest where d=?").argDate(datePlus).update(1);
} finally {
TimeZone.setDefault(defaultTZ);
}
ZoneId dateMinusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4));
LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, dateMinusZone);
ZoneId datePlusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4));
LocalDateTime datePlus = LocalDateTime.ofInstant(instant,datePlusZone);
logger.log(Level.INFO, "dateMinus = " + dateMinus);
db.toInsert("insert into dbtest (d) values (?)")
.argLocalDateTime(dateMinus)
.insert(1);
LocalDateTime localDateTimeMinus = db.toSelect("select d from dbtest")
.queryLocalDateTimeOrNull();
assertEquals(dateMinus, localDateTimeMinus);
assertEquals("1970-01-02 18:17:36.789",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimeMinus));
db.toDelete("delete from dbtest where d = ?").argLocalDateTime(dateMinus).update(1);
db.toInsert("insert into dbtest (d) values (?)")
.argLocalDateTime(datePlus)
.insert(1);
LocalDateTime localDateTimePlus = db.toSelect("select d from dbtest")
.queryLocalDateTimeOrNull(datePlusZone);
assertEquals(datePlus, localDateTimePlus);
assertEquals("1970-01-03 02:17:36.789",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTimePlus));
db.toDelete("delete from dbtest where d = ?").argLocalDateTime(datePlus).update(1);
}
/**
* Verify the appropriate database flavor can correctly convert a {@code Date}
* Verify the appropriate database flavor can correctly convert a {@code LocalDateTime}
* into a SQL function representing a conversion from string to timestamp. This
* function is used to write debug SQL to the log in a way that could be manually
* executed if desired.
*/
@Test
public void stringDateFunctions() {
public void stringLocalDateTimeFunctions() {
Instant instant = Instant.ofEpochMilli(166656789L);
LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)));
LocalDateTime datePlus = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4)));
logger.info("LocalDateTime: " + dateMinus + " " + datePlus);
TimeZone defaultTZ = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("GMT-4:00"));
new Schema().addTable("dbtest").addColumn("d").asDate().schema().execute(db);
db.toInsert("insert into dbtest (d) values ("
+ db.flavor().dateAsSqlFunction(Timestamp.valueOf(dateMinus), db.options().calendarForTimestamps()).replace(":", "::") + ")")
.insert(1);
assertEquals("1970-01-02 18:17:36.789",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(db.toSelect("select d from dbtest").queryDateOrNull()));
// Now do some client operations in a different time zone
TimeZone.setDefault(TimeZone.getTimeZone("GMT+4:00"));
// Verify regular arg maps date the same way even though our TimeZone is now different
db.toDelete("delete from dbtest where d=?").argDate(datePlus).update(1);
db.toInsert("insert into dbtest (d) values ("
+ db.flavor().dateAsSqlFunction(Timestamp.valueOf(datePlus), db.options().calendarForTimestamps()).replace(":", "::") + ")")
.insert(1);
assertEquals("1970-01-03 02:17:36.789",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(db.toSelect("select d from dbtest").queryDateOrNull()));
// Verify the function maps correctly for equals operations as well
db.toDelete("delete from dbtest where d=" + db.flavor().dateAsSqlFunction(Timestamp.valueOf(datePlus),
db.options().calendarForTimestamps()).replace(":", "::")).update(1);
} finally {
TimeZone.setDefault(defaultTZ);
}
ZoneId dateMinusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4));
LocalDateTime dateMinus = LocalDateTime.ofInstant(instant, dateMinusZone);
ZoneId datePlusZone = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+4));
LocalDateTime datePlus = LocalDateTime.ofInstant(instant, datePlusZone);
logger.info("LocalDateTime: dateMinus=" + dateMinus + " datePlus=" + datePlus);
new Schema().addTable("dbtest").addColumn("d")
.asLocalDateTime()
.schema()
.execute(db);
db.toInsert("insert into dbtest (d) values (?)")
.argLocalDateTime(dateMinus)
.insert(1);
LocalDateTime localDateTime = db.toSelect("select d from dbtest")
.queryLocalDateTimeOrNull();
assertEquals("1970-01-02 18:17:36.789",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTime.atZone(dateMinusZone)));
db.toDelete("delete from dbtest where d = ?")
.argLocalDateTime(dateMinus)
.update(1);
db.toInsert("insert into dbtest (d) values (?)")
.argLocalDateTime(datePlus)
.insert(1);
localDateTime = db.toSelect("select d from dbtest")
.queryLocalDateTimeOrNull(datePlusZone);
assertEquals("1970-01-03 02:17:36.789",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTime.atZone(datePlusZone)));
db.toDelete("delete from dbtest where d = ?")
.argLocalDateTime(datePlus)
.update(1);
}
@Test
@ -1687,12 +1691,12 @@ public abstract class CommonTest {
new Schema()
.addTable("dbtest")
.addColumn("pk").primaryKey().table()
.addColumn("d").asDate().table()
.addColumn("d").asLocalDateTime().table()
.addColumn("a").asInteger().table().schema()
.execute(db);
db.toSelect("select pk as \"time:: now??\" from dbtest where a=? and d=:now")
.argInteger(1).argDateNowPerDb("now").query(rs -> {
.argInteger(1).argLocalDateTimeNowPerDb("now").query(rs -> {
assertFalse(rs.next());
return null;
});

@ -57,12 +57,6 @@ 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() {
@ -89,7 +83,7 @@ public class HsqldbTest extends CommonTest {
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("boolean_flag").asBoolean().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().schema().execute(db);
db.toInsert("insert into dbtest (nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal, str_varchar,"
@ -104,7 +98,7 @@ public class HsqldbTest extends CommonTest {
.argClobString("hello again")
.argBlobBytes(new byte[]{'1', '2'})
.argBoolean(true)
.argDateNowPerApp()
.argLocalDateTime(now)
.argLocalDate(localDateNow)
.insert(1);
@ -120,7 +114,7 @@ public class HsqldbTest extends CommonTest {
.argClobString("bye again")
.argBlobBytes(new byte[]{'3', '4'})
.argBoolean(false)
.argDateNowPerApp()
.argLocalDateTime(now)
.argLocalDate(localDateNow)
.insert(1);
@ -135,7 +129,7 @@ public class HsqldbTest extends CommonTest {
.addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table()
.addColumn("boolean_flag").asBoolean().table()
.addColumn("date_millis").asDate().table()
.addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().schema().print(db.flavor());
List<SqlArgs> args = db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal,"
@ -175,7 +169,7 @@ public class HsqldbTest extends CommonTest {
.argClobString("str_lob", "bye again")
.argBlobBytes("bin_blob", new byte[]{'3', '4'})
.argString("boolean_flag", "N")//.argBoolean("boolean_flag", false)
.argDate("date_millis", now)
.argLocalDateTime("date_millis", now)
.argLocalDate("local_date", localDateNow),
new SqlArgs()
.argInteger("nbr_integer", Integer.MAX_VALUE)
@ -188,7 +182,7 @@ public class HsqldbTest extends CommonTest {
.argClobString("str_lob", "hello again")
.argBlobBytes("bin_blob", new byte[]{'1', '2'})
.argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true)
.argDate("date_millis", now)
.argLocalDateTime("date_millis", now)
.argLocalDate("local_date", localDateNow)),
db.toSelect("select 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 from dbtest2 order by 1")

@ -12,6 +12,7 @@ import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@ -628,21 +629,32 @@ public class RowStub {
@Override
public LocalDateTime getLocalDateTimeOrNull() {
return toDate(rows.get(row)[++col]);
return toLocalDateTime(rows.get(row)[++col]);
}
@Override
public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) {
col = columnOneBased;
return toDate(rows.get(row)[columnOneBased - 1]);
return toLocalDateTime(rows.get(row)[columnOneBased - 1]);
}
@Override
public LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId) {
col = columnOneBased;
return toLocalDateTime(rows.get(row)[columnOneBased - 1], zoneId);
}
@Override
public LocalDateTime getLocalDateTimeOrNull(String columnName) {
col = columnIndexByName(columnName) + 1;
return toDate(rows.get(row)[columnIndexByName(columnName)]);
return toLocalDateTime(rows.get(row)[columnIndexByName(columnName)]);
}
@Override
public LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId) {
return null;
}
/**
@ -758,14 +770,20 @@ public class RowStub {
return (String) o;
}
private LocalDateTime toDate(Object o) {
private LocalDateTime toLocalDateTime(Object o) {
return toLocalDateTime(o, ZoneId.systemDefault());
}
private LocalDateTime toLocalDateTime(Object o, ZoneId zoneId) {
if (o instanceof String) {
String s = (String) o;
if (s.length() == "yyyy-MM-dd".length()) {
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
.atZone(zoneId).toLocalDateTime();
}
if (s.length() == "yyyy-MM-ddThh:mm:ss".length()) {
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"));
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-ddThh:mm:ss"))
.atZone(zoneId).toLocalDateTime();
}
throw new DatabaseException("Didn't understand date string: " + s);
}

@ -21,7 +21,7 @@ public class InsertReturning extends DerbyExample {
new Schema()
.addTable("t")
.addColumn("pk").primaryKey().table()
.addColumn("d").asDate().table()
.addColumn("d").asLocalDateTime().table()
.addColumn("s").asString(80).schema()
.addSequence("pk_seq").schema().execute(db);
@ -33,7 +33,7 @@ public class InsertReturning extends DerbyExample {
Long pk = db.toInsert(
"insert into t (pk,d,s) values (?,?,?)")
.argPkSeq("pk_seq")
.argDateNowPerDb()
.argLocalDateTimeNowPerDb()
.argString("Hi")
.insertReturningPkSeq("pk");

@ -3,7 +3,6 @@ package org.xbib.jdbc.query.test.example;
import org.xbib.jdbc.query.Database;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.function.Supplier;
/**
@ -19,20 +18,19 @@ public class SampleDao {
public void createSample(final Sample sample, Long userIdMakingChange) {
Database db = dbp.get();
LocalDateTime updateTime = db.nowPerApp();
LocalDateTime updateTime = LocalDateTime.now();
Long sampleId = db.toInsert(
"insert into sample (sample_id, sample_name, update_sequence, update_time) values (?,?,0,?)")
.argPkSeq("id_seq")
.argString(sample.getName())
.argDate(updateTime)
.argLocalDateTime(updateTime)
.insertReturningPkSeq("sample_id");
db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id,"
+ " is_deleted) values (?,?,0,?,?,'N')")
.argLong(sampleId)
.argString(sample.getName())
.argDate(updateTime)
.argLocalDateTime(updateTime)
.argLong(userIdMakingChange)
.insert(1);
@ -61,20 +59,20 @@ public class SampleDao {
// Insert the history row first, so it will fail (non-unique sample_id + update_sequence)
// if someone else modified the row. This is an optimistic locking strategy.
int newUpdateSequence = sample.getUpdateSequence() + 1;
LocalDateTime newUpdateTime = db.nowPerApp();
LocalDateTime newUpdateTime = LocalDateTime.now();
db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id,"
+ " is_deleted) values (?,?,?,?,?,'N')")
.argLong(sample.getSampleId())
.argString(sample.getName())
.argInteger(newUpdateSequence)
.argDate(newUpdateTime)
.argLocalDateTime(newUpdateTime)
.argLong(userIdMakingChange)
.insert(1);
db.toUpdate("update sample set sample_name=?, update_sequence=?, update_time=? where sample_id=?")
.argString(sample.getName())
.argInteger(newUpdateSequence)
.argDate(newUpdateTime)
.argLocalDateTime(newUpdateTime)
.argLong(sample.getSampleId())
.update(1);
@ -89,13 +87,13 @@ public class SampleDao {
// Insert the history row first, so it will fail (non-unique sample_id + update_sequence)
// if someone else modified the row. This is an optimistic locking strategy.
int newUpdateSequence = sample.getUpdateSequence() + 1;
LocalDateTime newUpdateTime = db.nowPerApp();
LocalDateTime newUpdateTime = LocalDateTime.now();
db.toInsert("insert into sample_history (sample_id, sample_name, update_sequence, update_time, update_user_id,"
+ " is_deleted) values (?,?,?,?,?,'Y')")
.argLong(sample.getSampleId())
.argString(sample.getName())
.argInteger(newUpdateSequence)
.argDate(newUpdateTime)
.argLocalDateTime(newUpdateTime)
.argLong(userIdMakingChange)
.insert(1);

Loading…
Cancel
Save