#662 - Postgres Timestamp with time zone testing

This commit is contained in:
Robin Bygrave
2016-04-26 16:41:11 +12:00
parent 2e7aec3b3d
commit 4cec878242
34 changed files with 472 additions and 120 deletions
@@ -224,6 +224,11 @@ public class ServerConfig {
private boolean eagerFetchLobs;
/**
* Timezone used to get/set Timestamp values via JDBC.
*/
private String dataTimeZone;
private boolean ddlGenerate;
private boolean ddlRun;
@@ -841,6 +846,23 @@ public class ServerConfig {
this.migrationConfig = migrationConfig;
}
/**
* Return the time zone to use when reading/writing Timestamps via JDBC.
* <p>
* When set a Calendar object is used in JDBC calls when reading/writing Timestamp objects.
* </p>
*/
public String getDataTimeZone() {
return System.getProperty("ebean.dataTimeZone", dataTimeZone);
}
/**
* Set the time zone to use when reading/writing Timestamps via JDBC.
*/
public void setDataTimeZone(String dataTimeZone) {
this.dataTimeZone = dataTimeZone;
}
/**
* Return the suffix appended to the base table to derive the view that contains the union
* of the base table and the history table in order to support asOf queries.
@@ -2369,6 +2391,7 @@ public class ServerConfig {
changeLogIncludeInserts = p.getBoolean("changeLogIncludeInserts", changeLogIncludeInserts);
expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
dataTimeZone = p.get("dataTimeZone", dataTimeZone);
asOfViewSuffix = p.get("asOfViewSuffix", asOfViewSuffix);
asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
historyTableSuffix = p.get("historyTableSuffix", historyTableSuffix);
@@ -16,6 +16,7 @@ import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.query.CQuery;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import java.util.List;
@@ -187,4 +188,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* user context information (user id, user ip address etc).
*/
ReadAuditPrepare getReadAuditPrepare();
/**
* Return the DataTimeZone to use when reading/writing timestamps via JDBC.
*/
DataTimeZone getDataTimeZone();
}
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,35 +13,35 @@ import org.slf4j.LoggerFactory;
public abstract class BeanRequest {
private static final Logger log = LoggerFactory.getLogger(BeanRequest.class);
/**
* The server processing the request.
*/
protected final SpiEbeanServer ebeanServer;
/**
* The transaction this is part of.
*/
protected SpiTransaction transaction;
/**
* The server processing the request.
*/
protected final SpiEbeanServer ebeanServer;
protected boolean createdTransaction;
/**
* The transaction this is part of.
*/
protected SpiTransaction transaction;
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
this.transaction = t;
}
protected boolean createdTransaction;
/**
* A helper method for creating an implicit transaction is it is required.
* <p>
* A transaction may have been passed in or active in the thread local. If
* not then create one implicitly to handle the request.
* </p>
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
this.transaction = t;
}
/**
* A helper method for creating an implicit transaction is it is required.
* <p>
* A transaction may have been passed in or active in the thread local. If
* not then create one implicitly to handle the request.
* </p>
*
* @return True if a transaction was set (from current or created).
*/
public boolean createImplicitTransIfRequired() {
if (transaction != null) {
*/
public boolean createImplicitTransIfRequired() {
if (transaction != null) {
return false;
}
transaction = ebeanServer.getCurrentServerTransaction();
@@ -50,7 +51,7 @@ public abstract class BeanRequest {
createdTransaction = true;
}
return true;
}
}
/**
* Commit this transaction if it was created for this request.
@@ -61,40 +62,40 @@ public abstract class BeanRequest {
}
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
try {
transaction.rollback();
} catch (Exception e) {
// Just log this and carry on. A previous exception has been
// thrown and if this rollback throws exception it likely means
// that the connection is broken (and the datasource and db will cleanup)
log.error("Error trying to rollback a transaction (after a prior exception thrown)", e);
}
}
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
try {
transaction.rollback();
} catch (Exception e) {
// Just log this and carry on. A previous exception has been
// thrown and if this rollback throws exception it likely means
// that the connection is broken (and the datasource and db will cleanup)
log.error("Error trying to rollback a transaction (after a prior exception thrown)", e);
}
}
}
/**
* Return the server processing the request. Made available for
* BeanController and BeanFinder.
*/
public EbeanServer getEbeanServer() {
return ebeanServer;
}
/**
* Return the server processing the request. Made available for
* BeanController and BeanFinder.
*/
public EbeanServer getEbeanServer() {
return ebeanServer;
}
public SpiEbeanServer getServer() {
return ebeanServer;
}
/**
* Return the Transaction associated with this request.
*/
public SpiTransaction getTransaction() {
return transaction;
}
/**
* Return the Transaction associated with this request.
*/
public SpiTransaction getTransaction() {
return transaction;
}
/**
* Return true if SQL should be logged for this transaction.
@@ -109,4 +110,11 @@ public abstract class BeanRequest {
public boolean isLogSummary() {
return transaction.isLogSummary();
}
/**
* Return the DataTimeZone to use.
*/
public DataTimeZone getDataTimeZone() {
return ebeanServer.getDataTimeZone();
}
}
@@ -64,6 +64,7 @@ import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
@@ -108,6 +109,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final TransactionScopeManager transactionScopeManager;
private final DataTimeZone dataTimeZone;
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
private final int maxCallStack;
@@ -228,6 +231,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
this.dataTimeZone = config.getDataTimeZone();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
@@ -284,7 +288,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public DatabasePlatform getDatabasePlatform() {
return databasePlatform;
}
@Override
public DataTimeZone getDataTimeZone() {
return dataTimeZone;
}
@Override
public MetaInfoManager getMetaInfoManager() {
return metaInfoManager;
@@ -22,6 +22,9 @@ import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.timezone.CloneDataTimeZone;
import com.avaje.ebeaninternal.server.core.timezone.NoDataTimeZone;
import com.avaje.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
@@ -29,6 +32,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import org.avaje.datasource.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
@@ -79,6 +83,8 @@ public class InternalConfiguration {
private final TypeManager typeManager;
private final DataTimeZone dataTimeZone;
private final Binder binder;
private final DeployCreateProperties deployCreateProperties;
@@ -136,7 +142,8 @@ public class InternalConfiguration {
DatabasePlatform databasePlatform = serverConfig.getDatabasePlatform();
this.binder = getBinder(typeManager, databasePlatform);
this.dataTimeZone = initDataTimeZone();
this.binder = getBinder(typeManager, databasePlatform, dataTimeZone);
this.cQueryEngine = new CQueryEngine(databasePlatform, binder, asOfTableMapping, serverConfig.getAsOfSysPeriod(), draftTableMap);
}
@@ -218,15 +225,15 @@ public class InternalConfiguration {
/**
* For 'As Of' queries return the number of bind variables per predicate.
*/
private Binder getBinder(TypeManager typeManager, DatabasePlatform databasePlatform) {
private Binder getBinder(TypeManager typeManager, DatabasePlatform databasePlatform, DataTimeZone dataTimeZone) {
JsonExpressionHandler jsonHandler = getJsonExpressionHandler(databasePlatform);
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
if (historySupport == null) {
return new Binder(typeManager, 0, false, jsonHandler);
return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone);
}
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isBindWithFromClause(), jsonHandler);
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isBindWithFromClause(), jsonHandler, dataTimeZone);
}
/**
@@ -381,4 +388,24 @@ public class InternalConfiguration {
return new DefaultTransactionScopeManager(transactionManager);
}
}
/**
* Create the DataTimeZone implementation to use.
*/
private DataTimeZone initDataTimeZone() {
String tz = serverConfig.getDataTimeZone();
if (tz == null) {
return new NoDataTimeZone();
}
if (getDatabasePlatform().getName().toLowerCase().startsWith("oracle")) {
return new CloneDataTimeZone(tz);
} else {
return new SimpleDataTimeZone(tz);
}
}
public DataTimeZone getDataTimeZone() {
return dataTimeZone;
}
}
@@ -0,0 +1,22 @@
package com.avaje.ebeaninternal.server.core.timezone;
import java.util.Calendar;
/**
* Implementation of DataTimeZone that clones the Calendar instance.
* <p>
* Used with Oracle JDBC driver as that wants to mutate the Calender.
* </p>
*/
public class CloneDataTimeZone extends SimpleDataTimeZone {
public CloneDataTimeZone(String zoneId) {
super(zoneId);
}
@Override
public Calendar getTimeZone() {
// return cloned copy for Oracle to muck around with
return (Calendar)zone.clone();
}
}
@@ -0,0 +1,14 @@
package com.avaje.ebeaninternal.server.core.timezone;
import java.util.Calendar;
/**
* Define if a Calendar representing the time zone should be used in JDBC calls.
*/
public interface DataTimeZone {
/**
* Return the Calendar to use for Timezone information.
*/
Calendar getTimeZone();
}
@@ -0,0 +1,15 @@
package com.avaje.ebeaninternal.server.core.timezone;
import java.util.Calendar;
/**
* Implementation of DataTimeZone when no time zone is specified.
*/
public class NoDataTimeZone implements DataTimeZone {
@Override
public Calendar getTimeZone() {
// return null so Calendar is not used
return null;
}
}
@@ -0,0 +1,22 @@
package com.avaje.ebeaninternal.server.core.timezone;
import java.util.Calendar;
import java.util.TimeZone;
/**
* Implementation of DataTimeZone when single Calendar instance is used.
*/
public class SimpleDataTimeZone implements DataTimeZone {
protected final Calendar zone;
public SimpleDataTimeZone(String zoneId) {
this.zone = Calendar.getInstance(TimeZone.getTimeZone(zoneId));
}
@Override
public Calendar getTimeZone() {
// return null so Calendar is not used
return zone;
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.persist;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
@@ -13,6 +14,7 @@ import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.TypeManager;
@@ -34,14 +36,19 @@ public class Binder {
private final JsonExpressionHandler jsonExpressionHandler;
private final DataTimeZone dataTimeZone;
/**
* Set the PreparedStatement with which to bind variables to.
*/
public Binder(TypeManager typeManager, int asOfBindCount, boolean bindAsOfWithFromClause, JsonExpressionHandler jsonExpressionHandler) {
public Binder(TypeManager typeManager, int asOfBindCount, boolean bindAsOfWithFromClause,
JsonExpressionHandler jsonExpressionHandler, DataTimeZone dataTimeZone) {
this.typeManager = typeManager;
this.asOfBindCount = asOfBindCount;
this.bindAsOfWithFromClause = bindAsOfWithFromClause;
this.jsonExpressionHandler = jsonExpressionHandler;
this.dataTimeZone = dataTimeZone;
}
/**
@@ -91,6 +98,13 @@ public class Binder {
}
}
/**
* Bind the parameters to the preparedStatement returning the bind log.
*/
public String bind(BindParams bindParams, PreparedStatement statement) throws SQLException {
return bind(bindParams, new DataBind(dataTimeZone, statement));
}
/**
* Bind the list of positionedParameters in BindParams.
*/
@@ -392,4 +406,11 @@ public class Binder {
public JsonExpressionHandler getJsonExpressionHandler() {
return jsonExpressionHandler;
}
/**
* Create and return a DataBind for the statement.
*/
public DataBind dataBind(PreparedStatement stmt) {
return new DataBind(dataTimeZone, stmt);
}
}
@@ -4,7 +4,6 @@ import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiCallableSql;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -96,7 +95,7 @@ public class ExeCallableSql {
String bindLog = null;
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(cstmt));
bindLog = binder.bind(bindParams, cstmt);
}
request.setBindLog(bindLog);
@@ -113,7 +113,7 @@ public class ExeOrmUpdate {
String bindLog = null;
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
bindLog = binder.bind(bindParams, pstmt);
}
request.setBindLog(bindLog);
@@ -100,7 +100,7 @@ public class ExeUpdateSql {
String bindLog = null;
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
bindLog = binder.bind(bindParams, pstmt);
}
request.setBindLog(bindLog);
@@ -1,14 +1,12 @@
package com.avaje.ebeaninternal.server.persist.dml;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.persistence.OptimisticLockException;
import com.avaje.ebeaninternal.api.DerivedRelationshipData;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.type.DataBind;
import javax.persistence.OptimisticLockException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Delete bean handler.
@@ -37,7 +35,7 @@ public class DeleteHandler extends DmlHandler {
} else {
pstmt = getPstmt(t, sql, false);
}
dataBind = new DataBind(pstmt);
dataBind = bind(pstmt);
meta.bind(persistRequest, this);
logSql(sql);
}
@@ -74,6 +74,13 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
return persistRequest;
}
/**
* Bind to the statement returning the DataBind.
*/
protected DataBind bind(PreparedStatement stmt) {
return new DataBind(persistRequest.getDataTimeZone(), stmt);
}
/**
* Get the sql and bind the statement.
*/
@@ -9,7 +9,6 @@ import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.persist.DmlUtil;
import com.avaje.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -98,9 +97,7 @@ public class InsertHandler extends DmlHandler {
} else {
pstmt = getPstmt(t, sql, useGeneratedKeys);
}
dataBind = new DataBind(pstmt);
// bind the bean property values
dataBind = bind(pstmt);
meta.bind(this, bean, withId, persistRequest.isPublish());
logSql(sql);
@@ -4,7 +4,6 @@ import com.avaje.ebeaninternal.api.DerivedRelationshipData;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.type.DataBind;
import javax.persistence.OptimisticLockException;
import java.sql.PreparedStatement;
@@ -47,8 +46,7 @@ public class UpdateHandler extends DmlHandler {
} else {
pstmt = getPstmt(t, sql, false);
}
dataBind = new DataBind(pstmt);
dataBind = bind(pstmt);
meta.bind(persistRequest, this, updatePlan);
setUpdateGenValues();
@@ -335,11 +335,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
DataBind dataBind = new DataBind(pstmt);
// bind keys for encrypted properties
queryPlan.bindEncryptedProperties(dataBind);
DataBind dataBind = queryPlan.bindEncryptedProperties(pstmt);
bindLog = predicates.bind(dataBind);
// executeQuery
@@ -105,7 +105,7 @@ public class CQueryDelete {
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
bindLog = predicates.bind(pstmt);
rowCount = pstmt.executeUpdate();
long exeNano = System.nanoTime() - startNano;
@@ -12,7 +12,6 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReader;
import org.slf4j.Logger;
@@ -144,10 +143,10 @@ public class CQueryFetchIds {
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
bindLog = predicates.bind(pstmt);
ResultSet rset = pstmt.executeQuery();
dataReader = new RsetDataReader(rset);
dataReader = new RsetDataReader(request.getDataTimeZone(), rset);
boolean hitMaxRows = false;
boolean hasMoreRows = false;
@@ -9,11 +9,13 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import com.avaje.ebeaninternal.server.type.RsetDataReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -65,6 +67,8 @@ public class CQueryPlan {
private final Class<?> beanType;
protected final DataTimeZone dataTimeZone;
/**
* Key used to identify the query plan in audit logging.
*/
@@ -76,6 +80,7 @@ public class CQueryPlan {
public CQueryPlan(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, boolean rawSql, String logWhereSql) {
this.server = request.getServer();
this.dataTimeZone = server.getDataTimeZone();
this.beanType = request.getBeanDescriptor().getBeanType();
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
this.planKey = request.getQueryPlanKey();
@@ -100,6 +105,7 @@ public class CQueryPlan {
boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
this.server = request.getServer();
this.dataTimeZone = server.getDataTimeZone();
this.beanType = request.getBeanDescriptor().getBeanType();
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
@@ -127,17 +133,21 @@ public class CQueryPlan {
}
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReader(rset);
return new RsetDataReader(dataTimeZone, rset);
}
public void bindEncryptedProperties(DataBind dataBind) throws SQLException {
/**
* Bind keys for encrypted properties if necessary returning the DataBind.
*/
public DataBind bindEncryptedProperties(PreparedStatement stmt) throws SQLException {
DataBind dataBind = new DataBind(dataTimeZone, stmt);
if (encryptedProps != null) {
for (int i = 0; i < encryptedProps.length; i++) {
String key = encryptedProps[i].getEncryptKey().getStringValue();
dataBind.setString(key);
}
}
return dataBind;
}
public boolean isAutoTuned() {
@@ -24,9 +24,9 @@ public class CQueryPlanRawSql extends CQueryPlan {
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
@Override
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
return new RsetDataReaderIndexed(dataTimeZone, rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
@@ -17,6 +17,7 @@ import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.HashSet;
@@ -125,6 +126,10 @@ public class CQueryPredicates {
this.idValue = query.getId();
}
public String bind(PreparedStatement stmt) throws SQLException {
return bind(binder.dataBind(stmt));
}
public String bind(DataBind dataBind) throws SQLException {
if (query.isVersionsBetween() && binder.isBindAsOfWithFromClause()) {
@@ -116,8 +116,7 @@ public class CQueryRowCount {
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
bindLog = predicates.bind(pstmt);
rset = pstmt.executeQuery();
if (!rset.next()) {
@@ -81,7 +81,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
}
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
bindLog = binder.bind(bindParams, pstmt);
}
if (request.isLogSql()) {
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.type;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
@@ -9,16 +11,20 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
public class DataBind {
private final DataTimeZone dataTimeZone;
private final PreparedStatement pstmt;
private final StringBuilder bindLog = new StringBuilder();
private int pos;
public DataBind(PreparedStatement pstmt) {
public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt) {
this.dataTimeZone = dataTimeZone;
this.pstmt = pstmt;
}
@@ -108,7 +114,12 @@ public class DataBind {
}
public void setTimestamp(Timestamp v) throws SQLException {
pstmt.setTimestamp(++pos, v);
Calendar timeZone = dataTimeZone.getTimeZone();
if (timeZone != null) {
pstmt.setTimestamp(++pos, v, timeZone);
} else {
pstmt.setTimestamp(++pos, v);
}
}
public void setTime(Time v) throws SQLException {
@@ -1,6 +1,7 @@
package com.avaje.ebeaninternal.server.type;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -14,6 +15,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
public class RsetDataReader implements DataReader {
@@ -23,11 +25,14 @@ public class RsetDataReader implements DataReader {
static final int stringInitialSize = 512;
private final DataTimeZone dataTimeZone;
private final ResultSet rset;
protected int pos;
public RsetDataReader(ResultSet rset) {
public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) {
this.dataTimeZone = dataTimeZone;
this.rset = rset;
}
@@ -149,9 +154,13 @@ public class RsetDataReader implements DataReader {
return rset.getTime(pos());
}
public Timestamp getTimestamp() throws SQLException {
return rset.getTimestamp(pos());
Calendar cal = dataTimeZone.getTimeZone();
if (cal != null) {
return rset.getTimestamp(pos(), cal);
} else {
return rset.getTimestamp(pos());
}
}
public String getStringFromStream() throws SQLException {
@@ -1,13 +1,15 @@
package com.avaje.ebeaninternal.server.type;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import java.sql.ResultSet;
public class RsetDataReaderIndexed extends RsetDataReader {
private final int[] rsetIndexPositions;
public RsetDataReaderIndexed(ResultSet rset, int[] rsetIndexPositions, boolean rowNumberIncluded) {
super(rset);
public RsetDataReaderIndexed(DataTimeZone dataTimeZone, ResultSet rset, int[] rsetIndexPositions, boolean rowNumberIncluded) {
super(dataTimeZone, rset);
if (!rowNumberIncluded) {
this.rsetIndexPositions = rsetIndexPositions;
} else {