move java.util.Date to java.time

This commit is contained in:
Jörg Prante 2022-10-27 23:38:13 +02:00
parent 1047be8456
commit 625c8999a3
36 changed files with 796 additions and 922 deletions

View file

@ -2,7 +2,6 @@ package org.xbib.jdbc.query;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -90,11 +89,6 @@ public interface Database extends Supplier<Database> {
*/ */
Long nextSequenceValue( String sequenceName); 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 * Cause the underlying connection to commit its transaction immediately. This
* must be explicitly enabled (see {@link Options}, * must be explicitly enabled (see {@link Options},

View file

@ -10,7 +10,6 @@ import java.sql.Statement;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -95,11 +94,6 @@ public class DatabaseImpl implements Database {
return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull(); return toSelect(flavor().sequenceSelectNextVal(sequenceName)).queryLongOrNull();
} }
@Override
public LocalDateTime nowPerApp() {
return options.currentDate();
}
@Override @Override
public void commitNow() { public void commitNow() {
if (options.ignoreTransactionControl()) { if (options.ignoreTransactionControl()) {
@ -250,8 +244,9 @@ public class DatabaseImpl implements Database {
@Override @Override
public void assertTimeSynchronized(long millisToWarn, long millisToError) { public void assertTimeSynchronized(long millisToWarn, long millisToError) {
toSelect("select ?" + flavor().fromAny()) toSelect("select ?" + flavor().fromAny())
.argDateNowPerDb().queryFirstOrNull(r -> { .argLocalDateTimeNowPerDb()
LocalDateTime appDate = nowPerApp(); .queryFirstOrNull(r -> {
LocalDateTime appDate = LocalDateTime.now();
LocalDateTime dbDate = r.getLocalDateTimeOrNull(); LocalDateTime dbDate = r.getLocalDateTimeOrNull();
if (dbDate == null) { if (dbDate == null) {
throw new DatabaseException("Expecting a date in the result"); throw new DatabaseException("Expecting a date in the result");

View file

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

View file

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

View file

@ -1,8 +1,5 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.ServiceLoader; import java.util.ServiceLoader;
public interface Flavor { public interface Flavor {
@ -39,7 +36,7 @@ public interface Flavor {
String typeBlob(); String typeBlob();
String typeDate(); String typeLocalDateTime();
String typeLocalDate(); String typeLocalDate();
@ -72,17 +69,6 @@ public interface Flavor {
*/ */
String fromAny(); 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(); String sequenceOptions();
/** /**

View file

@ -1,8 +1,5 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.time.LocalDateTime;
import java.util.Calendar;
/** /**
* Control various optional behavior for the database interactions. * Control various optional behavior for the database interactions.
*/ */
@ -95,27 +92,10 @@ public interface Options {
Flavor flavor(); Flavor flavor();
/** /**
* The value returned by this method will be used for argDateNowPerApp() calls. It * This is useful for testing purposes as you can use OptionsOverride to provide your
* may also be used for argDateNowPerDb() calls if you have enabled that. * own clock that will be used.
*/ */
LocalDateTime currentDate(); boolean useLocalDateTimeOnly();
/**
* 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>
*/
Calendar calendarForTimestamps();
/** /**
* The maximum number of characters to print in debug SQL for a given String type * The maximum number of characters to print in debug SQL for a given String type

View file

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

View file

@ -2,9 +2,6 @@ package org.xbib.jdbc.query;
import org.xbib.jdbc.query.flavor.Postgresql; 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. * Base class for selectively overriding another Options object.
@ -90,18 +87,8 @@ public class OptionsOverride implements Options {
} }
@Override @Override
public LocalDateTime currentDate() { public boolean useLocalDateTimeOnly() {
return parent.currentDate(); return parent.useLocalDateTimeOnly();
}
@Override
public boolean useDatePerAppOnly() {
return parent.useDatePerAppOnly();
}
@Override
public Calendar calendarForTimestamps() {
return parent.calendarForTimestamps();
} }
@Override @Override

View file

@ -6,6 +6,7 @@ import java.math.BigDecimal;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
/** /**
* Interface for reading results from a database query. * 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 * 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. * method tries to reduce scale if there is zero padding to the right of the decimal.
*/ */
BigDecimal getBigDecimalOrNull(); BigDecimal getBigDecimalOrNull();
BigDecimal getBigDecimalOrNull(int columnOneBased); BigDecimal getBigDecimalOrNull(int columnOneBased);
BigDecimal getBigDecimalOrNull(String columnName); BigDecimal getBigDecimalOrNull(String columnName);
BigDecimal getBigDecimalOrZero(); BigDecimal getBigDecimalOrZero();
BigDecimal getBigDecimalOrZero(int columnOneBased); BigDecimal getBigDecimalOrZero(int columnOneBased);
BigDecimal getBigDecimalOrZero(String columnName); BigDecimal getBigDecimalOrZero(String columnName);
/** /**
* @return the value, or null if it is SQL null; never returns the empty string * @return the value, or null if it is SQL null; never returns the empty string
*/ */
String getStringOrNull(); String getStringOrNull();
/** /**
* @return the value, or null if it is SQL null; never returns the empty string * @return the value, or null if it is SQL null; never returns the empty string
*/ */
String getStringOrNull(int columnOneBased); String getStringOrNull(int columnOneBased);
/** /**
* @return the value, or null if it is SQL null; never returns the empty string * @return the value, or null if it is SQL null; never returns the empty string
*/ */
String getStringOrNull(String columnName); String getStringOrNull(String columnName);
/** /**
* @return the value, or the empty string if it is SQL null; never returns null * @return the value, or the empty string if it is SQL null; never returns null
*/ */
String getStringOrEmpty(); String getStringOrEmpty();
/** /**
* @return the value, or the empty string if it is SQL null; never returns null * @return the value, or the empty string if it is SQL null; never returns null
*/ */
String getStringOrEmpty(int columnOneBased); String getStringOrEmpty(int columnOneBased);
/** /**
* @return the value, or the empty string if it is SQL null; never returns null * @return the value, or the empty string if it is SQL null; never returns null
*/ */
String getStringOrEmpty(String columnName); String getStringOrEmpty(String columnName);
/** /**
* @return the value, or null if it is SQL null; never returns the empty string * @return the value, or null if it is SQL null; never returns the empty string
*/ */
String getClobStringOrNull(); String getClobStringOrNull();
/** /**
* @return the value, or null if it is SQL null; never returns the empty string * @return the value, or null if it is SQL null; never returns the empty string
*/ */
String getClobStringOrNull(int columnOneBased); String getClobStringOrNull(int columnOneBased);
/** /**
* @return the value, or null if it is SQL null; never returns the empty string * @return the value, or null if it is SQL null; never returns the empty string
*/ */
String getClobStringOrNull(String columnName); String getClobStringOrNull(String columnName);
/** /**
* @return the value, or the empty string if it is SQL null; never returns null * @return the value, or the empty string if it is SQL null; never returns null
*/ */
String getClobStringOrEmpty(); String getClobStringOrEmpty();
/** /**
* @return the value, or the empty string if it is SQL null; never returns null * @return the value, or the empty string if it is SQL null; never returns null
*/ */
String getClobStringOrEmpty(int columnOneBased); String getClobStringOrEmpty(int columnOneBased);
/** /**
* @return the value, or the empty string if it is SQL null; never returns null * @return the value, or the empty string if it is SQL null; never returns null
*/ */
String getClobStringOrEmpty(String columnName); String getClobStringOrEmpty(String columnName);
/** /**
* @return the value, or null if it is SQL null * @return the value, or null if it is SQL null
*/ */
Reader getClobReaderOrNull(); Reader getClobReaderOrNull();
/** /**
* @return the value, or null if it is SQL null * @return the value, or null if it is SQL null
*/ */
Reader getClobReaderOrNull(int columnOneBased); Reader getClobReaderOrNull(int columnOneBased);
/** /**
* @return the value, or null if it is SQL null * @return the value, or null if it is SQL null
*/ */
Reader getClobReaderOrNull(String columnName); Reader getClobReaderOrNull(String columnName);
/** /**
* @return the value, or a StringReader containing the empty string if it is SQL null * @return the value, or a StringReader containing the empty string if it is SQL null
*/ */
Reader getClobReaderOrEmpty(); Reader getClobReaderOrEmpty();
/** /**
* @return the value, or a StringReader containing the empty string if it is SQL null * @return the value, or a StringReader containing the empty string if it is SQL null
*/ */
Reader getClobReaderOrEmpty(int columnOneBased); Reader getClobReaderOrEmpty(int columnOneBased);
/** /**
* @return the value, or a StringReader containing the empty string if it is SQL null * @return the value, or a StringReader containing the empty string if it is SQL null
*/ */
Reader getClobReaderOrEmpty(String columnName); Reader getClobReaderOrEmpty(String columnName);
byte[] getBlobBytesOrNull(); byte[] getBlobBytesOrNull();
byte[] getBlobBytesOrNull(int columnOneBased); byte[] getBlobBytesOrNull(int columnOneBased);
byte[] getBlobBytesOrNull(String columnName); byte[] getBlobBytesOrNull(String columnName);
byte[] getBlobBytesOrZeroLen(); byte[] getBlobBytesOrZeroLen();
byte[] getBlobBytesOrZeroLen(int columnOneBased); byte[] getBlobBytesOrZeroLen(int columnOneBased);
byte[] getBlobBytesOrZeroLen(String columnName); byte[] getBlobBytesOrZeroLen(String columnName);
InputStream getBlobInputStreamOrNull(); InputStream getBlobInputStreamOrNull();
InputStream getBlobInputStreamOrNull(int columnOneBased); InputStream getBlobInputStreamOrNull(int columnOneBased);
InputStream getBlobInputStreamOrNull(String columnName); InputStream getBlobInputStreamOrNull(String columnName);
InputStream getBlobInputStreamOrEmpty(); InputStream getBlobInputStreamOrEmpty();
InputStream getBlobInputStreamOrEmpty(int columnOneBased); InputStream getBlobInputStreamOrEmpty(int columnOneBased);
InputStream getBlobInputStreamOrEmpty(String columnName); InputStream getBlobInputStreamOrEmpty(String columnName);
LocalDateTime getLocalDateTimeOrNull(); LocalDateTime getLocalDateTimeOrNull();
LocalDateTime getLocalDateTimeOrNull(int columnOneBased); LocalDateTime getLocalDateTimeOrNull(int columnOneBased);
LocalDateTime getLocalDateTimeOrNull(int columnOneBased, ZoneId zoneId);
LocalDateTime getLocalDateTimeOrNull(String columnName); LocalDateTime getLocalDateTimeOrNull(String columnName);
LocalDateTime getLocalDateTimeOrNull(String columnName, ZoneId zoneId);
/** /**
* Retrieve column as LocalDate, .i.e, date with no time. * Retrieve column as LocalDate, .i.e, date with no time.
* *

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,6 +5,7 @@ import java.io.Reader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
/** /**
* Interface for configuring (setting parameters) and executing a chunk of SQL. * 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 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 argLocalDateTimeNowPerDb(String argName);
SqlInsert argDateNowPerDb(String argName);
SqlInsert argBlobBytes(byte[] arg); 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 * <p>This version of insert expects exactly one row to be inserted, and will throw
* a DatabaseException if that isn't the case.</p> * a DatabaseException if that isn't the case.</p>
*/ */
Long insertReturningPkSeq(String primaryKeyColumnName); Long insertReturningPkSeq(String primaryKeyColumnName);
<T> T insertReturning(String tableName, String primaryKeyColumnName, RowsHandler<T> rowsHandler, <T> T insertReturning(String tableName, String primaryKeyColumnName, RowsHandler<T> rowsHandler,

View file

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

View file

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

View file

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

View file

@ -39,21 +39,17 @@ public interface SqlUpdate {
SqlUpdate argString(String argName, String arg); 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(LocalDate arg);
SqlUpdate argLocalDate(String argName, LocalDate arg); SqlUpdate argLocalDate(String argName, LocalDate arg);
SqlUpdate argDateNowPerApp(); SqlUpdate argLocalDateTimeNowPerDb();
SqlUpdate argDateNowPerApp(String argName);
SqlUpdate argDateNowPerDb(); SqlUpdate argLocalDateTimeNowPerDb(String argName);
SqlUpdate argDateNowPerDb(String argName);
SqlUpdate argBlobBytes(byte[] arg); SqlUpdate argBlobBytes(byte[] arg);

View file

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

View file

@ -1,6 +1,6 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.io.ByteArrayOutputStream; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
@ -13,75 +13,32 @@ import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date; import java.util.Date;
import java.util.Scanner;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors;
/** /**
* Deal with mapping parameters into prepared statements. * Deal with mapping parameters into prepared statements.
*/ */
public class StatementAdaptor { public class StatementAdaptor {
private static final Logger logger = Logger.getLogger(StatementAdaptor.class.getName());
private final Options options; private final Options options;
public StatementAdaptor(Options options) { public StatementAdaptor(Options options) {
this.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 { public void addParameters(PreparedStatement ps, Object[] parameters) throws SQLException {
for (int i = 0; i < parameters.length; i++) { for (int i = 0; i < parameters.length; i++) {
Object parameter = parameters[i]; Object parameter = parameters[i];
// Unwrap secret args here so we can use them // Unwrap secret args here so we can use them
if (parameter instanceof SecretArg) { if (parameter instanceof SecretArg) {
parameter = ((SecretArg) parameter).getArg(); parameter = ((SecretArg) parameter).getArg();
} }
if (parameter == null) { if (parameter == null) {
ParameterMetaData metaData; ParameterMetaData metaData;
int parameterType; int parameterType;
@ -106,57 +63,52 @@ public class StatementAdaptor {
} else if (parameter instanceof java.sql.Date) { } else if (parameter instanceof java.sql.Date) {
ps.setDate(i + 1, (java.sql.Date) parameter); ps.setDate(i + 1, (java.sql.Date) parameter);
} else if (parameter instanceof Date) { } 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 // 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 // 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) { } else if (parameter instanceof Reader) {
if (options.useStringForClob()) { 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 { } else {
ps.setCharacterStream(i + 1, (Reader) parameter); ps.setCharacterStream(i + 1, (Reader) parameter);
} }
} else if (parameter instanceof InputStream) { } else if (parameter instanceof InputStream) {
if (options.useBytesForBlob()) { 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 { } else {
ps.setBinaryStream(i + 1, (InputStream) parameter); ps.setBinaryStream(i + 1, (InputStream) parameter);
} }
} else if (parameter instanceof Float) { } else if (parameter instanceof Float) {
//if (options.flavor() == Flavor.oracle && ps.isWrapperFor(OraclePreparedStatement.class)) { ps.setFloat(i + 1, (Float) parameter);
// 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);
//}
} else if (parameter instanceof Double) { } else if (parameter instanceof Double) {
//if (options.flavor() == Flavor.oracle && ps.isWrapperFor(OraclePreparedStatement.class)) { ps.setDouble(i + 1, (Double) parameter);
// 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);
//}
} else { } else {
ps.setObject(i + 1, parameter); ps.setObject(i + 1, parameter);
} }
} }
} }
public Object nullDate(LocalDateTime arg) { public Object nullLocalDateTime(LocalDateTime arg) {
if (arg == null) { return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg);
return new SqlNull(Types.TIMESTAMP);
}
return Timestamp.valueOf(arg);
} }
// Processes a true date without time information. public Object nullLocalDateTime(LocalDateTime arg, ZoneId zoneId) {
public Object nullLocalDate(LocalDate arg) { return arg == null ? new SqlNull(Types.TIMESTAMP) : Timestamp.valueOf(arg.atZone(zoneId).toLocalDateTime());
if (arg == null) { }
return new SqlNull(Types.DATE);
}
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) { public Object nullNumeric(Number arg) {
@ -217,5 +169,4 @@ public class StatementAdaptor {
} }
} }
} }
} }

View file

@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor;
import 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 { public class BigQuery implements Flavor {
@Override @Override
@ -81,7 +76,7 @@ public class BigQuery implements Flavor {
} }
@Override @Override
public String typeDate() { public String typeLocalDateTime() {
return "datetime"; return "datetime";
} }
@ -150,20 +145,6 @@ public class BigQuery implements Flavor {
return ""; 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 @Override
public String sequenceOptions() { public String sequenceOptions() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View file

@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor;
import 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 { public class Derby implements Flavor {
@Override @Override
@ -80,7 +75,7 @@ public class Derby implements Flavor {
} }
@Override @Override
public String typeDate() { public String typeLocalDateTime() {
return "timestamp"; return "timestamp";
} }
@ -149,18 +144,6 @@ public class Derby implements Flavor {
return " from sysibm.sysdummy1"; 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 @Override
public String sequenceOptions() { public String sequenceOptions() {
return " as bigint"; return " as bigint";

View file

@ -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();
}
}

View file

@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor;
import 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 { public class Hsql implements Flavor {
@Override @Override
@ -80,8 +75,8 @@ public class Hsql implements Flavor {
} }
@Override @Override
public String typeDate() { public String typeLocalDateTime() {
return "timestamp(3)"; return "timestamp with time zone";
} }
@Override @Override
@ -149,18 +144,6 @@ public class Hsql implements Flavor {
return ""; 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 @Override
public String sequenceOptions() { public String sequenceOptions() {
return " as bigint"; return " as bigint";

View file

@ -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();
}
}

View file

@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor;
import 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 { public class Oracle implements Flavor {
@Override @Override
@ -60,7 +55,7 @@ public class Oracle implements Flavor {
} }
@Override @Override
public String typeDate() { public String typeLocalDateTime() {
return "timestamp(3)"; return "timestamp(3)";
} }
@ -152,18 +147,6 @@ public class Oracle implements Flavor {
return " from dual"; 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 @Override
public String sequenceOptions() { public String sequenceOptions() {
return ""; return "";

View file

@ -2,11 +2,6 @@ package org.xbib.jdbc.query.flavor;
import 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 { public class Postgresql implements Flavor {
@Override @Override
@ -80,7 +75,7 @@ public class Postgresql implements Flavor {
} }
@Override @Override
public String typeDate() { public String typeLocalDateTime() {
return "timestamp(3)"; return "timestamp(3)";
} }
@ -149,18 +144,6 @@ public class Postgresql implements Flavor {
return " cache " + nbrValuesToCache; return " cache " + nbrValuesToCache;
} }
@Override
public String dateAsSqlFunction(Timestamp date, Calendar calendar) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS000");
dateFormat.setCalendar(calendar);
return "'" + dateFormat.format(date) + " GMT'::timestamp";
}
@Override
public String localDateAsSqlFunction(Date date) {
return "'" + date.toString() + "'";
}
@Override @Override
public String sequenceOptions() { public String sequenceOptions() {
return ""; return "";

View file

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

View file

@ -6,7 +6,8 @@ import org.xbib.jdbc.query.SqlNull;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.sql.Timestamp; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -77,9 +78,15 @@ public class DebugSql {
} else if (argToPrint instanceof SqlNull || argToPrint == null) { } else if (argToPrint instanceof SqlNull || argToPrint == null) {
buf.append("null"); buf.append("null");
} else if (argToPrint instanceof java.sql.Timestamp) { } 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) { } 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) { } else if (argToPrint instanceof Number) {
buf.append(argToPrint); buf.append(argToPrint);
} else if (argToPrint instanceof Boolean) { } else if (argToPrint instanceof Boolean) {

View file

@ -1,6 +1,8 @@
org.xbib.jdbc.query.flavor.BigQuery org.xbib.jdbc.query.flavor.BigQuery
org.xbib.jdbc.query.flavor.Derby org.xbib.jdbc.query.flavor.Derby
org.xbib.jdbc.query.flavor.Hsql 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.Oracle
org.xbib.jdbc.query.flavor.Postgresql org.xbib.jdbc.query.flavor.Postgresql
org.xbib.jdbc.query.flavor.SqlServer org.xbib.jdbc.query.flavor.SqlServer

View file

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

View file

@ -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.") @Disabled("LocalDate implementations should be TimeZone agnostic, but HSQLDB implementation has a bug.")
@Test @Test
public void argLocalDateTimeZones() { public void argLocalDateTimeZones() {
@ -89,7 +83,7 @@ public class HsqldbTest extends CommonTest {
.addColumn("str_lob").asClob().table() .addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table() .addColumn("bin_blob").asBlob().table()
.addColumn("boolean_flag").asBoolean().table() .addColumn("boolean_flag").asBoolean().table()
.addColumn("date_millis").asDate().table() .addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().schema().execute(db); .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," 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") .argClobString("hello again")
.argBlobBytes(new byte[]{'1', '2'}) .argBlobBytes(new byte[]{'1', '2'})
.argBoolean(true) .argBoolean(true)
.argDateNowPerApp() .argLocalDateTime(now)
.argLocalDate(localDateNow) .argLocalDate(localDateNow)
.insert(1); .insert(1);
@ -120,7 +114,7 @@ public class HsqldbTest extends CommonTest {
.argClobString("bye again") .argClobString("bye again")
.argBlobBytes(new byte[]{'3', '4'}) .argBlobBytes(new byte[]{'3', '4'})
.argBoolean(false) .argBoolean(false)
.argDateNowPerApp() .argLocalDateTime(now)
.argLocalDate(localDateNow) .argLocalDate(localDateNow)
.insert(1); .insert(1);
@ -135,7 +129,7 @@ public class HsqldbTest extends CommonTest {
.addColumn("str_lob").asClob().table() .addColumn("str_lob").asClob().table()
.addColumn("bin_blob").asBlob().table() .addColumn("bin_blob").asBlob().table()
.addColumn("boolean_flag").asBoolean().table() .addColumn("boolean_flag").asBoolean().table()
.addColumn("date_millis").asDate().table() .addColumn("date_millis").asLocalDateTime().table()
.addColumn("local_date").asLocalDate().schema().print(db.flavor()); .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," 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") .argClobString("str_lob", "bye again")
.argBlobBytes("bin_blob", new byte[]{'3', '4'}) .argBlobBytes("bin_blob", new byte[]{'3', '4'})
.argString("boolean_flag", "N")//.argBoolean("boolean_flag", false) .argString("boolean_flag", "N")//.argBoolean("boolean_flag", false)
.argDate("date_millis", now) .argLocalDateTime("date_millis", now)
.argLocalDate("local_date", localDateNow), .argLocalDate("local_date", localDateNow),
new SqlArgs() new SqlArgs()
.argInteger("nbr_integer", Integer.MAX_VALUE) .argInteger("nbr_integer", Integer.MAX_VALUE)
@ -188,7 +182,7 @@ public class HsqldbTest extends CommonTest {
.argClobString("str_lob", "hello again") .argClobString("str_lob", "hello again")
.argBlobBytes("bin_blob", new byte[]{'1', '2'}) .argBlobBytes("bin_blob", new byte[]{'1', '2'})
.argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true) .argString("boolean_flag", "Y")//.argBoolean("boolean_flag", true)
.argDate("date_millis", now) .argLocalDateTime("date_millis", now)
.argLocalDate("local_date", localDateNow)), .argLocalDate("local_date", localDateNow)),
db.toSelect("select nbr_integer, nbr_long, nbr_float, nbr_double, nbr_big_decimal," 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") + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1")

View file

@ -12,6 +12,7 @@ import java.sql.ResultSetMetaData;
import java.sql.Types; import java.sql.Types;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -628,21 +629,32 @@ public class RowStub {
@Override @Override
public LocalDateTime getLocalDateTimeOrNull() { public LocalDateTime getLocalDateTimeOrNull() {
return toDate(rows.get(row)[++col]); return toLocalDateTime(rows.get(row)[++col]);
} }
@Override @Override
public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) { public LocalDateTime getLocalDateTimeOrNull(int columnOneBased) {
col = 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 @Override
public LocalDateTime getLocalDateTimeOrNull(String columnName) { public LocalDateTime getLocalDateTimeOrNull(String columnName) {
col = columnIndexByName(columnName) + 1; 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; 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) { if (o instanceof String) {
String s = (String) o; String s = (String) o;
if (s.length() == "yyyy-MM-dd".length()) { 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()) { 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); throw new DatabaseException("Didn't understand date string: " + s);
} }

View file

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

View file

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