You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2015/04/28 12:45:06 UTC
[1/7] cayenne git commit: Refactoring SelectTranslator for better
extensibility
Repository: cayenne
Updated Branches:
refs/heads/master a6b55f792 -> a6d153a28
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
index 6608413..1ea09cb 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
@@ -19,57 +19,47 @@
package org.apache.cayenne.access.translator.select;
-import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.QueryAssembler;
-import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.JoinType;
import org.apache.cayenne.query.Query;
public class TstQueryAssembler extends QueryAssembler {
- protected List<DbRelationship> dbRels;
+ protected List<DbRelationship> dbRels;
- public TstQueryAssembler(Query q, DataNode node, Connection connection) throws SQLException {
- super(q, node, connection);
- dbRels = new ArrayList<DbRelationship>();
- }
+ public TstQueryAssembler(Query q, DbAdapter adapter, EntityResolver resolver) throws SQLException {
+ super(q, adapter, resolver);
+ dbRels = new ArrayList<DbRelationship>();
+ }
- @Override
- public void dbRelationshipAdded(DbRelationship relationship, JoinType joinType, String joinSplitAlias) {
- dbRels.add(relationship);
- }
+ @Override
+ public void dbRelationshipAdded(DbRelationship relationship, JoinType joinType, String joinSplitAlias) {
+ dbRels.add(relationship);
+ }
- @Override
- public String getCurrentAlias() {
- return "ta";
- }
+ @Override
+ public String getCurrentAlias() {
+ return "ta";
+ }
- @Override
- public void resetJoinStack() {
- // noop
- }
+ @Override
+ public void resetJoinStack() {
+ // noop
+ }
- @Override
- public boolean supportsTableAliases() {
- return true;
- }
+ @Override
+ public boolean supportsTableAliases() {
+ return true;
+ }
- @Override
- public String createSqlString() {
- return "SELECT * FROM ARTIST";
- }
-
- public List<DbAttribute> getAttributes() {
- return attributes;
- }
-
- public List getValues() {
- return values;
- }
+ @Override
+ protected void doTranslate() {
+ this.sql = "SELECT * FROM ARTIST";
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/merge/ValueForNullIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/merge/ValueForNullIT.java b/cayenne-server/src/test/java/org/apache/cayenne/merge/ValueForNullIT.java
index 6fd121b..fb43eb3 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/merge/ValueForNullIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/merge/ValueForNullIT.java
@@ -30,7 +30,7 @@ import junit.framework.AssertionFailedError;
import org.apache.cayenne.DataObject;
import org.apache.cayenne.Persistent;
import org.apache.cayenne.access.DataContext;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
@@ -107,11 +107,11 @@ public class ValueForNullIT extends MergeCase {
return super.createMerger(mergerFactory, new DefaultValueForNullProvider() {
@Override
- protected ParameterBinding get(DbEntity entity, DbAttribute column) {
+ protected SQLParameterBinding get(DbEntity entity, DbAttribute column) {
int type = column.getType();
switch (type) {
case Types.VARCHAR:
- return new ParameterBinding(DEFAULT_VALUE_STRING, type, -1);
+ return new SQLParameterBinding(DEFAULT_VALUE_STRING, type, -1);
default:
throw new AssertionFailedError("should not get here");
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessorTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessorTest.java b/cayenne-server/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessorTest.java
index f21e0f3..9387468 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessorTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessorTest.java
@@ -31,7 +31,7 @@ import java.util.Map;
import org.apache.cayenne.CayenneDataObject;
import org.apache.cayenne.DataObject;
import org.apache.cayenne.ObjectId;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.cayenne.access.jdbc.SQLStatement;
import org.junit.Before;
import org.junit.Test;
@@ -223,12 +223,12 @@ public class VelocitySQLTemplateProcessorTest {
}
private void assertBindingValue(Object expectedValue, Object binding) {
- assertTrue("Not a binding!", binding instanceof ParameterBinding);
- assertEquals(expectedValue, ((ParameterBinding) binding).getValue());
+ assertTrue("Not a binding!", binding instanceof SQLParameterBinding);
+ assertEquals(expectedValue, ((SQLParameterBinding) binding).getValue());
}
private void assertBindingType(int expectedType, Object binding) {
- assertTrue("Not a binding!", binding instanceof ParameterBinding);
- assertEquals(expectedType, ((ParameterBinding) binding).getJdbcType());
+ assertTrue("Not a binding!", binding instanceof SQLParameterBinding);
+ assertEquals(expectedType, ((SQLParameterBinding) binding).getJdbcType());
}
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 05564c8..46dd250 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -20,6 +20,7 @@ CAY-1995 Add support for iterators to Select
CAY-2001 Saving a display state of Project
CAY-2004 EJBQL: Support for ordering on aggregate expressions
CAY-2005 Improving generated DISTINCT performance on PostgreSQL
+CAY-2007 Refactoring SelectTranslator for better extensibility
Bug Fixes:
[4/7] cayenne git commit: Refactoring SelectTranslator for better
extensibility
Posted by aa...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
index a62b4d1..d652885 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
@@ -19,634 +19,27 @@
package org.apache.cayenne.access.translator.select;
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.access.DataNode;
+import java.util.Map;
+
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.exp.ExpressionFactory;
-import org.apache.cayenne.exp.parser.ASTDbPath;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbJoin;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.JoinType;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.map.ObjAttribute;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.ObjRelationship;
-import org.apache.cayenne.map.PathComponent;
-import org.apache.cayenne.query.PrefetchSelectQuery;
-import org.apache.cayenne.query.PrefetchTreeNode;
-import org.apache.cayenne.query.Query;
import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.reflect.ArcProperty;
-import org.apache.cayenne.reflect.AttributeProperty;
-import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.reflect.PropertyVisitor;
-import org.apache.cayenne.reflect.ToManyProperty;
-import org.apache.cayenne.reflect.ToOneProperty;
-import org.apache.cayenne.util.CayenneMapEntry;
-import org.apache.cayenne.util.EqualsBuilder;
-import org.apache.cayenne.util.HashCodeBuilder;
-
-import java.sql.Connection;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
- * A builder of JDBC PreparedStatements based on Cayenne SelectQueries.
- * Translates SelectQuery to parameterized SQL string and wraps it in a
- * PreparedStatement. SelectTranslator is stateful and thread-unsafe.
+ * An abstraction of {@link SelectQuery} translator.
+ *
+ * @since 4.0 this is an interface.
*/
-public class SelectTranslator extends QueryAssembler {
-
- protected static final int[] UNSUPPORTED_DISTINCT_TYPES = { Types.BLOB, Types.CLOB, Types.NCLOB,
- Types.LONGVARBINARY, Types.LONGVARCHAR, Types.LONGNVARCHAR };
-
- protected static boolean isUnsupportedForDistinct(int type) {
- for (int unsupportedDistinctType : UNSUPPORTED_DISTINCT_TYPES) {
- if (unsupportedDistinctType == type) {
- return true;
- }
- }
-
- return false;
- }
-
- JoinStack joinStack;
-
- /**
- * @since 4.0
- */
- public SelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
-
- public JoinStack getJoinStack() {
- if (joinStack == null) {
- joinStack = createJoinStack();
- }
- return joinStack;
- }
-
- List<ColumnDescriptor> resultColumns;
- Map<ObjAttribute, ColumnDescriptor> attributeOverrides;
- Map<ColumnDescriptor, ObjAttribute> defaultAttributesByColumn;
-
- boolean suppressingDistinct;
-
- /**
- * If set to <code>true</code>, indicates that distinct select query is
- * required no matter what the original query settings where. This flag can
- * be set when joins are created using "to-many" relationships.
- */
- boolean forcingDistinct;
-
- protected JoinStack createJoinStack() {
- return new JoinStack(getAdapter(), queryMetadata.getDataMap(), this);
- }
-
- /**
- * Returns query translated to SQL. This is a main work method of the
- * SelectTranslator.
- */
- @Override
- public String createSqlString() throws Exception {
- if (cachedSqlString != null) {
- return cachedSqlString;
- }
-
- DataMap dataMap = queryMetadata.getDataMap();
- JoinStack joins = getJoinStack();
-
- QuotingStrategy strategy = getAdapter().getQuotingStrategy();
- forcingDistinct = false;
-
- // build column list
- this.resultColumns = buildResultColumns();
-
- // build qualifier
- QualifierTranslator qualifierTranslator = adapter.getQualifierTranslator(this);
- StringBuilder qualifierBuffer = qualifierTranslator.appendPart(new StringBuilder());
-
- // build ORDER BY
- OrderingTranslator orderingTranslator = new OrderingTranslator(this);
- StringBuilder orderingBuffer = orderingTranslator.appendPart(new StringBuilder());
-
- // assemble
- StringBuilder queryBuf = new StringBuilder();
- queryBuf.append("SELECT ");
-
- // check if DISTINCT is appropriate
- // side effect: "suppressingDistinct" flag may end up being flipped here
- if (forcingDistinct || getSelectQuery().isDistinct()) {
- suppressingDistinct = false;
-
- for (ColumnDescriptor column : resultColumns) {
- if (isUnsupportedForDistinct(column.getJdbcType())) {
- suppressingDistinct = true;
- break;
- }
- }
-
- if (!suppressingDistinct) {
- queryBuf.append(buildDistinctStatement() + " ");
- }
- }
-
- // convert ColumnDescriptors to column names
- List<String> selectColumnExpList = new ArrayList<String>();
- for (ColumnDescriptor column : resultColumns) {
- String fullName = strategy.quotedIdentifier(dataMap, column.getNamePrefix(), column.getName());
- selectColumnExpList.add(fullName);
- }
-
- // append any column expressions used in the order by if this query
- // uses the DISTINCT modifier
- if (forcingDistinct || getSelectQuery().isDistinct()) {
- List<String> orderByColumnList = orderingTranslator.getOrderByColumnList();
- for (String orderByColumnExp : orderByColumnList) {
- // Convert to ColumnDescriptors??
- if (!selectColumnExpList.contains(orderByColumnExp)) {
- selectColumnExpList.add(orderByColumnExp);
- }
- }
- }
-
- appendSelectColumns(queryBuf, selectColumnExpList);
-
- // append from clause
- queryBuf.append(" FROM ");
-
- // append tables and joins
- joins.appendRootWithQuoteSqlIdentifiers(queryBuf, getRootDbEntity());
-
- // join parameters will be added to head of query
- parameterIndex = 0;
-
- joins.appendJoins(queryBuf);
- joins.appendQualifier(qualifierBuffer, qualifierBuffer.length() == 0);
-
- // append qualifier
- if (qualifierBuffer.length() > 0) {
- queryBuf.append(" WHERE ");
- queryBuf.append(qualifierBuffer);
- }
-
- // append prebuilt ordering
- if (orderingBuffer.length() > 0) {
- queryBuf.append(" ORDER BY ").append(orderingBuffer);
- }
-
- if (!isSuppressingDistinct()) {
- appendLimitAndOffsetClauses(queryBuf);
- }
-
- cachedSqlString = queryBuf.toString();
- return cachedSqlString;
- }
-
- /**
- * Allows subclasses to insert their own dialect of DISTINCT statement to
- * improve performance.
- *
- * @return string representing the DISTINCT statement
- * @since 4.0
- */
- protected String buildDistinctStatement() {
- return "DISTINCT";
- }
-
- /**
- * @since 3.1
- */
- protected void appendSelectColumns(StringBuilder buffer, List<String> selectColumnExpList) {
-
- // append columns (unroll the loop's first element)
- int columnCount = selectColumnExpList.size();
- buffer.append(selectColumnExpList.get(0));
-
- // assume there is at least 1 element
- for (int i = 1; i < columnCount; i++) {
- buffer.append(", ");
- buffer.append(selectColumnExpList.get(i));
- }
- }
-
- /**
- * Handles appending optional limit and offset clauses. This implementation
- * does nothing, deferring to subclasses to define the LIMIT/OFFSET clause
- * syntax.
- *
- * @since 3.0
- */
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
-
- }
-
- @Override
- public String getCurrentAlias() {
- return getJoinStack().getCurrentAlias();
- }
-
- /**
- * Returns a list of ColumnDescriptors for the query columns.
- *
- * @since 1.2
- */
- public ColumnDescriptor[] getResultColumns() {
- if (resultColumns == null || resultColumns.isEmpty()) {
- return new ColumnDescriptor[0];
- }
-
- return resultColumns.toArray(new ColumnDescriptor[resultColumns.size()]);
- }
-
- /**
- * Returns a map of ColumnDescriptors keyed by ObjAttribute for columns that
- * may need to be reprocessed manually due to incompatible mappings along
- * the inheritance hierarchy.
- *
- * @since 1.2
- */
- public Map<ObjAttribute, ColumnDescriptor> getAttributeOverrides() {
- if (attributeOverrides != null) {
- return attributeOverrides;
- } else {
- return Collections.emptyMap();
- }
- }
-
- /**
- * Returns true if SelectTranslator determined that a query requiring
- * DISTINCT can't be run with DISTINCT keyword for internal reasons. If this
- * method returns true, DataNode may need to do in-memory distinct
- * filtering.
- *
- * @since 1.1
- */
- public boolean isSuppressingDistinct() {
- return suppressingDistinct;
- }
-
- private SelectQuery<?> getSelectQuery() {
- return (SelectQuery<?>) getQuery();
- }
-
- protected List<ColumnDescriptor> buildResultColumns() {
-
- this.defaultAttributesByColumn = new HashMap<ColumnDescriptor, ObjAttribute>();
-
- List<ColumnDescriptor> columns = new ArrayList<ColumnDescriptor>();
- SelectQuery<?> query = getSelectQuery();
-
- if (query.getRoot() instanceof DbEntity) {
- appendDbEntityColumns(columns, query);
- } else if (getQueryMetadata().getPageSize() > 0) {
- appendIdColumns(columns, query);
- } else {
- appendQueryColumns(columns, query);
- }
-
- return columns;
- }
-
- <T> List<ColumnDescriptor> appendDbEntityColumns(List<ColumnDescriptor> columns, SelectQuery<T> query) {
-
- Set<ColumnTracker> attributes = new HashSet<ColumnTracker>();
-
- DbEntity table = getRootDbEntity();
- for (DbAttribute dba : table.getAttributes()) {
- appendColumn(columns, null, dba, attributes, null);
- }
-
- return columns;
- }
-
- /**
- * Appends columns needed for object SelectQuery to the provided columns
- * list.
- */
- <T> List<ColumnDescriptor> appendQueryColumns(final List<ColumnDescriptor> columns, SelectQuery<T> query) {
-
- final Set<ColumnTracker> attributes = new HashSet<ColumnTracker>();
-
- // fetched attributes include attributes that are either:
- //
- // * class properties
- // * PK
- // * FK used in relationship
- // * joined prefetch PK
-
- ClassDescriptor descriptor = queryMetadata.getClassDescriptor();
- ObjEntity oe = descriptor.getEntity();
-
- PropertyVisitor visitor = new PropertyVisitor() {
-
- public boolean visitAttribute(AttributeProperty property) {
- ObjAttribute oa = property.getAttribute();
-
- resetJoinStack();
- Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
- while (dbPathIterator.hasNext()) {
- Object pathPart = dbPathIterator.next();
-
- if (pathPart == null) {
- throw new CayenneRuntimeException("ObjAttribute has no component: " + oa.getName());
- } else if (pathPart instanceof DbRelationship) {
- DbRelationship rel = (DbRelationship) pathPart;
- dbRelationshipAdded(rel, JoinType.LEFT_OUTER, null);
- } else if (pathPart instanceof DbAttribute) {
- DbAttribute dbAttr = (DbAttribute) pathPart;
-
- appendColumn(columns, oa, dbAttr, attributes, null);
- }
- }
- return true;
- }
-
- public boolean visitToMany(ToManyProperty property) {
- visitRelationship(property);
- return true;
- }
-
- public boolean visitToOne(ToOneProperty property) {
- visitRelationship(property);
- return true;
- }
-
- private void visitRelationship(ArcProperty property) {
- resetJoinStack();
-
- ObjRelationship rel = property.getRelationship();
- DbRelationship dbRel = rel.getDbRelationships().get(0);
-
- List<DbJoin> joins = dbRel.getJoins();
- for (DbJoin join : joins) {
- DbAttribute src = join.getSource();
- appendColumn(columns, null, src, attributes, null);
- }
- }
- };
-
- descriptor.visitAllProperties(visitor);
-
- // stack should be reset, because all root table attributes go with "t0"
- // table alias
- resetJoinStack();
-
- // add remaining needed attrs from DbEntity
- DbEntity table = getRootDbEntity();
- for (DbAttribute dba : table.getPrimaryKeys()) {
- appendColumn(columns, null, dba, attributes, null);
- }
-
- // special handling of a disjoint query...
-
- if (query instanceof PrefetchSelectQuery) {
-
- // for each relationship path add PK of the target entity...
- for (String path : ((PrefetchSelectQuery) query).getResultPaths()) {
-
- ASTDbPath pathExp = (ASTDbPath) oe.translateToDbPath(ExpressionFactory.exp(path));
-
- // add joins and find terminating element
-
- resetJoinStack();
-
- PathComponent<DbAttribute, DbRelationship> lastComponent = null;
- for (PathComponent<DbAttribute, DbRelationship> component : table
- .resolvePath(pathExp, getPathAliases())) {
-
- if (component.getRelationship() != null) {
- // do not invoke dbRelationshipAdded(), invoke
- // pushJoin() instead. This is to prevent
- // 'forcingDistinct' flipping to true, that will result
- // in unneeded extra processing and sometimes in invalid
- // results (see CAY-1979). Distinctness of each row is
- // guaranteed by the prefetch query semantics - we
- // include target ID in the result columns
- getJoinStack().pushJoin(component.getRelationship(), component.getJoinType(), null);
- }
-
- lastComponent = component;
- }
-
- // process terminating element
- if (lastComponent != null) {
-
- DbRelationship relationship = lastComponent.getRelationship();
-
- if (relationship != null) {
-
- String labelPrefix = pathExp.getPath();
- DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
-
- for (DbAttribute pk : targetEntity.getPrimaryKeys()) {
-
- // note that we my select a source attribute, but
- // label it as
- // target for simplified snapshot processing
- appendColumn(columns, null, pk, attributes, labelPrefix + '.' + pk.getName());
- }
- }
- }
- }
- }
-
- // handle joint prefetches directly attached to this query...
- if (query.getPrefetchTree() != null) {
-
- for (PrefetchTreeNode prefetch : query.getPrefetchTree().adjacentJointNodes()) {
-
- // for each prefetch add all joins plus columns from the target
- // entity
- Expression prefetchExp = ExpressionFactory.exp(prefetch.getPath());
- ASTDbPath dbPrefetch = (ASTDbPath) oe.translateToDbPath(prefetchExp);
-
- resetJoinStack();
- DbRelationship r = null;
- for (PathComponent<DbAttribute, DbRelationship> component : table.resolvePath(dbPrefetch,
- getPathAliases())) {
- r = component.getRelationship();
- dbRelationshipAdded(r, JoinType.LEFT_OUTER, null);
- }
-
- if (r == null) {
- throw new CayenneRuntimeException("Invalid joint prefetch '" + prefetch + "' for entity: "
- + oe.getName());
- }
-
- // add columns from the target entity, including those that are
- // matched
- // against the FK of the source entity. This is needed to
- // determine
- // whether optional relationships are null
-
- // go via target OE to make sure that Java types are mapped
- // correctly...
- ObjRelationship targetRel = (ObjRelationship) prefetchExp.evaluate(oe);
- ObjEntity targetEntity = (ObjEntity) targetRel.getTargetEntity();
-
- String labelPrefix = dbPrefetch.getPath();
- for (ObjAttribute oa : targetEntity.getAttributes()) {
- Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
- while (dbPathIterator.hasNext()) {
- Object pathPart = dbPathIterator.next();
-
- if (pathPart == null) {
- throw new CayenneRuntimeException("ObjAttribute has no component: " + oa.getName());
- } else if (pathPart instanceof DbRelationship) {
- DbRelationship rel = (DbRelationship) pathPart;
- dbRelationshipAdded(rel, JoinType.INNER, null);
- } else if (pathPart instanceof DbAttribute) {
- DbAttribute attribute = (DbAttribute) pathPart;
-
- appendColumn(columns, oa, attribute, attributes, labelPrefix + '.' + attribute.getName());
- }
- }
- }
-
- // append remaining target attributes such as keys
- DbEntity targetDbEntity = (DbEntity) r.getTargetEntity();
- for (DbAttribute attribute : targetDbEntity.getAttributes()) {
- appendColumn(columns, null, attribute, attributes, labelPrefix + '.' + attribute.getName());
- }
- }
- }
-
- return columns;
- }
-
- <T> List<ColumnDescriptor> appendIdColumns(final List<ColumnDescriptor> columns, SelectQuery<T> query) {
-
- Set<ColumnTracker> skipSet = new HashSet<ColumnTracker>();
-
- ClassDescriptor descriptor = queryMetadata.getClassDescriptor();
- ObjEntity oe = descriptor.getEntity();
- DbEntity dbEntity = oe.getDbEntity();
- for (ObjAttribute attribute : oe.getPrimaryKeys()) {
-
- // synthetic objattributes can't reliably lookup their DbAttribute,
- // so do it manually..
- DbAttribute dbAttribute = dbEntity.getAttribute(attribute.getDbAttributeName());
- appendColumn(columns, attribute, dbAttribute, skipSet, null);
- }
-
- return columns;
- }
-
- private void appendColumn(List<ColumnDescriptor> columns, ObjAttribute objAttribute, DbAttribute attribute,
- Set<ColumnTracker> skipSet, String label) {
-
- String alias = getCurrentAlias();
- if (skipSet.add(new ColumnTracker(alias, attribute))) {
-
- ColumnDescriptor column = (objAttribute != null) ? new ColumnDescriptor(objAttribute, attribute, alias)
- : new ColumnDescriptor(attribute, alias);
-
- if (label != null) {
- column.setDataRowKey(label);
- }
-
- columns.add(column);
-
- // TODO: andrus, 5/7/2006 - replace 'columns' collection with this
- // map, as it
- // is redundant
- defaultAttributesByColumn.put(column, objAttribute);
- } else if (objAttribute != null) {
-
- // record ObjAttribute override
- for (ColumnDescriptor column : columns) {
- if (attribute.getName().equals(column.getName())) {
-
- if (attributeOverrides == null) {
- attributeOverrides = new HashMap<ObjAttribute, ColumnDescriptor>();
- }
-
- // kick out the original attribute
- ObjAttribute original = defaultAttributesByColumn.remove(column);
-
- if (original != null) {
- attributeOverrides.put(original, column);
- }
-
- attributeOverrides.put(objAttribute, column);
- column.setJavaClass(Void.TYPE.getName());
-
- break;
- }
- }
- }
- }
-
- /**
- * @since 3.0
- */
- @Override
- public void resetJoinStack() {
- getJoinStack().resetStack();
- }
-
- /**
- * @since 3.0
- */
- @Override
- public void dbRelationshipAdded(DbRelationship relationship, JoinType joinType, String joinSplitAlias) {
- if (relationship.isToMany()) {
- forcingDistinct = true;
- }
-
- getJoinStack().pushJoin(relationship, joinType, joinSplitAlias);
- }
-
- /**
- * Always returns true.
- */
- @Override
- public boolean supportsTableAliases() {
- return true;
- }
-
- static final class ColumnTracker {
-
- private DbAttribute attribute;
- private String alias;
+public interface SelectTranslator {
- ColumnTracker(String alias, DbAttribute attribute) {
- this.attribute = attribute;
- this.alias = alias;
- }
+ String getSql() throws Exception;
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof ColumnTracker)) {
- return false;
- }
+ ParameterBinding[] getBindings();
- ColumnTracker other = (ColumnTracker) object;
- return new EqualsBuilder()
- .append(alias, other.alias)
- .append(attribute, other.attribute)
- .isEquals();
- }
+ Map<ObjAttribute, ColumnDescriptor> getAttributeOverrides();
- @Override
- public int hashCode() {
- return new HashCodeBuilder(31, 5)
- .append(alias)
- .append(attribute)
- .toHashCode();
- }
+ ColumnDescriptor[] getResultColumns();
- }
+ boolean isSuppressingDistinct();
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TrimmingQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TrimmingQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TrimmingQualifierTranslator.java
index c992cfd..31eb2c9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TrimmingQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TrimmingQualifierTranslator.java
@@ -19,75 +19,74 @@
package org.apache.cayenne.access.translator.select;
-import java.io.IOException;
import java.sql.Types;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbAttribute;
/**
- * QualifierTranslator that allows translation of qualifiers that perform comparison with
- * CHAR columns. Some databases require trimming the values for this to work.
+ * QualifierTranslator that allows translation of qualifiers that perform
+ * comparison with CHAR columns. Some databases require trimming the values for
+ * this to work.
*
*/
public class TrimmingQualifierTranslator extends QualifierTranslator {
- protected String trimFunction;
+ protected String trimFunction;
- /**
- * Constructor for TrimmingQualifierTranslator.
- */
- public TrimmingQualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
- super(queryAssembler);
- this.trimFunction = trimFunction;
- }
+ /**
+ * Constructor for TrimmingQualifierTranslator.
+ */
+ public TrimmingQualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
+ super(queryAssembler);
+ this.trimFunction = trimFunction;
+ }
- /**
- * Adds special handling of CHAR columns.
- */
- @Override
- protected void processColumn(DbAttribute dbAttr) throws IOException {
- if (dbAttr.getType() == Types.CHAR) {
- out.append(trimFunction).append("(");
- super.processColumn(dbAttr);
- out.append(')');
- }
- else {
- super.processColumn(dbAttr);
- }
- }
-
- /**
- * Adds special handling of CHAR columns.
- */
- @Override
- protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) throws IOException {
-
- if (dbAttr.getType() == Types.CHAR) {
- out.append(trimFunction).append("(");
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- out.append(')');
- }
- else {
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- }
- }
+ /**
+ * Adds special handling of CHAR columns.
+ */
+ @Override
+ protected void processColumn(DbAttribute dbAttr) {
+ if (dbAttr.getType() == Types.CHAR) {
+ out.append(trimFunction).append("(");
+ super.processColumn(dbAttr);
+ out.append(')');
+ } else {
+ super.processColumn(dbAttr);
+ }
+ }
- /**
- * Returns the trimFunction.
- *
- * @return String
- */
- public String getTrimFunction() {
- return trimFunction;
- }
+ /**
+ * Adds special handling of CHAR columns.
+ */
+ @Override
+ protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) {
- /**
- * Sets the trimFunction.
- *
- * @param trimFunction The trimFunction to set
- */
- public void setTrimFunction(String trimFunction) {
- this.trimFunction = trimFunction;
- }
+ if (dbAttr.getType() == Types.CHAR) {
+ out.append(trimFunction).append("(");
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
+ out.append(')');
+ } else {
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
+ }
+ }
+
+ /**
+ * Returns the trimFunction.
+ *
+ * @return String
+ */
+ public String getTrimFunction() {
+ return trimFunction;
+ }
+
+ /**
+ * Sets the trimFunction.
+ *
+ * @param trimFunction
+ * The trimFunction to set
+ */
+ public void setTrimFunction(String trimFunction) {
+ this.trimFunction = trimFunction;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java
index 1cdbcd0..67f22ca 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java
@@ -34,96 +34,84 @@ import org.apache.cayenne.map.DbAttribute;
public class DB2QualifierTranslator extends TrimmingQualifierTranslator {
- public DB2QualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
- super(queryAssembler, trimFunction);
- }
-
- @Override
- protected void appendLiteralDirect(
- Object val,
- DbAttribute attr,
- Expression parentExpression) throws IOException {
-
- boolean castNeeded = false;
-
- if (parentExpression != null) {
- int type = parentExpression.getType();
-
- castNeeded = attr != null
- && (type == Expression.LIKE
- || type == Expression.LIKE_IGNORE_CASE
- || type == Expression.NOT_LIKE || type == Expression.NOT_LIKE_IGNORE_CASE);
- }
-
- if (castNeeded) {
- out.append("CAST (");
- }
-
- super.appendLiteralDirect(val, attr, parentExpression);
-
- if (castNeeded) {
- int jdbcType = attr.getType();
- int len = attr.getMaxLength();
-
- // determine CAST type
-
- // LIKE on CHAR may produce unpredictible results
- // LIKE on LONVARCHAR doesn't seem to be supported
- if (jdbcType == Types.CHAR || jdbcType == Types.LONGVARCHAR) {
- jdbcType = Types.VARCHAR;
-
- // length is required for VARCHAR
- if (len <= 0) {
- len = 254;
- }
- }
-
- out.append(" AS ");
- String[] types = queryAssembler.getAdapter().externalTypesForJdbcType(
- jdbcType);
-
- if (types == null || types.length == 0) {
- throw new CayenneRuntimeException(
- "Can't find database type for JDBC type '"
- + TypesMapping.getSqlNameByType(jdbcType));
- }
-
- out.append(types[0]);
- if (len > 0 && queryAssembler.getAdapter().typeSupportsLength(jdbcType)) {
- out.append("(");
- out.append(String.valueOf(len));
- out.append(")");
- }
-
- out.append(")");
- }
- }
-
- @Override
- protected void processColumnWithQuoteSqlIdentifiers(
- DbAttribute dbAttr,
- Expression pathExp) throws IOException {
-
- SimpleNode parent = null;
- if (pathExp instanceof SimpleNode) {
- parent = (SimpleNode) ((SimpleNode) pathExp).jjtGetParent();
- }
-
- // problem in db2 : Comparisons between CLOB and CLOB are not supported.
- // we need do it by casting the Clob to VARCHAR.
- if (parent != null
- && (parent instanceof ASTEqual || parent instanceof ASTNotEqual)
- && dbAttr.getType() == Types.CLOB
- && parent.getOperandCount() == 2
- && parent.getOperand(1) instanceof String) {
- Integer size = parent.getOperand(1).toString().length() + 1;
-
- out.append("CAST(");
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- out.append(" AS VARCHAR(" + size + "))");
- }
- else {
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- }
- }
+ public DB2QualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
+ super(queryAssembler, trimFunction);
+ }
+
+ @Override
+ protected void appendLiteralDirect(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
+
+ boolean castNeeded = false;
+
+ if (parentExpression != null) {
+ int type = parentExpression.getType();
+
+ castNeeded = attr != null
+ && (type == Expression.LIKE || type == Expression.LIKE_IGNORE_CASE || type == Expression.NOT_LIKE || type == Expression.NOT_LIKE_IGNORE_CASE);
+ }
+
+ if (castNeeded) {
+ out.append("CAST (");
+ }
+
+ super.appendLiteralDirect(val, attr, parentExpression);
+
+ if (castNeeded) {
+ int jdbcType = attr.getType();
+ int len = attr.getMaxLength();
+
+ // determine CAST type
+
+ // LIKE on CHAR may produce unpredictible results
+ // LIKE on LONVARCHAR doesn't seem to be supported
+ if (jdbcType == Types.CHAR || jdbcType == Types.LONGVARCHAR) {
+ jdbcType = Types.VARCHAR;
+
+ // length is required for VARCHAR
+ if (len <= 0) {
+ len = 254;
+ }
+ }
+
+ out.append(" AS ");
+ String[] types = queryAssembler.getAdapter().externalTypesForJdbcType(jdbcType);
+
+ if (types == null || types.length == 0) {
+ throw new CayenneRuntimeException("Can't find database type for JDBC type '"
+ + TypesMapping.getSqlNameByType(jdbcType));
+ }
+
+ out.append(types[0]);
+ if (len > 0 && queryAssembler.getAdapter().typeSupportsLength(jdbcType)) {
+ out.append("(");
+ out.append(String.valueOf(len));
+ out.append(")");
+ }
+
+ out.append(")");
+ }
+ }
+
+ @Override
+ protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) {
+
+ SimpleNode parent = null;
+ if (pathExp instanceof SimpleNode) {
+ parent = (SimpleNode) ((SimpleNode) pathExp).jjtGetParent();
+ }
+
+ // problem in db2 : Comparisons between CLOB and CLOB are not supported.
+ // we need do it by casting the Clob to VARCHAR.
+ if (parent != null && (parent instanceof ASTEqual || parent instanceof ASTNotEqual)
+ && dbAttr.getType() == Types.CLOB && parent.getOperandCount() == 2
+ && parent.getOperand(1) instanceof String) {
+ Integer size = parent.getOperand(1).toString().length() + 1;
+
+ out.append("CAST(");
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
+ out.append(" AS VARCHAR(" + size + "))");
+ } else {
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java
index d2bcf58..1b59288 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.derby;
-import java.io.IOException;
import java.sql.Types;
import org.apache.cayenne.access.translator.select.QueryAssembler;
@@ -31,35 +30,31 @@ import org.apache.cayenne.map.DbAttribute;
public class DerbyQualifierTranslator extends TrimmingQualifierTranslator {
- public DerbyQualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
- super(queryAssembler, trimFunction);
- }
-
- @Override
- protected void processColumnWithQuoteSqlIdentifiers(
- DbAttribute dbAttr,
- Expression pathExp) throws IOException {
-
- SimpleNode parent = null;
- if (pathExp instanceof SimpleNode) {
- parent = (SimpleNode) ((SimpleNode) pathExp).jjtGetParent();
- }
-
- // problem in derby : Comparisons between 'CLOB (UCS_BASIC)' and 'CLOB (UCS_BASIC)' are not supported.
- // we need do it by casting the Clob to VARCHAR.
- if (parent != null
- && (parent instanceof ASTEqual || parent instanceof ASTNotEqual)
- && dbAttr.getType() == Types.CLOB
- && parent.getOperandCount() == 2
- && parent.getOperand(1) instanceof String) {
- Integer size = parent.getOperand(1).toString().length() + 1;
-
- out.append("CAST(");
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- out.append(" AS VARCHAR(" + size + "))");
- }
- else {
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- }
- }
+ public DerbyQualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
+ super(queryAssembler, trimFunction);
+ }
+
+ @Override
+ protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) {
+
+ SimpleNode parent = null;
+ if (pathExp instanceof SimpleNode) {
+ parent = (SimpleNode) ((SimpleNode) pathExp).jjtGetParent();
+ }
+
+ // problem in derby : Comparisons between 'CLOB (UCS_BASIC)' and 'CLOB
+ // (UCS_BASIC)' are not supported.
+ // we need do it by casting the Clob to VARCHAR.
+ if (parent != null && (parent instanceof ASTEqual || parent instanceof ASTNotEqual)
+ && dbAttr.getType() == Types.CLOB && parent.getOperandCount() == 2
+ && parent.getOperand(1) instanceof String) {
+ Integer size = parent.getOperand(1).toString().length() + 1;
+
+ out.append("CAST(");
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
+ out.append(" AS VARCHAR(" + size + "))");
+ } else {
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java
index 047799c..f41bdf2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java
@@ -16,10 +16,8 @@
* specific language governing permissions and limitations
* under the License.
****************************************************************/
-
-package org.apache.cayenne.dba.firebird;
-import java.io.IOException;
+package org.apache.cayenne.dba.firebird;
import org.apache.cayenne.access.translator.select.QualifierTranslator;
import org.apache.cayenne.access.translator.select.QueryAssembler;
@@ -27,18 +25,18 @@ import org.apache.cayenne.dba.oracle.OracleQualifierTranslator;
import org.apache.cayenne.exp.Expression;
public class FirebirdQualifierTranslator extends QualifierTranslator {
- public FirebirdQualifierTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler);
- }
+ public FirebirdQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
- @Override
- protected void doAppendPart(Expression rootNode) throws IOException {
- if (rootNode == null) {
- return;
- }
- // IN statements with more than 1500 values are denied in Firebird
- // so we need to split one big statement on few smaller ones
- rootNode = rootNode.transform(new OracleQualifierTranslator.INTrimmer());
- rootNode.traverse(this);
- }
+ @Override
+ protected void doAppendPart(Expression rootNode) {
+ if (rootNode == null) {
+ return;
+ }
+ // IN statements with more than 1500 values are denied in Firebird
+ // so we need to split one big statement on few smaller ones
+ rootNode = rootNode.transform(new OracleQualifierTranslator.INTrimmer());
+ rootNode.traverse(this);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseActionBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseActionBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseActionBuilder.java
index 1078354..5ed5cf2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseActionBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseActionBuilder.java
@@ -19,8 +19,6 @@
package org.apache.cayenne.dba.frontbase;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -39,8 +37,8 @@ class FrontBaseActionBuilder extends JdbcActionBuilder {
return new SelectAction(query, dataNode) {
@Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new FrontBaseSelectTranslator(query, dataNode, connection);
+ protected SelectTranslator createTranslator() {
+ return new FrontBaseSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
}
};
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSelectTranslator.java
index bc89501..1e71f5f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSelectTranslator.java
@@ -19,35 +19,34 @@
package org.apache.cayenne.dba.frontbase;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
* @since 1.2
*/
-class FrontBaseSelectTranslator extends SelectTranslator {
+class FrontBaseSelectTranslator extends DefaultSelectTranslator {
- static final String SELECT_PREFIX = "SELECT";
+ static final String SELECT_PREFIX = "SELECT";
- /**
- * @since 4.0
- */
- public FrontBaseSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
+ /**
+ * @since 4.0
+ */
+ public FrontBaseSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
- @Override
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+ @Override
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
- int limit = queryMetadata.getFetchLimit();
- if (limit > 0 && buffer.length() > SELECT_PREFIX.length()) {
+ int limit = queryMetadata.getFetchLimit();
+ if (limit > 0 && buffer.length() > SELECT_PREFIX.length()) {
- if (SELECT_PREFIX.equals(buffer.substring(0, SELECT_PREFIX.length()))) {
- buffer.insert(SELECT_PREFIX.length(), " TOP " + limit);
- }
- }
- }
+ if (SELECT_PREFIX.equals(buffer.substring(0, SELECT_PREFIX.length()))) {
+ buffer.insert(SELECT_PREFIX.length(), " TOP " + limit);
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectAction.java
index 2b5e098..af721ac 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectAction.java
@@ -18,8 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.hsqldb;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -30,17 +28,17 @@ import org.apache.cayenne.query.SelectQuery;
*/
class HSQLSelectAction extends SelectAction {
- <T> HSQLSelectAction(SelectQuery<T> query, DataNode dataNode) {
- super(query, dataNode);
- }
+ <T> HSQLSelectAction(SelectQuery<T> query, DataNode dataNode) {
+ super(query, dataNode);
+ }
- @Override
- protected int getInMemoryOffset(int queryOffset) {
- return 0;
- }
+ @Override
+ protected int getInMemoryOffset(int queryOffset) {
+ return 0;
+ }
- @Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new HSQLSelectTranslator(query, dataNode, connection);
- }
+ @Override
+ protected SelectTranslator createTranslator() {
+ return new HSQLSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectTranslator.java
index af11ea5..ce3bb58 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLSelectTranslator.java
@@ -19,39 +19,39 @@
package org.apache.cayenne.dba.hsqldb;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
* @since 1.2
*/
-class HSQLSelectTranslator extends SelectTranslator {
-
- /**
- * @since 4.0
- */
- public HSQLSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
-
- @Override
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
- int offset = queryMetadata.getFetchOffset();
- int limit = queryMetadata.getFetchLimit();
-
- if (offset > 0 || limit > 0) {
- buffer.append(" LIMIT ");
-
- // both OFFSET and LIMIT must be present, so come up with defaults if one of
- // them is not set by the user
- if (limit == 0) {
- limit = Integer.MAX_VALUE;
- }
-
- buffer.append(limit).append(" OFFSET ").append(offset);
- }
- }
+class HSQLSelectTranslator extends DefaultSelectTranslator {
+
+ /**
+ * @since 4.0
+ */
+ public HSQLSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
+
+ @Override
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+ int offset = queryMetadata.getFetchOffset();
+ int limit = queryMetadata.getFetchLimit();
+
+ if (offset > 0 || limit > 0) {
+ buffer.append(" LIMIT ");
+
+ // both OFFSET and LIMIT must be present, so come up with defaults
+ // if one of
+ // them is not set by the user
+ if (limit == 0) {
+ limit = Integer.MAX_VALUE;
+ }
+
+ buffer.append(limit).append(" OFFSET ").append(offset);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectAction.java
index 2f93ad4..07f05b1 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectAction.java
@@ -18,8 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.ingres;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -37,7 +35,7 @@ public class IngresSelectAction extends SelectAction {
}
@Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new IngresSelectTranslator(query, dataNode, connection);
+ protected SelectTranslator createTranslator() {
+ return new IngresSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
}
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectTranslator.java
index 18558ec..f466d5f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSelectTranslator.java
@@ -18,19 +18,18 @@
****************************************************************/
package org.apache.cayenne.dba.ingres;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
-public class IngresSelectTranslator extends SelectTranslator {
+public class IngresSelectTranslator extends DefaultSelectTranslator {
/**
* @since 4.0
*/
- public IngresSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
+ public IngresSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
}
@Override
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java
index 5eb3b4d..a39bd16 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java
@@ -18,8 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.mysql;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -30,17 +28,17 @@ import org.apache.cayenne.query.SelectQuery;
*/
class MySQLSelectAction extends SelectAction {
- <T> MySQLSelectAction(SelectQuery<T> query, DataNode dataNode) {
- super(query, dataNode);
- }
+ <T> MySQLSelectAction(SelectQuery<T> query, DataNode dataNode) {
+ super(query, dataNode);
+ }
- @Override
- protected int getInMemoryOffset(int queryOffset) {
- return 0;
- }
+ @Override
+ protected int getInMemoryOffset(int queryOffset) {
+ return 0;
+ }
- @Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new MySQLSelectTranslator(query, dataNode, connection);
- }
+ @Override
+ protected SelectTranslator createTranslator() {
+ return new MySQLSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectTranslator.java
index bd20ef2..c9f0809 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectTranslator.java
@@ -19,40 +19,39 @@
package org.apache.cayenne.dba.mysql;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
* @since 1.2
*/
-class MySQLSelectTranslator extends SelectTranslator {
-
- /**
- * @since 4.0
- */
- public MySQLSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
-
- @Override
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
- int offset = queryMetadata.getFetchOffset();
- int limit = queryMetadata.getFetchLimit();
-
- if (offset > 0 || limit > 0) {
- buffer.append(" LIMIT ");
-
- // both OFFSET and LIMIT must be present, so come up with defaults
- // if one of
- // them is not set by the user
- if (limit == 0) {
- limit = Integer.MAX_VALUE;
- }
-
- buffer.append(limit).append(" OFFSET ").append(offset);
- }
- }
+class MySQLSelectTranslator extends DefaultSelectTranslator {
+
+ /**
+ * @since 4.0
+ */
+ public MySQLSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
+
+ @Override
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+ int offset = queryMetadata.getFetchOffset();
+ int limit = queryMetadata.getFetchLimit();
+
+ if (offset > 0 || limit > 0) {
+ buffer.append(" LIMIT ");
+
+ // both OFFSET and LIMIT must be present, so come up with defaults
+ // if one of
+ // them is not set by the user
+ if (limit == 0) {
+ limit = Integer.MAX_VALUE;
+ }
+
+ buffer.append(limit).append(" OFFSET ").append(offset);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java
index 9ce7b23..fb03bde 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java
@@ -19,8 +19,6 @@
package org.apache.cayenne.dba.openbase;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -33,18 +31,18 @@ import org.apache.cayenne.query.SelectQuery;
*/
class OpenBaseActionBuilder extends JdbcActionBuilder {
- OpenBaseActionBuilder(DataNode dataNode) {
- super(dataNode);
- }
+ OpenBaseActionBuilder(DataNode dataNode) {
+ super(dataNode);
+ }
- @Override
- public <T> SQLAction objectSelectAction(SelectQuery<T> query) {
- return new SelectAction(query, dataNode) {
+ @Override
+ public <T> SQLAction objectSelectAction(SelectQuery<T> query) {
+ return new SelectAction(query, dataNode) {
- @Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new OpenBaseSelectTranslator(query, dataNode, connection);
- }
- };
- }
+ @Override
+ protected SelectTranslator createTranslator() {
+ return new OpenBaseSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
+ };
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java
index 98c5548..a9c7579 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.openbase;
-import java.io.IOException;
import java.util.List;
import org.apache.cayenne.access.translator.select.JoinStack;
@@ -31,91 +30,86 @@ import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
/**
- * OpenBase does not support standard JOIN keyword and have strange syntax for defining
- * inner/outer joins
+ * OpenBase does not support standard JOIN keyword and have strange syntax for
+ * defining inner/outer joins
*
- * @see http
- * ://www.openbase.com/help/KnowledgeBase/400_OpenBaseSQL/401_SelectStatements.html
+ * @see http ://www.openbase.com/help/KnowledgeBase/
+ * 400_OpenBaseSQL/401_SelectStatements.html
* @since 3.0
*/
class OpenBaseJoinStack extends JoinStack {
- protected OpenBaseJoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
- super(dbAdapter, dataMap, assembler);
- }
-
- @Override
- protected void appendJoinSubtree(Appendable out, JoinTreeNode node)
- throws IOException {
- DbRelationship relationship = node.getRelationship();
-
- if (relationship == null) {
- return;
- }
-
- DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
- String targetAlias = node.getTargetTableAlias();
-
- out.append(", ").append(targetEntity.getFullyQualifiedName()).append(' ').append(
- targetAlias);
-
- for (JoinTreeNode child : node.getChildren()) {
- appendJoinSubtree(out, child);
- }
- }
-
- @Override
- protected void appendQualifier(Appendable out, boolean firstQualifierElement)
- throws IOException {
- boolean first = firstQualifierElement;
- for (JoinTreeNode node : rootNode.getChildren()) {
- if (!first) {
- out.append(" AND ");
- }
- appendQualifierSubtree(out, node);
- first = false;
- }
- }
-
- protected void appendQualifierSubtree(Appendable out, JoinTreeNode node)
- throws IOException {
- DbRelationship relationship = node.getRelationship();
-
- String srcAlias = node.getSourceTableAlias();
- String targetAlias = node.getTargetTableAlias();
-
- List<DbJoin> joins = relationship.getJoins();
- int len = joins.size();
- for (int i = 0; i < len; i++) {
- DbJoin join = joins.get(i);
-
- if (i > 0) {
- out.append(" AND ");
- }
-
- out.append(srcAlias).append('.').append(join.getSourceName());
-
- switch (node.getJoinType()) {
- case INNER:
- out.append(" = ");
- break;
- case LEFT_OUTER:
- out.append(" * ");
- break;
- default:
- throw new IllegalArgumentException("Unsupported join type: "
- + node.getJoinType());
- }
-
- out.append(targetAlias).append('.').append(join.getTargetName());
-
- }
-
- for (JoinTreeNode child : node.getChildren()) {
- out.append(" AND ");
- appendQualifierSubtree(out, child);
- }
-
- }
+ protected OpenBaseJoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
+ super(dbAdapter, dataMap, assembler);
+ }
+
+ @Override
+ protected void appendJoinSubtree(StringBuilder out, JoinTreeNode node) {
+ DbRelationship relationship = node.getRelationship();
+
+ if (relationship == null) {
+ return;
+ }
+
+ DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
+ String targetAlias = node.getTargetTableAlias();
+
+ out.append(", ").append(targetEntity.getFullyQualifiedName()).append(' ').append(targetAlias);
+
+ for (JoinTreeNode child : node.getChildren()) {
+ appendJoinSubtree(out, child);
+ }
+ }
+
+ @Override
+ protected void appendQualifier(StringBuilder out, boolean firstQualifierElement) {
+ boolean first = firstQualifierElement;
+ for (JoinTreeNode node : rootNode.getChildren()) {
+ if (!first) {
+ out.append(" AND ");
+ }
+ appendQualifierSubtree(out, node);
+ first = false;
+ }
+ }
+
+ protected void appendQualifierSubtree(StringBuilder out, JoinTreeNode node) {
+ DbRelationship relationship = node.getRelationship();
+
+ String srcAlias = node.getSourceTableAlias();
+ String targetAlias = node.getTargetTableAlias();
+
+ List<DbJoin> joins = relationship.getJoins();
+ int len = joins.size();
+ for (int i = 0; i < len; i++) {
+ DbJoin join = joins.get(i);
+
+ if (i > 0) {
+ out.append(" AND ");
+ }
+
+ out.append(srcAlias).append('.').append(join.getSourceName());
+
+ switch (node.getJoinType()) {
+ case INNER:
+ out.append(" = ");
+ break;
+ case LEFT_OUTER:
+ out.append(" * ");
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported join type: " + node.getJoinType());
+ }
+
+ out.append(targetAlias).append('.').append(join.getTargetName());
+
+ }
+
+ for (JoinTreeNode child : node.getChildren()) {
+ out.append(" AND ");
+ appendQualifierSubtree(out, child);
+ }
+
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java
index df58745..42383b9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java
@@ -29,146 +29,134 @@ import org.apache.cayenne.exp.parser.PatternMatchNode;
import org.apache.cayenne.map.DbAttribute;
/**
- * Translates query qualifier to SQL. Used as a helper class by query translators.
+ * Translates query qualifier to SQL. Used as a helper class by query
+ * translators.
*
* @since 1.1
*/
public class OpenBaseQualifierTranslator extends QualifierTranslator {
- public OpenBaseQualifierTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler);
- }
-
- @Override
- public void startNode(Expression node, Expression parentNode) {
-
- if (node.getOperandCount() == 2) {
- // binary nodes are the only ones that currently require this
- detectObjectMatch(node);
-
- try {
- if (parenthesisNeeded(node, parentNode)) {
- out.append('(');
- }
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // OpenBase is case-insensitive by default
- // ...
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
- else {
- super.startNode(node, parentNode);
- }
- }
-
- @Override
- public void endNode(Expression node, Expression parentNode) {
- if (node.getOperandCount() == 2) {
-
- try {
- // check if we need to use objectMatchTranslator to finish building the
- // expression
- if (matchingObject) {
- appendObjectMatch();
- }
-
- if(PatternMatchNode.class.isAssignableFrom(node.getClass())) {
- appendLikeEscapeCharacter((PatternMatchNode) node);
- }
-
- if (parenthesisNeeded(node, parentNode)) {
- out.append(')');
- }
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // OpenBase is case-insensitive by default
- // ...
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
- else {
- super.endNode(node, parentNode);
- }
- }
-
- @Override
- protected void appendLiteralDirect(
- Object val,
- DbAttribute attr,
- Expression parentExpression) throws IOException {
-
- // Special handling of string matching is needed:
- // Case-sensitive LIKE must be converted to [x][Y][z] format
- if (val instanceof String
- && (parentExpression.getType() == Expression.LIKE || parentExpression
- .getType() == Expression.NOT_LIKE)) {
-
- val = caseSensitiveLikePattern((String) val);
- }
-
- super.appendLiteralDirect(val, attr, parentExpression);
- }
-
- private String caseSensitiveLikePattern(String pattern) {
- int len = pattern.length();
- StringBuilder buffer = new StringBuilder(len * 3);
-
- for (int i = 0; i < len; i++) {
- char c = pattern.charAt(i);
- if (c == '%' || c == '?') {
- buffer.append(c);
- }
- else {
- buffer.append("[").append(c).append("]");
- }
- }
-
- return buffer.toString();
- }
-
- @Override
- public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
- if (!hasMoreChildren) {
- return;
- }
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // OpenBase is case-insensitive by default
- // ...
-
- try {
- switch (node.getType()) {
-
- case Expression.LIKE_IGNORE_CASE:
- finishedChildNodeAppendExpression(node, " LIKE ");
- break;
- case Expression.NOT_LIKE_IGNORE_CASE:
- finishedChildNodeAppendExpression(node, " NOT LIKE ");
- break;
- default:
- super.finishedChild(node, childIndex, hasMoreChildren);
- }
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
-
- private void finishedChildNodeAppendExpression(Expression node, String operation)
- throws IOException {
- Appendable out = matchingObject ? new StringBuilder() : this.out;
- out.append(operation);
- if (matchingObject) {
- objectMatchTranslator.setOperation(out.toString());
- objectMatchTranslator.setExpression(node);
- }
- }
+ public OpenBaseQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
+
+ @Override
+ public void startNode(Expression node, Expression parentNode) {
+
+ if (node.getOperandCount() == 2) {
+ // binary nodes are the only ones that currently require this
+ detectObjectMatch(node);
+
+ if (parenthesisNeeded(node, parentNode)) {
+ out.append('(');
+ }
+
+ // super implementation has special handling
+ // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // OpenBase is case-insensitive by default
+ // ...
+
+ } else {
+ super.startNode(node, parentNode);
+ }
+ }
+
+ @Override
+ public void endNode(Expression node, Expression parentNode) {
+ if (node.getOperandCount() == 2) {
+
+ try {
+ // check if we need to use objectMatchTranslator to finish
+ // building the
+ // expression
+ if (matchingObject) {
+ appendObjectMatch();
+ }
+
+ if (PatternMatchNode.class.isAssignableFrom(node.getClass())) {
+ appendLikeEscapeCharacter((PatternMatchNode) node);
+ }
+
+ if (parenthesisNeeded(node, parentNode)) {
+ out.append(')');
+ }
+
+ // super implementation has special handling
+ // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // OpenBase is case-insensitive by default
+ // ...
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+ } else {
+ super.endNode(node, parentNode);
+ }
+ }
+
+ @Override
+ protected void appendLiteralDirect(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
+
+ // Special handling of string matching is needed:
+ // Case-sensitive LIKE must be converted to [x][Y][z] format
+ if (val instanceof String
+ && (parentExpression.getType() == Expression.LIKE || parentExpression.getType() == Expression.NOT_LIKE)) {
+
+ val = caseSensitiveLikePattern((String) val);
+ }
+
+ super.appendLiteralDirect(val, attr, parentExpression);
+ }
+
+ private String caseSensitiveLikePattern(String pattern) {
+ int len = pattern.length();
+ StringBuilder buffer = new StringBuilder(len * 3);
+
+ for (int i = 0; i < len; i++) {
+ char c = pattern.charAt(i);
+ if (c == '%' || c == '?') {
+ buffer.append(c);
+ } else {
+ buffer.append("[").append(c).append("]");
+ }
+ }
+
+ return buffer.toString();
+ }
+
+ @Override
+ public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
+ if (!hasMoreChildren) {
+ return;
+ }
+
+ // super implementation has special handling
+ // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // OpenBase is case-insensitive by default
+ // ...
+
+ try {
+ switch (node.getType()) {
+
+ case Expression.LIKE_IGNORE_CASE:
+ finishedChildNodeAppendExpression(node, " LIKE ");
+ break;
+ case Expression.NOT_LIKE_IGNORE_CASE:
+ finishedChildNodeAppendExpression(node, " NOT LIKE ");
+ break;
+ default:
+ super.finishedChild(node, childIndex, hasMoreChildren);
+ }
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+ }
+
+ private void finishedChildNodeAppendExpression(Expression node, String operation) throws IOException {
+ Appendable out = matchingObject ? new StringBuilder() : this.out;
+ out.append(operation);
+ if (matchingObject) {
+ objectMatchTranslator.setOperation(out.toString());
+ objectMatchTranslator.setExpression(node);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java
index d0dc5a6..342d0fd 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java
@@ -19,36 +19,35 @@
package org.apache.cayenne.dba.openbase;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
import org.apache.cayenne.access.translator.select.JoinStack;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
* @since 1.2
*/
-class OpenBaseSelectTranslator extends SelectTranslator {
+class OpenBaseSelectTranslator extends DefaultSelectTranslator {
+
+ /**
+ * @since 4.0
+ */
+ public OpenBaseSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
- /**
- * @since 4.0
- */
- public OpenBaseSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
-
- @Override
- protected JoinStack createJoinStack() {
- return new OpenBaseJoinStack(getAdapter(), queryMetadata.getDataMap(), this);
- }
+ @Override
+ protected JoinStack createJoinStack() {
+ return new OpenBaseJoinStack(getAdapter(), queryMetadata.getDataMap(), this);
+ }
- @Override
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
- int limit = queryMetadata.getFetchLimit();
- if (limit > 0) {
- buffer.append(" RETURN RESULTS ").append(limit);
- }
- }
+ @Override
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+ int limit = queryMetadata.getFetchLimit();
+ if (limit > 0) {
+ buffer.append(" RETURN RESULTS ").append(limit);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java
index 0900b56..e27ee90 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.oracle;
-import java.io.IOException;
import java.util.List;
import org.apache.cayenne.access.translator.select.JoinStack;
@@ -36,79 +35,74 @@ import org.apache.cayenne.map.DbRelationship;
// cloned from OpenBaseJoin stack... need better strategies of reuse...
class Oracle8JoinStack extends JoinStack {
- Oracle8JoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
- super(dbAdapter, dataMap, assembler);
- }
-
- @Override
- protected void appendJoinSubtree(Appendable out, JoinTreeNode node)
- throws IOException {
- DbRelationship relationship = node.getRelationship();
-
- if (relationship == null) {
- return;
- }
-
- DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
- String targetAlias = node.getTargetTableAlias();
-
- out.append(", ").append(targetEntity.getFullyQualifiedName()).append(' ').append(
- targetAlias);
-
- for (JoinTreeNode child : node.getChildren()) {
- appendJoinSubtree(out, child);
- }
- }
-
- @Override
- protected void appendQualifier(Appendable out, boolean firstQualifierElement)
- throws IOException {
- boolean first = firstQualifierElement;
- for (JoinTreeNode node : rootNode.getChildren()) {
- if (!first) {
- out.append(" AND ");
- }
- appendQualifierSubtree(out, node);
- first = false;
- }
- }
-
- protected void appendQualifierSubtree(Appendable out, JoinTreeNode node)
- throws IOException {
- DbRelationship relationship = node.getRelationship();
-
- String srcAlias = node.getSourceTableAlias();
- String targetAlias = node.getTargetTableAlias();
-
- List<DbJoin> joins = relationship.getJoins();
- int len = joins.size();
- for (int i = 0; i < len; i++) {
- DbJoin join = joins.get(i);
-
- if (i > 0) {
- out.append(" AND ");
- }
-
- out.append(srcAlias).append('.').append(join.getSourceName());
-
- switch (node.getJoinType()) {
- case INNER:
- out.append(" = ");
- break;
- case LEFT_OUTER:
- out.append(" * ");
- break;
- default:
- throw new IllegalArgumentException("Unsupported join type: "
- + node.getJoinType());
- }
-
- out.append(targetAlias).append('.').append(join.getTargetName());
- }
-
- for (JoinTreeNode child : node.getChildren()) {
- out.append(" AND ");
- appendQualifierSubtree(out, child);
- }
- }
+ Oracle8JoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
+ super(dbAdapter, dataMap, assembler);
+ }
+
+ @Override
+ protected void appendJoinSubtree(StringBuilder out, JoinTreeNode node) {
+ DbRelationship relationship = node.getRelationship();
+
+ if (relationship == null) {
+ return;
+ }
+
+ DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
+ String targetAlias = node.getTargetTableAlias();
+
+ out.append(", ").append(targetEntity.getFullyQualifiedName()).append(' ').append(targetAlias);
+
+ for (JoinTreeNode child : node.getChildren()) {
+ appendJoinSubtree(out, child);
+ }
+ }
+
+ @Override
+ protected void appendQualifier(StringBuilder out, boolean firstQualifierElement) {
+ boolean first = firstQualifierElement;
+ for (JoinTreeNode node : rootNode.getChildren()) {
+ if (!first) {
+ out.append(" AND ");
+ }
+ appendQualifierSubtree(out, node);
+ first = false;
+ }
+ }
+
+ protected void appendQualifierSubtree(StringBuilder out, JoinTreeNode node) {
+ DbRelationship relationship = node.getRelationship();
+
+ String srcAlias = node.getSourceTableAlias();
+ String targetAlias = node.getTargetTableAlias();
+
+ List<DbJoin> joins = relationship.getJoins();
+ int len = joins.size();
+ for (int i = 0; i < len; i++) {
+ DbJoin join = joins.get(i);
+
+ if (i > 0) {
+ out.append(" AND ");
+ }
+
+ out.append(srcAlias).append('.').append(join.getSourceName());
+
+ switch (node.getJoinType()) {
+ case INNER:
+ out.append(" = ");
+ break;
+ case LEFT_OUTER:
+ out.append(" * ");
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported join type: " + node.getJoinType());
+ }
+
+ out.append(targetAlias).append('.').append(join.getTargetName());
+ }
+
+ for (JoinTreeNode child : node.getChildren()) {
+ out.append(" AND ");
+ appendQualifierSubtree(out, child);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java
index d90ce6e..59766cb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java
@@ -35,7 +35,7 @@ import java.util.List;
import org.apache.cayenne.CayenneException;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.OperationObserver;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.log.JdbcEventLogger;
import org.apache.cayenne.map.DbAttribute;
@@ -55,10 +55,10 @@ class Oracle8LOBBatchAction implements SQLAction {
private DbAdapter adapter;
private JdbcEventLogger logger;
- private static void bind(DbAdapter adapter, PreparedStatement statement, BatchParameterBinding[] bindings)
+ private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings)
throws SQLException, Exception {
- for (BatchParameterBinding b : bindings) {
+ for (ParameterBinding b : bindings) {
adapter.bindParameter(statement, b.getValue(), b.getStatementPosition(), b.getAttribute().getType(), b
.getAttribute().getScale());
}
@@ -104,7 +104,7 @@ class Oracle8LOBBatchAction implements SQLAction {
PreparedStatement statement = connection.prepareStatement(updateStr);
try {
- BatchParameterBinding[] bindings = translator.updateBindings(row);
+ ParameterBinding[] bindings = translator.updateBindings(row);
logger.logQueryParameters("bind", bindings);
bind(adapter, statement, bindings);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java
index 8244ca5..82a95ce 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java
@@ -24,7 +24,7 @@ import java.util.Iterator;
import java.util.List;
import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.access.translator.batch.DefaultBatchTranslator;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
@@ -110,28 +110,28 @@ abstract class Oracle8LOBBatchTranslator extends DefaultBatchTranslator {
}
@Override
- protected BatchParameterBinding[] createBindings() {
+ protected ParameterBinding[] createBindings() {
List<DbAttribute> dbAttributes = query.getDbAttributes();
int len = dbAttributes.size();
- BatchParameterBinding[] bindings = new BatchParameterBinding[len];
+ ParameterBinding[] bindings = new ParameterBinding[len];
for (int i = 0; i < len; i++) {
DbAttribute attribute = dbAttributes.get(i);
- bindings[i] = new BatchParameterBinding(attribute);
+ bindings[i] = new ParameterBinding(attribute);
}
return bindings;
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
int len = bindings.length;
for (int i = 0, j = 1; i < len; i++) {
- BatchParameterBinding b = bindings[i];
+ ParameterBinding b = bindings[i];
Object value = row.getValue(i);
DbAttribute attribute = b.getAttribute();
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8QualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8QualifierTranslator.java
index bed30da..d756392 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8QualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8QualifierTranslator.java
@@ -18,34 +18,32 @@
****************************************************************/
package org.apache.cayenne.dba.oracle;
-import java.io.IOException;
-
import org.apache.cayenne.access.translator.select.QueryAssembler;
/**
- * Extends the TrimmingQualifierTranslator that Cayenne normally uses for Oracle.
- * Overrides doAppendPart() to wrap the qualifierBuffer in parentheses if it contains an
- * "OR" expression. This avoids a bug that can happen on Oracle8 if the query also
- * contains a join.
+ * Extends the TrimmingQualifierTranslator that Cayenne normally uses for
+ * Oracle. Overrides doAppendPart() to wrap the qualifierBuffer in parentheses
+ * if it contains an "OR" expression. This avoids a bug that can happen on
+ * Oracle8 if the query also contains a join.
*
* @since 3.0
*/
class Oracle8QualifierTranslator extends OracleQualifierTranslator {
- public Oracle8QualifierTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler);
- }
+ public Oracle8QualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
- @Override
- protected void doAppendPart() throws IOException {
- super.doAppendPart();
+ @Override
+ protected void doAppendPart() {
+ super.doAppendPart();
- if (out instanceof StringBuilder) {
- StringBuilder buffer = (StringBuilder) out;
- if (buffer.indexOf(" OR ") != -1) {
- buffer.insert(0, '(');
- buffer.append(')');
- }
- }
- }
+ if (out instanceof StringBuilder) {
+ StringBuilder buffer = (StringBuilder) out;
+ if (buffer.indexOf(" OR ") != -1) {
+ buffer.insert(0, '(');
+ buffer.append(')');
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
index e3ff158..abef7a7 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
@@ -18,8 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.oracle;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.translator.select.SelectTranslator;
import org.apache.cayenne.query.SelectQuery;
@@ -29,12 +27,12 @@ import org.apache.cayenne.query.SelectQuery;
*/
class Oracle8SelectAction extends OracleSelectAction {
- <T> Oracle8SelectAction(SelectQuery<T> query, DataNode dataNode) {
- super(query, dataNode);
- }
+ <T> Oracle8SelectAction(SelectQuery<T> query, DataNode dataNode) {
+ super(query, dataNode);
+ }
- @Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new Oracle8SelectTranslator(query, dataNode, connection);
- }
+ @Override
+ protected SelectTranslator createTranslator() {
+ return new Oracle8SelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
}
[3/7] cayenne git commit: Refactoring SelectTranslator for better
extensibility
Posted by aa...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java
index aa35ada..6d1b16f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java
@@ -18,10 +18,9 @@
****************************************************************/
package org.apache.cayenne.dba.oracle;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.translator.select.JoinStack;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
@@ -29,20 +28,20 @@ import org.apache.cayenne.query.Query;
*/
class Oracle8SelectTranslator extends OracleSelectTranslator {
- /**
- * @since 4.0
- */
- public Oracle8SelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
-
- /**
- * Returns an old style joint stack for Oracle8 that does not support explicit join
- * syntax.
- */
- @Override
- protected JoinStack createJoinStack() {
- return new Oracle8JoinStack(getAdapter(), queryMetadata.getDataMap(), this);
- }
+ /**
+ * @since 4.0
+ */
+ public Oracle8SelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
+
+ /**
+ * Returns an old style joint stack for Oracle8 that does not support
+ * explicit join syntax.
+ */
+ @Override
+ protected JoinStack createJoinStack() {
+ return new Oracle8JoinStack(getAdapter(), queryMetadata.getDataMap(), this);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java
index b375e3f..1ace92e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.oracle;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -33,68 +32,65 @@ import org.apache.cayenne.exp.parser.ASTPath;
import org.apache.commons.collections.Transformer;
/**
- * Oracle qualifier translator. In particular, trims INs with more than 1000 elements to
- * an OR-set of INs with <= 1000 elements
+ * Oracle qualifier translator. In particular, trims INs with more than 1000
+ * elements to an OR-set of INs with <= 1000 elements
*/
public class OracleQualifierTranslator extends TrimmingQualifierTranslator {
- public OracleQualifierTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler, OracleAdapter.TRIM_FUNCTION);
- }
+ public OracleQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler, OracleAdapter.TRIM_FUNCTION);
+ }
- @Override
- protected void doAppendPart(Expression rootNode) throws IOException {
- if (rootNode == null) {
- return;
- }
- rootNode = rootNode.transform(new INTrimmer());
- rootNode.traverse(this);
- }
+ @Override
+ protected void doAppendPart(Expression rootNode) {
+ if (rootNode == null) {
+ return;
+ }
+ rootNode = rootNode.transform(new INTrimmer());
+ rootNode.traverse(this);
+ }
- public static class INTrimmer implements Transformer {
+ public static class INTrimmer implements Transformer {
- public Expression trimmedInExpression(Expression exp, int maxInSize) {
- Expression list = (Expression) exp.getOperand(1);
- Object[] objects = (Object[]) list.evaluate(null);
+ public Expression trimmedInExpression(Expression exp, int maxInSize) {
+ Expression list = (Expression) exp.getOperand(1);
+ Object[] objects = (Object[]) list.evaluate(null);
- if (objects.length <= maxInSize) {
- return exp;
- }
+ if (objects.length <= maxInSize) {
+ return exp;
+ }
- Expression trimmed = trimmedInExpression(
- (ASTPath) exp.getOperand(0),
- objects,
- maxInSize);
- if (exp instanceof ASTNotIn) {
- return new ASTNegate(trimmed);
- }
- return trimmed;
- }
+ Expression trimmed = trimmedInExpression((ASTPath) exp.getOperand(0), objects, maxInSize);
+ if (exp instanceof ASTNotIn) {
+ return new ASTNegate(trimmed);
+ }
+ return trimmed;
+ }
- Expression trimmedInExpression(ASTPath path, Object[] values, int maxInSize) {
- Expression res = null;
+ Expression trimmedInExpression(ASTPath path, Object[] values, int maxInSize) {
+ Expression res = null;
- List<Object> in = new ArrayList<Object>(maxInSize);
- for (Object v : values) {
- in.add(v);
- if (in.size() == maxInSize) {
- Expression inExp = new ASTIn(path, new ASTList(in));
- res = res != null ? res.orExp(inExp) : inExp;
- in = new ArrayList<Object>(maxInSize);
- }
- }
- if (in.size() > 0) {
- Expression inExp = new ASTIn(path, new ASTList(in));
- res = res != null ? res.orExp(inExp) : inExp;
- }
- return res;
- }
+ List<Object> in = new ArrayList<Object>(maxInSize);
+ for (Object v : values) {
+ in.add(v);
+ if (in.size() == maxInSize) {
+ Expression inExp = new ASTIn(path, new ASTList(in));
+ res = res != null ? res.orExp(inExp) : inExp;
+ in = new ArrayList<Object>(maxInSize);
+ }
+ }
+ if (in.size() > 0) {
+ Expression inExp = new ASTIn(path, new ASTList(in));
+ res = res != null ? res.orExp(inExp) : inExp;
+ }
+ return res;
+ }
- public Object transform(Object input) {
- if (input instanceof ASTIn || input instanceof ASTNotIn) {
- return trimmedInExpression((Expression) input, 1000);
- }
- return input;
- }
- }
+ public Object transform(Object input) {
+ if (input instanceof ASTIn || input instanceof ASTNotIn) {
+ return trimmedInExpression((Expression) input, 1000);
+ }
+ return input;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
index de145f3..f361aa4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
@@ -19,8 +19,6 @@
package org.apache.cayenne.dba.oracle;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -31,17 +29,17 @@ import org.apache.cayenne.query.SelectQuery;
*/
class OracleSelectAction extends SelectAction {
- public <T> OracleSelectAction(SelectQuery<T> query, DataNode dataNode) {
- super(query, dataNode);
- }
+ public <T> OracleSelectAction(SelectQuery<T> query, DataNode dataNode) {
+ super(query, dataNode);
+ }
- @Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new OracleSelectTranslator(query, dataNode, connection);
- }
+ @Override
+ protected SelectTranslator createTranslator() {
+ return new OracleSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
- @Override
- protected int getInMemoryOffset(int queryOffset) {
- return 0;
- }
+ @Override
+ protected int getInMemoryOffset(int queryOffset) {
+ return 0;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
index 911a2c2..fa6e95b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
@@ -19,75 +19,54 @@
package org.apache.cayenne.dba.oracle;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.util.List;
import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
* Select translator that implements Oracle-specific optimizations.
*
*/
-class OracleSelectTranslator extends SelectTranslator {
-
- /**
- * @since 4.0
- */
- public OracleSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
+class OracleSelectTranslator extends DefaultSelectTranslator {
- @Override
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
- int offset = queryMetadata.getFetchOffset();
- int limit = queryMetadata.getFetchLimit();
+ /**
+ * @since 4.0
+ */
+ public OracleSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
- if (limit > 0 || offset > 0) {
- int max = (limit <= 0) ? Integer.MAX_VALUE : limit + offset;
+ @Override
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+ int offset = queryMetadata.getFetchOffset();
+ int limit = queryMetadata.getFetchLimit();
- buffer.insert(0, "select * from ( select tid.*, ROWNUM rnum from (");
- buffer
- .append(") tid where ROWNUM <=")
- .append(max)
- .append(") where rnum > ")
- .append(offset);
- }
- }
-
- @Override
- protected void appendSelectColumns(StringBuilder buffer, List<String> selectColumnExpList) {
-
- // we need to add aliases to all columns to make fetch
- // limit and offset work properly on Oracle (see CAY-1266)
-
- // append columns (unroll the loop's first element)
- int columnCount = selectColumnExpList.size();
- buffer.append(selectColumnExpList.get(0)).append(" AS c0");
+ if (limit > 0 || offset > 0) {
+ int max = (limit <= 0) ? Integer.MAX_VALUE : limit + offset;
- // assume there is at least 1 element
- for (int i = 1; i < columnCount; i++) {
- buffer.append(", ");
- buffer
- .append(selectColumnExpList.get(i))
- .append(" AS c" + i);
- }
- }
+ buffer.insert(0, "select * from ( select tid.*, ROWNUM rnum from (");
+ buffer.append(") tid where ROWNUM <=").append(max).append(") where rnum > ").append(offset);
+ }
+ }
- /**
- * Translates internal query into PreparedStatement, applying Oracle optimizations if
- * possible.
- */
- @Override
- public PreparedStatement createStatement() throws Exception {
- String sqlStr = createSqlString();
- logger.logQuery(sqlStr, values);
- PreparedStatement stmt = connection.prepareStatement(sqlStr);
+ @Override
+ protected void appendSelectColumns(StringBuilder buffer, List<String> selectColumnExpList) {
- initStatement(stmt);
+ // we need to add aliases to all columns to make fetch
+ // limit and offset work properly on Oracle (see CAY-1266)
- return stmt;
- }
+ // append columns (unroll the loop's first element)
+ int columnCount = selectColumnExpList.size();
+ buffer.append(selectColumnExpList.get(0)).append(" AS c0");
+
+ // assume there is at least 1 element
+ for (int i = 1; i < columnCount; i++) {
+ buffer.append(", ");
+ buffer.append(selectColumnExpList.get(i)).append(" AS c" + i);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java
index 3904805..8b8e731 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java
@@ -34,102 +34,94 @@ import org.apache.cayenne.exp.parser.PatternMatchNode;
*/
public class PostgresQualifierTranslator extends TrimmingQualifierTranslator {
- public PostgresQualifierTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler, "RTRIM");
- }
-
- @Override
- public void startNode(Expression node, Expression parentNode) {
-
- if (node.getOperandCount() == 2) {
- // binary nodes are the only ones that currently require this
- detectObjectMatch(node);
-
- try {
- if (parenthesisNeeded(node, parentNode)) {
- out.append('(');
- }
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // Postgres uses ILIKE
- // ...
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
- else {
- super.startNode(node, parentNode);
- }
- }
-
- @Override
- public void endNode(Expression node, Expression parentNode) {
- if (node.getOperandCount() == 2) {
-
- try {
- // check if we need to use objectMatchTranslator to finish building the
- // expression
- if (matchingObject) {
- appendObjectMatch();
- }
-
- if(PatternMatchNode.class.isAssignableFrom(node.getClass())) {
- appendLikeEscapeCharacter((PatternMatchNode) node);
- }
-
- if (parenthesisNeeded(node, parentNode)) {
- out.append(')');
- }
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // Postgres uses ILIKE
- // ...
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
- else {
- super.endNode(node, parentNode);
- }
- }
-
- @Override
- public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
- if (!hasMoreChildren) {
- return;
- }
-
- try {
- // use ILIKE
-
- switch (node.getType()) {
-
- case Expression.LIKE_IGNORE_CASE:
- finishedChildNodeAppendExpression(node, " ILIKE ");
- break;
- case Expression.NOT_LIKE_IGNORE_CASE:
- finishedChildNodeAppendExpression(node, " NOT ILIKE ");
- break;
- default:
- super.finishedChild(node, childIndex, hasMoreChildren);
- }
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
-
- private void finishedChildNodeAppendExpression(Expression node, String operation)
- throws IOException {
- Appendable buf = matchingObject ? new StringBuilder() : this.out;
- buf.append(operation);
- if (matchingObject) {
- objectMatchTranslator.setOperation(buf.toString());
- objectMatchTranslator.setExpression(node);
- }
- }
+ public PostgresQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler, "RTRIM");
+ }
+
+ @Override
+ public void startNode(Expression node, Expression parentNode) {
+
+ if (node.getOperandCount() == 2) {
+ // binary nodes are the only ones that currently require this
+ detectObjectMatch(node);
+
+ if (parenthesisNeeded(node, parentNode)) {
+ out.append('(');
+ }
+
+ // super implementation has special handling
+ // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // Postgres uses ILIKE
+ // ...
+
+ } else {
+ super.startNode(node, parentNode);
+ }
+ }
+
+ @Override
+ public void endNode(Expression node, Expression parentNode) {
+ if (node.getOperandCount() == 2) {
+
+ try {
+ // check if we need to use objectMatchTranslator to finish
+ // building the
+ // expression
+ if (matchingObject) {
+ appendObjectMatch();
+ }
+
+ if (PatternMatchNode.class.isAssignableFrom(node.getClass())) {
+ appendLikeEscapeCharacter((PatternMatchNode) node);
+ }
+
+ if (parenthesisNeeded(node, parentNode)) {
+ out.append(')');
+ }
+
+ // super implementation has special handling
+ // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // Postgres uses ILIKE
+ // ...
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+ } else {
+ super.endNode(node, parentNode);
+ }
+ }
+
+ @Override
+ public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
+ if (!hasMoreChildren) {
+ return;
+ }
+
+ try {
+ // use ILIKE
+
+ switch (node.getType()) {
+
+ case Expression.LIKE_IGNORE_CASE:
+ finishedChildNodeAppendExpression(node, " ILIKE ");
+ break;
+ case Expression.NOT_LIKE_IGNORE_CASE:
+ finishedChildNodeAppendExpression(node, " NOT ILIKE ");
+ break;
+ default:
+ super.finishedChild(node, childIndex, hasMoreChildren);
+ }
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+ }
+
+ private void finishedChildNodeAppendExpression(Expression node, String operation) throws IOException {
+ Appendable buf = matchingObject ? new StringBuilder() : this.out;
+ buf.append(operation);
+ if (matchingObject) {
+ objectMatchTranslator.setOperation(buf.toString());
+ objectMatchTranslator.setExpression(node);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectAction.java
index 0833f96..b9e8fe5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectAction.java
@@ -18,8 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.postgres;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -30,17 +28,17 @@ import org.apache.cayenne.query.SelectQuery;
*/
class PostgresSelectAction extends SelectAction {
- <T> PostgresSelectAction(SelectQuery<T> query, DataNode dataNode) {
- super(query, dataNode);
- }
+ <T> PostgresSelectAction(SelectQuery<T> query, DataNode dataNode) {
+ super(query, dataNode);
+ }
- @Override
- protected int getInMemoryOffset(int queryOffset) {
- return 0;
- }
+ @Override
+ protected int getInMemoryOffset(int queryOffset) {
+ return 0;
+ }
- @Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new PostgresSelectTranslator(query, dataNode, connection);
- }
+ @Override
+ protected SelectTranslator createTranslator() {
+ return new PostgresSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
index 84b3521..b7c1917 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
@@ -19,23 +19,22 @@
package org.apache.cayenne.dba.postgres;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
/**
* @since 1.2
*/
-class PostgresSelectTranslator extends SelectTranslator {
+class PostgresSelectTranslator extends DefaultSelectTranslator {
/**
* @since 4.0
*/
- public PostgresSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
+ public PostgresSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
}
@Override
@@ -81,7 +80,7 @@ class PostgresSelectTranslator extends SelectTranslator {
if (foundKey) {
builder.append(")");
}
-
+
return builder.toString();
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectAction.java
index d274d70..413a6a6 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectAction.java
@@ -18,8 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.sqlserver;
-import java.sql.Connection;
-
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.jdbc.SelectAction;
import org.apache.cayenne.access.translator.select.SelectTranslator;
@@ -32,8 +30,8 @@ public class SQLServerSelectAction extends SelectAction {
}
@Override
- protected SelectTranslator createTranslator(Connection connection) {
- return new SQLServerSelectTranslator(query, dataNode, connection);
+ protected SelectTranslator createTranslator() {
+ return new SQLServerSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
}
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectTranslator.java
index 3556c7b..b78877c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSelectTranslator.java
@@ -18,40 +18,37 @@
****************************************************************/
package org.apache.cayenne.dba.sqlserver;
-import java.sql.Connection;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
-
-public class SQLServerSelectTranslator extends SelectTranslator {
-
- /**
- * @since 4.0
- */
- public SQLServerSelectTranslator(Query query, DataNode dataNode, Connection connection) {
- super(query, dataNode, connection);
- }
-
- @Override
- protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
- QueryMetadata metadata = getQuery().getMetaData(getEntityResolver());
-
- int limit = metadata.getFetchLimit();
- int offset = metadata.getFetchOffset();
-
- if (limit > 0) {
- String sql = buffer.toString();
-
- // If contains distinct insert top limit after
- if (sql.startsWith("SELECT DISTINCT ")) {
- buffer.replace(0, 15, "SELECT DISTINCT TOP " + (offset + limit));
-
- } else {
- buffer.replace(0, 6, "SELECT TOP " + (offset + limit));
- }
- }
- }
+
+public class SQLServerSelectTranslator extends DefaultSelectTranslator {
+
+ /**
+ * @since 4.0
+ */
+ public SQLServerSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
+
+ @Override
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+
+ int limit = queryMetadata.getFetchLimit();
+ int offset = queryMetadata.getFetchOffset();
+
+ if (limit > 0) {
+ String sql = buffer.toString();
+
+ // If contains distinct insert top limit after
+ if (sql.startsWith("SELECT DISTINCT ")) {
+ buffer.replace(0, 15, "SELECT DISTINCT TOP " + (offset + limit));
+
+ } else {
+ buffer.replace(0, 6, "SELECT TOP " + (offset + limit));
+ }
+ }
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java
index 2c0649f..0b4fa68 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.dba.sqlserver;
-import java.io.IOException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
@@ -33,87 +32,84 @@ import org.apache.cayenne.map.DbAttribute;
*/
class SQLServerTrimmingQualifierTranslator extends TrimmingQualifierTranslator {
- // since LIKE IGNORE CASE requires more contextual information than the super
- // translator can provide, we are using an internal element stack to trace translation
- // context.. Maybe it is a good idea to introduce it in the superclass?
- private List<Expression> expressionStack;
-
- SQLServerTrimmingQualifierTranslator(QueryAssembler queryAssembler,
- String trimFunction) {
- super(queryAssembler, trimFunction);
- expressionStack = new ArrayList<Expression>();
- }
-
- @Override
- public void startNode(Expression node, Expression parentNode) {
- push(node);
- super.startNode(node, parentNode);
- }
-
- @Override
- protected void processColumn(DbAttribute dbAttr) throws IOException {
-
- Expression node = peek(1);
-
- boolean likeCI = node != null
- && dbAttr.getType() == Types.CLOB
- && (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
-
- if (likeCI) {
- out.append("CAST(");
- }
-
- super.processColumn(dbAttr);
-
- if (likeCI) {
- out.append(" AS NVARCHAR(MAX))");
- }
- }
-
- @Override
- protected void processColumnWithQuoteSqlIdentifiers(
- DbAttribute dbAttr,
- Expression pathExp) throws IOException {
- Expression node = peek(1);
-
- boolean likeCI = node != null
- && dbAttr.getType() == Types.CLOB
- && (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
-
- if (likeCI) {
- out.append("CAST(");
- }
-
- super.processColumnWithQuoteSqlIdentifiers(dbAttr, node);
-
- if (likeCI) {
- out.append(" AS NVARCHAR(MAX))");
- }
- }
-
- @Override
- public void endNode(Expression node, Expression parentNode) {
- super.endNode(node, parentNode);
- pop();
- }
-
- private void push(Expression node) {
- expressionStack.add(node);
- }
-
- private void pop() {
- int len = expressionStack.size();
- if (len > 0) {
- expressionStack.remove(len - 1);
- }
- }
-
- private Expression peek(int tailIndex) {
- int index = expressionStack.size() - tailIndex - 1;
- if (index < 0) {
- return null;
- }
-
- return expressionStack.get(index);
- }
+ // since LIKE IGNORE CASE requires more contextual information than the
+ // super
+ // translator can provide, we are using an internal element stack to trace
+ // translation
+ // context.. Maybe it is a good idea to introduce it in the superclass?
+ private List<Expression> expressionStack;
+
+ SQLServerTrimmingQualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
+ super(queryAssembler, trimFunction);
+ expressionStack = new ArrayList<Expression>();
+ }
+
+ @Override
+ public void startNode(Expression node, Expression parentNode) {
+ push(node);
+ super.startNode(node, parentNode);
+ }
+
+ @Override
+ protected void processColumn(DbAttribute dbAttr) {
+
+ Expression node = peek(1);
+
+ boolean likeCI = node != null && dbAttr.getType() == Types.CLOB
+ && (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
+
+ if (likeCI) {
+ out.append("CAST(");
+ }
+
+ super.processColumn(dbAttr);
+
+ if (likeCI) {
+ out.append(" AS NVARCHAR(MAX))");
+ }
+ }
+
+ @Override
+ protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) {
+ Expression node = peek(1);
+
+ boolean likeCI = node != null && dbAttr.getType() == Types.CLOB
+ && (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
+
+ if (likeCI) {
+ out.append("CAST(");
+ }
+
+ super.processColumnWithQuoteSqlIdentifiers(dbAttr, node);
+
+ if (likeCI) {
+ out.append(" AS NVARCHAR(MAX))");
+ }
+ }
+
+ @Override
+ public void endNode(Expression node, Expression parentNode) {
+ super.endNode(node, parentNode);
+ pop();
+ }
+
+ private void push(Expression node) {
+ expressionStack.add(node);
+ }
+
+ private void pop() {
+ int len = expressionStack.size();
+ if (len > 0) {
+ expressionStack.remove(len - 1);
+ }
+ }
+
+ private Expression peek(int tailIndex) {
+ int index = expressionStack.size() - tailIndex - 1;
+ if (index < 0) {
+ return null;
+ }
+
+ return expressionStack.get(index);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/log/CommonsJdbcEventLogger.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/log/CommonsJdbcEventLogger.java b/cayenne-server/src/main/java/org/apache/cayenne/log/CommonsJdbcEventLogger.java
index 43cd923..d31f89f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/log/CommonsJdbcEventLogger.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/log/CommonsJdbcEventLogger.java
@@ -25,8 +25,8 @@ import java.util.List;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ExtendedEnumeration;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.configuration.Constants;
import org.apache.cayenne.configuration.RuntimeProperties;
import org.apache.cayenne.conn.DataSourceInfo;
@@ -44,397 +44,426 @@ import org.apache.commons.logging.LogFactory;
*/
public class CommonsJdbcEventLogger implements JdbcEventLogger {
- private static final Log logger = LogFactory.getLog(CommonsJdbcEventLogger.class);
-
- private static final int TRIM_VALUES_THRESHOLD = 30;
-
- protected long queryExecutionTimeLoggingThreshold;
-
- public CommonsJdbcEventLogger(@Inject RuntimeProperties runtimeProperties) {
- this.queryExecutionTimeLoggingThreshold = runtimeProperties.getLong(
- Constants.QUERY_EXECUTION_TIME_LOGGING_THRESHOLD_PROPERTY, 0);
- }
-
- void sqlLiteralForObject(StringBuilder buffer, Object object) {
- if (object == null) {
- buffer.append("NULL");
- } else if (object instanceof String) {
- buffer.append('\'');
- // lets escape quotes
- String literal = (String) object;
- if (literal.length() > TRIM_VALUES_THRESHOLD) {
- literal = literal.substring(0, TRIM_VALUES_THRESHOLD) + "...";
- }
-
- int curPos = 0;
- int endPos = 0;
-
- while ((endPos = literal.indexOf('\'', curPos)) >= 0) {
- buffer.append(literal.substring(curPos, endPos + 1)).append('\'');
- curPos = endPos + 1;
- }
-
- if (curPos < literal.length())
- buffer.append(literal.substring(curPos));
-
- buffer.append('\'');
- }
- // handle byte pretty formatting
- else if (object instanceof Byte) {
- IDUtil.appendFormattedByte(buffer, ((Byte) object).byteValue());
- } else if (object instanceof Number) {
- // process numeric value (do something smart in the future)
- buffer.append(object);
- } else if (object instanceof java.sql.Date) {
- buffer.append('\'').append(object).append('\'');
- } else if (object instanceof java.sql.Time) {
- buffer.append('\'').append(object).append('\'');
- } else if (object instanceof java.util.Date) {
- long time = ((java.util.Date) object).getTime();
- buffer.append('\'').append(new java.sql.Timestamp(time)).append('\'');
- } else if (object instanceof java.util.Calendar) {
- long time = ((java.util.Calendar) object).getTimeInMillis();
- buffer.append(object.getClass().getName()).append('(').append(new java.sql.Timestamp(time)).append(')');
- } else if (object instanceof Character) {
- buffer.append(((Character) object).charValue());
- } else if (object instanceof Boolean) {
- buffer.append('\'').append(object).append('\'');
- } else if (object instanceof Enum<?>) {
- // buffer.append(object.getClass().getName()).append(".");
- buffer.append(((Enum<?>) object).name()).append("=");
- if (object instanceof ExtendedEnumeration) {
- Object value = ((ExtendedEnumeration) object).getDatabaseValue();
- if (value instanceof String)
- buffer.append("'");
- buffer.append(value);
- if (value instanceof String)
- buffer.append("'");
- } else {
- buffer.append(((Enum<?>) object).ordinal());
- // FIXME -- this isn't quite right
- }
- } else if (object instanceof ParameterBinding) {
- sqlLiteralForObject(buffer, ((ParameterBinding) object).getValue());
- } else if (object.getClass().isArray()) {
- buffer.append("< ");
-
- int len = Array.getLength(object);
- boolean trimming = false;
- if (len > TRIM_VALUES_THRESHOLD) {
- len = TRIM_VALUES_THRESHOLD;
- trimming = true;
- }
-
- for (int i = 0; i < len; i++) {
- if (i > 0) {
- buffer.append(",");
- }
- sqlLiteralForObject(buffer, Array.get(object, i));
- }
-
- if (trimming) {
- buffer.append("...");
- }
-
- buffer.append('>');
- } else {
- buffer.append(object.getClass().getName()).append("@").append(System.identityHashCode(object));
- }
- }
-
- @Override
- public void log(String message) {
- if (message != null) {
- logger.info(message);
- }
- }
-
- @Override
- public void logConnect(String dataSource) {
- if (isLoggable()) {
- logger.info("Connecting. JNDI path: " + dataSource);
- }
- }
-
- @Override
- public void logConnect(String url, String userName, String password) {
- if (isLoggable()) {
- StringBuilder buf = new StringBuilder("Opening connection: ");
-
- // append URL on the same line to make log somewhat grep-friendly
- buf.append(url);
- buf.append("\n\tLogin: ").append(userName);
- buf.append("\n\tPassword: *******");
-
- logger.info(buf.toString());
- }
- }
-
- @Override
- public void logPoolCreated(DataSourceInfo dsi) {
- if (isLoggable()) {
- StringBuilder buf = new StringBuilder("Created connection pool: ");
-
- if (dsi != null) {
- // append URL on the same line to make log somewhat
- // grep-friendly
- buf.append(dsi.getDataSourceUrl());
-
- if (dsi.getAdapterClassName() != null) {
- buf.append("\n\tCayenne DbAdapter: ").append(dsi.getAdapterClassName());
- }
-
- buf.append("\n\tDriver class: ").append(dsi.getJdbcDriver());
-
- if (dsi.getMinConnections() >= 0) {
- buf.append("\n\tMin. connections in the pool: ").append(dsi.getMinConnections());
- }
- if (dsi.getMaxConnections() >= 0) {
- buf.append("\n\tMax. connections in the pool: ").append(dsi.getMaxConnections());
- }
- } else {
- buf.append(" pool information unavailable");
- }
-
- logger.info(buf.toString());
- }
- }
-
- @Override
- public void logConnectSuccess() {
- logger.info("+++ Connecting: SUCCESS.");
- }
-
- @Override
- public void logConnectFailure(Throwable th) {
- logger.info("*** Connecting: FAILURE.", th);
- }
-
- @Override
- public void logGeneratedKey(DbAttribute attribute, Object value) {
- if (isLoggable()) {
- String entity = attribute.getEntity().getName();
- String key = attribute.getName();
-
- logger.info("Generated PK: " + entity + "." + key + " = " + value);
- }
- }
-
- @Override
- public void logQuery(String queryStr, List<?> params) {
- logQuery(queryStr, null, params, -1);
- }
-
- @Deprecated
- private void buildLog(StringBuilder buffer, String prefix, String postfix, List<DbAttribute> attributes,
- List<?> parameters, boolean isInserting) {
- if (parameters != null && parameters.size() > 0) {
- DbAttribute attribute = null;
- Iterator<DbAttribute> attributeIterator = null;
- int position = 0;
-
- if (attributes != null)
- attributeIterator = attributes.iterator();
-
- for (Object parameter : parameters) {
- // If at the beginning, output the prefix, otherwise a
- // separator.
- if (position++ == 0)
- buffer.append(prefix);
- else
- buffer.append(", ");
-
- // Find the next attribute and SKIP generated attributes. Only
- // skip when logging inserts, though. Should show previously
- // generated keys on DELETE, UPDATE, or SELECT.
- while (attributeIterator != null && attributeIterator.hasNext()) {
- attribute = attributeIterator.next();
-
- if (isInserting == false || attribute.isGenerated() == false)
- break;
- }
-
- buffer.append(position);
-
- if (attribute != null) {
- buffer.append("->");
- buffer.append(attribute.getName());
- }
-
- buffer.append(":");
- sqlLiteralForObject(buffer, parameter);
- }
-
- buffer.append(postfix);
- }
- }
-
-
-
- private boolean isInserting(String query) {
- if (query == null || query.length() == 0)
- return false;
-
- char firstCharacter = query.charAt(0);
-
- if (firstCharacter == 'I' || firstCharacter == 'i')
- return true;
- else
- return false;
- }
-
- @Override
- public void logQuery(String queryStr, List<DbAttribute> attrs, List<?> params, long time) {
- if (isLoggable()) {
- StringBuilder buffer = new StringBuilder(queryStr);
- buildLog(buffer, " [bind: ", "]", attrs, params, isInserting(queryStr));
-
- // log preparation time only if it is something significant
- if (time > 5) {
- buffer.append(" - prepared in ").append(time).append(" ms.");
- }
-
- logger.info(buffer.toString());
- }
- }
-
- @Deprecated
- @Override
- public void logQueryParameters(String label, List<DbAttribute> attrs, List<Object> parameters, boolean isInserting) {
- String prefix = "[" + label + ": ";
- if (isLoggable() && parameters.size() > 0) {
- StringBuilder buffer = new StringBuilder();
- buildLog(buffer, prefix, "]", attrs, parameters, isInserting);
- logger.info(buffer.toString());
- }
- }
-
- @Override
- public void logQueryParameters(String label, BatchParameterBinding[] bindings) {
-
- if (isLoggable() && bindings.length > 0) {
-
- boolean hasIncluded = false;
- StringBuilder buffer = null;
-
- int len = bindings.length;
- for (int i = 0, j = 1; i < len; i++) {
- BatchParameterBinding b = bindings[i];
-
- if (b.isExcluded()) {
- continue;
- }
-
- if (hasIncluded) {
- buffer.append(", ");
- } else {
- hasIncluded = true;
- buffer = new StringBuilder();
- buffer.append("[").append(label).append(": ");
- }
-
- DbAttribute attribute = b.getAttribute();
-
- buffer.append(j++);
-
- if (attribute != null) {
- buffer.append("->");
- buffer.append(attribute.getName());
- }
-
- buffer.append(":");
- sqlLiteralForObject(buffer, b.getValue());
- }
-
- if (hasIncluded) {
- buffer.append("]");
-
- logger.info(buffer.toString());
- }
- }
- }
-
- @Override
- public void logSelectCount(int count, long time) {
- logSelectCount(count, time, null);
- }
-
- @Override
- public void logSelectCount(int count, long time, String sql) {
-
- if (isLoggable()) {
- StringBuilder buf = new StringBuilder();
-
- if (count == 1) {
- buf.append("=== returned 1 row.");
- } else {
- buf.append("=== returned ").append(count).append(" rows.");
- }
-
- if (time >= 0) {
- buf.append(" - took ").append(time).append(" ms.");
- }
-
- logger.info(buf.toString());
- }
-
- if (queryExecutionTimeLoggingThreshold > 0 && time > queryExecutionTimeLoggingThreshold) {
- StringBuilder buf = new StringBuilder();
- buf.append("Query time exceeded threshold (").append(time).append(" ms): ");
- buf.append(sql);
- String message = buf.toString();
- logger.warn(message, new CayenneRuntimeException(message));
- }
- }
-
- @Override
- public void logUpdateCount(int count) {
- if (isLoggable()) {
- if (count < 0) {
- logger.info("=== updated ? rows");
- } else {
- String countStr = (count == 1) ? "=== updated 1 row." : "=== updated " + count + " rows.";
- logger.info(countStr);
- }
- }
- }
-
- @Override
- public void logBeginTransaction(String transactionLabel) {
- logger.info("--- " + transactionLabel);
- }
-
- @Override
- public void logCommitTransaction(String transactionLabel) {
- logger.info("+++ " + transactionLabel);
- }
-
- @Override
- public void logRollbackTransaction(String transactionLabel) {
- logger.info("*** " + transactionLabel);
- }
-
- @Override
- public void logQueryError(Throwable th) {
- if (isLoggable()) {
- if (th != null) {
- th = Util.unwindException(th);
- }
-
- logger.info("*** error.", th);
-
- if (th instanceof SQLException) {
- SQLException sqlException = ((SQLException) th).getNextException();
- while (sqlException != null) {
- logger.info("*** nested SQL error.", sqlException);
- sqlException = sqlException.getNextException();
- }
- }
- }
- }
-
- @Override
- public boolean isLoggable() {
- return logger.isInfoEnabled();
- }
+ private static final Log logger = LogFactory.getLog(CommonsJdbcEventLogger.class);
+
+ private static final int TRIM_VALUES_THRESHOLD = 30;
+
+ protected long queryExecutionTimeLoggingThreshold;
+
+ public CommonsJdbcEventLogger(@Inject RuntimeProperties runtimeProperties) {
+ this.queryExecutionTimeLoggingThreshold = runtimeProperties.getLong(
+ Constants.QUERY_EXECUTION_TIME_LOGGING_THRESHOLD_PROPERTY, 0);
+ }
+
+ void sqlLiteralForObject(StringBuilder buffer, Object object) {
+ if (object == null) {
+ buffer.append("NULL");
+ } else if (object instanceof String) {
+ buffer.append('\'');
+ // lets escape quotes
+ String literal = (String) object;
+ if (literal.length() > TRIM_VALUES_THRESHOLD) {
+ literal = literal.substring(0, TRIM_VALUES_THRESHOLD) + "...";
+ }
+
+ int curPos = 0;
+ int endPos = 0;
+
+ while ((endPos = literal.indexOf('\'', curPos)) >= 0) {
+ buffer.append(literal.substring(curPos, endPos + 1)).append('\'');
+ curPos = endPos + 1;
+ }
+
+ if (curPos < literal.length())
+ buffer.append(literal.substring(curPos));
+
+ buffer.append('\'');
+ }
+ // handle byte pretty formatting
+ else if (object instanceof Byte) {
+ IDUtil.appendFormattedByte(buffer, ((Byte) object).byteValue());
+ } else if (object instanceof Number) {
+ // process numeric value (do something smart in the future)
+ buffer.append(object);
+ } else if (object instanceof java.sql.Date) {
+ buffer.append('\'').append(object).append('\'');
+ } else if (object instanceof java.sql.Time) {
+ buffer.append('\'').append(object).append('\'');
+ } else if (object instanceof java.util.Date) {
+ long time = ((java.util.Date) object).getTime();
+ buffer.append('\'').append(new java.sql.Timestamp(time)).append('\'');
+ } else if (object instanceof java.util.Calendar) {
+ long time = ((java.util.Calendar) object).getTimeInMillis();
+ buffer.append(object.getClass().getName()).append('(').append(new java.sql.Timestamp(time)).append(')');
+ } else if (object instanceof Character) {
+ buffer.append(((Character) object).charValue());
+ } else if (object instanceof Boolean) {
+ buffer.append('\'').append(object).append('\'');
+ } else if (object instanceof Enum<?>) {
+ // buffer.append(object.getClass().getName()).append(".");
+ buffer.append(((Enum<?>) object).name()).append("=");
+ if (object instanceof ExtendedEnumeration) {
+ Object value = ((ExtendedEnumeration) object).getDatabaseValue();
+ if (value instanceof String)
+ buffer.append("'");
+ buffer.append(value);
+ if (value instanceof String)
+ buffer.append("'");
+ } else {
+ buffer.append(((Enum<?>) object).ordinal());
+ // FIXME -- this isn't quite right
+ }
+ } else if (object instanceof SQLParameterBinding) {
+ sqlLiteralForObject(buffer, ((SQLParameterBinding) object).getValue());
+ } else if (object.getClass().isArray()) {
+ buffer.append("< ");
+
+ int len = Array.getLength(object);
+ boolean trimming = false;
+ if (len > TRIM_VALUES_THRESHOLD) {
+ len = TRIM_VALUES_THRESHOLD;
+ trimming = true;
+ }
+
+ for (int i = 0; i < len; i++) {
+ if (i > 0) {
+ buffer.append(",");
+ }
+ sqlLiteralForObject(buffer, Array.get(object, i));
+ }
+
+ if (trimming) {
+ buffer.append("...");
+ }
+
+ buffer.append('>');
+ } else {
+ buffer.append(object.getClass().getName()).append("@").append(System.identityHashCode(object));
+ }
+ }
+
+ @Override
+ public void log(String message) {
+ if (message != null) {
+ logger.info(message);
+ }
+ }
+
+ @Override
+ public void logConnect(String dataSource) {
+ if (isLoggable()) {
+ logger.info("Connecting. JNDI path: " + dataSource);
+ }
+ }
+
+ @Override
+ public void logConnect(String url, String userName, String password) {
+ if (isLoggable()) {
+ StringBuilder buf = new StringBuilder("Opening connection: ");
+
+ // append URL on the same line to make log somewhat grep-friendly
+ buf.append(url);
+ buf.append("\n\tLogin: ").append(userName);
+ buf.append("\n\tPassword: *******");
+
+ logger.info(buf.toString());
+ }
+ }
+
+ @Override
+ public void logPoolCreated(DataSourceInfo dsi) {
+ if (isLoggable()) {
+ StringBuilder buf = new StringBuilder("Created connection pool: ");
+
+ if (dsi != null) {
+ // append URL on the same line to make log somewhat
+ // grep-friendly
+ buf.append(dsi.getDataSourceUrl());
+
+ if (dsi.getAdapterClassName() != null) {
+ buf.append("\n\tCayenne DbAdapter: ").append(dsi.getAdapterClassName());
+ }
+
+ buf.append("\n\tDriver class: ").append(dsi.getJdbcDriver());
+
+ if (dsi.getMinConnections() >= 0) {
+ buf.append("\n\tMin. connections in the pool: ").append(dsi.getMinConnections());
+ }
+ if (dsi.getMaxConnections() >= 0) {
+ buf.append("\n\tMax. connections in the pool: ").append(dsi.getMaxConnections());
+ }
+ } else {
+ buf.append(" pool information unavailable");
+ }
+
+ logger.info(buf.toString());
+ }
+ }
+
+ @Override
+ public void logConnectSuccess() {
+ logger.info("+++ Connecting: SUCCESS.");
+ }
+
+ @Override
+ public void logConnectFailure(Throwable th) {
+ logger.info("*** Connecting: FAILURE.", th);
+ }
+
+ @Override
+ public void logGeneratedKey(DbAttribute attribute, Object value) {
+ if (isLoggable()) {
+ String entity = attribute.getEntity().getName();
+ String key = attribute.getName();
+
+ logger.info("Generated PK: " + entity + "." + key + " = " + value);
+ }
+ }
+
+ /**
+ * @deprecated since 4.0 use
+ * {@link #logQuery(String, ParameterBinding[], long)}.
+ */
+ @Deprecated
+ @Override
+ public void logQuery(String queryStr, List<?> params) {
+ logQuery(queryStr, null, params, -1);
+ }
+
+ /**
+ * @deprecated since 4.0 uses old style of parameter logging.
+ */
+ @Deprecated
+ private void buildLog(StringBuilder buffer, String prefix, String postfix, List<DbAttribute> attributes,
+ List<?> parameters, boolean isInserting) {
+ if (parameters != null && parameters.size() > 0) {
+ Iterator<DbAttribute> attributeIterator = null;
+ int position = 0;
+
+ if (attributes != null)
+ attributeIterator = attributes.iterator();
+
+ for (Object parameter : parameters) {
+ // If at the beginning, output the prefix, otherwise a
+ // separator.
+ if (position++ == 0)
+ buffer.append(prefix);
+ else
+ buffer.append(", ");
+
+ // Find the next attribute and SKIP generated attributes. Only
+ // skip when logging inserts, though. Should show previously
+ // generated keys on DELETE, UPDATE, or SELECT.
+ DbAttribute attribute = null;
+ while (attributeIterator != null && attributeIterator.hasNext()) {
+ attribute = attributeIterator.next();
+
+ if (isInserting == false || attribute.isGenerated() == false)
+ break;
+ }
+
+ buffer.append(position);
+
+ if (attribute != null) {
+ buffer.append("->");
+ buffer.append(attribute.getName());
+ }
+
+ buffer.append(":");
+ sqlLiteralForObject(buffer, parameter);
+ }
+
+ buffer.append(postfix);
+ }
+ }
+
+ private boolean isInserting(String query) {
+ if (query == null || query.length() == 0)
+ return false;
+
+ char firstCharacter = query.charAt(0);
+
+ if (firstCharacter == 'I' || firstCharacter == 'i')
+ return true;
+ else
+ return false;
+ }
+
+ @Override
+ public void logQuery(String sql, ParameterBinding[] bindings, long translatedIn) {
+ if (isLoggable()) {
+
+ StringBuilder buffer = new StringBuilder(sql).append(" ");
+ appendParameters(buffer, "bind", bindings);
+
+ if (buffer.length() > 0) {
+ logger.info(buffer.toString());
+ }
+ }
+ }
+
+ @Deprecated
+ @Override
+ public void logQuery(String queryStr, List<DbAttribute> attrs, List<?> params, long time) {
+ if (isLoggable()) {
+ StringBuilder buffer = new StringBuilder(queryStr);
+ buildLog(buffer, " [bind: ", "]", attrs, params, isInserting(queryStr));
+
+ // log preparation time only if it is something significant
+ if (time > 5) {
+ buffer.append(" - prepared in ").append(time).append(" ms.");
+ }
+
+ logger.info(buffer.toString());
+ }
+ }
+
+ @Deprecated
+ @Override
+ public void logQueryParameters(String label, List<DbAttribute> attrs, List<Object> parameters, boolean isInserting) {
+ String prefix = "[" + label + ": ";
+ if (isLoggable() && parameters.size() > 0) {
+ StringBuilder buffer = new StringBuilder();
+ buildLog(buffer, prefix, "]", attrs, parameters, isInserting);
+ logger.info(buffer.toString());
+ }
+ }
+
+ @Override
+ public void logQueryParameters(String label, ParameterBinding[] bindings) {
+
+ if (isLoggable() && bindings.length > 0) {
+
+ StringBuilder buffer = new StringBuilder();
+ appendParameters(buffer, label, bindings);
+
+ if (buffer.length() > 0) {
+ logger.info(buffer.toString());
+ }
+ }
+ }
+
+ private void appendParameters(StringBuilder buffer, String label, ParameterBinding[] bindings) {
+
+ int len = bindings.length;
+ if (len > 0) {
+
+ boolean hasIncluded = false;
+
+ for (int i = 0, j = 1; i < len; i++) {
+ ParameterBinding b = bindings[i];
+
+ if (b.isExcluded()) {
+ continue;
+ }
+
+ if (hasIncluded) {
+ buffer.append(", ");
+ } else {
+ hasIncluded = true;
+ buffer.append("[").append(label).append(": ");
+ }
+
+ DbAttribute attribute = b.getAttribute();
+
+ buffer.append(j++);
+
+ if (attribute != null) {
+ buffer.append("->");
+ buffer.append(attribute.getName());
+ }
+
+ buffer.append(":");
+ sqlLiteralForObject(buffer, b.getValue());
+ }
+
+ if (hasIncluded) {
+ buffer.append("]");
+ }
+ }
+ }
+
+ @Override
+ public void logSelectCount(int count, long time) {
+ logSelectCount(count, time, null);
+ }
+
+ @Override
+ public void logSelectCount(int count, long time, String sql) {
+
+ if (isLoggable()) {
+ StringBuilder buf = new StringBuilder();
+
+ if (count == 1) {
+ buf.append("=== returned 1 row.");
+ } else {
+ buf.append("=== returned ").append(count).append(" rows.");
+ }
+
+ if (time >= 0) {
+ buf.append(" - took ").append(time).append(" ms.");
+ }
+
+ logger.info(buf.toString());
+ }
+
+ if (queryExecutionTimeLoggingThreshold > 0 && time > queryExecutionTimeLoggingThreshold) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("Query time exceeded threshold (").append(time).append(" ms): ");
+ buf.append(sql);
+ String message = buf.toString();
+ logger.warn(message, new CayenneRuntimeException(message));
+ }
+ }
+
+ @Override
+ public void logUpdateCount(int count) {
+ if (isLoggable()) {
+ if (count < 0) {
+ logger.info("=== updated ? rows");
+ } else {
+ String countStr = (count == 1) ? "=== updated 1 row." : "=== updated " + count + " rows.";
+ logger.info(countStr);
+ }
+ }
+ }
+
+ @Override
+ public void logBeginTransaction(String transactionLabel) {
+ logger.info("--- " + transactionLabel);
+ }
+
+ @Override
+ public void logCommitTransaction(String transactionLabel) {
+ logger.info("+++ " + transactionLabel);
+ }
+
+ @Override
+ public void logRollbackTransaction(String transactionLabel) {
+ logger.info("*** " + transactionLabel);
+ }
+
+ @Override
+ public void logQueryError(Throwable th) {
+ if (isLoggable()) {
+ if (th != null) {
+ th = Util.unwindException(th);
+ }
+
+ logger.info("*** error.", th);
+
+ if (th instanceof SQLException) {
+ SQLException sqlException = ((SQLException) th).getNextException();
+ while (sqlException != null) {
+ logger.info("*** nested SQL error.", sqlException);
+ sqlException = sqlException.getNextException();
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isLoggable() {
+ return logger.isInfoEnabled();
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java b/cayenne-server/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java
index 59e3f1e..7e89611 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java
@@ -20,7 +20,7 @@ package org.apache.cayenne.log;
import java.util.List;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.conn.DataSourceInfo;
import org.apache.cayenne.map.DbAttribute;
@@ -54,18 +54,33 @@ public interface JdbcEventLogger {
void logGeneratedKey(DbAttribute attribute, Object value);
+ /**
+ * @deprecated since 4.0 use
+ * {@link #logQuery(String, ParameterBinding[], long)}.
+ */
+ @Deprecated
void logQuery(String sql, List<?> params);
+ /**
+ * @deprecated since 4.0 use
+ * {@link #logQuery(String, ParameterBinding[], long)}.
+ */
+ @Deprecated
void logQuery(String sql, List<DbAttribute> attrs, List<?> params, long time);
/**
* @since 4.0
*/
- void logQueryParameters(String label, BatchParameterBinding[] bindings);
+ void logQuery(String sql, ParameterBinding[] bindings, long translatedIn);
+
+ /**
+ * @since 4.0
+ */
+ void logQueryParameters(String label, ParameterBinding[] bindings);
/**
* @deprecated since 4.0 in favor of
- * {@link #logQueryParameters(String, BatchParameterBinding[])}
+ * {@link #logQueryParameters(String, ParameterBinding[])}
*/
@Deprecated
void logQueryParameters(String label, List<DbAttribute> attrs, List<Object> parameters, boolean isInserting);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java b/cayenne-server/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java
index 5db1115..03f29ab 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java
@@ -20,7 +20,7 @@ package org.apache.cayenne.log;
import java.util.List;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.conn.DataSourceInfo;
import org.apache.cayenne.map.DbAttribute;
@@ -29,78 +29,96 @@ import org.apache.cayenne.map.DbAttribute;
*/
public class NoopJdbcEventLogger implements JdbcEventLogger {
- private static final NoopJdbcEventLogger instance = new NoopJdbcEventLogger();
+ private static final NoopJdbcEventLogger instance = new NoopJdbcEventLogger();
- public static NoopJdbcEventLogger getInstance() {
- return instance;
- }
+ public static NoopJdbcEventLogger getInstance() {
+ return instance;
+ }
- private NoopJdbcEventLogger() {
+ private NoopJdbcEventLogger() {
- }
+ }
- public void log(String message) {
- }
+ @Override
+ public void log(String message) {
+ }
- public void logConnect(String dataSource) {
- }
+ @Override
+ public void logConnect(String dataSource) {
+ }
- public void logConnect(String url, String userName, String password) {
- }
+ @Override
+ public void logConnect(String url, String userName, String password) {
+ }
- public void logPoolCreated(DataSourceInfo dsi) {
- }
+ @Override
+ public void logPoolCreated(DataSourceInfo dsi) {
+ }
- public void logConnectSuccess() {
- }
+ @Override
+ public void logConnectSuccess() {
+ }
- public void logConnectFailure(Throwable th) {
- }
+ @Override
+ public void logConnectFailure(Throwable th) {
+ }
- public void logGeneratedKey(DbAttribute attribute, Object value) {
- }
+ @Override
+ public void logGeneratedKey(DbAttribute attribute, Object value) {
+ }
- public void logQuery(String sql, List<?> params) {
- }
+ @Override
+ public void logQuery(String sql, List<?> params) {
+ }
- public void logQuery(String sql, List<DbAttribute> attrs, List<?> params, long time) {
- }
-
- @Override
- @Deprecated
- public void logQueryParameters(
- String label,
- List<DbAttribute> attrs,
- List<Object> parameters,
- boolean isInserting) {
- }
-
- @Override
- public void logQueryParameters(String label, BatchParameterBinding[] bindings) {
- }
-
- public void logSelectCount(int count, long time) {
- }
-
- public void logSelectCount(int count, long time, String sql) {
- }
+ @Deprecated
+ @Override
+ public void logQuery(String sql, List<DbAttribute> attrs, List<?> params, long time) {
+ }
- public void logUpdateCount(int count) {
- }
-
- public void logBeginTransaction(String transactionLabel) {
- }
-
- public void logCommitTransaction(String transactionLabel) {
- }
-
- public void logRollbackTransaction(String transactionLabel) {
- }
-
- public void logQueryError(Throwable th) {
- }
-
- public boolean isLoggable() {
- return false;
- }
+ @Override
+ public void logQuery(String sql, ParameterBinding[] bindings, long translatedIn) {
+ }
+
+ @Override
+ @Deprecated
+ public void logQueryParameters(String label, List<DbAttribute> attrs, List<Object> parameters, boolean isInserting) {
+ }
+
+ @Override
+ public void logQueryParameters(String label, ParameterBinding[] bindings) {
+ }
+
+ @Override
+ public void logSelectCount(int count, long time) {
+ }
+
+ @Override
+ public void logSelectCount(int count, long time, String sql) {
+ }
+
+ @Override
+ public void logUpdateCount(int count) {
+ }
+
+ @Override
+ public void logBeginTransaction(String transactionLabel) {
+ }
+
+ @Override
+ public void logCommitTransaction(String transactionLabel) {
+ }
+
+ @Override
+ public void logRollbackTransaction(String transactionLabel) {
+ }
+
+ @Override
+ public void logQueryError(Throwable th) {
+ }
+
+ @Override
+ public boolean isLoggable() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultValueForNullProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultValueForNullProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultValueForNullProvider.java
index ae63f53..6db83c0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultValueForNullProvider.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultValueForNullProvider.java
@@ -23,25 +23,25 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
public class DefaultValueForNullProvider implements ValueForNullProvider {
- private Map<String, ParameterBinding> values = new HashMap<String, ParameterBinding>();
+ private Map<String, SQLParameterBinding> values = new HashMap<String, SQLParameterBinding>();
public void set(DbEntity entity, DbAttribute column, Object value, int type) {
- values.put(createKey(entity, column), new ParameterBinding(value, type, column
+ values.put(createKey(entity, column), new SQLParameterBinding(value, type, column
.getAttributePrecision()));
}
- protected ParameterBinding get(DbEntity entity, DbAttribute column) {
+ protected SQLParameterBinding get(DbEntity entity, DbAttribute column) {
return values.get(createKey(entity, column));
}
public List<String> createSql(DbEntity entity, DbAttribute column) {
- ParameterBinding value = get(entity, column);
+ SQLParameterBinding value = get(entity, column);
if (value == null) {
return Collections.emptyList();
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
index 43e807a..8529a77 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
@@ -24,7 +24,7 @@ import java.io.Writer;
import java.util.Collection;
import java.util.Iterator;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.util.ConversionUtil;
import org.apache.velocity.context.InternalContextAdapter;
@@ -81,7 +81,7 @@ public class BindDirective extends Directive {
/**
* Extracts the value of the object property to render and passes control to
- * {@link #render(InternalContextAdapter, Writer, ParameterBinding)} to do
+ * {@link #render(InternalContextAdapter, Writer, SQLParameterBinding)} to do
* the actual rendering.
*/
@Override
@@ -130,10 +130,10 @@ public class BindDirective extends Directive {
+ ") at line " + node.getLine() + ", column " + node.getColumn());
}
- render(context, writer, new ParameterBinding(value, jdbcType, scale));
+ render(context, writer, new SQLParameterBinding(value, jdbcType, scale));
}
- protected void render(InternalContextAdapter context, Writer writer, ParameterBinding binding) throws IOException {
+ protected void render(InternalContextAdapter context, Writer writer, SQLParameterBinding binding) throws IOException {
bind(context, binding);
writer.write('?');
@@ -146,7 +146,7 @@ public class BindDirective extends Directive {
/**
* Adds value to the list of bindings in the context.
*/
- protected void bind(InternalContextAdapter context, ParameterBinding binding) {
+ protected void bind(InternalContextAdapter context, SQLParameterBinding binding) {
Collection bindings = (Collection) context.getInternalUserContext().get(
VelocitySQLTemplateProcessor.BINDINGS_LIST_KEY);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java
index b773cf5..f109b82 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java
@@ -22,7 +22,7 @@ package org.apache.cayenne.velocity;
import java.io.IOException;
import java.io.Writer;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.velocity.context.InternalContextAdapter;
/**
@@ -43,7 +43,7 @@ public class BindEqualDirective extends BindDirective {
protected void render(
InternalContextAdapter context,
Writer writer,
- ParameterBinding binding)
+ SQLParameterBinding binding)
throws IOException {
if (binding.getValue() != null) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java
index a49a8d6..3b17732 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java
@@ -22,7 +22,7 @@ package org.apache.cayenne.velocity;
import java.io.IOException;
import java.io.Writer;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.velocity.context.InternalContextAdapter;
/**
@@ -43,7 +43,7 @@ public class BindNotEqualDirective extends BindDirective {
protected void render(
InternalContextAdapter context,
Writer writer,
- ParameterBinding binding) throws IOException {
+ SQLParameterBinding binding) throws IOException {
if (binding.getValue() != null) {
bind(context, binding);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java
index ed1f7d0..35785d3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java
@@ -27,7 +27,7 @@ import java.util.Map;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
@@ -94,7 +94,7 @@ public class BindObjectEqualDirective extends BindDirective {
renderColumn(context, writer, sqlColumnsArray[i], i);
writer.write(' ');
- render(context, writer, new ParameterBinding(value, jdbcType, -1));
+ render(context, writer, new SQLParameterBinding(value, jdbcType, -1));
}
return true;
@@ -151,7 +151,7 @@ public class BindObjectEqualDirective extends BindDirective {
protected void render(
InternalContextAdapter context,
Writer writer,
- ParameterBinding binding) throws IOException {
+ SQLParameterBinding binding) throws IOException {
if (binding.getValue() != null) {
bind(context, binding);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java
index 0b610ab..bf76993 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java
@@ -21,7 +21,7 @@ package org.apache.cayenne.velocity;
import java.io.IOException;
import java.io.Writer;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.velocity.context.InternalContextAdapter;
/**
@@ -57,7 +57,7 @@ public class BindObjectNotEqualDirective extends BindObjectEqualDirective {
protected void render(
InternalContextAdapter context,
Writer writer,
- ParameterBinding binding) throws IOException {
+ SQLParameterBinding binding) throws IOException {
if (binding.getValue() != null) {
bind(context, binding);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
index 6246821..2e6ab40 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
@@ -28,7 +28,7 @@ import java.util.Map;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLParameterBinding;
import org.apache.cayenne.access.jdbc.SQLStatement;
import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
import org.apache.cayenne.exp.ExpressionException;
@@ -152,7 +152,7 @@ public class VelocitySQLTemplateProcessor implements SQLTemplateProcessor {
}
SQLStatement processTemplate(String template, SimpleNode parsedTemplate, Map<String, Object> parameters) {
- List<ParameterBinding> bindings = new ArrayList<ParameterBinding>();
+ List<SQLParameterBinding> bindings = new ArrayList<SQLParameterBinding>();
List<ColumnDescriptor> results = new ArrayList<ColumnDescriptor>();
parameters.put(BINDINGS_LIST_KEY, bindings);
parameters.put(RESULT_COLUMNS_LIST_KEY, results);
@@ -165,7 +165,7 @@ public class VelocitySQLTemplateProcessor implements SQLTemplateProcessor {
throw new CayenneRuntimeException("Error processing Velocity template", e);
}
- ParameterBinding[] bindingsArray = new ParameterBinding[bindings.size()];
+ SQLParameterBinding[] bindingsArray = new SQLParameterBinding[bindings.size()];
bindings.toArray(bindingsArray);
ColumnDescriptor[] resultsArray = new ColumnDescriptor[results.size()];
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLStatementTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLStatementTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLStatementTest.java
index 88791b5..e0cc575 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLStatementTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLStatementTest.java
@@ -31,7 +31,7 @@ public class SQLStatementTest {
@Test
public void testConstructor() throws Exception {
- ParameterBinding[] bindings = new ParameterBinding[0];
+ SQLParameterBinding[] bindings = new SQLParameterBinding[0];
SQLStatement statement = new SQLStatement("abc", bindings);
assertEquals("abc", statement.getSql());
assertSame(bindings, statement.getBindings());
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslatorIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslatorIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslatorIT.java
index 439fb20..0fdc48b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslatorIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslatorIT.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.access.translator.batch;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.JdbcAdapter;
import org.apache.cayenne.di.AdhocObjectFactory;
@@ -54,13 +55,13 @@ public class DefaultBatchTranslatorIT extends ServerCase {
}
@Override
- protected BatchParameterBinding[] createBindings() {
- return new BatchParameterBinding[0];
+ protected ParameterBinding[] createBindings() {
+ return new ParameterBinding[0];
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
- return new BatchParameterBinding[0];
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ return new ParameterBinding[0];
}
};
@@ -80,13 +81,13 @@ public class DefaultBatchTranslatorIT extends ServerCase {
}
@Override
- protected BatchParameterBinding[] createBindings() {
- return new BatchParameterBinding[0];
+ protected ParameterBinding[] createBindings() {
+ return new ParameterBinding[0];
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
- return new BatchParameterBinding[0];
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ return new ParameterBinding[0];
}
};
@@ -115,13 +116,13 @@ public class DefaultBatchTranslatorIT extends ServerCase {
}
@Override
- protected BatchParameterBinding[] createBindings() {
- return new BatchParameterBinding[0];
+ protected ParameterBinding[] createBindings() {
+ return new ParameterBinding[0];
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
- return new BatchParameterBinding[0];
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ return new ParameterBinding[0];
}
};
[6/7] cayenne git commit: Refactoring SelectTranslator for better
extensibility
Posted by aa...@apache.org.
Refactoring SelectTranslator for better extensibility
* Removing checked exceptions from translator API... we no longer pretend to write to Appendable, and
will write to StringBuilder instead
* unifying ParameterBinding objects between query types
* fixing a bug in SelecTranslator - iterators will not work when supress distinct is in effect
* removing Connection from translators; SQLAction should take care of all connection ops
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/81f4fb50
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/81f4fb50
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/81f4fb50
Branch: refs/heads/master
Commit: 81f4fb50b75895fee1c0a1b73b173ca97634abfa
Parents: a6b55f7
Author: aadamchik <aa...@apache.org>
Authored: Tue Apr 28 12:29:07 2015 +0200
Committer: aadamchik <aa...@apache.org>
Committed: Tue Apr 28 12:43:55 2015 +0200
----------------------------------------------------------------------
.../CryptoBatchTranslatorFactoryDecorator.java | 8 +-
.../crypto/transformer/BindingsTransformer.java | 4 +-
.../transformer/DefaultBindingsTransformer.java | 6 +-
.../transformer/DefaultTransformerFactory.java | 6 +-
.../crypto/transformer/TransformerFactory.java | 4 +-
.../apache/cayenne/access/jdbc/BatchAction.java | 10 +-
.../cayenne/access/jdbc/ParameterBinding.java | 68 --
.../access/jdbc/SQLParameterBinding.java | 62 ++
.../cayenne/access/jdbc/SQLStatement.java | 10 +-
.../cayenne/access/jdbc/SQLTemplateAction.java | 2 +-
.../cayenne/access/jdbc/SelectAction.java | 344 ++++---
.../access/translator/ParameterBinding.java | 81 ++
.../translator/batch/BatchParameterBinding.java | 79 --
.../translator/batch/BatchTranslator.java | 5 +-
.../batch/DefaultBatchTranslator.java | 11 +-
.../translator/batch/DeleteBatchTranslator.java | 11 +-
.../translator/batch/InsertBatchTranslator.java | 11 +-
.../batch/SoftDeleteBatchTranslator.java | 13 +-
.../translator/batch/UpdateBatchTranslator.java | 11 +-
.../select/DefaultSelectTranslator.java | 630 ++++++++++++
.../access/translator/select/JoinStack.java | 349 ++++---
.../translator/select/OrderingTranslator.java | 166 ++--
.../translator/select/QualifierTranslator.java | 949 +++++++++----------
.../translator/select/QueryAssembler.java | 302 +++---
.../translator/select/QueryAssemblerHelper.java | 885 +++++++++--------
.../translator/select/SelectTranslator.java | 631 +-----------
.../select/TrimmingQualifierTranslator.java | 113 ++-
.../cayenne/dba/db2/DB2QualifierTranslator.java | 172 ++--
.../dba/derby/DerbyQualifierTranslator.java | 59 +-
.../firebird/FirebirdQualifierTranslator.java | 30 +-
.../dba/frontbase/FrontBaseActionBuilder.java | 6 +-
.../frontbase/FrontBaseSelectTranslator.java | 41 +-
.../cayenne/dba/hsqldb/HSQLSelectAction.java | 24 +-
.../dba/hsqldb/HSQLSelectTranslator.java | 60 +-
.../cayenne/dba/ingres/IngresSelectAction.java | 6 +-
.../dba/ingres/IngresSelectTranslator.java | 13 +-
.../cayenne/dba/mysql/MySQLSelectAction.java | 24 +-
.../dba/mysql/MySQLSelectTranslator.java | 61 +-
.../dba/openbase/OpenBaseActionBuilder.java | 26 +-
.../cayenne/dba/openbase/OpenBaseJoinStack.java | 158 ++-
.../openbase/OpenBaseQualifierTranslator.java | 262 +++--
.../dba/openbase/OpenBaseSelectTranslator.java | 45 +-
.../cayenne/dba/oracle/Oracle8JoinStack.java | 146 ++-
.../dba/oracle/Oracle8LOBBatchAction.java | 8 +-
.../dba/oracle/Oracle8LOBBatchTranslator.java | 12 +-
.../dba/oracle/Oracle8QualifierTranslator.java | 38 +-
.../cayenne/dba/oracle/Oracle8SelectAction.java | 16 +-
.../dba/oracle/Oracle8SelectTranslator.java | 35 +-
.../dba/oracle/OracleQualifierTranslator.java | 104 +-
.../cayenne/dba/oracle/OracleSelectAction.java | 24 +-
.../dba/oracle/OracleSelectTranslator.java | 89 +-
.../postgres/PostgresQualifierTranslator.java | 188 ++--
.../dba/postgres/PostgresSelectAction.java | 24 +-
.../dba/postgres/PostgresSelectTranslator.java | 15 +-
.../dba/sqlserver/SQLServerSelectAction.java | 6 +-
.../sqlserver/SQLServerSelectTranslator.java | 65 +-
.../SQLServerTrimmingQualifierTranslator.java | 164 ++--
.../cayenne/log/CommonsJdbcEventLogger.java | 819 ++++++++--------
.../org/apache/cayenne/log/JdbcEventLogger.java | 21 +-
.../apache/cayenne/log/NoopJdbcEventLogger.java | 142 +--
.../merge/DefaultValueForNullProvider.java | 10 +-
.../apache/cayenne/velocity/BindDirective.java | 10 +-
.../cayenne/velocity/BindEqualDirective.java | 4 +-
.../cayenne/velocity/BindNotEqualDirective.java | 4 +-
.../velocity/BindObjectEqualDirective.java | 6 +-
.../velocity/BindObjectNotEqualDirective.java | 4 +-
.../velocity/VelocitySQLTemplateProcessor.java | 6 +-
.../cayenne/access/jdbc/SQLStatementTest.java | 2 +-
.../batch/DefaultBatchTranslatorIT.java | 25 +-
.../select/DefaultSelectTranslatorIT.java | 787 +++++++++++++++
.../translator/select/OrderingTranslatorIT.java | 216 ++---
.../select/QualifierTranslatorIT.java | 197 ++--
.../translator/select/QueryAssemblerIT.java | 62 +-
.../translator/select/SelectTranslatorIT.java | 794 ----------------
.../translator/select/TstQueryAssembler.java | 64 +-
.../apache/cayenne/merge/ValueForNullIT.java | 6 +-
.../VelocitySQLTemplateProcessorTest.java | 10 +-
docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 +
78 files changed, 4870 insertions(+), 5012 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java
index cbb19f2..e86e564 100644
--- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java
+++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java
@@ -18,7 +18,7 @@
****************************************************************/
package org.apache.cayenne.crypto.batch;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.access.translator.batch.BatchTranslator;
import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
import org.apache.cayenne.crypto.transformer.BindingsTransformer;
@@ -65,16 +65,16 @@ public class CryptoBatchTranslatorFactoryDecorator implements BatchTranslatorFac
}
@Override
- public BatchParameterBinding[] getBindings() {
+ public ParameterBinding[] getBindings() {
return delegateTranslator.getBindings();
}
@Override
- public BatchParameterBinding[] updateBindings(BatchQueryRow row) {
+ public ParameterBinding[] updateBindings(BatchQueryRow row) {
ensureEncryptorCompiled();
- BatchParameterBinding[] bindings = delegateTranslator.updateBindings(row);
+ ParameterBinding[] bindings = delegateTranslator.updateBindings(row);
if (encryptor != null) {
encryptor.transform(bindings);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java
index 2d782ee..c6f9577 100644
--- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java
+++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java
@@ -18,12 +18,12 @@
****************************************************************/
package org.apache.cayenne.crypto.transformer;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
/**
* @since 4.0
*/
public interface BindingsTransformer {
- void transform(BatchParameterBinding[] bindings);
+ void transform(ParameterBinding[] bindings);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java
index 0ef8caf..db31eea 100644
--- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java
+++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java
@@ -18,7 +18,7 @@
****************************************************************/
package org.apache.cayenne.crypto.transformer;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.crypto.transformer.bytes.BytesEncryptor;
import org.apache.cayenne.crypto.transformer.value.ValueEncryptor;
@@ -38,12 +38,12 @@ public class DefaultBindingsTransformer implements BindingsTransformer {
}
@Override
- public void transform(BatchParameterBinding[] bindings) {
+ public void transform(ParameterBinding[] bindings) {
int len = positions.length;
for (int i = 0; i < len; i++) {
- BatchParameterBinding b = bindings[positions[i]];
+ ParameterBinding b = bindings[positions[i]];
Object transformed = transformers[i].encrypt(encryptor, b.getValue());
b.setValue(transformed);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java
index 3b3e73a..178a2f8 100644
--- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java
+++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java
@@ -23,7 +23,7 @@ import java.util.List;
import java.util.Map;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.crypto.map.ColumnMapper;
import org.apache.cayenne.crypto.transformer.bytes.BytesTransformerFactory;
import org.apache.cayenne.crypto.transformer.value.ValueDecryptor;
@@ -90,7 +90,7 @@ public class DefaultTransformerFactory implements TransformerFactory {
}
@Override
- public BindingsTransformer encryptor(BatchParameterBinding[] bindings) {
+ public BindingsTransformer encryptor(ParameterBinding[] bindings) {
int len = bindings.length;
List<Integer> cryptoColumns = null;
@@ -115,7 +115,7 @@ public class DefaultTransformerFactory implements TransformerFactory {
for (int i = 0; i < dlen; i++) {
int pos = cryptoColumns.get(i);
- BatchParameterBinding b = bindings[pos];
+ ParameterBinding b = bindings[pos];
positions[i] = pos;
transformers[i] = transformerFactory.encryptor(b.getAttribute());
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java
index 0d6ed2b..9bc28a8 100644
--- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java
+++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java
@@ -19,7 +19,7 @@
package org.apache.cayenne.crypto.transformer;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
/**
* A factory that creates encryption transformers used for processing batch
@@ -29,7 +29,7 @@ import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
*/
public interface TransformerFactory {
- BindingsTransformer encryptor(BatchParameterBinding[] bindings);
+ BindingsTransformer encryptor(ParameterBinding[] bindings);
MapTransformer decryptor(ColumnDescriptor[] columns, Object sampleRow);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
index 7efe4e3..ca3d1ef 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
@@ -33,7 +33,7 @@ import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.OperationObserver;
import org.apache.cayenne.access.OptimisticLockException;
import org.apache.cayenne.access.jdbc.reader.RowReader;
-import org.apache.cayenne.access.translator.batch.BatchParameterBinding;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.access.translator.batch.BatchTranslator;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.TypesMapping;
@@ -53,10 +53,10 @@ public class BatchAction extends BaseSQLAction {
protected BatchQuery query;
protected RowDescriptor keyRowDescriptor;
- private static void bind(DbAdapter adapter, PreparedStatement statement, BatchParameterBinding[] bindings)
+ private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings)
throws SQLException, Exception {
- for (BatchParameterBinding b : bindings) {
+ for (ParameterBinding b : bindings) {
if (!b.isExcluded()) {
adapter.bindParameter(statement, b.getValue(), b.getStatementPosition(), b.getAttribute().getType(), b
.getAttribute().getScale());
@@ -114,7 +114,7 @@ public class BatchAction extends BaseSQLAction {
try {
for (BatchQueryRow row : query.getRows()) {
- BatchParameterBinding[] bindings = translator.updateBindings(row);
+ ParameterBinding[] bindings = translator.updateBindings(row);
logger.logQueryParameters("batch bind", bindings);
bind(adapter, statement, bindings);
@@ -171,7 +171,7 @@ public class BatchAction extends BaseSQLAction {
try {
for (BatchQueryRow row : query.getRows()) {
- BatchParameterBinding[] bindings = translator.updateBindings(row);
+ ParameterBinding[] bindings = translator.updateBindings(row);
logger.logQueryParameters("bind", bindings);
bind(adapter, statement, bindings);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ParameterBinding.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ParameterBinding.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ParameterBinding.java
deleted file mode 100644
index 8eb5f2b..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ParameterBinding.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- ****************************************************************/
-
-package org.apache.cayenne.access.jdbc;
-
-/**
- * Describes PreparedStatement parameter binding.
- *
- * @since 1.1
- */
-public class ParameterBinding {
-
- protected int jdbcType;
- protected int scale;
- protected Object value;
-
- public ParameterBinding(Object value, int jdbcType, int scale) {
- this.value = value;
- this.jdbcType = jdbcType;
- this.scale = scale;
- }
-
- public int getJdbcType() {
- return jdbcType;
- }
-
- /**
- * @since 3.0
- */
- public int getScale() {
- return scale;
- }
-
- public Object getValue() {
- return value;
- }
-
- public void setJdbcType(int i) {
- jdbcType = i;
- }
-
- /**
- * @since 3.0
- */
- public void setScale(int i) {
- scale = i;
- }
-
- public void setValue(Object object) {
- value = object;
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLParameterBinding.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLParameterBinding.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLParameterBinding.java
new file mode 100644
index 0000000..f2b2369
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLParameterBinding.java
@@ -0,0 +1,62 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access.jdbc;
+
+/**
+ * Describes PreparedStatement parameter binding.
+ *
+ * @since 4.0
+ */
+public class SQLParameterBinding {
+
+ protected int jdbcType;
+ protected int scale;
+ protected Object value;
+
+ public SQLParameterBinding(Object value, int jdbcType, int scale) {
+ this.value = value;
+ this.jdbcType = jdbcType;
+ this.scale = scale;
+ }
+
+ public int getJdbcType() {
+ return jdbcType;
+ }
+
+ public int getScale() {
+ return scale;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setJdbcType(int i) {
+ jdbcType = i;
+ }
+
+ public void setScale(int i) {
+ scale = i;
+ }
+
+ public void setValue(Object object) {
+ value = object;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLStatement.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLStatement.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLStatement.java
index b13050a..33d385d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLStatement.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLStatement.java
@@ -28,13 +28,13 @@ package org.apache.cayenne.access.jdbc;
public class SQLStatement {
protected String sql;
- protected ParameterBinding[] bindings;
+ protected SQLParameterBinding[] bindings;
protected ColumnDescriptor[] resultColumns;
public SQLStatement() {
}
- public SQLStatement(String sql, ParameterBinding[] bindings) {
+ public SQLStatement(String sql, SQLParameterBinding[] bindings) {
this(sql, null, bindings);
}
@@ -42,7 +42,7 @@ public class SQLStatement {
* @since 1.2
*/
public SQLStatement(String sql, ColumnDescriptor[] resultColumns,
- ParameterBinding[] bindings) {
+ SQLParameterBinding[] bindings) {
setSql(sql);
setBindings(bindings);
@@ -63,7 +63,7 @@ public class SQLStatement {
resultColumns = descriptors;
}
- public ParameterBinding[] getBindings() {
+ public SQLParameterBinding[] getBindings() {
return bindings;
}
@@ -71,7 +71,7 @@ public class SQLStatement {
return sql;
}
- public void setBindings(ParameterBinding[] bindings) {
+ public void setBindings(SQLParameterBinding[] bindings) {
this.bindings = bindings;
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
index 9c76fe9..97a1067 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
@@ -343,7 +343,7 @@ public class SQLTemplateAction implements SQLAction {
/**
* Binds parameters to the PreparedStatement.
*/
- protected void bind(PreparedStatement preparedStatement, ParameterBinding[] bindings) throws SQLException,
+ protected void bind(PreparedStatement preparedStatement, SQLParameterBinding[] bindings) throws SQLException,
Exception {
// bind parameters
if (bindings.length > 0) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
index f1e6419..00785c9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
@@ -30,7 +30,11 @@ import org.apache.cayenne.ResultIterator;
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.OperationObserver;
import org.apache.cayenne.access.jdbc.reader.RowReader;
+import org.apache.cayenne.access.translator.ParameterBinding;
+import org.apache.cayenne.access.translator.select.DefaultSelectTranslator;
import org.apache.cayenne.access.translator.select.SelectTranslator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.log.JdbcEventLogger;
import org.apache.cayenne.query.PrefetchProcessor;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.QueryMetadata;
@@ -43,149 +47,199 @@ import org.apache.cayenne.query.SelectQuery;
*/
public class SelectAction extends BaseSQLAction {
- protected SelectQuery<?> query;
-
- /**
- * @since 4.0
- */
- public SelectAction(SelectQuery<?> query, DataNode dataNode) {
- super(dataNode);
- this.query = query;
- }
-
- protected SelectTranslator createTranslator(Connection connection) {
- return new SelectTranslator(query, dataNode, connection);
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @Override
- public void performAction(Connection connection, OperationObserver observer) throws SQLException, Exception {
-
- final long t1 = System.currentTimeMillis();
-
- final SelectTranslator translator = createTranslator(connection);
- PreparedStatement prepStmt = translator.createStatement();
-
- // TODO: ugly... 'createSqlString' is already called inside
- // 'createStatement', but calling it here again to store for logging
- // purposes
- final String sqlString = translator.createSqlString();
-
- ResultSet rs;
-
- // need to run in try-catch block to close statement properly if
- // exception happens
- try {
- rs = prepStmt.executeQuery();
- } catch (Exception ex) {
- prepStmt.close();
- throw ex;
- }
- QueryMetadata md = query.getMetaData(dataNode.getEntityResolver());
- RowDescriptor descriptor = new RowDescriptorBuilder().setColumns(translator.getResultColumns()).getDescriptor(
- dataNode.getAdapter().getExtendedTypes());
-
- RowReader<?> rowReader = dataNode.rowReader(descriptor, md, translator.getAttributeOverrides());
-
- JDBCResultIterator workerIterator = new JDBCResultIterator(prepStmt, rs, rowReader);
-
- ResultIterator it = workerIterator;
-
- if (observer.isIteratedResult()) {
- it = new ConnectionAwareResultIterator(it, connection) {
- @Override
- protected void doClose() {
- dataNode.getJdbcEventLogger().logSelectCount(rowCounter, System.currentTimeMillis() - t1, sqlString);
- super.doClose();
- }
- };
- }
-
- // wrap result iterator if distinct has to be suppressed
- if (translator.isSuppressingDistinct()) {
-
- // a joint prefetch warrants full row compare
-
- final boolean[] compareFullRows = new boolean[1];
-
- final PrefetchTreeNode rootPrefetch = md.getPrefetchTree();
-
- if (rootPrefetch != null) {
- rootPrefetch.traverse(new PrefetchProcessor() {
-
- public void finishPrefetch(PrefetchTreeNode node) {
- }
-
- public boolean startDisjointPrefetch(PrefetchTreeNode node) {
- // continue to children only if we are at root
- return rootPrefetch == node;
- }
-
- public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
- // continue to children only if we are at root
- return rootPrefetch == node;
- }
-
- public boolean startUnknownPrefetch(PrefetchTreeNode node) {
- // continue to children only if we are at root
- return rootPrefetch == node;
- }
-
- public boolean startJointPrefetch(PrefetchTreeNode node) {
- if (rootPrefetch != node) {
- compareFullRows[0] = true;
- return false;
- }
-
- return true;
- }
-
- public boolean startPhantomPrefetch(PrefetchTreeNode node) {
- return true;
- }
- });
- }
-
- it = new DistinctResultIterator(workerIterator, translator.getRootDbEntity(), compareFullRows[0]);
- }
-
- // wrap iterator in a fetch limit checker ... there are a few cases when
- // in-memory
- // fetch limit is a noop, however in a general case this is needed, as
- // the SQL
- // result count does not directly correspond to the number of objects
- // returned
- // from Cayenne.
-
- int fetchLimit = query.getFetchLimit();
- int offset = translator.isSuppressingDistinct() ? query.getFetchOffset() : getInMemoryOffset(query
- .getFetchOffset());
- if (fetchLimit > 0 || offset > 0) {
- it = new LimitResultIterator(it, offset, fetchLimit);
- }
-
- // TODO: Should do something about closing ResultSet and
- // PreparedStatement in this
- // method, instead of relying on DefaultResultIterator to do that later
-
- if (observer.isIteratedResult()) {
- try {
- observer.nextRows(translator.getQuery(), it);
- } catch (Exception ex) {
- it.close();
- throw ex;
- }
- } else {
- List<DataRow> resultRows;
- try {
- resultRows = it.allRows();
- } finally {
- it.close();
- }
-
- dataNode.getJdbcEventLogger().logSelectCount(resultRows.size(), System.currentTimeMillis() - t1, sqlString);
-
- observer.nextRows(query, resultRows);
- }
- }
+ private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings)
+ throws SQLException, Exception {
+
+ for (ParameterBinding b : bindings) {
+
+ if (b.isExcluded()) {
+ continue;
+ }
+
+ // null DbAttributes are a result of inferior qualifier
+ // processing (qualifier can't map parameters to DbAttributes
+ // and therefore only supports standard java types now) hence, a
+ // special moronic case here:
+ if (b.getAttribute() == null) {
+ statement.setObject(b.getStatementPosition(), b.getValue());
+ } else {
+ adapter.bindParameter(statement, b.getValue(), b.getStatementPosition(), b.getAttribute().getType(), b
+ .getAttribute().getScale());
+ }
+ }
+
+ }
+
+ protected SelectQuery<?> query;
+ protected QueryMetadata queryMetadata;
+
+ /**
+ * @since 4.0
+ */
+ public SelectAction(SelectQuery<?> query, DataNode dataNode) {
+ super(dataNode);
+ this.query = query;
+ this.queryMetadata = query.getMetaData(dataNode.getEntityResolver());
+ }
+
+ protected SelectTranslator createTranslator() {
+ return new DefaultSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes", "resource" })
+ @Override
+ public void performAction(Connection connection, OperationObserver observer) throws SQLException, Exception {
+
+ final long t1 = System.currentTimeMillis();
+
+ JdbcEventLogger logger = dataNode.getJdbcEventLogger();
+ SelectTranslator translator = createTranslator();
+ final String sql = translator.getSql();
+
+ ParameterBinding[] bindings = translator.getBindings();
+ PreparedStatement statement = connection.prepareStatement(sql);
+ bind(dataNode.getAdapter(), statement, bindings);
+
+ int fetchSize = queryMetadata.getStatementFetchSize();
+ if (fetchSize != 0) {
+ statement.setFetchSize(fetchSize);
+ }
+
+ logger.logQuery(sql, bindings, System.currentTimeMillis() - t1);
+
+ ResultSet rs;
+
+ // need to run in try-catch block to close statement properly if
+ // exception happens
+ try {
+ rs = statement.executeQuery();
+ } catch (Exception ex) {
+ statement.close();
+ throw ex;
+ }
+ RowDescriptor descriptor = new RowDescriptorBuilder().setColumns(translator.getResultColumns()).getDescriptor(
+ dataNode.getAdapter().getExtendedTypes());
+
+ RowReader<?> rowReader = dataNode.rowReader(descriptor, queryMetadata, translator.getAttributeOverrides());
+
+ ResultIterator it = new JDBCResultIterator(statement, rs, rowReader);
+ it = forIteratedResult(it, observer, connection, t1, sql);
+ it = forSuppressedDistinct(it, translator);
+ it = forFetchLimit(it, translator);
+
+ // TODO: Should do something about closing ResultSet and
+ // PreparedStatement in this method, instead of relying on
+ // DefaultResultIterator to do that later
+
+ if (observer.isIteratedResult()) {
+ try {
+ observer.nextRows(query, it);
+ } catch (Exception ex) {
+ it.close();
+ throw ex;
+ }
+ } else {
+ List<DataRow> resultRows;
+ try {
+ resultRows = it.allRows();
+ } finally {
+ it.close();
+ }
+
+ dataNode.getJdbcEventLogger().logSelectCount(resultRows.size(), System.currentTimeMillis() - t1, sql);
+
+ observer.nextRows(query, resultRows);
+ }
+ }
+
+ private <T> ResultIterator<T> forIteratedResult(ResultIterator<T> iterator, OperationObserver observer,
+ Connection connection, final long queryStartedAt, final String sql) {
+ if (!observer.isIteratedResult()) {
+ return iterator;
+ }
+
+ return new ConnectionAwareResultIterator<T>(iterator, connection) {
+ @Override
+ protected void doClose() {
+ dataNode.getJdbcEventLogger().logSelectCount(rowCounter, System.currentTimeMillis() - queryStartedAt,
+ sql);
+ super.doClose();
+ }
+ };
+ }
+
+ private <T> ResultIterator<T> forFetchLimit(ResultIterator<T> iterator, SelectTranslator translator) {
+ // wrap iterator in a fetch limit checker ... there are a few cases when
+ // in-memory fetch limit is a noop, however in a general case this is
+ // needed, as the SQL result count does not directly correspond to the
+ // number of objects returned from Cayenne.
+
+ int fetchLimit = query.getFetchLimit();
+ int offset = translator.isSuppressingDistinct() ? query.getFetchOffset() : getInMemoryOffset(query
+ .getFetchOffset());
+
+ if (fetchLimit > 0 || offset > 0) {
+ return new LimitResultIterator<T>(iterator, offset, fetchLimit);
+ } else {
+ return iterator;
+ }
+ }
+
+ private <T> ResultIterator<T> forSuppressedDistinct(ResultIterator<T> iterator, SelectTranslator translator) {
+ if (!translator.isSuppressingDistinct()) {
+ return iterator;
+ }
+
+ // wrap result iterator if distinct has to be suppressed
+
+ // a joint prefetch warrants full row compare
+
+ final boolean[] compareFullRows = new boolean[1];
+ final PrefetchTreeNode rootPrefetch = queryMetadata.getPrefetchTree();
+
+ if (rootPrefetch != null) {
+ rootPrefetch.traverse(new PrefetchProcessor() {
+
+ @Override
+ public void finishPrefetch(PrefetchTreeNode node) {
+ }
+
+ @Override
+ public boolean startDisjointPrefetch(PrefetchTreeNode node) {
+ // continue to children only if we are at root
+ return rootPrefetch == node;
+ }
+
+ @Override
+ public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+ // continue to children only if we are at root
+ return rootPrefetch == node;
+ }
+
+ @Override
+ public boolean startUnknownPrefetch(PrefetchTreeNode node) {
+ // continue to children only if we are at root
+ return rootPrefetch == node;
+ }
+
+ @Override
+ public boolean startJointPrefetch(PrefetchTreeNode node) {
+ if (rootPrefetch != node) {
+ compareFullRows[0] = true;
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean startPhantomPrefetch(PrefetchTreeNode node) {
+ return true;
+ }
+ });
+ }
+
+ return new DistinctResultIterator<T>(iterator, queryMetadata.getDbEntity(), compareFullRows[0]);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java
new file mode 100644
index 0000000..7a6a7e6
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java
@@ -0,0 +1,81 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.access.translator;
+
+import org.apache.cayenne.map.DbAttribute;
+
+/**
+ * Describes a PreparedStatement parameter binding mapped to a DbAttribute.
+ *
+ * @since 4.0
+ */
+public class ParameterBinding {
+
+ static final int EXCLUDED_POSITION = -1;
+
+ private DbAttribute attribute;
+ private Object value;
+ private int statementPosition;
+
+ public ParameterBinding(DbAttribute attribute) {
+ this.attribute = attribute;
+ this.statementPosition = EXCLUDED_POSITION;
+ }
+
+ public DbAttribute getAttribute() {
+ return attribute;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ public int getStatementPosition() {
+ return statementPosition;
+ }
+
+ public void setStatementPosition(int statementPosition) {
+ this.statementPosition = statementPosition;
+ }
+
+ public boolean isExcluded() {
+ return statementPosition == EXCLUDED_POSITION;
+ }
+
+ /**
+ * Marks the binding object as excluded for the current iteration.
+ */
+ public void exclude() {
+ this.statementPosition = EXCLUDED_POSITION;
+ this.value = null;
+ }
+
+ /**
+ * Sets the value of the binding and initializes statement position var,
+ * thus "including" this binding in the current iteration.
+ */
+ public void include(int statementPosition, Object value) {
+ this.statementPosition = statementPosition;
+ this.value = value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchParameterBinding.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchParameterBinding.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchParameterBinding.java
deleted file mode 100644
index a37a145..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchParameterBinding.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- ****************************************************************/
-package org.apache.cayenne.access.translator.batch;
-
-import org.apache.cayenne.map.DbAttribute;
-
-/**
- * @since 4.0
- */
-public class BatchParameterBinding {
-
- static final int EXCLUDED_POSITION = -1;
-
- private DbAttribute attribute;
- private Object value;
- private int statementPosition;
-
- public BatchParameterBinding(DbAttribute attribute) {
- this.attribute = attribute;
- this.statementPosition = EXCLUDED_POSITION;
- }
-
- public DbAttribute getAttribute() {
- return attribute;
- }
-
- public Object getValue() {
- return value;
- }
-
- public void setValue(Object value) {
- this.value = value;
- }
-
- public int getStatementPosition() {
- return statementPosition;
- }
-
- public void setStatementPosition(int statementPosition) {
- this.statementPosition = statementPosition;
- }
-
- public boolean isExcluded() {
- return statementPosition == EXCLUDED_POSITION;
- }
-
- /**
- * Marks the binding object as excluded for the current iteration.
- */
- public void exclude() {
- this.statementPosition = EXCLUDED_POSITION;
- this.value = null;
- }
-
- /**
- * Sets the value of the binding and initializes statement position var,
- * thus "including" this binding in the current iteration.
- */
- public void include(int statementPosition, Object value) {
- this.statementPosition = statementPosition;
- this.value = value;
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java
index 989fcf1..04a4902 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.access.translator.batch;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.query.BatchQueryRow;
/**
@@ -36,7 +37,7 @@ public interface BatchTranslator {
/**
* Returns the widest possible array of bindings for this query.
*/
- BatchParameterBinding[] getBindings();
+ ParameterBinding[] getBindings();
/**
* Updates internal bindings to be used with a given row, returning updated
@@ -46,5 +47,5 @@ public interface BatchTranslator {
* parameter). Usually the returned array is actually the same object reused
* for every iteration, only with changed object state.
*/
- BatchParameterBinding[] updateBindings(BatchQueryRow row);
+ ParameterBinding[] updateBindings(BatchQueryRow row);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslator.java
index 82f945e..732ec64 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DefaultBatchTranslator.java
@@ -20,6 +20,7 @@ package org.apache.cayenne.access.translator.batch;
import java.sql.Types;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.map.DbAttribute;
@@ -39,7 +40,7 @@ public abstract class DefaultBatchTranslator implements BatchTranslator {
protected boolean translated;
protected String sql;
- protected BatchParameterBinding[] bindings;
+ protected ParameterBinding[] bindings;
public DefaultBatchTranslator(BatchQuery query, DbAdapter adapter, String trimFunction) {
this.query = query;
@@ -66,22 +67,22 @@ public abstract class DefaultBatchTranslator implements BatchTranslator {
}
@Override
- public BatchParameterBinding[] getBindings() {
+ public ParameterBinding[] getBindings() {
ensureTranslated();
return bindings;
}
@Override
- public BatchParameterBinding[] updateBindings(BatchQueryRow row) {
+ public ParameterBinding[] updateBindings(BatchQueryRow row) {
ensureTranslated();
return doUpdateBindings(row);
}
protected abstract String createSql();
- protected abstract BatchParameterBinding[] createBindings();
+ protected abstract ParameterBinding[] createBindings();
- protected abstract BatchParameterBinding[] doUpdateBindings(BatchQueryRow row);
+ protected abstract ParameterBinding[] doUpdateBindings(BatchQueryRow row);
/**
* Appends the name of the column to the query buffer. Subclasses use this
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
index bf91404..e6636da 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.access.translator.batch;
import java.util.Iterator;
import java.util.List;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.map.DbAttribute;
@@ -71,23 +72,23 @@ public class DeleteBatchTranslator extends DefaultBatchTranslator {
}
@Override
- protected BatchParameterBinding[] createBindings() {
+ protected ParameterBinding[] createBindings() {
DeleteBatchQuery deleteBatch = (DeleteBatchQuery) query;
List<DbAttribute> attributes = deleteBatch.getDbAttributes();
int len = attributes.size();
- BatchParameterBinding[] bindings = new BatchParameterBinding[len];
+ ParameterBinding[] bindings = new ParameterBinding[len];
for (int i = 0; i < len; i++) {
DbAttribute a = attributes.get(i);
- bindings[i] = new BatchParameterBinding(a);
+ bindings[i] = new ParameterBinding(a);
}
return bindings;
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
int len = bindings.length;
@@ -95,7 +96,7 @@ public class DeleteBatchTranslator extends DefaultBatchTranslator {
for (int i = 0, j = 1; i < len; i++) {
- BatchParameterBinding b = bindings[i];
+ ParameterBinding b = bindings[i];
// skip null attributes... they are translated as "IS NULL"
if (deleteBatch.isNull(b.getAttribute())) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
index 43ce612..3deb552 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.access.translator.batch;
import java.util.List;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.map.DbAttribute;
@@ -81,15 +82,15 @@ public class InsertBatchTranslator extends DefaultBatchTranslator {
}
@Override
- protected BatchParameterBinding[] createBindings() {
+ protected ParameterBinding[] createBindings() {
List<DbAttribute> attributes = query.getDbAttributes();
int len = attributes.size();
- BatchParameterBinding[] bindings = new BatchParameterBinding[len];
+ ParameterBinding[] bindings = new ParameterBinding[len];
for (int i = 0; i < len; i++) {
DbAttribute a = attributes.get(i);
- bindings[i] = new BatchParameterBinding(a);
+ bindings[i] = new ParameterBinding(a);
// include/exclude state depends on DbAttribute only and can be
// precompiled here
@@ -106,12 +107,12 @@ public class InsertBatchTranslator extends DefaultBatchTranslator {
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
int len = bindings.length;
for (int i = 0, j = 1; i < len; i++) {
- BatchParameterBinding b = bindings[i];
+ ParameterBinding b = bindings[i];
// exclusions are permanent
if (!b.isExcluded()) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
index 7311cc5..4e793e5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
@@ -18,6 +18,7 @@
****************************************************************/
package org.apache.cayenne.access.translator.batch;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.map.DbAttribute;
@@ -53,16 +54,16 @@ public class SoftDeleteBatchTranslator extends DeleteBatchTranslator {
}
@Override
- protected BatchParameterBinding[] createBindings() {
+ protected ParameterBinding[] createBindings() {
- BatchParameterBinding[] superBindings = super.createBindings();
+ ParameterBinding[] superBindings = super.createBindings();
int slen = superBindings.length;
- BatchParameterBinding[] bindings = new BatchParameterBinding[slen + 1];
+ ParameterBinding[] bindings = new ParameterBinding[slen + 1];
DbAttribute deleteAttribute = query.getDbEntity().getAttribute(deletedFieldName);
- bindings[0] = new BatchParameterBinding(deleteAttribute);
+ bindings[0] = new ParameterBinding(deleteAttribute);
bindings[0].include(1, true);
System.arraycopy(superBindings, 0, bindings, 1, slen);
@@ -71,7 +72,7 @@ public class SoftDeleteBatchTranslator extends DeleteBatchTranslator {
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
int len = bindings.length;
DeleteBatchQuery deleteBatch = (DeleteBatchQuery) query;
@@ -79,7 +80,7 @@ public class SoftDeleteBatchTranslator extends DeleteBatchTranslator {
// skip position 0... Otherwise follow super algorithm
for (int i = 1, j = 2; i < len; i++) {
- BatchParameterBinding b = bindings[i];
+ ParameterBinding b = bindings[i];
// skip null attributes... they are translated as "IS NULL"
if (deleteBatch.isNull(b.getAttribute())) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
index 61a49b3..a44a373 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.access.translator.batch;
import java.util.Iterator;
import java.util.List;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.map.DbAttribute;
@@ -78,7 +79,7 @@ public class UpdateBatchTranslator extends DefaultBatchTranslator {
}
@Override
- protected BatchParameterBinding[] createBindings() {
+ protected ParameterBinding[] createBindings() {
UpdateBatchQuery updateBatch = (UpdateBatchQuery) query;
List<DbAttribute> updatedDbAttributes = updateBatch.getUpdatedAttributes();
@@ -87,23 +88,23 @@ public class UpdateBatchTranslator extends DefaultBatchTranslator {
int ul = updatedDbAttributes.size();
int ql = qualifierAttributes.size();
- BatchParameterBinding[] bindings = new BatchParameterBinding[ul + ql];
+ ParameterBinding[] bindings = new ParameterBinding[ul + ql];
for (int i = 0; i < ul; i++) {
DbAttribute a = updatedDbAttributes.get(i);
- bindings[i] = new BatchParameterBinding(a);
+ bindings[i] = new ParameterBinding(a);
}
for (int i = 0; i < ql; i++) {
DbAttribute a = qualifierAttributes.get(i);
- bindings[ul + i] = new BatchParameterBinding(a);
+ bindings[ul + i] = new ParameterBinding(a);
}
return bindings;
}
@Override
- protected BatchParameterBinding[] doUpdateBindings(BatchQueryRow row) {
+ protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) {
UpdateBatchQuery updateBatch = (UpdateBatchQuery) query;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
new file mode 100644
index 0000000..38b5763
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
@@ -0,0 +1,630 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.access.translator.select;
+
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.access.jdbc.ColumnDescriptor;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.JoinType;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.PathComponent;
+import org.apache.cayenne.query.PrefetchSelectQuery;
+import org.apache.cayenne.query.PrefetchTreeNode;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+import org.apache.cayenne.util.CayenneMapEntry;
+import org.apache.cayenne.util.EqualsBuilder;
+import org.apache.cayenne.util.HashCodeBuilder;
+
+/**
+ * @since 4.0
+ */
+public class DefaultSelectTranslator extends QueryAssembler implements SelectTranslator {
+
+ protected static final int[] UNSUPPORTED_DISTINCT_TYPES = { Types.BLOB, Types.CLOB, Types.NCLOB,
+ Types.LONGVARBINARY, Types.LONGVARCHAR, Types.LONGNVARCHAR };
+
+ protected static boolean isUnsupportedForDistinct(int type) {
+ for (int unsupportedDistinctType : UNSUPPORTED_DISTINCT_TYPES) {
+ if (unsupportedDistinctType == type) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ JoinStack joinStack;
+
+ public DefaultSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ super(query, adapter, entityResolver);
+ }
+
+ protected JoinStack getJoinStack() {
+ if (joinStack == null) {
+ joinStack = createJoinStack();
+ }
+ return joinStack;
+ }
+
+ List<ColumnDescriptor> resultColumns;
+ Map<ObjAttribute, ColumnDescriptor> attributeOverrides;
+ Map<ColumnDescriptor, ObjAttribute> defaultAttributesByColumn;
+
+ boolean suppressingDistinct;
+
+ /**
+ * If set to <code>true</code>, indicates that distinct select query is
+ * required no matter what the original query settings where. This flag can
+ * be set when joins are created using "to-many" relationships.
+ */
+ boolean forcingDistinct;
+
+ protected JoinStack createJoinStack() {
+ return new JoinStack(getAdapter(), queryMetadata.getDataMap(), this);
+ }
+
+ @Override
+ protected void doTranslate() {
+
+ DataMap dataMap = queryMetadata.getDataMap();
+ JoinStack joins = getJoinStack();
+
+ QuotingStrategy strategy = getAdapter().getQuotingStrategy();
+ forcingDistinct = false;
+
+ // build column list
+ this.resultColumns = buildResultColumns();
+
+ // build qualifier
+ QualifierTranslator qualifierTranslator = adapter.getQualifierTranslator(this);
+ StringBuilder qualifierBuffer = qualifierTranslator.appendPart(new StringBuilder());
+
+ // build ORDER BY
+ OrderingTranslator orderingTranslator = new OrderingTranslator(this);
+ StringBuilder orderingBuffer = orderingTranslator.appendPart(new StringBuilder());
+
+ // assemble
+ StringBuilder queryBuf = new StringBuilder();
+ queryBuf.append("SELECT ");
+
+ // check if DISTINCT is appropriate
+ // side effect: "suppressingDistinct" flag may end up being flipped here
+ if (forcingDistinct || getSelectQuery().isDistinct()) {
+ suppressingDistinct = false;
+
+ for (ColumnDescriptor column : resultColumns) {
+ if (isUnsupportedForDistinct(column.getJdbcType())) {
+ suppressingDistinct = true;
+ break;
+ }
+ }
+
+ if (!suppressingDistinct) {
+ queryBuf.append(buildDistinctStatement() + " ");
+ }
+ }
+
+ // convert ColumnDescriptors to column names
+ List<String> selectColumnExpList = new ArrayList<String>();
+ for (ColumnDescriptor column : resultColumns) {
+ String fullName = strategy.quotedIdentifier(dataMap, column.getNamePrefix(), column.getName());
+ selectColumnExpList.add(fullName);
+ }
+
+ // append any column expressions used in the order by if this query
+ // uses the DISTINCT modifier
+ if (forcingDistinct || getSelectQuery().isDistinct()) {
+ List<String> orderByColumnList = orderingTranslator.getOrderByColumnList();
+ for (String orderByColumnExp : orderByColumnList) {
+ // Convert to ColumnDescriptors??
+ if (!selectColumnExpList.contains(orderByColumnExp)) {
+ selectColumnExpList.add(orderByColumnExp);
+ }
+ }
+ }
+
+ appendSelectColumns(queryBuf, selectColumnExpList);
+
+ // append from clause
+ queryBuf.append(" FROM ");
+
+ // append tables and joins
+ joins.appendRootWithQuoteSqlIdentifiers(queryBuf, getQueryMetadata().getDbEntity());
+
+ joins.appendJoins(queryBuf);
+ joins.appendQualifier(qualifierBuffer, qualifierBuffer.length() == 0);
+
+ // append qualifier
+ if (qualifierBuffer.length() > 0) {
+ queryBuf.append(" WHERE ");
+ queryBuf.append(qualifierBuffer);
+ }
+
+ // append prebuilt ordering
+ if (orderingBuffer.length() > 0) {
+ queryBuf.append(" ORDER BY ").append(orderingBuffer);
+ }
+
+ if (!isSuppressingDistinct()) {
+ appendLimitAndOffsetClauses(queryBuf);
+ }
+
+ this.sql = queryBuf.toString();
+ }
+
+ /**
+ * Allows subclasses to insert their own dialect of DISTINCT statement to
+ * improve performance.
+ *
+ * @return string representing the DISTINCT statement
+ * @since 4.0
+ */
+ protected String buildDistinctStatement() {
+ return "DISTINCT";
+ }
+
+ /**
+ * @since 3.1
+ */
+ protected void appendSelectColumns(StringBuilder buffer, List<String> selectColumnExpList) {
+
+ // append columns (unroll the loop's first element)
+ int columnCount = selectColumnExpList.size();
+ buffer.append(selectColumnExpList.get(0));
+
+ // assume there is at least 1 element
+ for (int i = 1; i < columnCount; i++) {
+ buffer.append(", ");
+ buffer.append(selectColumnExpList.get(i));
+ }
+ }
+
+ /**
+ * Handles appending optional limit and offset clauses. This implementation
+ * does nothing, deferring to subclasses to define the LIMIT/OFFSET clause
+ * syntax.
+ *
+ * @since 3.0
+ */
+ protected void appendLimitAndOffsetClauses(StringBuilder buffer) {
+
+ }
+
+ @Override
+ public String getCurrentAlias() {
+ return getJoinStack().getCurrentAlias();
+ }
+
+ /**
+ * Returns a list of ColumnDescriptors for the query columns.
+ *
+ * @since 1.2
+ */
+ public ColumnDescriptor[] getResultColumns() {
+ if (resultColumns == null || resultColumns.isEmpty()) {
+ return new ColumnDescriptor[0];
+ }
+
+ return resultColumns.toArray(new ColumnDescriptor[resultColumns.size()]);
+ }
+
+ /**
+ * Returns a map of ColumnDescriptors keyed by ObjAttribute for columns that
+ * may need to be reprocessed manually due to incompatible mappings along
+ * the inheritance hierarchy.
+ *
+ * @since 1.2
+ */
+ public Map<ObjAttribute, ColumnDescriptor> getAttributeOverrides() {
+ if (attributeOverrides != null) {
+ return attributeOverrides;
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+
+ /**
+ * Returns true if SelectTranslator determined that a query requiring
+ * DISTINCT can't be run with DISTINCT keyword for internal reasons. If this
+ * method returns true, DataNode may need to do in-memory distinct
+ * filtering.
+ *
+ * @since 1.1
+ */
+ public boolean isSuppressingDistinct() {
+ return suppressingDistinct;
+ }
+
+ private SelectQuery<?> getSelectQuery() {
+ return (SelectQuery<?>) getQuery();
+ }
+
+ protected List<ColumnDescriptor> buildResultColumns() {
+
+ this.defaultAttributesByColumn = new HashMap<ColumnDescriptor, ObjAttribute>();
+
+ List<ColumnDescriptor> columns = new ArrayList<ColumnDescriptor>();
+ SelectQuery<?> query = getSelectQuery();
+
+ if (query.getRoot() instanceof DbEntity) {
+ appendDbEntityColumns(columns, query);
+ } else if (getQueryMetadata().getPageSize() > 0) {
+ appendIdColumns(columns, query);
+ } else {
+ appendQueryColumns(columns, query);
+ }
+
+ return columns;
+ }
+
+ <T> List<ColumnDescriptor> appendDbEntityColumns(List<ColumnDescriptor> columns, SelectQuery<T> query) {
+
+ Set<ColumnTracker> attributes = new HashSet<ColumnTracker>();
+
+ DbEntity table = getQueryMetadata().getDbEntity();
+ for (DbAttribute dba : table.getAttributes()) {
+ appendColumn(columns, null, dba, attributes, null);
+ }
+
+ return columns;
+ }
+
+ /**
+ * Appends columns needed for object SelectQuery to the provided columns
+ * list.
+ */
+ <T> List<ColumnDescriptor> appendQueryColumns(final List<ColumnDescriptor> columns, SelectQuery<T> query) {
+
+ final Set<ColumnTracker> attributes = new HashSet<ColumnTracker>();
+
+ // fetched attributes include attributes that are either:
+ //
+ // * class properties
+ // * PK
+ // * FK used in relationship
+ // * joined prefetch PK
+
+ ClassDescriptor descriptor = queryMetadata.getClassDescriptor();
+ ObjEntity oe = descriptor.getEntity();
+
+ PropertyVisitor visitor = new PropertyVisitor() {
+
+ public boolean visitAttribute(AttributeProperty property) {
+ ObjAttribute oa = property.getAttribute();
+
+ resetJoinStack();
+ Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
+ while (dbPathIterator.hasNext()) {
+ Object pathPart = dbPathIterator.next();
+
+ if (pathPart == null) {
+ throw new CayenneRuntimeException("ObjAttribute has no component: " + oa.getName());
+ } else if (pathPart instanceof DbRelationship) {
+ DbRelationship rel = (DbRelationship) pathPart;
+ dbRelationshipAdded(rel, JoinType.LEFT_OUTER, null);
+ } else if (pathPart instanceof DbAttribute) {
+ DbAttribute dbAttr = (DbAttribute) pathPart;
+
+ appendColumn(columns, oa, dbAttr, attributes, null);
+ }
+ }
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ visitRelationship(property);
+ return true;
+ }
+
+ public boolean visitToOne(ToOneProperty property) {
+ visitRelationship(property);
+ return true;
+ }
+
+ private void visitRelationship(ArcProperty property) {
+ resetJoinStack();
+
+ ObjRelationship rel = property.getRelationship();
+ DbRelationship dbRel = rel.getDbRelationships().get(0);
+
+ List<DbJoin> joins = dbRel.getJoins();
+ for (DbJoin join : joins) {
+ DbAttribute src = join.getSource();
+ appendColumn(columns, null, src, attributes, null);
+ }
+ }
+ };
+
+ descriptor.visitAllProperties(visitor);
+
+ // stack should be reset, because all root table attributes go with "t0"
+ // table alias
+ resetJoinStack();
+
+ // add remaining needed attrs from DbEntity
+ DbEntity table = getQueryMetadata().getDbEntity();
+ for (DbAttribute dba : table.getPrimaryKeys()) {
+ appendColumn(columns, null, dba, attributes, null);
+ }
+
+ // special handling of a disjoint query...
+
+ if (query instanceof PrefetchSelectQuery) {
+
+ // for each relationship path add PK of the target entity...
+ for (String path : ((PrefetchSelectQuery) query).getResultPaths()) {
+
+ ASTDbPath pathExp = (ASTDbPath) oe.translateToDbPath(ExpressionFactory.exp(path));
+
+ // add joins and find terminating element
+
+ resetJoinStack();
+
+ PathComponent<DbAttribute, DbRelationship> lastComponent = null;
+ for (PathComponent<DbAttribute, DbRelationship> component : table
+ .resolvePath(pathExp, getPathAliases())) {
+
+ if (component.getRelationship() != null) {
+ // do not invoke dbRelationshipAdded(), invoke
+ // pushJoin() instead. This is to prevent
+ // 'forcingDistinct' flipping to true, that will result
+ // in unneeded extra processing and sometimes in invalid
+ // results (see CAY-1979). Distinctness of each row is
+ // guaranteed by the prefetch query semantics - we
+ // include target ID in the result columns
+ getJoinStack().pushJoin(component.getRelationship(), component.getJoinType(), null);
+ }
+
+ lastComponent = component;
+ }
+
+ // process terminating element
+ if (lastComponent != null) {
+
+ DbRelationship relationship = lastComponent.getRelationship();
+
+ if (relationship != null) {
+
+ String labelPrefix = pathExp.getPath();
+ DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
+
+ for (DbAttribute pk : targetEntity.getPrimaryKeys()) {
+
+ // note that we my select a source attribute, but
+ // label it as
+ // target for simplified snapshot processing
+ appendColumn(columns, null, pk, attributes, labelPrefix + '.' + pk.getName());
+ }
+ }
+ }
+ }
+ }
+
+ // handle joint prefetches directly attached to this query...
+ if (query.getPrefetchTree() != null) {
+
+ for (PrefetchTreeNode prefetch : query.getPrefetchTree().adjacentJointNodes()) {
+
+ // for each prefetch add all joins plus columns from the target
+ // entity
+ Expression prefetchExp = ExpressionFactory.exp(prefetch.getPath());
+ ASTDbPath dbPrefetch = (ASTDbPath) oe.translateToDbPath(prefetchExp);
+
+ resetJoinStack();
+ DbRelationship r = null;
+ for (PathComponent<DbAttribute, DbRelationship> component : table.resolvePath(dbPrefetch,
+ getPathAliases())) {
+ r = component.getRelationship();
+ dbRelationshipAdded(r, JoinType.LEFT_OUTER, null);
+ }
+
+ if (r == null) {
+ throw new CayenneRuntimeException("Invalid joint prefetch '" + prefetch + "' for entity: "
+ + oe.getName());
+ }
+
+ // add columns from the target entity, including those that are
+ // matched
+ // against the FK of the source entity. This is needed to
+ // determine
+ // whether optional relationships are null
+
+ // go via target OE to make sure that Java types are mapped
+ // correctly...
+ ObjRelationship targetRel = (ObjRelationship) prefetchExp.evaluate(oe);
+ ObjEntity targetEntity = (ObjEntity) targetRel.getTargetEntity();
+
+ String labelPrefix = dbPrefetch.getPath();
+ for (ObjAttribute oa : targetEntity.getAttributes()) {
+ Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
+ while (dbPathIterator.hasNext()) {
+ Object pathPart = dbPathIterator.next();
+
+ if (pathPart == null) {
+ throw new CayenneRuntimeException("ObjAttribute has no component: " + oa.getName());
+ } else if (pathPart instanceof DbRelationship) {
+ DbRelationship rel = (DbRelationship) pathPart;
+ dbRelationshipAdded(rel, JoinType.INNER, null);
+ } else if (pathPart instanceof DbAttribute) {
+ DbAttribute attribute = (DbAttribute) pathPart;
+
+ appendColumn(columns, oa, attribute, attributes, labelPrefix + '.' + attribute.getName());
+ }
+ }
+ }
+
+ // append remaining target attributes such as keys
+ DbEntity targetDbEntity = (DbEntity) r.getTargetEntity();
+ for (DbAttribute attribute : targetDbEntity.getAttributes()) {
+ appendColumn(columns, null, attribute, attributes, labelPrefix + '.' + attribute.getName());
+ }
+ }
+ }
+
+ return columns;
+ }
+
+ <T> List<ColumnDescriptor> appendIdColumns(final List<ColumnDescriptor> columns, SelectQuery<T> query) {
+
+ Set<ColumnTracker> skipSet = new HashSet<ColumnTracker>();
+
+ ClassDescriptor descriptor = queryMetadata.getClassDescriptor();
+ ObjEntity oe = descriptor.getEntity();
+ DbEntity dbEntity = oe.getDbEntity();
+ for (ObjAttribute attribute : oe.getPrimaryKeys()) {
+
+ // synthetic objattributes can't reliably lookup their DbAttribute,
+ // so do it manually..
+ DbAttribute dbAttribute = dbEntity.getAttribute(attribute.getDbAttributeName());
+ appendColumn(columns, attribute, dbAttribute, skipSet, null);
+ }
+
+ return columns;
+ }
+
+ private void appendColumn(List<ColumnDescriptor> columns, ObjAttribute objAttribute, DbAttribute attribute,
+ Set<ColumnTracker> skipSet, String label) {
+
+ String alias = getCurrentAlias();
+ if (skipSet.add(new ColumnTracker(alias, attribute))) {
+
+ ColumnDescriptor column = (objAttribute != null) ? new ColumnDescriptor(objAttribute, attribute, alias)
+ : new ColumnDescriptor(attribute, alias);
+
+ if (label != null) {
+ column.setDataRowKey(label);
+ }
+
+ columns.add(column);
+
+ // TODO: andrus, 5/7/2006 - replace 'columns' collection with this
+ // map, as it
+ // is redundant
+ defaultAttributesByColumn.put(column, objAttribute);
+ } else if (objAttribute != null) {
+
+ // record ObjAttribute override
+ for (ColumnDescriptor column : columns) {
+ if (attribute.getName().equals(column.getName())) {
+
+ if (attributeOverrides == null) {
+ attributeOverrides = new HashMap<ObjAttribute, ColumnDescriptor>();
+ }
+
+ // kick out the original attribute
+ ObjAttribute original = defaultAttributesByColumn.remove(column);
+
+ if (original != null) {
+ attributeOverrides.put(original, column);
+ }
+
+ attributeOverrides.put(objAttribute, column);
+ column.setJavaClass(Void.TYPE.getName());
+
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ @Override
+ public void resetJoinStack() {
+ getJoinStack().resetStack();
+ }
+
+ /**
+ * @since 3.0
+ */
+ @Override
+ public void dbRelationshipAdded(DbRelationship relationship, JoinType joinType, String joinSplitAlias) {
+ if (relationship.isToMany()) {
+ forcingDistinct = true;
+ }
+
+ getJoinStack().pushJoin(relationship, joinType, joinSplitAlias);
+ }
+
+ /**
+ * Always returns true.
+ */
+ @Override
+ public boolean supportsTableAliases() {
+ return true;
+ }
+
+ static final class ColumnTracker {
+
+ private DbAttribute attribute;
+ private String alias;
+
+ ColumnTracker(String alias, DbAttribute attribute) {
+ this.attribute = attribute;
+ this.alias = alias;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof ColumnTracker)) {
+ return false;
+ }
+
+ ColumnTracker other = (ColumnTracker) object;
+ return new EqualsBuilder().append(alias, other.alias).append(attribute, other.attribute).isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(31, 5).append(alias).append(attribute).toHashCode();
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/JoinStack.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/JoinStack.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/JoinStack.java
index 440f7b1..416f14c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/JoinStack.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/JoinStack.java
@@ -18,7 +18,6 @@
****************************************************************/
package org.apache.cayenne.access.translator.select;
-import java.io.IOException;
import java.util.List;
import org.apache.cayenne.dba.DbAdapter;
@@ -45,178 +44,178 @@ import org.apache.commons.collections.Transformer;
*/
public class JoinStack {
- protected JoinTreeNode rootNode;
- protected JoinTreeNode topNode;
- private QuotingStrategy quotingStrategy;
-
- private int aliasCounter;
-
- /**
- * Helper class to process DbEntity qualifiers
- */
- private QualifierTranslator qualifierTranslator;
-
- protected JoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
- this.rootNode = new JoinTreeNode(this);
- this.rootNode.setTargetTableAlias(newAlias());
-
- this.quotingStrategy = dbAdapter.getQuotingStrategy();
- this.qualifierTranslator = dbAdapter.getQualifierTranslator(assembler);
-
- resetStack();
- }
-
- String getCurrentAlias() {
- return topNode.getTargetTableAlias();
- }
-
- /**
- * Returns the number of configured joins.
- */
- protected int size() {
- // do not count root as a join
- return rootNode.size() - 1;
- }
-
- void appendRootWithQuoteSqlIdentifiers(Appendable out, DbEntity rootEntity) throws IOException {
-
- out.append(quotingStrategy.quotedFullyQualifiedName(rootEntity));
- out.append(' ');
- out.append(quotingStrategy.quotedIdentifier(rootEntity, rootNode.getTargetTableAlias()));
- }
-
- /**
- * Appends all configured joins to the provided output object.
- */
- protected void appendJoins(Appendable out) throws IOException {
-
- // skip root, recursively append its children
- for (JoinTreeNode child : rootNode.getChildren()) {
- appendJoinSubtree(out, child);
- }
- }
-
- protected void appendJoinSubtree(Appendable out, JoinTreeNode node) throws IOException {
-
- DbRelationship relationship = node.getRelationship();
-
- DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
- String srcAlias = node.getSourceTableAlias();
- String targetAlias = node.getTargetTableAlias();
-
- switch (node.getJoinType()) {
- case INNER:
- out.append(" JOIN");
- break;
- case LEFT_OUTER:
- out.append(" LEFT JOIN");
- break;
- default:
- throw new IllegalArgumentException("Unsupported join type: " + node.getJoinType());
- }
-
- out.append(' ');
- out.append(quotingStrategy.quotedFullyQualifiedName(targetEntity));
-
- out.append(' ');
- out.append(quotingStrategy.quotedIdentifier(targetEntity, targetAlias));
- out.append(" ON (");
-
- List<DbJoin> joins = relationship.getJoins();
- int len = joins.size();
- for (int i = 0; i < len; i++) {
- DbJoin join = joins.get(i);
- if (i > 0) {
- out.append(" AND ");
- }
-
- out.append(quotingStrategy.quotedIdentifier(relationship.getSourceEntity(), srcAlias, join.getSourceName()));
- out.append(" = ");
- out.append(quotingStrategy.quotedIdentifier(targetEntity, targetAlias, join.getTargetName()));
- }
-
- /**
- * Attaching root Db entity's qualifier
- */
- Expression dbQualifier = targetEntity.getQualifier();
- if (dbQualifier != null) {
- dbQualifier = dbQualifier.transform(new JoinedDbEntityQualifierTransformer(node));
-
- if (len > 0) {
- out.append(" AND ");
- }
- qualifierTranslator.setOut(out);
- qualifierTranslator.doAppendPart(dbQualifier);
- }
-
- out.append(')');
-
- for (JoinTreeNode child : node.getChildren()) {
- appendJoinSubtree(out, child);
- }
- }
-
- /**
- * Append join information to the qualifier - the part after "WHERE".
- */
- protected void appendQualifier(Appendable out, boolean firstQualifierElement) throws IOException {
- // nothing as standard join is performed before "WHERE"
- }
-
- /**
- * Pops the stack all the way to the root node.
- */
- void resetStack() {
- topNode = rootNode;
- }
-
- /**
- * Finds or creates a JoinTreeNode for the given arguments and sets it as
- * the next current join.
- */
- void pushJoin(DbRelationship relationship, JoinType joinType, String alias) {
- topNode = topNode.findOrCreateChild(relationship, joinType, alias);
- }
-
- protected String newAlias() {
- return "t" + aliasCounter++;
- }
-
- /**
- * Class to translate *joined* DB Entity qualifiers annotation to *current*
- * Obj-entity qualifiers annotation This is done by changing all Obj-paths
- * to concatenated Db-paths to root entity and rejecting all original
- * Db-paths
- */
- class JoinedDbEntityQualifierTransformer implements Transformer {
-
- StringBuilder pathToRoot;
-
- JoinedDbEntityQualifierTransformer(JoinTreeNode node) {
- pathToRoot = new StringBuilder();
- while (node != null && node.getRelationship() != null) {
- String relName = node.getRelationship().getName();
-
- /**
- * We must be in the same join as 'node', otherwise incorrect
- * join statement like JOIN t1 ... ON (t0.id=t1.id AND
- * t2.qualifier=0) could be generated
- */
- if (node.getJoinType() == JoinType.LEFT_OUTER) {
- relName += Entity.OUTER_JOIN_INDICATOR;
- }
- relName += ObjEntity.PATH_SEPARATOR;
-
- pathToRoot.insert(0, relName);
- node = node.getParent();
- }
- }
-
- public Object transform(Object input) {
- if (input instanceof ASTObjPath) {
- return new ASTDbPath(pathToRoot.toString() + ((SimpleNode) input).getOperand(0));
- }
- return input;
- }
- }
+ protected JoinTreeNode rootNode;
+ protected JoinTreeNode topNode;
+ private QuotingStrategy quotingStrategy;
+
+ private int aliasCounter;
+
+ /**
+ * Helper class to process DbEntity qualifiers
+ */
+ private QualifierTranslator qualifierTranslator;
+
+ protected JoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
+ this.rootNode = new JoinTreeNode(this);
+ this.rootNode.setTargetTableAlias(newAlias());
+
+ this.quotingStrategy = dbAdapter.getQuotingStrategy();
+ this.qualifierTranslator = dbAdapter.getQualifierTranslator(assembler);
+
+ resetStack();
+ }
+
+ String getCurrentAlias() {
+ return topNode.getTargetTableAlias();
+ }
+
+ /**
+ * Returns the number of configured joins.
+ */
+ protected int size() {
+ // do not count root as a join
+ return rootNode.size() - 1;
+ }
+
+ void appendRootWithQuoteSqlIdentifiers(StringBuilder out, DbEntity rootEntity) {
+
+ out.append(quotingStrategy.quotedFullyQualifiedName(rootEntity));
+ out.append(' ');
+ out.append(quotingStrategy.quotedIdentifier(rootEntity, rootNode.getTargetTableAlias()));
+ }
+
+ /**
+ * Appends all configured joins to the provided output object.
+ */
+ protected void appendJoins(StringBuilder out) {
+
+ // skip root, recursively append its children
+ for (JoinTreeNode child : rootNode.getChildren()) {
+ appendJoinSubtree(out, child);
+ }
+ }
+
+ protected void appendJoinSubtree(StringBuilder out, JoinTreeNode node) {
+
+ DbRelationship relationship = node.getRelationship();
+
+ DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
+ String srcAlias = node.getSourceTableAlias();
+ String targetAlias = node.getTargetTableAlias();
+
+ switch (node.getJoinType()) {
+ case INNER:
+ out.append(" JOIN");
+ break;
+ case LEFT_OUTER:
+ out.append(" LEFT JOIN");
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported join type: " + node.getJoinType());
+ }
+
+ out.append(' ');
+ out.append(quotingStrategy.quotedFullyQualifiedName(targetEntity));
+
+ out.append(' ');
+ out.append(quotingStrategy.quotedIdentifier(targetEntity, targetAlias));
+ out.append(" ON (");
+
+ List<DbJoin> joins = relationship.getJoins();
+ int len = joins.size();
+ for (int i = 0; i < len; i++) {
+ DbJoin join = joins.get(i);
+ if (i > 0) {
+ out.append(" AND ");
+ }
+
+ out.append(quotingStrategy.quotedIdentifier(relationship.getSourceEntity(), srcAlias, join.getSourceName()));
+ out.append(" = ");
+ out.append(quotingStrategy.quotedIdentifier(targetEntity, targetAlias, join.getTargetName()));
+ }
+
+ /**
+ * Attaching root Db entity's qualifier
+ */
+ Expression dbQualifier = targetEntity.getQualifier();
+ if (dbQualifier != null) {
+ dbQualifier = dbQualifier.transform(new JoinedDbEntityQualifierTransformer(node));
+
+ if (len > 0) {
+ out.append(" AND ");
+ }
+ qualifierTranslator.setOut(out);
+ qualifierTranslator.doAppendPart(dbQualifier);
+ }
+
+ out.append(')');
+
+ for (JoinTreeNode child : node.getChildren()) {
+ appendJoinSubtree(out, child);
+ }
+ }
+
+ /**
+ * Append join information to the qualifier - the part after "WHERE".
+ */
+ protected void appendQualifier(StringBuilder out, boolean firstQualifierElement) {
+ // nothing as standard join is performed before "WHERE"
+ }
+
+ /**
+ * Pops the stack all the way to the root node.
+ */
+ void resetStack() {
+ topNode = rootNode;
+ }
+
+ /**
+ * Finds or creates a JoinTreeNode for the given arguments and sets it as
+ * the next current join.
+ */
+ void pushJoin(DbRelationship relationship, JoinType joinType, String alias) {
+ topNode = topNode.findOrCreateChild(relationship, joinType, alias);
+ }
+
+ protected String newAlias() {
+ return "t" + aliasCounter++;
+ }
+
+ /**
+ * Class to translate *joined* DB Entity qualifiers annotation to *current*
+ * Obj-entity qualifiers annotation This is done by changing all Obj-paths
+ * to concatenated Db-paths to root entity and rejecting all original
+ * Db-paths
+ */
+ class JoinedDbEntityQualifierTransformer implements Transformer {
+
+ StringBuilder pathToRoot;
+
+ JoinedDbEntityQualifierTransformer(JoinTreeNode node) {
+ pathToRoot = new StringBuilder();
+ while (node != null && node.getRelationship() != null) {
+ String relName = node.getRelationship().getName();
+
+ /**
+ * We must be in the same join as 'node', otherwise incorrect
+ * join statement like JOIN t1 ... ON (t0.id=t1.id AND
+ * t2.qualifier=0) could be generated
+ */
+ if (node.getJoinType() == JoinType.LEFT_OUTER) {
+ relName += Entity.OUTER_JOIN_INDICATOR;
+ }
+ relName += ObjEntity.PATH_SEPARATOR;
+
+ pathToRoot.insert(0, relName);
+ node = node.getParent();
+ }
+ }
+
+ public Object transform(Object input) {
+ if (input instanceof ASTObjPath) {
+ return new ASTDbPath(pathToRoot.toString() + ((SimpleNode) input).getOperand(0));
+ }
+ return input;
+ }
+ }
}
[5/7] cayenne git commit: Refactoring SelectTranslator for better
extensibility
Posted by aa...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingTranslator.java
index b875374..4a16678 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingTranslator.java
@@ -19,7 +19,6 @@
package org.apache.cayenne.access.translator.select;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -35,88 +34,85 @@ import org.apache.cayenne.query.SelectQuery;
*/
public class OrderingTranslator extends QueryAssemblerHelper {
- protected List<String> orderByColumnList = new ArrayList<String>();
-
- public OrderingTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler);
- }
-
- /**
- * Translates query Ordering list to SQL ORDER BY clause. Ordering list is obtained
- * from <code>queryAssembler</code>'s query object. In a process of building of
- * ORDER BY clause, <code>queryAssembler</code> is notified when a join needs to be
- * added.
- *
- * @since 3.0
- */
- @Override
- protected void doAppendPart() throws IOException {
-
- Query q = queryAssembler.getQuery();
-
- // only select queries can have ordering...
- if (q == null || !(q instanceof SelectQuery)) {
- return;
- }
-
- Iterator<Ordering> it = ((SelectQuery<?>) q).getOrderings().iterator();
-
- Appendable mainBuffer = this.out;
-
- try {
- while (it.hasNext()) {
- Ordering ord = it.next();
-
- // reset buffer to collect SQL for the single column, that we'll be reusing
- this.out = new StringBuilder();
-
- if (ord.isCaseInsensitive()) {
- out.append("UPPER(");
- }
-
- Expression exp = ord.getSortSpec();
-
- if (exp.getType() == Expression.OBJ_PATH) {
- appendObjPath(exp);
- }
- else if (exp.getType() == Expression.DB_PATH) {
- appendDbPath(exp);
- }
- else {
- throw new CayenneRuntimeException("Unsupported ordering expression: "
- + exp);
- }
-
- // Close UPPER() modifier
- if (ord.isCaseInsensitive()) {
- out.append(")");
- }
-
- String columnSQL = out.toString();
- mainBuffer.append(columnSQL);
- orderByColumnList.add(columnSQL);
-
- // "ASC" is a noop, omit it from the query
- if (!ord.isAscending()) {
- mainBuffer.append(" DESC");
- }
-
- if (it.hasNext()) {
- mainBuffer.append(", ");
- }
- }
- }
- finally {
- this.out = mainBuffer;
- }
- }
-
- /**
- * Returns the column expressions (not Expressions) used in the order by clause. E.g.,
- * in the case of an case-insensitive order by, an element of the list would be
- * <code>UPPER(<column reference>)</code>
- */
- public List<String> getOrderByColumnList() {
- return orderByColumnList;
- }
+ protected List<String> orderByColumnList = new ArrayList<String>();
+
+ public OrderingTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
+
+ /**
+ * Translates query Ordering list to SQL ORDER BY clause. Ordering list is
+ * obtained from <code>queryAssembler</code>'s query object. In a process of
+ * building of ORDER BY clause, <code>queryAssembler</code> is notified when
+ * a join needs to be added.
+ *
+ * @since 3.0
+ */
+ @Override
+ protected void doAppendPart() {
+
+ Query q = queryAssembler.getQuery();
+
+ // only select queries can have ordering...
+ if (q == null || !(q instanceof SelectQuery)) {
+ return;
+ }
+
+ Iterator<Ordering> it = ((SelectQuery<?>) q).getOrderings().iterator();
+
+ StringBuilder mainBuffer = this.out;
+
+ try {
+ while (it.hasNext()) {
+ Ordering ord = it.next();
+
+ // reset buffer to collect SQL for the single column, that we'll
+ // be reusing
+ this.out = new StringBuilder();
+
+ if (ord.isCaseInsensitive()) {
+ out.append("UPPER(");
+ }
+
+ Expression exp = ord.getSortSpec();
+
+ if (exp.getType() == Expression.OBJ_PATH) {
+ appendObjPath(exp);
+ } else if (exp.getType() == Expression.DB_PATH) {
+ appendDbPath(exp);
+ } else {
+ throw new CayenneRuntimeException("Unsupported ordering expression: " + exp);
+ }
+
+ // Close UPPER() modifier
+ if (ord.isCaseInsensitive()) {
+ out.append(")");
+ }
+
+ String columnSQL = out.toString();
+ mainBuffer.append(columnSQL);
+ orderByColumnList.add(columnSQL);
+
+ // "ASC" is a noop, omit it from the query
+ if (!ord.isAscending()) {
+ mainBuffer.append(" DESC");
+ }
+
+ if (it.hasNext()) {
+ mainBuffer.append(", ");
+ }
+ }
+ } finally {
+ this.out = mainBuffer;
+ }
+ }
+
+ /**
+ * Returns the column expressions (not Expressions) used in the order by
+ * clause. E.g., in the case of an case-insensitive order by, an element of
+ * the list would be <code>UPPER(<column reference>)</code>
+ */
+ public List<String> getOrderByColumnList() {
+ return orderByColumnList;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
index 4fa0fcc..c5fd13a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
@@ -43,381 +43,358 @@ import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.Transformer;
/**
- * Translates query qualifier to SQL. Used as a helper class by query translators.
+ * Translates query qualifier to SQL. Used as a helper class by query
+ * translators.
*/
public class QualifierTranslator extends QueryAssemblerHelper implements TraversalHandler {
- protected DataObjectMatchTranslator objectMatchTranslator;
- protected boolean matchingObject;
- protected boolean caseInsensitive;
-
- public QualifierTranslator(QueryAssembler queryAssembler) {
- super(queryAssembler);
-
- caseInsensitive = false;
- }
-
- /**
- * Translates query qualifier to SQL WHERE clause. Qualifier is obtained from the
- * parent queryAssembler.
- *
- * @since 3.0
- */
- @Override
- protected void doAppendPart() throws IOException {
- doAppendPart(extractQualifier());
- }
-
- public void setCaseInsensitive(boolean caseInsensitive) {
- this.caseInsensitive = caseInsensitive;
- }
-
- /**
- * Translates query qualifier to SQL WHERE clause. Qualifier is a method parameter.
- *
- * @since 3.0
- */
- protected void doAppendPart(Expression rootNode) throws IOException {
- if (rootNode == null) {
- return;
- }
- rootNode.traverse(this);
- }
-
- protected Expression extractQualifier() {
- Query q = queryAssembler.getQuery();
-
- Expression qualifier = ((SelectQuery<?>) q).getQualifier();
-
- // append Entity qualifiers, taking inheritance into account
- ObjEntity entity = getObjEntity();
-
- if (entity != null) {
-
- ClassDescriptor descriptor = queryAssembler
- .getEntityResolver()
- .getClassDescriptor(entity.getName());
- Expression entityQualifier = descriptor
- .getEntityInheritanceTree()
- .qualifierForEntityAndSubclasses();
- if (entityQualifier != null) {
- qualifier = (qualifier != null)
- ? qualifier.andExp(entityQualifier)
- : entityQualifier;
- }
- }
-
- /**
- * Attaching root Db entity's qualifier
- */
- if (getDbEntity() != null) {
- Expression dbQualifier = getDbEntity().getQualifier();
- if (dbQualifier != null) {
- dbQualifier = dbQualifier.transform(new DbEntityQualifierTransformer());
-
- qualifier = qualifier == null ? dbQualifier : qualifier
- .andExp(dbQualifier);
- }
- }
-
- return qualifier;
- }
-
- /**
- * Called before processing an expression to initialize objectMatchTranslator if
- * needed.
- */
- protected void detectObjectMatch(Expression exp) {
- // On demand initialization of
- // objectMatchTranslator is not possible since there may be null
- // object values that would not allow to detect the need for
- // such translator in the right time (e.g.: null = dbpath)
-
- matchingObject = false;
-
- if (exp.getOperandCount() != 2) {
- // only binary expressions are supported
- return;
- }
-
- // check if there are DataObjects among direct children of the Expression
- for (int i = 0; i < 2; i++) {
- Object op = exp.getOperand(i);
- if (op instanceof Persistent || op instanceof ObjectId) {
- matchingObject = true;
-
- if (objectMatchTranslator == null) {
- objectMatchTranslator = new DataObjectMatchTranslator();
- }
- else {
- objectMatchTranslator.reset();
- }
- break;
- }
- }
- }
-
- protected void appendObjectMatch() throws IOException {
- if (!matchingObject || objectMatchTranslator == null) {
- throw new IllegalStateException("An invalid attempt to append object match.");
- }
-
- // turn off special handling, so that all the methods behave as a superclass's
- // impl.
- matchingObject = false;
-
- boolean first = true;
-
- DbRelationship relationship = objectMatchTranslator.getRelationship();
- if (!relationship.isToMany() && !relationship.isToPK()) {
- queryAssembler.dbRelationshipAdded(
- relationship,
- JoinType.INNER,
- objectMatchTranslator.getJoinSplitAlias());
- }
-
- Iterator<String> it = objectMatchTranslator.keys();
- while (it.hasNext()) {
- if (first) {
- first = false;
- }
- else {
- out.append(" AND ");
- }
-
- String key = it.next();
- DbAttribute attr = objectMatchTranslator.getAttribute(key);
- Object val = objectMatchTranslator.getValue(key);
-
- processColumn(attr);
- out.append(objectMatchTranslator.getOperation());
- appendLiteral(val, attr, objectMatchTranslator.getExpression());
- }
-
- objectMatchTranslator.reset();
- }
-
- public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
-
- if (!hasMoreChildren) {
- return;
- }
-
- Appendable out = (matchingObject) ? new StringBuilder() : this.out;
-
- try {
- switch (node.getType()) {
- case Expression.AND:
- out.append(" AND ");
- break;
- case Expression.OR:
- out.append(" OR ");
- break;
- case Expression.EQUAL_TO:
- // translate NULL as IS NULL
- if (childIndex == 0
- && node.getOperandCount() == 2
- && node.getOperand(1) == null) {
- out.append(" IS ");
- }
- else {
- out.append(" = ");
- }
- break;
- case Expression.NOT_EQUAL_TO:
- // translate NULL as IS NOT NULL
- if (childIndex == 0
- && node.getOperandCount() == 2
- && node.getOperand(1) == null) {
- out.append(" IS NOT ");
- }
- else {
- out.append(" <> ");
- }
- break;
- case Expression.LESS_THAN:
- out.append(" < ");
- break;
- case Expression.GREATER_THAN:
- out.append(" > ");
- break;
- case Expression.LESS_THAN_EQUAL_TO:
- out.append(" <= ");
- break;
- case Expression.GREATER_THAN_EQUAL_TO:
- out.append(" >= ");
- break;
- case Expression.IN:
- out.append(" IN ");
- break;
- case Expression.NOT_IN:
- out.append(" NOT IN ");
- break;
- case Expression.LIKE:
- out.append(" LIKE ");
- break;
- case Expression.NOT_LIKE:
- out.append(" NOT LIKE ");
- break;
- case Expression.LIKE_IGNORE_CASE:
- if (caseInsensitive) {
- out.append(" LIKE ");
- }
- else {
- out.append(") LIKE UPPER(");
- }
- break;
- case Expression.NOT_LIKE_IGNORE_CASE:
- if (caseInsensitive) {
- out.append(" NOT LIKE ");
- }
- else {
- out.append(") NOT LIKE UPPER(");
- }
- break;
- case Expression.ADD:
- out.append(" + ");
- break;
- case Expression.SUBTRACT:
- out.append(" - ");
- break;
- case Expression.MULTIPLY:
- out.append(" * ");
- break;
- case Expression.DIVIDE:
- out.append(" / ");
- break;
- case Expression.BETWEEN:
- if (childIndex == 0)
- out.append(" BETWEEN ");
- else if (childIndex == 1)
- out.append(" AND ");
- break;
- case Expression.NOT_BETWEEN:
- if (childIndex == 0)
- out.append(" NOT BETWEEN ");
- else if (childIndex == 1)
- out.append(" AND ");
- break;
- case Expression.BITWISE_OR:
- out.append(" ").append(operandForBitwiseOr()).append(" ");
- break;
- case Expression.BITWISE_AND:
- out.append(" ").append(operandForBitwiseAnd()).append(" ");
- break;
- case Expression.BITWISE_XOR:
- out.append(" ").append(operandForBitwiseXor()).append(" ");
- break;
- case Expression.BITWISE_LEFT_SHIFT:
- out.append(" ").append(operandForBitwiseLeftShift()).append(" ");
- break;
- case Expression.BITWISE_RIGHT_SHIFT:
- out.append(" ").append(operandForBitwiseRightShift()).append("" );
- break;
- }
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
-
- if (matchingObject) {
- objectMatchTranslator.setOperation(out.toString());
- objectMatchTranslator.setExpression(node);
- }
- }
-
- /**
- * @since 3.1
- */
- protected String operandForBitwiseNot() {
- return "~";
- }
-
- /**
- * @since 3.1
- */
- protected String operandForBitwiseOr() {
- return "|";
- }
-
- /**
- * @since 3.1
- */
- protected String operandForBitwiseAnd() {
- return "&";
- }
-
- /**
- * @since 3.1
- */
- protected String operandForBitwiseXor() {
- return "^";
- }
-
- /**
- * @since 4.0
- */
- protected String operandForBitwiseLeftShift() {
- return "<<";
- }
-
- /**
- * @since 4.0
- */
- protected String operandForBitwiseRightShift() {
- return ">>";
- }
-
- public void startNode(Expression node, Expression parentNode) {
- int count = node.getOperandCount();
-
- if (count == 2) {
- // binary nodes are the only ones that currently require this
- detectObjectMatch(node);
- }
-
- try {
-
- if (parenthesisNeeded(node, parentNode)) {
- out.append('(');
- }
-
- if (count == 0) {
- // not all databases handle true/false
- if (node.getType() == Expression.TRUE) {
- out.append("1 = 1");
- }
- if (node.getType() == Expression.FALSE) {
- out.append("1 = 0");
- }
- }
-
- if (count == 1) {
- if (node.getType() == Expression.NEGATIVE)
- out.append('-');
- // ignore POSITIVE - it is a NOOP
- // else if(node.getType() == Expression.POSITIVE)
- // qualBuf.append('+');
- else if (node.getType() == Expression.NOT)
- out.append("NOT ");
- else if (node.getType() == Expression.BITWISE_NOT) {
- out.append(operandForBitwiseNot());
- }
- }
- else if ((node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE)
- && !caseInsensitive) {
- out.append("UPPER(");
- }
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
-
- /**
- * @since 1.1
- */
- public void endNode(Expression node, Expression parentNode) {
+ protected DataObjectMatchTranslator objectMatchTranslator;
+ protected boolean matchingObject;
+ protected boolean caseInsensitive;
+
+ public QualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+
+ caseInsensitive = false;
+ }
+
+ /**
+ * Translates query qualifier to SQL WHERE clause. Qualifier is obtained
+ * from the parent queryAssembler.
+ *
+ * @since 3.0
+ */
+ @Override
+ protected void doAppendPart() {
+ doAppendPart(extractQualifier());
+ }
+
+ public void setCaseInsensitive(boolean caseInsensitive) {
+ this.caseInsensitive = caseInsensitive;
+ }
+
+ /**
+ * Translates query qualifier to SQL WHERE clause. Qualifier is a method
+ * parameter.
+ *
+ * @since 3.0
+ */
+ protected void doAppendPart(Expression rootNode) {
+ if (rootNode == null) {
+ return;
+ }
+ rootNode.traverse(this);
+ }
+
+ protected Expression extractQualifier() {
+ Query q = queryAssembler.getQuery();
+
+ Expression qualifier = ((SelectQuery<?>) q).getQualifier();
+
+ // append Entity qualifiers, taking inheritance into account
+ ObjEntity entity = getObjEntity();
+
+ if (entity != null) {
+
+ ClassDescriptor descriptor = queryAssembler.getEntityResolver().getClassDescriptor(entity.getName());
+ Expression entityQualifier = descriptor.getEntityInheritanceTree().qualifierForEntityAndSubclasses();
+ if (entityQualifier != null) {
+ qualifier = (qualifier != null) ? qualifier.andExp(entityQualifier) : entityQualifier;
+ }
+ }
+
+ /**
+ * Attaching root Db entity's qualifier
+ */
+ if (getDbEntity() != null) {
+ Expression dbQualifier = getDbEntity().getQualifier();
+ if (dbQualifier != null) {
+ dbQualifier = dbQualifier.transform(new DbEntityQualifierTransformer());
+
+ qualifier = qualifier == null ? dbQualifier : qualifier.andExp(dbQualifier);
+ }
+ }
+
+ return qualifier;
+ }
+
+ /**
+ * Called before processing an expression to initialize
+ * objectMatchTranslator if needed.
+ */
+ protected void detectObjectMatch(Expression exp) {
+ // On demand initialization of
+ // objectMatchTranslator is not possible since there may be null
+ // object values that would not allow to detect the need for
+ // such translator in the right time (e.g.: null = dbpath)
+
+ matchingObject = false;
+
+ if (exp.getOperandCount() != 2) {
+ // only binary expressions are supported
+ return;
+ }
+
+ // check if there are DataObjects among direct children of the
+ // Expression
+ for (int i = 0; i < 2; i++) {
+ Object op = exp.getOperand(i);
+ if (op instanceof Persistent || op instanceof ObjectId) {
+ matchingObject = true;
+
+ if (objectMatchTranslator == null) {
+ objectMatchTranslator = new DataObjectMatchTranslator();
+ } else {
+ objectMatchTranslator.reset();
+ }
+ break;
+ }
+ }
+ }
+
+ protected void appendObjectMatch() throws IOException {
+ if (!matchingObject || objectMatchTranslator == null) {
+ throw new IllegalStateException("An invalid attempt to append object match.");
+ }
+
+ // turn off special handling, so that all the methods behave as a
+ // superclass's
+ // impl.
+ matchingObject = false;
+
+ boolean first = true;
+
+ DbRelationship relationship = objectMatchTranslator.getRelationship();
+ if (!relationship.isToMany() && !relationship.isToPK()) {
+ queryAssembler.dbRelationshipAdded(relationship, JoinType.INNER, objectMatchTranslator.getJoinSplitAlias());
+ }
+
+ Iterator<String> it = objectMatchTranslator.keys();
+ while (it.hasNext()) {
+ if (first) {
+ first = false;
+ } else {
+ out.append(" AND ");
+ }
+
+ String key = it.next();
+ DbAttribute attr = objectMatchTranslator.getAttribute(key);
+ Object val = objectMatchTranslator.getValue(key);
+
+ processColumn(attr);
+ out.append(objectMatchTranslator.getOperation());
+ appendLiteral(val, attr, objectMatchTranslator.getExpression());
+ }
+
+ objectMatchTranslator.reset();
+ }
+
+ public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
+
+ if (!hasMoreChildren) {
+ return;
+ }
+
+ Appendable out = (matchingObject) ? new StringBuilder() : this.out;
+
+ try {
+ switch (node.getType()) {
+ case Expression.AND:
+ out.append(" AND ");
+ break;
+ case Expression.OR:
+ out.append(" OR ");
+ break;
+ case Expression.EQUAL_TO:
+ // translate NULL as IS NULL
+ if (childIndex == 0 && node.getOperandCount() == 2 && node.getOperand(1) == null) {
+ out.append(" IS ");
+ } else {
+ out.append(" = ");
+ }
+ break;
+ case Expression.NOT_EQUAL_TO:
+ // translate NULL as IS NOT NULL
+ if (childIndex == 0 && node.getOperandCount() == 2 && node.getOperand(1) == null) {
+ out.append(" IS NOT ");
+ } else {
+ out.append(" <> ");
+ }
+ break;
+ case Expression.LESS_THAN:
+ out.append(" < ");
+ break;
+ case Expression.GREATER_THAN:
+ out.append(" > ");
+ break;
+ case Expression.LESS_THAN_EQUAL_TO:
+ out.append(" <= ");
+ break;
+ case Expression.GREATER_THAN_EQUAL_TO:
+ out.append(" >= ");
+ break;
+ case Expression.IN:
+ out.append(" IN ");
+ break;
+ case Expression.NOT_IN:
+ out.append(" NOT IN ");
+ break;
+ case Expression.LIKE:
+ out.append(" LIKE ");
+ break;
+ case Expression.NOT_LIKE:
+ out.append(" NOT LIKE ");
+ break;
+ case Expression.LIKE_IGNORE_CASE:
+ if (caseInsensitive) {
+ out.append(" LIKE ");
+ } else {
+ out.append(") LIKE UPPER(");
+ }
+ break;
+ case Expression.NOT_LIKE_IGNORE_CASE:
+ if (caseInsensitive) {
+ out.append(" NOT LIKE ");
+ } else {
+ out.append(") NOT LIKE UPPER(");
+ }
+ break;
+ case Expression.ADD:
+ out.append(" + ");
+ break;
+ case Expression.SUBTRACT:
+ out.append(" - ");
+ break;
+ case Expression.MULTIPLY:
+ out.append(" * ");
+ break;
+ case Expression.DIVIDE:
+ out.append(" / ");
+ break;
+ case Expression.BETWEEN:
+ if (childIndex == 0)
+ out.append(" BETWEEN ");
+ else if (childIndex == 1)
+ out.append(" AND ");
+ break;
+ case Expression.NOT_BETWEEN:
+ if (childIndex == 0)
+ out.append(" NOT BETWEEN ");
+ else if (childIndex == 1)
+ out.append(" AND ");
+ break;
+ case Expression.BITWISE_OR:
+ out.append(" ").append(operandForBitwiseOr()).append(" ");
+ break;
+ case Expression.BITWISE_AND:
+ out.append(" ").append(operandForBitwiseAnd()).append(" ");
+ break;
+ case Expression.BITWISE_XOR:
+ out.append(" ").append(operandForBitwiseXor()).append(" ");
+ break;
+ case Expression.BITWISE_LEFT_SHIFT:
+ out.append(" ").append(operandForBitwiseLeftShift()).append(" ");
+ break;
+ case Expression.BITWISE_RIGHT_SHIFT:
+ out.append(" ").append(operandForBitwiseRightShift()).append("");
+ break;
+ }
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+
+ if (matchingObject) {
+ objectMatchTranslator.setOperation(out.toString());
+ objectMatchTranslator.setExpression(node);
+ }
+ }
+
+ /**
+ * @since 3.1
+ */
+ protected String operandForBitwiseNot() {
+ return "~";
+ }
+
+ /**
+ * @since 3.1
+ */
+ protected String operandForBitwiseOr() {
+ return "|";
+ }
+
+ /**
+ * @since 3.1
+ */
+ protected String operandForBitwiseAnd() {
+ return "&";
+ }
+
+ /**
+ * @since 3.1
+ */
+ protected String operandForBitwiseXor() {
+ return "^";
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected String operandForBitwiseLeftShift() {
+ return "<<";
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected String operandForBitwiseRightShift() {
+ return ">>";
+ }
+
+ public void startNode(Expression node, Expression parentNode) {
+ int count = node.getOperandCount();
+
+ if (count == 2) {
+ // binary nodes are the only ones that currently require this
+ detectObjectMatch(node);
+ }
+
+ if (parenthesisNeeded(node, parentNode)) {
+ out.append('(');
+ }
+
+ if (count == 0) {
+ // not all databases handle true/false
+ if (node.getType() == Expression.TRUE) {
+ out.append("1 = 1");
+ }
+ if (node.getType() == Expression.FALSE) {
+ out.append("1 = 0");
+ }
+ }
+
+ if (count == 1) {
+ if (node.getType() == Expression.NEGATIVE)
+ out.append('-');
+ // ignore POSITIVE - it is a NOOP
+ // else if(node.getType() == Expression.POSITIVE)
+ // qualBuf.append('+');
+ else if (node.getType() == Expression.NOT)
+ out.append("NOT ");
+ else if (node.getType() == Expression.BITWISE_NOT) {
+ out.append(operandForBitwiseNot());
+ }
+ } else if ((node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE)
+ && !caseInsensitive) {
+ out.append("UPPER(");
+ }
+
+ }
+
+ /**
+ * @since 1.1
+ */
+ public void endNode(Expression node, Expression parentNode) {
try {
// check if we need to use objectMatchTranslator to finish building
@@ -444,128 +421,110 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
if (parenthesisNeeded) {
out.append(')');
}
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+ }
+
+ public void objectNode(Object leaf, Expression parentNode) {
+
+ try {
+ if (parentNode.getType() == Expression.OBJ_PATH) {
+ appendObjPath(parentNode);
+ } else if (parentNode.getType() == Expression.DB_PATH) {
+ appendDbPath(parentNode);
+ } else if (parentNode.getType() == Expression.LIST) {
+ appendList(parentNode, paramsDbType(parentNode));
+ } else {
+ appendLiteral(leaf, paramsDbType(parentNode), parentNode);
+ }
+ } catch (IOException ioex) {
+ throw new CayenneRuntimeException("Error appending content", ioex);
+ }
+ }
+
+ protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
+ if (parentNode == null)
+ return false;
+
+ // only unary expressions can go w/o parenthesis
+ if (node.getOperandCount() > 1)
+ return true;
+
+ if (node.getType() == Expression.OBJ_PATH)
+ return false;
+
+ if (node.getType() == Expression.DB_PATH)
+ return false;
+
+ return true;
+ }
+
+ private final void appendList(Expression listExpr, DbAttribute paramDesc) throws IOException {
+ Iterator<?> it = null;
+ Object list = listExpr.getOperand(0);
+ if (list instanceof List) {
+ it = ((List<?>) list).iterator();
+ } else if (list instanceof Object[]) {
+ it = IteratorUtils.arrayIterator((Object[]) list);
+ } else {
+ String className = (list != null) ? list.getClass().getName() : "<null>";
+ throw new IllegalArgumentException("Unsupported type for the list expressions: " + className);
+ }
+
+ // process first element outside the loop
+ // (unroll loop to avoid condition checking
+ if (it.hasNext())
+ appendLiteral(it.next(), paramDesc, listExpr);
+ else
+ return;
+
+ while (it.hasNext()) {
+ out.append(", ");
+ appendLiteral(it.next(), paramDesc, listExpr);
+ }
+ }
+
+ @Override
+ protected void appendLiteral(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
+
+ if (!matchingObject) {
+ super.appendLiteral(val, attr, parentExpression);
+ } else if (val == null || (val instanceof Persistent)) {
+ objectMatchTranslator.setDataObject((Persistent) val);
+ } else if (val instanceof ObjectId) {
+ objectMatchTranslator.setObjectId((ObjectId) val);
+ } else {
+ throw new IllegalArgumentException("Attempt to use literal other than DataObject during object match.");
+ }
+ }
+
+ @Override
+ protected void processRelTermination(DbRelationship rel, JoinType joinType, String joinSplitAlias) {
+
+ if (!matchingObject) {
+ super.processRelTermination(rel, joinType, joinSplitAlias);
+ } else {
+ if (rel.isToMany()) {
+ // append joins
+ queryAssembler.dbRelationshipAdded(rel, joinType, joinSplitAlias);
+ }
+ objectMatchTranslator.setRelationship(rel, joinSplitAlias);
+ }
+ }
+
+ /**
+ * Class to translate DB Entity qualifiers annotation to Obj-entity
+ * qualifiers annotation This is done by changing all Obj-paths to Db-paths
+ * and rejecting all original Db-paths
+ */
+ class DbEntityQualifierTransformer implements Transformer {
+
+ public Object transform(Object input) {
+ if (input instanceof ASTObjPath) {
+ return new ASTDbPath(((SimpleNode) input).getOperand(0));
+ }
+ return input;
}
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
-
- public void objectNode(Object leaf, Expression parentNode) {
-
- try {
- if (parentNode.getType() == Expression.OBJ_PATH) {
- appendObjPath(parentNode);
- }
- else if (parentNode.getType() == Expression.DB_PATH) {
- appendDbPath(parentNode);
- }
- else if (parentNode.getType() == Expression.LIST) {
- appendList(parentNode, paramsDbType(parentNode));
- }
- else {
- appendLiteral(leaf, paramsDbType(parentNode), parentNode);
- }
- }
- catch (IOException ioex) {
- throw new CayenneRuntimeException("Error appending content", ioex);
- }
- }
-
- protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
- if (parentNode == null)
- return false;
-
- // only unary expressions can go w/o parenthesis
- if (node.getOperandCount() > 1)
- return true;
-
- if (node.getType() == Expression.OBJ_PATH)
- return false;
-
- if (node.getType() == Expression.DB_PATH)
- return false;
-
- return true;
- }
-
- private final void appendList(Expression listExpr, DbAttribute paramDesc)
- throws IOException {
- Iterator<?> it = null;
- Object list = listExpr.getOperand(0);
- if (list instanceof List) {
- it = ((List<?>) list).iterator();
- }
- else if (list instanceof Object[]) {
- it = IteratorUtils.arrayIterator((Object[]) list);
- }
- else {
- String className = (list != null) ? list.getClass().getName() : "<null>";
- throw new IllegalArgumentException(
- "Unsupported type for the list expressions: " + className);
- }
-
- // process first element outside the loop
- // (unroll loop to avoid condition checking
- if (it.hasNext())
- appendLiteral(it.next(), paramDesc, listExpr);
- else
- return;
-
- while (it.hasNext()) {
- out.append(", ");
- appendLiteral(it.next(), paramDesc, listExpr);
- }
- }
-
- @Override
- protected void appendLiteral(Object val, DbAttribute attr, Expression parentExpression)
- throws IOException {
-
- if (!matchingObject) {
- super.appendLiteral(val, attr, parentExpression);
- }
- else if (val == null || (val instanceof Persistent)) {
- objectMatchTranslator.setDataObject((Persistent) val);
- }
- else if (val instanceof ObjectId) {
- objectMatchTranslator.setObjectId((ObjectId) val);
- }
- else {
- throw new IllegalArgumentException(
- "Attempt to use literal other than DataObject during object match.");
- }
- }
-
- @Override
- protected void processRelTermination(
- DbRelationship rel,
- JoinType joinType,
- String joinSplitAlias) throws IOException {
-
- if (!matchingObject) {
- super.processRelTermination(rel, joinType, joinSplitAlias);
- }
- else {
- if (rel.isToMany()) {
- // append joins
- queryAssembler.dbRelationshipAdded(rel, joinType, joinSplitAlias);
- }
- objectMatchTranslator.setRelationship(rel, joinSplitAlias);
- }
- }
-
- /**
- * Class to translate DB Entity qualifiers annotation to Obj-entity qualifiers
- * annotation This is done by changing all Obj-paths to Db-paths and rejecting all
- * original Db-paths
- */
- class DbEntityQualifierTransformer implements Transformer {
-
- public Object transform(Object input) {
- if (input instanceof ASTObjPath) {
- return new ASTDbPath(((SimpleNode) input).getOperand(0));
- }
- return input;
- }
- }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
index d83e6b2..af1ade2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
@@ -19,21 +19,16 @@
package org.apache.cayenne.access.translator.select;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import org.apache.cayenne.access.DataNode;
+import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.log.JdbcEventLogger;
import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.JoinType;
-import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.query.Query;
import org.apache.cayenne.query.QueryMetadata;
@@ -42,176 +37,127 @@ import org.apache.cayenne.query.QueryMetadata;
*/
public abstract class QueryAssembler {
- protected Query query;
- protected QueryMetadata queryMetadata;
- protected String cachedSqlString;
- protected Connection connection;
- protected DbAdapter adapter;
- protected EntityResolver entityResolver;
- protected JdbcEventLogger logger;
-
- /**
- * Holds PreparedStatement values.
- */
- protected List<Object> values = new ArrayList<Object>();
-
- /**
- * PreparedStatement attributes matching entries in <code>values</code>
- * list.
- */
- protected List<DbAttribute> attributes = new ArrayList<DbAttribute>();
-
- /**
- * The index parameter will be inserted at in parameter list
- */
- protected int parameterIndex;
-
- /**
- * @since 4.0
- */
- public QueryAssembler(Query query, DataNode dataNode, Connection connection) {
- this.logger = dataNode.getJdbcEventLogger();
- this.entityResolver = dataNode.getEntityResolver();
- this.adapter = dataNode.getAdapter();
- this.query = query;
- this.connection = connection;
- this.queryMetadata = query.getMetaData(entityResolver);
- }
-
- /**
- * Returns aliases for the path splits defined in the query.
- *
- * @since 3.0
- */
- protected Map<String, String> getPathAliases() {
- return query.getMetaData(entityResolver).getPathSplitAliases();
- }
-
- public EntityResolver getEntityResolver() {
- return entityResolver;
- }
-
- public DbAdapter getAdapter() {
- return adapter;
- }
-
- /**
- * Returns query object being processed.
- */
- public Query getQuery() {
- return query;
- }
-
- public QueryMetadata getQueryMetadata() {
- return queryMetadata;
- }
-
- /**
- * @since 3.1
- */
- public JdbcEventLogger getJdbcEventLogger() {
- return logger;
- }
-
- public DbEntity getRootDbEntity() {
- return queryMetadata.getDbEntity();
- }
-
- public ObjEntity getRootEntity() {
- return queryMetadata.getObjEntity();
- }
-
- /**
- * A callback invoked by a child qualifier or ordering processor allowing
- * query assembler to reset its join stack.
- *
- * @since 3.0
- */
- public abstract void resetJoinStack();
-
- /**
- * Returns an alias of the table which is currently at the top of the join
- * stack.
- *
- * @since 3.0
- */
- public abstract String getCurrentAlias();
-
- /**
- * Appends a join with given semantics to the query.
- *
- * @since 3.0
- */
- public abstract void dbRelationshipAdded(DbRelationship relationship, JoinType joinType, String joinSplitAlias);
-
- /**
- * Translates query into sql string. This is a workhorse method of
- * QueryAssembler. It is called internally from <code>createStatement</code>
- * . Usually there is no need to invoke it explicitly.
- */
- public abstract String createSqlString() throws Exception;
-
- /**
- * Returns <code>true</code> if table aliases are supported. Default
- * implementation returns false.
- */
- public boolean supportsTableAliases() {
- return false;
- }
-
- /**
- * Registers <code>anObject</code> as a PreparedStatement parameter.
- *
- * @param anObject
- * object that represents a value of DbAttribute
- * @param dbAttr
- * DbAttribute being processed.
- */
- public void addToParamList(DbAttribute dbAttr, Object anObject) {
- attributes.add(parameterIndex, dbAttr);
- values.add(parameterIndex++, anObject);
- }
-
- /**
- * Translates internal query into PreparedStatement.
- */
- public PreparedStatement createStatement() throws Exception {
- long t1 = System.currentTimeMillis();
- String sqlStr = createSqlString();
- logger.logQuery(sqlStr, attributes, values, System.currentTimeMillis() - t1);
- PreparedStatement stmt = connection.prepareStatement(sqlStr);
- initStatement(stmt);
- return stmt;
- }
-
- /**
- * Initializes prepared statements with collected parameters. Called
- * internally from "createStatement". Cayenne users shouldn't normally call
- * it directly.
- */
- protected void initStatement(PreparedStatement stmt) throws Exception {
- if (values != null && values.size() > 0) {
- int len = values.size();
- for (int i = 0; i < len; i++) {
- Object val = values.get(i);
-
- DbAttribute attr = attributes.get(i);
-
- // null DbAttributes are a result of inferior qualifier
- // processing
- // (qualifier can't map parameters to DbAttributes and therefore
- // only supports standard java types now)
- // hence, a special moronic case here:
- if (attr == null) {
- stmt.setObject(i + 1, val);
- } else {
- adapter.bindParameter(stmt, val, i + 1, attr.getType(), attr.getScale());
- }
- }
- }
-
- if (queryMetadata.getStatementFetchSize() != 0) {
- stmt.setFetchSize(queryMetadata.getStatementFetchSize());
- }
- }
+ protected Query query;
+ protected QueryMetadata queryMetadata;
+ protected boolean translated;
+ protected String sql;
+ protected DbAdapter adapter;
+ protected EntityResolver entityResolver;
+ protected List<ParameterBinding> bindings;
+
+ /**
+ * @since 4.0
+ */
+ public QueryAssembler(Query query, DbAdapter adapter, EntityResolver entityResolver) {
+ this.entityResolver = entityResolver;
+ this.adapter = adapter;
+ this.query = query;
+ this.queryMetadata = query.getMetaData(entityResolver);
+ this.bindings = new ArrayList<ParameterBinding>();
+ }
+
+ /**
+ * Returns aliases for the path splits defined in the query.
+ *
+ * @since 3.0
+ */
+ protected Map<String, String> getPathAliases() {
+ return queryMetadata.getPathSplitAliases();
+ }
+
+ public EntityResolver getEntityResolver() {
+ return entityResolver;
+ }
+
+ public DbAdapter getAdapter() {
+ return adapter;
+ }
+
+ /**
+ * Returns query object being processed.
+ */
+ public Query getQuery() {
+ return query;
+ }
+
+ public QueryMetadata getQueryMetadata() {
+ return queryMetadata;
+ }
+
+ /**
+ * A callback invoked by a child qualifier or ordering processor allowing
+ * query assembler to reset its join stack.
+ *
+ * @since 3.0
+ */
+ public abstract void resetJoinStack();
+
+ /**
+ * Returns an alias of the table which is currently at the top of the join
+ * stack.
+ *
+ * @since 3.0
+ */
+ public abstract String getCurrentAlias();
+
+ /**
+ * Appends a join with given semantics to the query.
+ *
+ * @since 3.0
+ */
+ public abstract void dbRelationshipAdded(DbRelationship relationship, JoinType joinType, String joinSplitAlias);
+
+ /**
+ * Translates query into an SQL string formatted to use in a
+ * PreparedStatement.
+ */
+ public String getSql() {
+ ensureTranslated();
+ return sql;
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void ensureTranslated() {
+ if (!translated) {
+ doTranslate();
+ translated = true;
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected abstract void doTranslate();
+
+ /**
+ * Returns <code>true</code> if table aliases are supported. Default
+ * implementation returns false.
+ */
+ public boolean supportsTableAliases() {
+ return false;
+ }
+
+ /**
+ * Registers <code>anObject</code> as a PreparedStatement parameter.
+ *
+ * @param anObject
+ * object that represents a value of DbAttribute
+ * @param dbAttr
+ * DbAttribute being processed.
+ */
+ public void addToParamList(DbAttribute dbAttr, Object anObject) {
+ ParameterBinding binding = new ParameterBinding(dbAttr);
+ binding.setValue(anObject);
+ binding.setStatementPosition(bindings.size() + 1);
+ bindings.add(binding);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ParameterBinding[] getBindings() {
+ return bindings.toArray(new ParameterBinding[bindings.size()]);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssemblerHelper.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssemblerHelper.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssemblerHelper.java
index 3152518..e56bc2c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssemblerHelper.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssemblerHelper.java
@@ -49,449 +49,444 @@ import org.apache.cayenne.util.CayenneMapEntry;
*/
public abstract class QueryAssemblerHelper {
- protected QueryAssembler queryAssembler;
- protected Appendable out;
- protected QuotingStrategy strategy;
-
- /**
- * Creates QueryAssemblerHelper initializing with parent
- * {@link QueryAssembler} and output buffer object.
- */
- public QueryAssemblerHelper(QueryAssembler queryAssembler) {
- this.queryAssembler = queryAssembler;
- strategy = queryAssembler.getAdapter().getQuotingStrategy();
- }
-
- public ObjEntity getObjEntity() {
- return queryAssembler.getRootEntity();
- }
-
- public DbEntity getDbEntity() {
- return queryAssembler.getRootDbEntity();
- }
-
- /**
- * @since 3.0
- */
- public <T extends Appendable> T appendPart(T out) throws IOException {
- this.out = out;
- doAppendPart();
- return out;
- }
-
- /**
- * Sets ouput buffer
- */
- void setOut(Appendable out) {
- this.out = out;
- }
-
- /**
- * @return output buffer
- */
- Appendable getOut() {
- return out;
- }
-
- /**
- * @since 3.0
- */
- protected abstract void doAppendPart() throws IOException;
-
- /**
- * <p>Outputs the standard JDBC (database agnostic) expression for supplying
- * the escape character to the database server when supplying a LIKE clause.
- * This has been factored-out because some database adaptors handle LIKE
- * differently and they need access to this common method in order not to
- * repeat this code.
- * <p>
- * If there is no escape character defined then this method will not output
- * anything. An escape character of 0 will mean no escape character.
- *
- * @since 3.1
- */
- protected void appendLikeEscapeCharacter(PatternMatchNode patternMatchNode) throws IOException {
- char escapeChar = patternMatchNode.getEscapeChar();
-
- if ('?' == escapeChar) {
- throw new CayenneRuntimeException("the escape character of '?' is illegal for LIKE clauses.");
- }
-
- if (0 != escapeChar) {
- out.append(" {escape '");
- out.append(escapeChar);
- out.append("'}");
- }
- }
-
- /**
- * Processes parts of the OBJ_PATH expression.
- */
- protected void appendObjPath(Expression pathExp) throws IOException {
-
- queryAssembler.resetJoinStack();
- String joinSplitAlias = null;
-
- for (PathComponent<ObjAttribute, ObjRelationship> component : getObjEntity().resolvePath(pathExp,
- queryAssembler.getPathAliases())) {
-
- if (component.isAlias()) {
- joinSplitAlias = component.getName();
- for (PathComponent<ObjAttribute, ObjRelationship> aliasPart : component.getAliasedPath()) {
-
- ObjRelationship relationship = aliasPart.getRelationship();
-
- if (relationship == null) {
- throw new IllegalStateException("Non-relationship aliased path part: " + aliasPart.getName());
- }
-
- if (aliasPart.isLast() && component.isLast()) {
- processRelTermination(relationship, aliasPart.getJoinType(), joinSplitAlias);
- } else {
- // find and add joins ....
- for (DbRelationship dbRel : relationship.getDbRelationships()) {
- queryAssembler.dbRelationshipAdded(dbRel, aliasPart.getJoinType(), joinSplitAlias);
- }
- }
- }
-
- continue;
- }
-
- ObjRelationship relationship = component.getRelationship();
- ObjAttribute attribute = component.getAttribute();
-
- if (relationship != null) {
-
- // if this is a last relationship in the path,
- // it needs special handling
- if (component.isLast()) {
- processRelTermination(relationship, component.getJoinType(), joinSplitAlias);
- } else {
- // find and add joins ....
- for (DbRelationship dbRel : relationship.getDbRelationships()) {
- queryAssembler.dbRelationshipAdded(dbRel, component.getJoinType(), joinSplitAlias);
- }
- }
- } else {
- Iterator<CayenneMapEntry> dbPathIterator = attribute.getDbPathIterator();
- while (dbPathIterator.hasNext()) {
- Object pathPart = dbPathIterator.next();
-
- if (pathPart == null) {
- throw new CayenneRuntimeException("ObjAttribute has no component: " + attribute.getName());
- } else if (pathPart instanceof DbRelationship) {
- queryAssembler.dbRelationshipAdded((DbRelationship) pathPart, JoinType.INNER, joinSplitAlias);
- } else if (pathPart instanceof DbAttribute) {
- processColumnWithQuoteSqlIdentifiers((DbAttribute) pathPart, pathExp);
- }
- }
-
- }
- }
- }
-
- protected void appendDbPath(Expression pathExp) throws IOException {
-
- queryAssembler.resetJoinStack();
- String joinSplitAlias = null;
-
- for (PathComponent<DbAttribute, DbRelationship> component : getDbEntity().resolvePath(pathExp,
- queryAssembler.getPathAliases())) {
-
- if (component.isAlias()) {
- joinSplitAlias = component.getName();
- for (PathComponent<DbAttribute, DbRelationship> aliasPart : component.getAliasedPath()) {
-
- DbRelationship relationship = aliasPart.getRelationship();
-
- if (relationship == null) {
- throw new IllegalStateException("Non-relationship aliased path part: " + aliasPart.getName());
- }
-
- if (aliasPart.isLast() && component.isLast()) {
- processRelTermination(relationship, aliasPart.getJoinType(), joinSplitAlias);
- } else {
- queryAssembler.dbRelationshipAdded(relationship, component.getJoinType(), joinSplitAlias);
- }
- }
-
- continue;
- }
-
- DbRelationship relationship = component.getRelationship();
-
- if (relationship != null) {
-
- // if this is a last relationship in the path,
- // it needs special handling
- if (component.isLast()) {
- processRelTermination(relationship, component.getJoinType(), joinSplitAlias);
- } else {
- // find and add joins ....
- queryAssembler.dbRelationshipAdded(relationship, component.getJoinType(), joinSplitAlias);
- }
- } else {
- processColumnWithQuoteSqlIdentifiers(component.getAttribute(), pathExp);
- }
- }
- }
-
- protected void processColumn(DbAttribute dbAttr) throws IOException {
- processColumnWithQuoteSqlIdentifiers(dbAttr, null);
- }
-
- protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) throws IOException {
-
- String alias = (queryAssembler.supportsTableAliases()) ? queryAssembler.getCurrentAlias() : null;
- out.append(strategy.quotedIdentifier(dbAttr.getEntity(), alias, dbAttr.getName()));
- }
-
- /**
- * Appends SQL code to the query buffer to handle <code>val</code> as a
- * parameter to the PreparedStatement being built. Adds <code>val</code>
- * into QueryAssembler parameter list.
- * <p>
- * If <code>val</code> is null, "NULL" is appended to the query.
- * </p>
- * <p>
- * If <code>val</code> is a DataObject, its primary key value is used as a
- * parameter. <i>Only objects with a single column primary key can be
- * used.</i>
- *
- * @param val
- * object that should be appended as a literal to the query. Must
- * be of one of "standard JDBC" types, null or a DataObject.
- * @param attr
- * DbAttribute that has information on what type of parameter is
- * being appended.
- */
- protected void appendLiteral(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
-
- if (val == null) {
- out.append("NULL");
- } else if (val instanceof Persistent) {
- // TODO: see cay1796
- // This check is unlikely to happen,
- // since Expression got ObjectId from Persistent object.
- // Left for future research.
- ObjectId id = ((Persistent) val).getObjectId();
-
- // check if this id is acceptable to be a parameter
- if (id == null) {
- throw new CayenneRuntimeException("Can't use TRANSIENT object as a query parameter.");
- }
-
- if (id.isTemporary()) {
- throw new CayenneRuntimeException("Can't use NEW object as a query parameter.");
- }
-
- Map<String, Object> snap = id.getIdSnapshot();
- if (snap.size() != 1) {
- StringBuilder msg = new StringBuilder();
- msg.append("Object must have a single primary key column ").append("to serve as a query parameter. ")
- .append("This object has ").append(snap.size()).append(": ").append(snap);
-
- throw new CayenneRuntimeException(msg.toString());
- }
-
- // checks have been passed, use id value
- appendLiteralDirect(snap.get(snap.keySet().iterator().next()), attr, parentExpression);
- } else if(val instanceof ObjectId){
-
- ObjectId id = (ObjectId)val;
-
- if (id.isTemporary()) {
- throw new CayenneRuntimeException("Can't use NEW object as a query parameter.");
- }
-
- Map<String, Object> snap = id.getIdSnapshot();
- if (snap.size() != 1) {
- StringBuilder msg = new StringBuilder();
- msg.append("Object must have a single primary key column ").append("to serve as a query parameter. ")
- .append("This object has ").append(snap.size()).append(": ").append(snap);
-
- throw new CayenneRuntimeException(msg.toString());
- }
-
- // checks have been passed, use id value
- appendLiteralDirect(snap.get(snap.keySet().iterator().next()), attr, parentExpression);
- } else {
- appendLiteralDirect(val, attr, parentExpression);
- }
- }
-
- /**
- * Appends SQL code to the query buffer to handle <code>val</code> as a
- * parameter to the PreparedStatement being built. Adds <code>val</code>
- * into QueryAssembler parameter list.
- */
- protected void appendLiteralDirect(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
- out.append('?');
-
- // we are hoping that when processing parameter list,
- // the correct type will be
- // guessed without looking at DbAttribute...
- queryAssembler.addToParamList(attr, val);
- }
-
- /**
- * Returns database type of expression parameters or null if it can not be
- * determined.
- */
- protected DbAttribute paramsDbType(Expression e) {
- int len = e.getOperandCount();
-
- // for unary expressions, find parent binary - this is a hack mainly to
- // support
- // ASTList
- if (len < 2) {
-
- if (e instanceof SimpleNode) {
- Expression parent = (Expression) ((SimpleNode) e).jjtGetParent();
- if (parent != null) {
- return paramsDbType(parent);
- }
- }
-
- return null;
- }
-
- // naive algorithm:
-
- // if at least one of the sibling operands is a
- // OBJ_PATH or DB_PATH expression, use its attribute type as
- // a final answer.
-
- // find attribute or relationship matching the value
- DbAttribute attribute = null;
- DbRelationship relationship = null;
- for (int i = 0; i < len; i++) {
- Object op = e.getOperand(i);
-
- if (op instanceof Expression) {
- Expression expression = (Expression) op;
- if (expression.getType() == Expression.OBJ_PATH) {
- PathComponent<ObjAttribute, ObjRelationship> last = getObjEntity().lastPathComponent(expression,
- queryAssembler.getPathAliases());
-
- // TODO: handle EmbeddableAttribute
- // if (last instanceof EmbeddableAttribute)
- // break;
-
- if (last.getAttribute() != null) {
- attribute = last.getAttribute().getDbAttribute();
- break;
- } else if (last.getRelationship() != null) {
- List<DbRelationship> dbPath = last.getRelationship().getDbRelationships();
- if (dbPath.size() > 0) {
- relationship = dbPath.get(dbPath.size() - 1);
- break;
- }
- }
- } else if (expression.getType() == Expression.DB_PATH) {
- PathComponent<DbAttribute, DbRelationship> last = getDbEntity().lastPathComponent(expression,
- queryAssembler.getPathAliases());
- if (last.getAttribute() != null) {
- attribute = last.getAttribute();
- break;
- } else if (last.getRelationship() != null) {
- relationship = last.getRelationship();
- break;
- }
- }
- }
- }
-
- if (attribute != null) {
- return attribute;
- }
-
- if (relationship != null) {
- // Can't properly handle multiple joins....
- if (relationship.getJoins().size() == 1) {
- DbJoin join = relationship.getJoins().get(0);
- return join.getSource();
- }
- }
-
- return null;
- }
-
- /**
- * Processes case when an OBJ_PATH expression ends with relationship. If
- * this is a "to many" relationship, a join is added and a column expression
- * for the target entity primary key. If this is a "to one" relationship,
- * column expression for the source foreign key is added.
- *
- * @since 3.0
- */
- protected void processRelTermination(ObjRelationship rel, JoinType joinType, String joinSplitAlias)
- throws IOException {
-
- Iterator<DbRelationship> dbRels = rel.getDbRelationships().iterator();
-
- // scan DbRelationships
- while (dbRels.hasNext()) {
- DbRelationship dbRel = dbRels.next();
-
- // if this is a last relationship in the path,
- // it needs special handling
- if (!dbRels.hasNext()) {
- processRelTermination(dbRel, joinType, joinSplitAlias);
- } else {
- // find and add joins ....
- queryAssembler.dbRelationshipAdded(dbRel, joinType, joinSplitAlias);
- }
- }
- }
-
- /**
- * Handles case when a DB_NAME expression ends with relationship. If this is
- * a "to many" relationship, a join is added and a column expression for the
- * target entity primary key. If this is a "to one" relationship, column
- * expression for the source foreign key is added.
- *
- * @since 3.0
- */
- protected void processRelTermination(DbRelationship rel, JoinType joinType, String joinSplitAlias)
- throws IOException {
-
- if (rel.isToMany()) {
- // append joins
- queryAssembler.dbRelationshipAdded(rel, joinType, joinSplitAlias);
- }
-
- // get last DbRelationship on the list
- List<DbJoin> joins = rel.getJoins();
- if (joins.size() != 1) {
- StringBuilder msg = new StringBuilder();
- msg.append("OBJ_PATH expressions are only supported ").append("for a single-join relationships. ")
- .append("This relationship has ").append(joins.size()).append(" joins.");
-
- throw new CayenneRuntimeException(msg.toString());
- }
-
- DbJoin join = joins.get(0);
-
- DbAttribute attribute = null;
-
- if (rel.isToMany()) {
- DbEntity ent = (DbEntity) join.getRelationship().getTargetEntity();
- Collection<DbAttribute> pk = ent.getPrimaryKeys();
- if (pk.size() != 1) {
- StringBuilder msg = new StringBuilder();
- msg.append("DB_NAME expressions can only support ").append("targets with a single column PK. ")
- .append("This entity has ").append(pk.size()).append(" columns in primary key.");
-
- throw new CayenneRuntimeException(msg.toString());
- }
-
- attribute = pk.iterator().next();
- } else {
- attribute = join.getSource();
- }
-
- processColumn(attribute);
- }
+ protected QueryAssembler queryAssembler;
+ protected StringBuilder out;
+ protected QuotingStrategy strategy;
+
+ /**
+ * Creates QueryAssemblerHelper initializing with parent
+ * {@link QueryAssembler} and output buffer object.
+ */
+ public QueryAssemblerHelper(QueryAssembler queryAssembler) {
+ this.queryAssembler = queryAssembler;
+ strategy = queryAssembler.getAdapter().getQuotingStrategy();
+ }
+
+ public ObjEntity getObjEntity() {
+ return queryAssembler.getQueryMetadata().getObjEntity();
+ }
+
+ public DbEntity getDbEntity() {
+ return queryAssembler.getQueryMetadata().getDbEntity();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public StringBuilder appendPart(StringBuilder out) {
+ this.out = out;
+ doAppendPart();
+ return out;
+ }
+
+ /**
+ * Sets ouput buffer
+ */
+ void setOut(StringBuilder out) {
+ this.out = out;
+ }
+
+ /**
+ * @return output buffer
+ */
+ StringBuilder getOut() {
+ return out;
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected abstract void doAppendPart();
+
+ /**
+ * <p>
+ * Outputs the standard JDBC (database agnostic) expression for supplying
+ * the escape character to the database server when supplying a LIKE clause.
+ * This has been factored-out because some database adaptors handle LIKE
+ * differently and they need access to this common method in order not to
+ * repeat this code.
+ * <p>
+ * If there is no escape character defined then this method will not output
+ * anything. An escape character of 0 will mean no escape character.
+ *
+ * @since 3.1
+ */
+ protected void appendLikeEscapeCharacter(PatternMatchNode patternMatchNode) throws IOException {
+ char escapeChar = patternMatchNode.getEscapeChar();
+
+ if ('?' == escapeChar) {
+ throw new CayenneRuntimeException("the escape character of '?' is illegal for LIKE clauses.");
+ }
+
+ if (0 != escapeChar) {
+ out.append(" {escape '");
+ out.append(escapeChar);
+ out.append("'}");
+ }
+ }
+
+ /**
+ * Processes parts of the OBJ_PATH expression.
+ */
+ protected void appendObjPath(Expression pathExp) {
+
+ queryAssembler.resetJoinStack();
+ String joinSplitAlias = null;
+
+ for (PathComponent<ObjAttribute, ObjRelationship> component : getObjEntity().resolvePath(pathExp,
+ queryAssembler.getPathAliases())) {
+
+ if (component.isAlias()) {
+ joinSplitAlias = component.getName();
+ for (PathComponent<ObjAttribute, ObjRelationship> aliasPart : component.getAliasedPath()) {
+
+ ObjRelationship relationship = aliasPart.getRelationship();
+
+ if (relationship == null) {
+ throw new IllegalStateException("Non-relationship aliased path part: " + aliasPart.getName());
+ }
+
+ if (aliasPart.isLast() && component.isLast()) {
+ processRelTermination(relationship, aliasPart.getJoinType(), joinSplitAlias);
+ } else {
+ // find and add joins ....
+ for (DbRelationship dbRel : relationship.getDbRelationships()) {
+ queryAssembler.dbRelationshipAdded(dbRel, aliasPart.getJoinType(), joinSplitAlias);
+ }
+ }
+ }
+
+ continue;
+ }
+
+ ObjRelationship relationship = component.getRelationship();
+ ObjAttribute attribute = component.getAttribute();
+
+ if (relationship != null) {
+
+ // if this is a last relationship in the path,
+ // it needs special handling
+ if (component.isLast()) {
+ processRelTermination(relationship, component.getJoinType(), joinSplitAlias);
+ } else {
+ // find and add joins ....
+ for (DbRelationship dbRel : relationship.getDbRelationships()) {
+ queryAssembler.dbRelationshipAdded(dbRel, component.getJoinType(), joinSplitAlias);
+ }
+ }
+ } else {
+ Iterator<CayenneMapEntry> dbPathIterator = attribute.getDbPathIterator();
+ while (dbPathIterator.hasNext()) {
+ Object pathPart = dbPathIterator.next();
+
+ if (pathPart == null) {
+ throw new CayenneRuntimeException("ObjAttribute has no component: " + attribute.getName());
+ } else if (pathPart instanceof DbRelationship) {
+ queryAssembler.dbRelationshipAdded((DbRelationship) pathPart, JoinType.INNER, joinSplitAlias);
+ } else if (pathPart instanceof DbAttribute) {
+ processColumnWithQuoteSqlIdentifiers((DbAttribute) pathPart, pathExp);
+ }
+ }
+
+ }
+ }
+ }
+
+ protected void appendDbPath(Expression pathExp) {
+
+ queryAssembler.resetJoinStack();
+ String joinSplitAlias = null;
+
+ for (PathComponent<DbAttribute, DbRelationship> component : getDbEntity().resolvePath(pathExp,
+ queryAssembler.getPathAliases())) {
+
+ if (component.isAlias()) {
+ joinSplitAlias = component.getName();
+ for (PathComponent<DbAttribute, DbRelationship> aliasPart : component.getAliasedPath()) {
+
+ DbRelationship relationship = aliasPart.getRelationship();
+
+ if (relationship == null) {
+ throw new IllegalStateException("Non-relationship aliased path part: " + aliasPart.getName());
+ }
+
+ if (aliasPart.isLast() && component.isLast()) {
+ processRelTermination(relationship, aliasPart.getJoinType(), joinSplitAlias);
+ } else {
+ queryAssembler.dbRelationshipAdded(relationship, component.getJoinType(), joinSplitAlias);
+ }
+ }
+
+ continue;
+ }
+
+ DbRelationship relationship = component.getRelationship();
+
+ if (relationship != null) {
+
+ // if this is a last relationship in the path,
+ // it needs special handling
+ if (component.isLast()) {
+ processRelTermination(relationship, component.getJoinType(), joinSplitAlias);
+ } else {
+ // find and add joins ....
+ queryAssembler.dbRelationshipAdded(relationship, component.getJoinType(), joinSplitAlias);
+ }
+ } else {
+ processColumnWithQuoteSqlIdentifiers(component.getAttribute(), pathExp);
+ }
+ }
+ }
+
+ protected void processColumn(DbAttribute dbAttr) {
+ processColumnWithQuoteSqlIdentifiers(dbAttr, null);
+ }
+
+ protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) {
+
+ String alias = (queryAssembler.supportsTableAliases()) ? queryAssembler.getCurrentAlias() : null;
+ out.append(strategy.quotedIdentifier(dbAttr.getEntity(), alias, dbAttr.getName()));
+ }
+
+ /**
+ * Appends SQL code to the query buffer to handle <code>val</code> as a
+ * parameter to the PreparedStatement being built. Adds <code>val</code>
+ * into QueryAssembler parameter list.
+ * <p>
+ * If <code>val</code> is null, "NULL" is appended to the query.
+ * </p>
+ * <p>
+ * If <code>val</code> is a DataObject, its primary key value is used as a
+ * parameter. <i>Only objects with a single column primary key can be
+ * used.</i>
+ *
+ * @param val
+ * object that should be appended as a literal to the query. Must
+ * be of one of "standard JDBC" types, null or a DataObject.
+ * @param attr
+ * DbAttribute that has information on what type of parameter is
+ * being appended.
+ */
+ protected void appendLiteral(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
+
+ if (val == null) {
+ out.append("NULL");
+ } else if (val instanceof Persistent) {
+ // TODO: see cay1796
+ // This check is unlikely to happen,
+ // since Expression got ObjectId from Persistent object.
+ // Left for future research.
+ ObjectId id = ((Persistent) val).getObjectId();
+
+ // check if this id is acceptable to be a parameter
+ if (id == null) {
+ throw new CayenneRuntimeException("Can't use TRANSIENT object as a query parameter.");
+ }
+
+ if (id.isTemporary()) {
+ throw new CayenneRuntimeException("Can't use NEW object as a query parameter.");
+ }
+
+ Map<String, Object> snap = id.getIdSnapshot();
+ if (snap.size() != 1) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("Object must have a single primary key column ").append("to serve as a query parameter. ")
+ .append("This object has ").append(snap.size()).append(": ").append(snap);
+
+ throw new CayenneRuntimeException(msg.toString());
+ }
+
+ // checks have been passed, use id value
+ appendLiteralDirect(snap.get(snap.keySet().iterator().next()), attr, parentExpression);
+ } else if (val instanceof ObjectId) {
+
+ ObjectId id = (ObjectId) val;
+
+ if (id.isTemporary()) {
+ throw new CayenneRuntimeException("Can't use NEW object as a query parameter.");
+ }
+
+ Map<String, Object> snap = id.getIdSnapshot();
+ if (snap.size() != 1) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("Object must have a single primary key column ").append("to serve as a query parameter. ")
+ .append("This object has ").append(snap.size()).append(": ").append(snap);
+
+ throw new CayenneRuntimeException(msg.toString());
+ }
+
+ // checks have been passed, use id value
+ appendLiteralDirect(snap.get(snap.keySet().iterator().next()), attr, parentExpression);
+ } else {
+ appendLiteralDirect(val, attr, parentExpression);
+ }
+ }
+
+ /**
+ * Appends SQL code to the query buffer to handle <code>val</code> as a
+ * parameter to the PreparedStatement being built. Adds <code>val</code>
+ * into QueryAssembler parameter list.
+ */
+ protected void appendLiteralDirect(Object val, DbAttribute attr, Expression parentExpression) throws IOException {
+ out.append('?');
+ queryAssembler.addToParamList(attr, val);
+ }
+
+ /**
+ * Returns database type of expression parameters or null if it can not be
+ * determined.
+ */
+ protected DbAttribute paramsDbType(Expression e) {
+ int len = e.getOperandCount();
+
+ // for unary expressions, find parent binary - this is a hack mainly to
+ // support
+ // ASTList
+ if (len < 2) {
+
+ if (e instanceof SimpleNode) {
+ Expression parent = (Expression) ((SimpleNode) e).jjtGetParent();
+ if (parent != null) {
+ return paramsDbType(parent);
+ }
+ }
+
+ return null;
+ }
+
+ // naive algorithm:
+
+ // if at least one of the sibling operands is a
+ // OBJ_PATH or DB_PATH expression, use its attribute type as
+ // a final answer.
+
+ // find attribute or relationship matching the value
+ DbAttribute attribute = null;
+ DbRelationship relationship = null;
+ for (int i = 0; i < len; i++) {
+ Object op = e.getOperand(i);
+
+ if (op instanceof Expression) {
+ Expression expression = (Expression) op;
+ if (expression.getType() == Expression.OBJ_PATH) {
+ PathComponent<ObjAttribute, ObjRelationship> last = getObjEntity().lastPathComponent(expression,
+ queryAssembler.getPathAliases());
+
+ // TODO: handle EmbeddableAttribute
+ // if (last instanceof EmbeddableAttribute)
+ // break;
+
+ if (last.getAttribute() != null) {
+ attribute = last.getAttribute().getDbAttribute();
+ break;
+ } else if (last.getRelationship() != null) {
+ List<DbRelationship> dbPath = last.getRelationship().getDbRelationships();
+ if (dbPath.size() > 0) {
+ relationship = dbPath.get(dbPath.size() - 1);
+ break;
+ }
+ }
+ } else if (expression.getType() == Expression.DB_PATH) {
+ PathComponent<DbAttribute, DbRelationship> last = getDbEntity().lastPathComponent(expression,
+ queryAssembler.getPathAliases());
+ if (last.getAttribute() != null) {
+ attribute = last.getAttribute();
+ break;
+ } else if (last.getRelationship() != null) {
+ relationship = last.getRelationship();
+ break;
+ }
+ }
+ }
+ }
+
+ if (attribute != null) {
+ return attribute;
+ }
+
+ if (relationship != null) {
+ // Can't properly handle multiple joins....
+ if (relationship.getJoins().size() == 1) {
+ DbJoin join = relationship.getJoins().get(0);
+ return join.getSource();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Processes case when an OBJ_PATH expression ends with relationship. If
+ * this is a "to many" relationship, a join is added and a column expression
+ * for the target entity primary key. If this is a "to one" relationship,
+ * column expression for the source foreign key is added.
+ *
+ * @since 3.0
+ */
+ protected void processRelTermination(ObjRelationship rel, JoinType joinType, String joinSplitAlias) {
+
+ Iterator<DbRelationship> dbRels = rel.getDbRelationships().iterator();
+
+ // scan DbRelationships
+ while (dbRels.hasNext()) {
+ DbRelationship dbRel = dbRels.next();
+
+ // if this is a last relationship in the path,
+ // it needs special handling
+ if (!dbRels.hasNext()) {
+ processRelTermination(dbRel, joinType, joinSplitAlias);
+ } else {
+ // find and add joins ....
+ queryAssembler.dbRelationshipAdded(dbRel, joinType, joinSplitAlias);
+ }
+ }
+ }
+
+ /**
+ * Handles case when a DB_NAME expression ends with relationship. If this is
+ * a "to many" relationship, a join is added and a column expression for the
+ * target entity primary key. If this is a "to one" relationship, column
+ * expression for the source foreign key is added.
+ *
+ * @since 3.0
+ */
+ protected void processRelTermination(DbRelationship rel, JoinType joinType, String joinSplitAlias) {
+
+ if (rel.isToMany()) {
+ // append joins
+ queryAssembler.dbRelationshipAdded(rel, joinType, joinSplitAlias);
+ }
+
+ // get last DbRelationship on the list
+ List<DbJoin> joins = rel.getJoins();
+ if (joins.size() != 1) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("OBJ_PATH expressions are only supported ").append("for a single-join relationships. ")
+ .append("This relationship has ").append(joins.size()).append(" joins.");
+
+ throw new CayenneRuntimeException(msg.toString());
+ }
+
+ DbJoin join = joins.get(0);
+
+ DbAttribute attribute = null;
+
+ if (rel.isToMany()) {
+ DbEntity ent = (DbEntity) join.getRelationship().getTargetEntity();
+ Collection<DbAttribute> pk = ent.getPrimaryKeys();
+ if (pk.size() != 1) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("DB_NAME expressions can only support ").append("targets with a single column PK. ")
+ .append("This entity has ").append(pk.size()).append(" columns in primary key.");
+
+ throw new CayenneRuntimeException(msg.toString());
+ }
+
+ attribute = pk.iterator().next();
+ } else {
+ attribute = join.getSource();
+ }
+
+ processColumn(attribute);
+ }
}
[2/7] cayenne git commit: Refactoring SelectTranslator for better
extensibility
Posted by aa...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
new file mode 100644
index 0000000..612f13a
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
@@ -0,0 +1,787 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access.translator.select;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.access.DataNode;
+import org.apache.cayenne.access.jdbc.ColumnDescriptor;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.ExpressionException;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.query.PrefetchTreeNode;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.query.SortOrder;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.ArtistExhibit;
+import org.apache.cayenne.testdo.testmap.CompoundPainting;
+import org.apache.cayenne.testdo.testmap.Painting;
+import org.apache.cayenne.unit.UnitDbAdapter;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class DefaultSelectTranslatorIT extends ServerCase {
+
+ @Inject
+ private DataContext context;
+
+ @Inject
+ private UnitDbAdapter unitAdapter;
+
+ @Inject
+ private DBHelper dbHelper;
+
+ @Inject
+ private JdbcEventLogger logger;
+
+ @Inject
+ private DataNode dataNode;
+
+ /**
+ * Tests query creation with qualifier and ordering.
+ */
+ @Test
+ public void testCreateSqlString1() throws Exception {
+ // query with qualifier and ordering
+ SelectQuery<Artist> q = new SelectQuery<Artist>(Artist.class, ExpressionFactory.likeExp("artistName", "a%"));
+ q.addOrdering("dateOfBirth", SortOrder.ASCENDING);
+
+ String generatedSql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver())
+ .getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM "));
+ assertTrue(generatedSql.indexOf(" ORDER BY ") > generatedSql.indexOf(" WHERE "));
+ }
+
+ /**
+ * Tests query creation with qualifier and ordering.
+ */
+ @Test
+ public void testDbEntityQualifier() throws Exception {
+
+ SelectQuery<Artist> q = new SelectQuery<Artist>(Artist.class);
+ final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+
+ entity.setQualifier(ExpressionFactory.exp("ARTIST_NAME = \"123\""));
+ middleEntity.setQualifier(ExpressionFactory.exp("GROUP_ID = 1987"));
+
+ try {
+
+ String generatedSql = transl.getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ if (generatedSql.contains("RTRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
+ } else if (generatedSql.contains("TRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
+ } else {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
+ }
+ } finally {
+ entity.setQualifier(null);
+ middleEntity.setQualifier(null);
+ }
+ }
+
+ @Test
+ public void testDbEntityQualifier_OuterJoin() throws Exception {
+
+ SelectQuery<Painting> q = SelectQuery.query(Painting.class);
+ q.addOrdering("toArtist+.artistName", SortOrder.ASCENDING);
+
+ final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+
+ entity.setQualifier(ExpressionFactory.exp("ARTIST_NAME = \"123\""));
+ middleEntity.setQualifier(ExpressionFactory.exp("GROUP_ID = 1987"));
+
+ try {
+
+ String generatedSql = transl.getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ if (generatedSql.contains("RTRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
+ } else if (generatedSql.contains("TRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
+ } else {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
+ }
+
+ } finally {
+ entity.setQualifier(null);
+ middleEntity.setQualifier(null);
+ }
+ }
+
+ @Test
+ public void testDbEntityQualifier_FlattenedRel() throws Exception {
+
+ SelectQuery<Artist> q = new SelectQuery<Artist>(Artist.class, ExpressionFactory.matchExp("groupArray.name",
+ "bar"));
+
+ final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+
+ entity.setQualifier(ExpressionFactory.exp("ARTIST_NAME = \"123\""));
+ middleEntity.setQualifier(ExpressionFactory.exp("GROUP_ID = 1987"));
+
+ try {
+
+ String generatedSql = transl.getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ if (generatedSql.contains("RTRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
+ } else if (generatedSql.contains("TRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
+ } else {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
+ }
+
+ } finally {
+ entity.setQualifier(null);
+ middleEntity.setQualifier(null);
+ }
+ }
+
+ @Test
+ public void testDbEntityQualifier_RelatedMatch() throws Exception {
+
+ SelectQuery<Artist> q = new SelectQuery(Painting.class,
+ ExpressionFactory.matchExp("toArtist.artistName", "foo"));
+
+ final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+
+ entity.setQualifier(ExpressionFactory.exp("ARTIST_NAME = \"123\""));
+ middleEntity.setQualifier(ExpressionFactory.exp("GROUP_ID = 1987"));
+
+ try {
+
+ String generatedSql = transl.getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ if (generatedSql.contains("RTRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
+ } else if (generatedSql.contains("TRIM")) {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
+ } else {
+ assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
+ }
+
+ } finally {
+ entity.setQualifier(null);
+ middleEntity.setQualifier(null);
+ }
+ }
+
+ /**
+ * Tests query creation with "distinct" specified.
+ */
+ @Test
+ public void testCreateSqlString2() throws Exception {
+ // query with "distinct" set
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.setDistinct(true);
+
+ String generatedSql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver())
+ .getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT DISTINCT"));
+ }
+
+ /**
+ * Test aliases when the same table used in more then 1 relationship. Check
+ * translation of relationship path "ArtistExhibit.toArtist.artistName" and
+ * "ArtistExhibit.toExhibit.toGallery.paintingArray.toArtist.artistName".
+ */
+ @Test
+ public void testCreateSqlString5() throws Exception {
+ // query with qualifier and ordering
+ SelectQuery q = new SelectQuery(ArtistExhibit.class);
+ q.setQualifier(ExpressionFactory.likeExp("toArtist.artistName", "a%"));
+ q.andQualifier(ExpressionFactory.likeExp("toExhibit.toGallery.paintingArray.toArtist.artistName", "a%"));
+
+ String generatedSql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver())
+ .getSql();
+ // logObj.warn("Query: " + generatedSql);
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM "));
+
+ // check that there are 2 distinct aliases for the ARTIST table
+ int ind1 = generatedSql.indexOf("ARTIST t", generatedSql.indexOf(" FROM "));
+ assertTrue(ind1 > 0);
+
+ int ind2 = generatedSql.indexOf("ARTIST t", ind1 + 1);
+ assertTrue(ind2 > 0);
+
+ assertTrue(generatedSql.charAt(ind1 + "ARTIST t".length()) != generatedSql.charAt(ind2 + "ARTIST t".length()));
+ }
+
+ /**
+ * Test aliases when the same table used in more then 1 relationship. Check
+ * translation of relationship path "ArtistExhibit.toArtist.artistName" and
+ * "ArtistExhibit.toArtist.paintingArray.paintingTitle".
+ */
+ @Test
+ public void testCreateSqlString6() throws Exception {
+ // query with qualifier and ordering
+ SelectQuery q = new SelectQuery(ArtistExhibit.class);
+ q.setQualifier(ExpressionFactory.likeExp("toArtist.artistName", "a%"));
+ q.andQualifier(ExpressionFactory.likeExp("toArtist.paintingArray.paintingTitle", "p%"));
+
+ String generatedSql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver())
+ .getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+ assertTrue(generatedSql.indexOf(" FROM ") > 0);
+ assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM "));
+
+ // check that there is only one distinct alias for the ARTIST
+ // table
+ int ind1 = generatedSql.indexOf("ARTIST t", generatedSql.indexOf(" FROM "));
+ assertTrue(ind1 > 0);
+
+ int ind2 = generatedSql.indexOf("ARTIST t", ind1 + 1);
+ assertTrue(generatedSql, ind2 < 0);
+ }
+
+ /**
+ * Test query when qualifying on the same attribute more than once. Check
+ * translation "Artist.dateOfBirth > ? AND Artist.dateOfBirth < ?".
+ */
+ @Test
+ public void testCreateSqlString7() throws Exception {
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.setQualifier(ExpressionFactory.greaterExp("dateOfBirth", new Date()));
+ q.andQualifier(ExpressionFactory.lessExp("dateOfBirth", new Date()));
+
+ String generatedSql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver())
+ .getSql();
+ // logObj.warn("Query: " + generatedSql);
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+
+ int i1 = generatedSql.indexOf(" FROM ");
+ assertTrue(i1 > 0);
+
+ int i2 = generatedSql.indexOf(" WHERE ");
+ assertTrue(i2 > i1);
+
+ int i3 = generatedSql.indexOf("DATE_OF_BIRTH", i2 + 1);
+ assertTrue(i3 > i2);
+
+ int i4 = generatedSql.indexOf("DATE_OF_BIRTH", i3 + 1);
+ assertTrue("No second DOB comparison: " + i4 + ", " + i3, i4 > i3);
+ }
+
+ /**
+ * Test query when qualifying on the same attribute accessed over
+ * relationship, more than once. Check translation
+ * "Painting.toArtist.dateOfBirth > ? AND Painting.toArtist.dateOfBirth <
+ * ?".
+ */
+ @Test
+ public void testCreateSqlString8() throws Exception {
+ SelectQuery q = new SelectQuery();
+ q.setRoot(Painting.class);
+ q.setQualifier(ExpressionFactory.greaterExp("toArtist.dateOfBirth", new Date()));
+ q.andQualifier(ExpressionFactory.lessExp("toArtist.dateOfBirth", new Date()));
+
+ String generatedSql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver())
+ .getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(generatedSql);
+ assertTrue(generatedSql.startsWith("SELECT "));
+
+ int i1 = generatedSql.indexOf(" FROM ");
+ assertTrue(i1 > 0);
+
+ int i2 = generatedSql.indexOf(" WHERE ");
+ assertTrue(i2 > i1);
+
+ int i3 = generatedSql.indexOf("DATE_OF_BIRTH", i2 + 1);
+ assertTrue(i3 > i2);
+
+ int i4 = generatedSql.indexOf("DATE_OF_BIRTH", i3 + 1);
+ assertTrue("No second DOB comparison: " + i4 + ", " + i3, i4 > i3);
+ }
+
+ @Test
+ public void testCreateSqlString9() throws Exception {
+ // query for a compound ObjEntity with qualifier
+ SelectQuery q = new SelectQuery(CompoundPainting.class, ExpressionFactory.likeExp("artistName", "a%"));
+
+ String sql = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver()).getSql();
+
+ // do some simple assertions to make sure all parts are in
+ assertNotNull(sql);
+ assertTrue(sql.startsWith("SELECT "));
+
+ int i1 = sql.indexOf(" FROM ");
+ assertTrue(i1 > 0);
+
+ int i2 = sql.indexOf("PAINTING");
+ assertTrue(i2 > 0);
+
+ int i3 = sql.indexOf("ARTIST");
+ assertTrue(i3 > 0);
+
+ int i4 = sql.indexOf("GALLERY");
+ assertTrue(i4 > 0);
+
+ int i5 = sql.indexOf("PAINTING_INFO");
+ assertTrue(i5 > 0);
+
+ int i6 = sql.indexOf("ARTIST_NAME");
+ assertTrue(i6 > 0);
+
+ int i7 = sql.indexOf("ESTIMATED_PRICE");
+ assertTrue(i7 > 0);
+
+ int i8 = sql.indexOf("GALLERY_NAME");
+ assertTrue(i8 > 0);
+
+ int i9 = sql.indexOf("PAINTING_TITLE");
+ assertTrue(i9 > 0);
+
+ int i10 = sql.indexOf("TEXT_REVIEW");
+ assertTrue(i10 > 0);
+
+ int i11 = sql.indexOf("PAINTING_ID");
+ assertTrue(i11 > 0);
+
+ int i12 = sql.indexOf("ARTIST_ID");
+ assertTrue(i12 > 0);
+
+ int i13 = sql.indexOf("GALLERY_ID");
+ assertTrue(i13 > 0);
+
+ }
+
+ @Test
+ public void testCreateSqlString10() throws Exception {
+ // query with to-many joint prefetches
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.addPrefetch(Artist.PAINTING_ARRAY_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+ String sql = transl.getSql();
+ assertNotNull(sql);
+ assertTrue(sql.startsWith("SELECT "));
+
+ int i1 = sql.indexOf("ARTIST_ID");
+ assertTrue(sql, i1 > 0);
+
+ int i2 = sql.indexOf("FROM");
+ assertTrue(sql, i2 > 0);
+
+ assertTrue(sql, sql.indexOf("PAINTING_ID") > 0);
+
+ // assert we have one join
+ assertEquals(1, transl.joinStack.size());
+ }
+
+ @Test
+ public void testCreateSqlString11() throws Exception {
+ // query with joint prefetches and other joins
+ SelectQuery q = new SelectQuery(Artist.class, ExpressionFactory.exp("paintingArray.paintingTitle = 'a'"));
+ q.addPrefetch(Artist.PAINTING_ARRAY_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+
+ transl.getSql();
+
+ // assert we only have one join
+ assertEquals(2, transl.joinStack.size());
+ }
+
+ @Test
+ public void testCreateSqlString12() throws Exception {
+ // query with to-one joint prefetches
+ SelectQuery q = new SelectQuery(Painting.class);
+ q.addPrefetch(Painting.TO_ARTIST_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ DefaultSelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(),
+ dataNode.getEntityResolver());
+
+ String sql = transl.getSql();
+ assertNotNull(sql);
+ assertTrue(sql.startsWith("SELECT "));
+
+ int i1 = sql.indexOf("ARTIST_ID");
+ assertTrue(sql, i1 > 0);
+
+ int i2 = sql.indexOf("FROM");
+ assertTrue(sql, i2 > 0);
+
+ assertTrue(sql, sql.indexOf("PAINTING_ID") > 0);
+
+ // assert we have one join
+ assertEquals(1, transl.joinStack.size());
+ }
+
+ @Test
+ public void testCreateSqlString13() throws Exception {
+ // query with invalid joint prefetches
+ SelectQuery q = new SelectQuery(Painting.class);
+ q.addPrefetch("invalid.invalid").setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ try {
+ new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver()).getSql();
+ fail("Invalid jointPrefetch must have thrown...");
+ } catch (ExpressionException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCreateSqlStringWithQuoteSqlIdentifiers() throws Exception {
+
+ try {
+ SelectQuery q = new SelectQuery(Artist.class);
+ DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ entity.getDataMap().setQuotingSQLIdentifiers(true);
+ q.addOrdering("dateOfBirth", SortOrder.ASCENDING);
+
+ String charStart = unitAdapter.getIdentifiersStartQuote();
+ String charEnd = unitAdapter.getIdentifiersEndQuote();
+
+ String s = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver()).getSql();
+ assertTrue(s.startsWith("SELECT "));
+ int iFrom = s.indexOf(" FROM ");
+ assertTrue(iFrom > 0);
+ int artistName = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
+ assertTrue(artistName > 0 && artistName < iFrom);
+ int artistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
+ assertTrue(artistId > 0 && artistId < iFrom);
+ int dateOfBirth = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
+ assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
+ int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t0" + charEnd);
+ assertTrue(iArtist > iFrom);
+ int iOrderBy = s.indexOf(" ORDER BY ");
+ int dateOfBirth2 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd,
+ iOrderBy);
+ assertTrue(iOrderBy > iArtist);
+ assertTrue(dateOfBirth2 > iOrderBy);
+
+ } finally {
+ DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ entity.getDataMap().setQuotingSQLIdentifiers(false);
+ }
+
+ }
+
+ @Test
+ public void testCreateSqlStringWithQuoteSqlIdentifiers2() throws Exception {
+
+ try {
+ SelectQuery q = new SelectQuery(Artist.class);
+ DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ entity.getDataMap().setQuotingSQLIdentifiers(true);
+ q.setQualifier(ExpressionFactory.greaterExp("dateOfBirth", new Date()));
+ q.andQualifier(ExpressionFactory.lessExp("dateOfBirth", new Date()));
+
+ String charStart = unitAdapter.getIdentifiersStartQuote();
+ String charEnd = unitAdapter.getIdentifiersEndQuote();
+
+ String s = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver()).getSql();
+
+ assertTrue(s.startsWith("SELECT "));
+ int iFrom = s.indexOf(" FROM ");
+ assertTrue(iFrom > 0);
+ int artistName = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
+ assertTrue(artistName > 0 && artistName < iFrom);
+ int artistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
+ assertTrue(artistId > 0 && artistId < iFrom);
+ int dateOfBirth = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
+ assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
+ int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t0" + charEnd);
+ assertTrue(iArtist > iFrom);
+ int iWhere = s.indexOf(" WHERE ");
+ assertTrue(iWhere > iArtist);
+
+ int dateOfBirth2 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd
+ + " > ?");
+ assertTrue(dateOfBirth2 > iWhere);
+
+ int iAnd = s.indexOf(" AND ");
+ assertTrue(iAnd > iWhere);
+ int dateOfBirth3 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd
+ + " < ?");
+ assertTrue(dateOfBirth3 > iAnd);
+
+ } finally {
+ DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ entity.getDataMap().setQuotingSQLIdentifiers(false);
+ }
+ }
+
+ @Test
+ public void testCreateSqlStringWithQuoteSqlIdentifiers3() throws Exception {
+
+ // query with joint prefetches and other joins
+ // and with QuoteSqlIdentifiers = true
+ try {
+ SelectQuery q = new SelectQuery(Artist.class, ExpressionFactory.exp("paintingArray.paintingTitle = 'a'"));
+ q.addPrefetch(Artist.PAINTING_ARRAY.joint());
+
+ DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ entity.getDataMap().setQuotingSQLIdentifiers(true);
+
+ String charStart = unitAdapter.getIdentifiersStartQuote();
+ String charEnd = unitAdapter.getIdentifiersEndQuote();
+
+ String s = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver()).getSql();
+
+ assertTrue(s.startsWith("SELECT DISTINCT "));
+ int iFrom = s.indexOf(" FROM ");
+ assertTrue(iFrom > 0);
+ int artistName = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
+ assertTrue(artistName > 0 && artistName < iFrom);
+ int artistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
+ assertTrue(artistId > 0 && artistId < iFrom);
+ int dateOfBirth = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
+ assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
+ int estimatedPrice = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ESTIMATED_PRICE" + charEnd);
+ assertTrue(estimatedPrice > 0 && estimatedPrice < iFrom);
+ int paintingDescription = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "PAINTING_DESCRIPTION"
+ + charEnd);
+ assertTrue(paintingDescription > 0 && paintingDescription < iFrom);
+ int paintingTitle = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "PAINTING_TITLE" + charEnd);
+ assertTrue(paintingTitle > 0 && paintingTitle < iFrom);
+ int artistIdT1 = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
+ assertTrue(artistIdT1 > 0 && artistIdT1 < iFrom);
+ int galleryId = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "GALLERY_ID" + charEnd);
+ assertTrue(galleryId > 0 && galleryId < iFrom);
+ int paintingId = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "PAINTING_ID" + charEnd);
+ assertTrue(paintingId > 0 && paintingId < iFrom);
+ int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t0" + charEnd);
+ assertTrue(iArtist > iFrom);
+ int iLeftJoin = s.indexOf("LEFT JOIN");
+ assertTrue(iLeftJoin > iFrom);
+ int iPainting = s.indexOf(charStart + "PAINTING" + charEnd + " " + charStart + "t1" + charEnd);
+ assertTrue(iPainting > iLeftJoin);
+ int iOn = s.indexOf(" ON ");
+ assertTrue(iOn > iLeftJoin);
+ int iArtistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
+ assertTrue(iArtistId > iOn);
+ int iArtistIdT1 = s
+ .indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
+ assertTrue(iArtistIdT1 > iOn);
+ int i = s.indexOf("=", iLeftJoin);
+ assertTrue(iArtistIdT1 > i || iArtistId > i);
+ int iJoin = s.indexOf("JOIN");
+ assertTrue(iJoin > iLeftJoin);
+ int iPainting2 = s.indexOf(charStart + "PAINTING" + charEnd + " " + charStart + "t2" + charEnd);
+ assertTrue(iPainting2 > iJoin);
+ int iOn2 = s.indexOf(" ON ");
+ assertTrue(iOn2 > iJoin);
+ int iArtistId2 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iJoin);
+ assertTrue(iArtistId2 > iOn2);
+ int iArtistId2T2 = s.indexOf(charStart + "t2" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iJoin);
+ assertTrue(iArtistId2T2 > iOn2);
+ int i2 = s.indexOf("=", iJoin);
+ assertTrue(iArtistId2T2 > i2 || iArtistId2 > i2);
+ int iWhere = s.indexOf(" WHERE ");
+ assertTrue(iWhere > iJoin);
+
+ int paintingTitle2 = s.indexOf(charStart + "t2" + charEnd + "." + charStart + "PAINTING_TITLE" + charEnd
+ + " = ?");
+ assertTrue(paintingTitle2 > iWhere);
+
+ } finally {
+ DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
+ entity.getDataMap().setQuotingSQLIdentifiers(false);
+ }
+ }
+
+ @Test
+ public void testCreateSqlStringWithQuoteSqlIdentifiers4() throws Exception {
+
+ // query with to-one joint prefetches
+ // and with QuoteSqlIdentifiers = true
+ try {
+ SelectQuery q = new SelectQuery(Painting.class);
+ q.addPrefetch(Painting.TO_ARTIST_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
+ entity.getDataMap().setQuotingSQLIdentifiers(true);
+
+ String charStart = unitAdapter.getIdentifiersStartQuote();
+ String charEnd = unitAdapter.getIdentifiersEndQuote();
+
+ String s = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver()).getSql();
+
+ assertTrue(s.startsWith("SELECT "));
+ int iFrom = s.indexOf(" FROM ");
+ assertTrue(iFrom > 0);
+
+ int paintingDescription = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "PAINTING_DESCRIPTION"
+ + charEnd);
+ assertTrue(paintingDescription > 0 && paintingDescription < iFrom);
+ int paintingTitle = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "PAINTING_TITLE" + charEnd);
+ assertTrue(paintingTitle > 0 && paintingTitle < iFrom);
+ int artistIdT1 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
+ assertTrue(artistIdT1 > 0 && artistIdT1 < iFrom);
+ int estimatedPrice = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ESTIMATED_PRICE" + charEnd);
+ assertTrue(estimatedPrice > 0 && estimatedPrice < iFrom);
+ int galleryId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "GALLERY_ID" + charEnd);
+ assertTrue(galleryId > 0 && galleryId < iFrom);
+ int paintingId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "PAINTING_ID" + charEnd);
+ assertTrue(paintingId > 0 && paintingId < iFrom);
+ int artistName = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
+ assertTrue(artistName > 0 && artistName < iFrom);
+ int artistId = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
+ assertTrue(artistId > 0 && artistId < iFrom);
+ int dateOfBirth = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
+ assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
+ int iPainting = s.indexOf(charStart + "PAINTING" + charEnd + " " + charStart + "t0" + charEnd);
+ assertTrue(iPainting > iFrom);
+
+ int iLeftJoin = s.indexOf("LEFT JOIN");
+ assertTrue(iLeftJoin > iFrom);
+ int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t1" + charEnd);
+ assertTrue(iArtist > iLeftJoin);
+ int iOn = s.indexOf(" ON ");
+ assertTrue(iOn > iLeftJoin);
+ int iArtistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
+ assertTrue(iArtistId > iOn);
+ int iArtistIdT1 = s
+ .indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
+ assertTrue(iArtistIdT1 > iOn);
+ int i = s.indexOf("=", iLeftJoin);
+ assertTrue(iArtistIdT1 > i || iArtistId > i);
+
+ } finally {
+ DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
+ entity.getDataMap().setQuotingSQLIdentifiers(false);
+ }
+ }
+
+ /**
+ * Tests columns generated for a simple object query.
+ */
+ @Test
+ public void testBuildResultColumns1() throws Exception {
+ SelectQuery q = new SelectQuery(Painting.class);
+ DefaultSelectTranslator tr = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver());
+
+ List<?> columns = tr.buildResultColumns();
+
+ // all DbAttributes must be included
+ DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
+ for (final DbAttribute a : entity.getAttributes()) {
+ ColumnDescriptor c = new ColumnDescriptor(a, "t0");
+ assertTrue("No descriptor for " + a + ", columns: " + columns, columns.contains(c));
+ }
+ }
+
+ /**
+ * Tests columns generated for an object query with joint prefetch.
+ */
+ @Test
+ public void testBuildResultColumns2() throws Exception {
+ SelectQuery q = new SelectQuery(Painting.class);
+ q.addPrefetch(Painting.TO_ARTIST_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ DefaultSelectTranslator tr = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver());
+
+ List<?> columns = tr.buildResultColumns();
+
+ // assert root entity columns
+ DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
+ for (final DbAttribute a : entity.getAttributes()) {
+ ColumnDescriptor c = new ColumnDescriptor(a, "t0");
+ assertTrue("No descriptor for " + a + ", columns: " + columns, columns.contains(c));
+ }
+
+ // assert joined columns
+ DbEntity joined = context.getEntityResolver().getDbEntity("ARTIST");
+ for (final DbAttribute a : joined.getAttributes()) {
+
+ // skip ARTIST PK, it is joined from painting
+ if (Artist.ARTIST_ID_PK_COLUMN.equals(a.getName())) {
+ continue;
+ }
+
+ ColumnDescriptor c = new ColumnDescriptor(a, "t1");
+ c.setDataRowKey("toArtist." + a.getName());
+ assertTrue("No descriptor for " + a + ", columns: " + columns, columns.contains(c));
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/OrderingTranslatorIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/OrderingTranslatorIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/OrderingTranslatorIT.java
index c051637..f2784f4 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/OrderingTranslatorIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/OrderingTranslatorIT.java
@@ -19,6 +19,8 @@
package org.apache.cayenne.access.translator.select;
+import static org.junit.Assert.assertTrue;
+
import org.apache.cayenne.TranslationCase;
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.di.Inject;
@@ -29,162 +31,144 @@ import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
-import java.sql.Connection;
-
-import static org.junit.Assert.assertTrue;
-
@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
public class OrderingTranslatorIT extends ServerCase {
- @Inject
- private DataNode node;
-
- @Inject
- private ServerCaseDataSourceFactory dataSourceFactory;
-
- private Connection connection;
-
- @Before
- public void setUp() throws Exception {
- this.connection = dataSourceFactory.getSharedDataSource().getConnection();
- }
+ @Inject
+ private DataNode node;
- @After
- public void tearDown() throws Exception {
- connection.close();
- }
+ @Inject
+ private ServerCaseDataSourceFactory dataSourceFactory;
- /**
- * Tests ascending ordering on string attribute.
- */
- @Test
- public void testDoTranslation1() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
- q.addOrdering("artistName", SortOrder.ASCENDING);
+ /**
+ * Tests ascending ordering on string attribute.
+ */
+ @Test
+ public void testDoTranslation1() throws Exception {
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.addOrdering("artistName", SortOrder.ASCENDING);
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
- TranslationCase tstCase = new TranslationCase("Artist", null, "ta.ARTIST_NAME");
+ TranslationCase tstCase = new TranslationCase("Artist", null, "ta.ARTIST_NAME");
- StringBuilder out = new StringBuilder();
- new OrderingTranslator(qa).appendPart(out);
+ StringBuilder out = new StringBuilder();
+ new OrderingTranslator(qa).appendPart(out);
- assertTrue(out.length() > 0);
- tstCase.assertTranslatedWell(out.toString());
- }
+ assertTrue(out.length() > 0);
+ tstCase.assertTranslatedWell(out.toString());
+ }
- /**
- * Tests descending ordering on string attribute.
- */
- @Test
- public void testDoTranslation2() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
- q.addOrdering("artistName", SortOrder.DESCENDING);
+ /**
+ * Tests descending ordering on string attribute.
+ */
+ @Test
+ public void testDoTranslation2() throws Exception {
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.addOrdering("artistName", SortOrder.DESCENDING);
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
- TranslationCase tstCase = new TranslationCase("Artist", null, "ta.ARTIST_NAME DESC");
+ TranslationCase tstCase = new TranslationCase("Artist", null, "ta.ARTIST_NAME DESC");
- StringBuilder out = new StringBuilder();
- new OrderingTranslator(qa).appendPart(out);
+ StringBuilder out = new StringBuilder();
+ new OrderingTranslator(qa).appendPart(out);
- assertTrue(out.length() > 0);
- tstCase.assertTranslatedWell(out.toString());
- }
+ assertTrue(out.length() > 0);
+ tstCase.assertTranslatedWell(out.toString());
+ }
- /**
- * Tests ascending case-insensitive ordering on string attribute.
- */
- @Test
- public void testDoTranslation4() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
- q.addOrdering("artistName", SortOrder.ASCENDING_INSENSITIVE);
+ /**
+ * Tests ascending case-insensitive ordering on string attribute.
+ */
+ @Test
+ public void testDoTranslation4() throws Exception {
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.addOrdering("artistName", SortOrder.ASCENDING_INSENSITIVE);
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
- TranslationCase tstCase = new TranslationCase("Artist", null, "UPPER(ta.ARTIST_NAME)");
+ TranslationCase tstCase = new TranslationCase("Artist", null, "UPPER(ta.ARTIST_NAME)");
- StringBuilder out = new StringBuilder();
- new OrderingTranslator(qa).appendPart(out);
+ StringBuilder out = new StringBuilder();
+ new OrderingTranslator(qa).appendPart(out);
- assertTrue(out.length() > 0);
- String orderBySql = out.toString();
- assertTrue(orderBySql.contains("UPPER("));
- tstCase.assertTranslatedWell(orderBySql);
- }
+ assertTrue(out.length() > 0);
+ String orderBySql = out.toString();
+ assertTrue(orderBySql.contains("UPPER("));
+ tstCase.assertTranslatedWell(orderBySql);
+ }
- @Test
- public void testDoTranslation5() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
- q.addOrdering("artistName", SortOrder.DESCENDING_INSENSITIVE);
- q.addOrdering("paintingArray.estimatedPrice", SortOrder.ASCENDING);
+ @Test
+ public void testDoTranslation5() throws Exception {
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.addOrdering("artistName", SortOrder.DESCENDING_INSENSITIVE);
+ q.addOrdering("paintingArray.estimatedPrice", SortOrder.ASCENDING);
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
- TranslationCase tstCase = new TranslationCase("Artist", null, "UPPER(ta.ARTIST_NAME) DESC, ta.ESTIMATED_PRICE");
+ TranslationCase tstCase = new TranslationCase("Artist", null, "UPPER(ta.ARTIST_NAME) DESC, ta.ESTIMATED_PRICE");
- StringBuilder out = new StringBuilder();
- new OrderingTranslator(qa).appendPart(out);
+ StringBuilder out = new StringBuilder();
+ new OrderingTranslator(qa).appendPart(out);
- assertTrue(out.length() > 0);
- String orderBySql = out.toString();
+ assertTrue(out.length() > 0);
+ String orderBySql = out.toString();
- // Check there is an UPPER modifier
- int indexOfUpper = orderBySql.indexOf("UPPER(");
- assertTrue(indexOfUpper != -1);
+ // Check there is an UPPER modifier
+ int indexOfUpper = orderBySql.indexOf("UPPER(");
+ assertTrue(indexOfUpper != -1);
- // and ensure there is only ONE upper modifier
- assertTrue(orderBySql.indexOf("UPPER(", indexOfUpper + 1) == -1);
- tstCase.assertTranslatedWell(orderBySql);
- }
+ // and ensure there is only ONE upper modifier
+ assertTrue(orderBySql.indexOf("UPPER(", indexOfUpper + 1) == -1);
+ tstCase.assertTranslatedWell(orderBySql);
+ }
- @Test
- public void testDoTranslation6() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
- q.addOrdering("artistName", SortOrder.ASCENDING_INSENSITIVE);
- q.addOrdering("paintingArray.estimatedPrice", SortOrder.ASCENDING_INSENSITIVE);
+ @Test
+ public void testDoTranslation6() throws Exception {
+ SelectQuery q = new SelectQuery(Artist.class);
+ q.addOrdering("artistName", SortOrder.ASCENDING_INSENSITIVE);
+ q.addOrdering("paintingArray.estimatedPrice", SortOrder.ASCENDING_INSENSITIVE);
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
- TranslationCase tstCase = new TranslationCase("Artist", null,
- "UPPER(ta.ARTIST_NAME), UPPER(ta.ESTIMATED_PRICE)");
+ TranslationCase tstCase = new TranslationCase("Artist", null,
+ "UPPER(ta.ARTIST_NAME), UPPER(ta.ESTIMATED_PRICE)");
- StringBuilder out = new StringBuilder();
- new OrderingTranslator(qa).appendPart(out);
+ StringBuilder out = new StringBuilder();
+ new OrderingTranslator(qa).appendPart(out);
- assertTrue(out.length() > 0);
- String orderBySql = out.toString();
+ assertTrue(out.length() > 0);
+ String orderBySql = out.toString();
- // Check there is at least one UPPER modifier
- int indexOfUpper = orderBySql.indexOf("UPPER(");
- assertTrue(indexOfUpper != -1);
+ // Check there is at least one UPPER modifier
+ int indexOfUpper = orderBySql.indexOf("UPPER(");
+ assertTrue(indexOfUpper != -1);
- // and ensure there is another after it
- assertTrue(orderBySql.indexOf("UPPER(", indexOfUpper + 1) != -1);
+ // and ensure there is another after it
+ assertTrue(orderBySql.indexOf("UPPER(", indexOfUpper + 1) != -1);
- tstCase.assertTranslatedWell(orderBySql);
- }
+ tstCase.assertTranslatedWell(orderBySql);
+ }
- @Test
- public void testDoTranslation3() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
+ @Test
+ public void testDoTranslation3() throws Exception {
+ SelectQuery<Artist> q = SelectQuery.query(Artist.class);
- q.addOrdering("artistName", SortOrder.DESCENDING);
- q.addOrdering("paintingArray.estimatedPrice", SortOrder.ASCENDING);
+ q.addOrdering("artistName", SortOrder.DESCENDING);
+ q.addOrdering("paintingArray.estimatedPrice", SortOrder.ASCENDING);
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
- TranslationCase tstCase = new TranslationCase("Artist", null, "ta.ARTIST_NAME DESC, ta.ESTIMATED_PRICE");
+ TranslationCase tstCase = new TranslationCase("Artist", null, "ta.ARTIST_NAME DESC, ta.ESTIMATED_PRICE");
- StringBuilder out = new StringBuilder();
- new OrderingTranslator(qa).appendPart(out);
+ StringBuilder out = new StringBuilder();
+ new OrderingTranslator(qa).appendPart(out);
- assertTrue(out.length() > 0);
- String orderBySql = out.toString();
- tstCase.assertTranslatedWell(orderBySql);
- }
+ assertTrue(out.length() > 0);
+ String orderBySql = out.toString();
+ tstCase.assertTranslatedWell(orderBySql);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
index e4ff4ef..6475873 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslatorIT.java
@@ -19,6 +19,10 @@
package org.apache.cayenne.access.translator.select;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.access.DataNode;
@@ -38,115 +42,96 @@ import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
-import java.sql.Connection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
public class QualifierTranslatorIT extends ServerCase {
- @Inject
- private DataNode node;
-
- @Inject
- private ServerCaseDataSourceFactory dataSourceFactory;
-
- private Connection connection;
-
- @Before
- public void setUp() throws Exception {
- this.connection = dataSourceFactory.getSharedDataSource().getConnection();
- }
-
- @After
- public void tearDown() throws Exception {
- connection.close();
- }
-
- @Test
- public void testNonQualifiedQuery() throws Exception {
- TstQueryAssembler qa = new TstQueryAssembler(new MockQuery(), node, connection);
-
- try {
- new QualifierTranslator(qa).appendPart(new StringBuilder());
- fail();
- } catch (ClassCastException ccex) {
- // exception expected
- }
- }
-
- @Test
- public void testNullQualifier() throws Exception {
- TstQueryAssembler qa = new TstQueryAssembler(new SelectQuery<Object>(), node, connection);
-
- StringBuilder out = new StringBuilder();
- new QualifierTranslator(qa).appendPart(out);
- assertEquals(0, out.length());
- }
-
- @Test
- public void testUnary() throws Exception {
- doExpressionTest(new TstUnaryExpSuite());
- }
-
- @Test
- public void testBinary() throws Exception {
- doExpressionTest(new TstBinaryExpSuite());
- }
-
- @Test
- public void testTernary() throws Exception {
- doExpressionTest(new TstTernaryExpSuite());
- }
-
- @Test
- public void testExtras() throws Exception {
- ObjectId oid1 = new ObjectId("Gallery", "GALLERY_ID", 1);
- ObjectId oid2 = new ObjectId("Gallery", "GALLERY_ID", 2);
- Gallery g1 = new Gallery();
- Gallery g2 = new Gallery();
- g1.setObjectId(oid1);
- g2.setObjectId(oid2);
-
- Expression e1 = ExpressionFactory.matchExp("toGallery", g1);
- Expression e2 = e1.orExp(ExpressionFactory.matchExp("toGallery", g2));
-
- TstExpressionCase extraCase = new TstExpressionCase("Exhibit", e2,
- "(ta.GALLERY_ID = ?) OR (ta.GALLERY_ID = ?)", 4, 4);
-
- TstExpressionSuite suite = new TstExpressionSuite() {
- };
- suite.addCase(extraCase);
- doExpressionTest(suite);
- }
-
- private void doExpressionTest(TstExpressionSuite suite) throws Exception {
-
- TstExpressionCase[] cases = suite.cases();
-
- int len = cases.length;
- for (int i = 0; i < len; i++) {
- try {
-
- ObjEntity entity = node.getEntityResolver().getObjEntity(cases[i].getRootEntity());
- assertNotNull(entity);
- SelectQuery q = new SelectQuery(entity);
- q.setQualifier(cases[i].getCayenneExp());
-
- TstQueryAssembler qa = new TstQueryAssembler(q, node, connection);
-
- StringBuilder out = new StringBuilder();
- new QualifierTranslator(qa).appendPart(out);
- cases[i].assertTranslatedWell(out.toString());
- } catch (Exception ex) {
- throw new CayenneRuntimeException("Failed case: [" + i + "]: " + cases[i], ex);
- }
- }
- }
+ @Inject
+ private DataNode node;
+
+ @Inject
+ private ServerCaseDataSourceFactory dataSourceFactory;
+
+ @Test
+ public void testNonQualifiedQuery() throws Exception {
+ TstQueryAssembler qa = new TstQueryAssembler(new MockQuery(), node.getAdapter(), node.getEntityResolver());
+
+ try {
+ new QualifierTranslator(qa).appendPart(new StringBuilder());
+ fail();
+ } catch (ClassCastException ccex) {
+ // exception expected
+ }
+ }
+
+ @Test
+ public void testNullQualifier() throws Exception {
+ TstQueryAssembler qa = new TstQueryAssembler(new SelectQuery<Object>(), node.getAdapter(),
+ node.getEntityResolver());
+
+ StringBuilder out = new StringBuilder();
+ new QualifierTranslator(qa).appendPart(out);
+ assertEquals(0, out.length());
+ }
+
+ @Test
+ public void testUnary() throws Exception {
+ doExpressionTest(new TstUnaryExpSuite());
+ }
+
+ @Test
+ public void testBinary() throws Exception {
+ doExpressionTest(new TstBinaryExpSuite());
+ }
+
+ @Test
+ public void testTernary() throws Exception {
+ doExpressionTest(new TstTernaryExpSuite());
+ }
+
+ @Test
+ public void testExtras() throws Exception {
+ ObjectId oid1 = new ObjectId("Gallery", "GALLERY_ID", 1);
+ ObjectId oid2 = new ObjectId("Gallery", "GALLERY_ID", 2);
+ Gallery g1 = new Gallery();
+ Gallery g2 = new Gallery();
+ g1.setObjectId(oid1);
+ g2.setObjectId(oid2);
+
+ Expression e1 = ExpressionFactory.matchExp("toGallery", g1);
+ Expression e2 = e1.orExp(ExpressionFactory.matchExp("toGallery", g2));
+
+ TstExpressionCase extraCase = new TstExpressionCase("Exhibit", e2,
+ "(ta.GALLERY_ID = ?) OR (ta.GALLERY_ID = ?)", 4, 4);
+
+ TstExpressionSuite suite = new TstExpressionSuite() {
+ };
+ suite.addCase(extraCase);
+ doExpressionTest(suite);
+ }
+
+ private void doExpressionTest(TstExpressionSuite suite) throws Exception {
+
+ TstExpressionCase[] cases = suite.cases();
+
+ int len = cases.length;
+ for (int i = 0; i < len; i++) {
+ try {
+
+ ObjEntity entity = node.getEntityResolver().getObjEntity(cases[i].getRootEntity());
+ assertNotNull(entity);
+ SelectQuery q = new SelectQuery(entity);
+ q.setQualifier(cases[i].getCayenneExp());
+
+ TstQueryAssembler qa = new TstQueryAssembler(q, node.getAdapter(), node.getEntityResolver());
+
+ StringBuilder out = new StringBuilder();
+ new QualifierTranslator(qa).appendPart(out);
+ cases[i].assertTranslatedWell(out.toString());
+ } catch (Exception ex) {
+ throw new CayenneRuntimeException("Failed case: [" + i + "]: " + cases[i], ex);
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QueryAssemblerIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QueryAssemblerIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QueryAssemblerIT.java
index 006e73c..05c51e6 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QueryAssemblerIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QueryAssemblerIT.java
@@ -19,65 +19,43 @@
package org.apache.cayenne.access.translator.select;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.sql.Connection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
public class QueryAssemblerIT extends ServerCase {
- @Inject
- private DataNode dataNode;
-
- @Inject
- private ServerCaseDataSourceFactory dataSourceFactory;
-
- private Connection connection;
-
- private TstQueryAssembler qa;
-
- @Before
- public void setUp() throws Exception {
- this.connection = dataSourceFactory.getSharedDataSource().getConnection();
- this.qa = new TstQueryAssembler(new SelectQuery<Object>(), dataNode, connection);
- }
+ @Inject
+ private DataNode node;
- @After
- public void tearDown() throws Exception {
- connection.close();
- }
+ private TstQueryAssembler qa;
- @Test
- public void testGetQuery() throws Exception {
- assertNotNull(qa.getQuery());
- }
+ @Before
+ public void setUp() throws Exception {
+ this.qa = new TstQueryAssembler(new SelectQuery<Object>(), node.getAdapter(), node.getEntityResolver());
+ }
- @Test
- public void testAddToParamList() throws Exception {
+ @Test
+ public void testGetQuery() throws Exception {
+ assertNotNull(qa.getQuery());
+ }
- assertEquals(0, qa.getAttributes().size());
- assertEquals(0, qa.getValues().size());
+ @Test
+ public void testAddToParamList() throws Exception {
- qa.addToParamList(new DbAttribute(), new Object());
- assertEquals(1, qa.getAttributes().size());
- assertEquals(1, qa.getValues().size());
- }
+ assertEquals(0, qa.getBindings().length);
- @Test
- public void testCreateStatement() throws Exception {
- assertNotNull(qa.createStatement());
- }
+ qa.addToParamList(new DbAttribute(), new Object());
+ assertEquals(1, qa.getBindings().length);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/81f4fb50/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/SelectTranslatorIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/SelectTranslatorIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/SelectTranslatorIT.java
deleted file mode 100644
index b0ff3f8..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/SelectTranslatorIT.java
+++ /dev/null
@@ -1,794 +0,0 @@
-/*****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- ****************************************************************/
-
-package org.apache.cayenne.access.translator.select;
-
-import org.apache.cayenne.access.DataContext;
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.exp.ExpressionException;
-import org.apache.cayenne.exp.ExpressionFactory;
-import org.apache.cayenne.log.JdbcEventLogger;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.query.PrefetchTreeNode;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.query.SortOrder;
-import org.apache.cayenne.test.jdbc.DBHelper;
-import org.apache.cayenne.testdo.testmap.Artist;
-import org.apache.cayenne.testdo.testmap.ArtistExhibit;
-import org.apache.cayenne.testdo.testmap.CompoundPainting;
-import org.apache.cayenne.testdo.testmap.Painting;
-import org.apache.cayenne.unit.UnitDbAdapter;
-import org.apache.cayenne.unit.di.server.CayenneProjects;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.sql.Connection;
-import java.util.Date;
-import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
-public class SelectTranslatorIT extends ServerCase {
-
- @Inject
- private ServerCaseDataSourceFactory dataSourceFactory;
-
- @Inject
- private DataContext context;
-
- @Inject
- private UnitDbAdapter unitAdapter;
-
- @Inject
- private DBHelper dbHelper;
-
- @Inject
- private JdbcEventLogger logger;
-
- @Inject
- private DataNode dataNode;
-
- private Connection connection;
-
- @Before
- public void setUp() throws Exception {
- this.connection = dataSourceFactory.getSharedDataSource().getConnection();
- }
-
- @After
- public void tearDown() throws Exception {
- connection.close();
- }
-
- /**
- * Tests query creation with qualifier and ordering.
- */
- @Test
- public void testCreateSqlString1() throws Exception {
- // query with qualifier and ordering
- SelectQuery<Artist> q = new SelectQuery<Artist>(Artist.class, ExpressionFactory.likeExp("artistName", "a%"));
- q.addOrdering("dateOfBirth", SortOrder.ASCENDING);
-
- String generatedSql = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM "));
- assertTrue(generatedSql.indexOf(" ORDER BY ") > generatedSql.indexOf(" WHERE "));
- }
-
- /**
- * Tests query creation with qualifier and ordering.
- */
- @Test
- public void testDbEntityQualifier() throws Exception {
-
- SelectQuery<Artist> q = new SelectQuery<Artist>(Artist.class);
- final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
-
- entity.setQualifier(Expression.fromString("ARTIST_NAME = \"123\""));
- middleEntity.setQualifier(Expression.fromString("GROUP_ID = 1987"));
-
- try {
-
- String generatedSql = transl.createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- if (generatedSql.contains("RTRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
- } else if (generatedSql.contains("TRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
- } else {
- assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
- }
- } finally {
- entity.setQualifier(null);
- middleEntity.setQualifier(null);
- }
- }
-
- @Test
- public void testDbEntityQualifier_OuterJoin() throws Exception {
-
- SelectQuery q = new SelectQuery(Painting.class);
- q.addOrdering("toArtist+.artistName", SortOrder.ASCENDING);
-
- final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
-
- entity.setQualifier(Expression.fromString("ARTIST_NAME = \"123\""));
- middleEntity.setQualifier(Expression.fromString("GROUP_ID = 1987"));
-
- try {
-
- String generatedSql = transl.createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- if (generatedSql.contains("RTRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
- } else if (generatedSql.contains("TRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
- } else {
- assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
- }
-
- } finally {
- entity.setQualifier(null);
- middleEntity.setQualifier(null);
- }
- }
-
- @Test
- public void testDbEntityQualifier_FlattenedRel() throws Exception {
-
- SelectQuery<Artist> q = new SelectQuery<Artist>(Artist.class, ExpressionFactory.matchExp("groupArray.name",
- "bar"));
-
- final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
-
- entity.setQualifier(Expression.fromString("ARTIST_NAME = \"123\""));
- middleEntity.setQualifier(Expression.fromString("GROUP_ID = 1987"));
-
- try {
-
- String generatedSql = transl.createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- if (generatedSql.contains("RTRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
- } else if (generatedSql.contains("TRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
- } else {
- assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
- }
-
- } finally {
- entity.setQualifier(null);
- middleEntity.setQualifier(null);
- }
- }
-
- @Test
- public void testDbEntityQualifier_RelatedMatch() throws Exception {
-
- SelectQuery<Artist> q = new SelectQuery(Painting.class,
- ExpressionFactory.matchExp("toArtist.artistName", "foo"));
-
- final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- final DbEntity middleEntity = context.getEntityResolver().getDbEntity("ARTIST_GROUP");
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
-
- entity.setQualifier(Expression.fromString("ARTIST_NAME = \"123\""));
- middleEntity.setQualifier(Expression.fromString("GROUP_ID = 1987"));
-
- try {
-
- String generatedSql = transl.createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- if (generatedSql.contains("RTRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("RTRIM("));
- } else if (generatedSql.contains("TRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME) = ") > generatedSql.indexOf("TRIM("));
- } else {
- assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
- }
-
- } finally {
- entity.setQualifier(null);
- middleEntity.setQualifier(null);
- }
- }
-
- /**
- * Tests query creation with "distinct" specified.
- */
- @Test
- public void testCreateSqlString2() throws Exception {
- // query with "distinct" set
- SelectQuery q = new SelectQuery(Artist.class);
- q.setDistinct(true);
-
- String generatedSql = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT DISTINCT"));
- }
-
- /**
- * Test aliases when the same table used in more then 1 relationship. Check
- * translation of relationship path "ArtistExhibit.toArtist.artistName" and
- * "ArtistExhibit.toExhibit.toGallery.paintingArray.toArtist.artistName".
- */
- @Test
- public void testCreateSqlString5() throws Exception {
- // query with qualifier and ordering
- SelectQuery q = new SelectQuery(ArtistExhibit.class);
- q.setQualifier(ExpressionFactory.likeExp("toArtist.artistName", "a%"));
- q.andQualifier(ExpressionFactory.likeExp("toExhibit.toGallery.paintingArray.toArtist.artistName", "a%"));
-
- String generatedSql = new SelectTranslator(q, dataNode, connection).createSqlString();
- // logObj.warn("Query: " + generatedSql);
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM "));
-
- // check that there are 2 distinct aliases for the ARTIST table
- int ind1 = generatedSql.indexOf("ARTIST t", generatedSql.indexOf(" FROM "));
- assertTrue(ind1 > 0);
-
- int ind2 = generatedSql.indexOf("ARTIST t", ind1 + 1);
- assertTrue(ind2 > 0);
-
- assertTrue(generatedSql.charAt(ind1 + "ARTIST t".length()) != generatedSql.charAt(ind2 + "ARTIST t".length()));
- }
-
- /**
- * Test aliases when the same table used in more then 1 relationship. Check
- * translation of relationship path "ArtistExhibit.toArtist.artistName" and
- * "ArtistExhibit.toArtist.paintingArray.paintingTitle".
- */
- @Test
- public void testCreateSqlString6() throws Exception {
- // query with qualifier and ordering
- SelectQuery q = new SelectQuery(ArtistExhibit.class);
- q.setQualifier(ExpressionFactory.likeExp("toArtist.artistName", "a%"));
- q.andQualifier(ExpressionFactory.likeExp("toArtist.paintingArray.paintingTitle", "p%"));
-
- String generatedSql = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM "));
-
- // check that there is only one distinct alias for the ARTIST
- // table
- int ind1 = generatedSql.indexOf("ARTIST t", generatedSql.indexOf(" FROM "));
- assertTrue(ind1 > 0);
-
- int ind2 = generatedSql.indexOf("ARTIST t", ind1 + 1);
- assertTrue(generatedSql, ind2 < 0);
- }
-
- /**
- * Test query when qualifying on the same attribute more than once. Check
- * translation "Artist.dateOfBirth > ? AND Artist.dateOfBirth < ?".
- */
- @Test
- public void testCreateSqlString7() throws Exception {
- SelectQuery q = new SelectQuery(Artist.class);
- q.setQualifier(ExpressionFactory.greaterExp("dateOfBirth", new Date()));
- q.andQualifier(ExpressionFactory.lessExp("dateOfBirth", new Date()));
-
- String generatedSql = new SelectTranslator(q, dataNode, connection).createSqlString();
- // logObj.warn("Query: " + generatedSql);
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
-
- int i1 = generatedSql.indexOf(" FROM ");
- assertTrue(i1 > 0);
-
- int i2 = generatedSql.indexOf(" WHERE ");
- assertTrue(i2 > i1);
-
- int i3 = generatedSql.indexOf("DATE_OF_BIRTH", i2 + 1);
- assertTrue(i3 > i2);
-
- int i4 = generatedSql.indexOf("DATE_OF_BIRTH", i3 + 1);
- assertTrue("No second DOB comparison: " + i4 + ", " + i3, i4 > i3);
- }
-
- /**
- * Test query when qualifying on the same attribute accessed over
- * relationship, more than once. Check translation
- * "Painting.toArtist.dateOfBirth > ? AND Painting.toArtist.dateOfBirth <
- * ?".
- */
- @Test
- public void testCreateSqlString8() throws Exception {
- SelectQuery q = new SelectQuery();
- q.setRoot(Painting.class);
- q.setQualifier(ExpressionFactory.greaterExp("toArtist.dateOfBirth", new Date()));
- q.andQualifier(ExpressionFactory.lessExp("toArtist.dateOfBirth", new Date()));
-
- String generatedSql = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(generatedSql);
- assertTrue(generatedSql.startsWith("SELECT "));
-
- int i1 = generatedSql.indexOf(" FROM ");
- assertTrue(i1 > 0);
-
- int i2 = generatedSql.indexOf(" WHERE ");
- assertTrue(i2 > i1);
-
- int i3 = generatedSql.indexOf("DATE_OF_BIRTH", i2 + 1);
- assertTrue(i3 > i2);
-
- int i4 = generatedSql.indexOf("DATE_OF_BIRTH", i3 + 1);
- assertTrue("No second DOB comparison: " + i4 + ", " + i3, i4 > i3);
- }
-
- @Test
- public void testCreateSqlString9() throws Exception {
- // query for a compound ObjEntity with qualifier
- SelectQuery q = new SelectQuery(CompoundPainting.class, ExpressionFactory.likeExp("artistName", "a%"));
-
- String sql = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- // do some simple assertions to make sure all parts are in
- assertNotNull(sql);
- assertTrue(sql.startsWith("SELECT "));
-
- int i1 = sql.indexOf(" FROM ");
- assertTrue(i1 > 0);
-
- int i2 = sql.indexOf("PAINTING");
- assertTrue(i2 > 0);
-
- int i3 = sql.indexOf("ARTIST");
- assertTrue(i3 > 0);
-
- int i4 = sql.indexOf("GALLERY");
- assertTrue(i4 > 0);
-
- int i5 = sql.indexOf("PAINTING_INFO");
- assertTrue(i5 > 0);
-
- int i6 = sql.indexOf("ARTIST_NAME");
- assertTrue(i6 > 0);
-
- int i7 = sql.indexOf("ESTIMATED_PRICE");
- assertTrue(i7 > 0);
-
- int i8 = sql.indexOf("GALLERY_NAME");
- assertTrue(i8 > 0);
-
- int i9 = sql.indexOf("PAINTING_TITLE");
- assertTrue(i9 > 0);
-
- int i10 = sql.indexOf("TEXT_REVIEW");
- assertTrue(i10 > 0);
-
- int i11 = sql.indexOf("PAINTING_ID");
- assertTrue(i11 > 0);
-
- int i12 = sql.indexOf("ARTIST_ID");
- assertTrue(i12 > 0);
-
- int i13 = sql.indexOf("GALLERY_ID");
- assertTrue(i13 > 0);
-
- }
-
- @Test
- public void testCreateSqlString10() throws Exception {
- // query with to-many joint prefetches
- SelectQuery q = new SelectQuery(Artist.class);
- q.addPrefetch(Artist.PAINTING_ARRAY_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
- String sql = transl.createSqlString();
- assertNotNull(sql);
- assertTrue(sql.startsWith("SELECT "));
-
- int i1 = sql.indexOf("ARTIST_ID");
- assertTrue(sql, i1 > 0);
-
- int i2 = sql.indexOf("FROM");
- assertTrue(sql, i2 > 0);
-
- assertTrue(sql, sql.indexOf("PAINTING_ID") > 0);
-
- // assert we have one join
- assertEquals(1, transl.joinStack.size());
- }
-
- @Test
- public void testCreateSqlString11() throws Exception {
- // query with joint prefetches and other joins
- SelectQuery q = new SelectQuery(Artist.class, Expression.fromString("paintingArray.paintingTitle = 'a'"));
- q.addPrefetch(Artist.PAINTING_ARRAY_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
-
- transl.createSqlString();
-
- // assert we only have one join
- assertEquals(2, transl.joinStack.size());
- }
-
- @Test
- public void testCreateSqlString12() throws Exception {
- // query with to-one joint prefetches
- SelectQuery q = new SelectQuery(Painting.class);
- q.addPrefetch(Painting.TO_ARTIST_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
-
- SelectTranslator transl = new SelectTranslator(q, dataNode, connection);
-
- String sql = transl.createSqlString();
- assertNotNull(sql);
- assertTrue(sql.startsWith("SELECT "));
-
- int i1 = sql.indexOf("ARTIST_ID");
- assertTrue(sql, i1 > 0);
-
- int i2 = sql.indexOf("FROM");
- assertTrue(sql, i2 > 0);
-
- assertTrue(sql, sql.indexOf("PAINTING_ID") > 0);
-
- // assert we have one join
- assertEquals(1, transl.joinStack.size());
- }
-
- @Test
- public void testCreateSqlString13() throws Exception {
- // query with invalid joint prefetches
- SelectQuery q = new SelectQuery(Painting.class);
- q.addPrefetch("invalid.invalid").setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
-
- try {
- new SelectTranslator(q, dataNode, connection).createSqlString();
- fail("Invalid jointPrefetch must have thrown...");
- } catch (ExpressionException e) {
- // expected
- }
- }
-
- @Test
- public void testCreateSqlStringWithQuoteSqlIdentifiers() throws Exception {
-
- try {
- SelectQuery q = new SelectQuery(Artist.class);
- DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- entity.getDataMap().setQuotingSQLIdentifiers(true);
- q.addOrdering("dateOfBirth", SortOrder.ASCENDING);
-
- String charStart = unitAdapter.getIdentifiersStartQuote();
- String charEnd = unitAdapter.getIdentifiersEndQuote();
-
- String s = new SelectTranslator(q, dataNode, connection).createSqlString();
- assertTrue(s.startsWith("SELECT "));
- int iFrom = s.indexOf(" FROM ");
- assertTrue(iFrom > 0);
- int artistName = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
- assertTrue(artistName > 0 && artistName < iFrom);
- int artistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
- assertTrue(artistId > 0 && artistId < iFrom);
- int dateOfBirth = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
- assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
- int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t0" + charEnd);
- assertTrue(iArtist > iFrom);
- int iOrderBy = s.indexOf(" ORDER BY ");
- int dateOfBirth2 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd,
- iOrderBy);
- assertTrue(iOrderBy > iArtist);
- assertTrue(dateOfBirth2 > iOrderBy);
-
- } finally {
- DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- entity.getDataMap().setQuotingSQLIdentifiers(false);
- }
-
- }
-
- @Test
- public void testCreateSqlStringWithQuoteSqlIdentifiers2() throws Exception {
-
- try {
- SelectQuery q = new SelectQuery(Artist.class);
- DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- entity.getDataMap().setQuotingSQLIdentifiers(true);
- q.setQualifier(ExpressionFactory.greaterExp("dateOfBirth", new Date()));
- q.andQualifier(ExpressionFactory.lessExp("dateOfBirth", new Date()));
-
- String charStart = unitAdapter.getIdentifiersStartQuote();
- String charEnd = unitAdapter.getIdentifiersEndQuote();
-
- String s = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- assertTrue(s.startsWith("SELECT "));
- int iFrom = s.indexOf(" FROM ");
- assertTrue(iFrom > 0);
- int artistName = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
- assertTrue(artistName > 0 && artistName < iFrom);
- int artistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
- assertTrue(artistId > 0 && artistId < iFrom);
- int dateOfBirth = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
- assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
- int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t0" + charEnd);
- assertTrue(iArtist > iFrom);
- int iWhere = s.indexOf(" WHERE ");
- assertTrue(iWhere > iArtist);
-
- int dateOfBirth2 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd
- + " > ?");
- assertTrue(dateOfBirth2 > iWhere);
-
- int iAnd = s.indexOf(" AND ");
- assertTrue(iAnd > iWhere);
- int dateOfBirth3 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd
- + " < ?");
- assertTrue(dateOfBirth3 > iAnd);
-
- } finally {
- DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- entity.getDataMap().setQuotingSQLIdentifiers(false);
- }
- }
-
- @Test
- public void testCreateSqlStringWithQuoteSqlIdentifiers3() throws Exception {
-
- // query with joint prefetches and other joins
- // and with QuoteSqlIdentifiers = true
- try {
- SelectQuery q = new SelectQuery(Artist.class, Expression.fromString("paintingArray.paintingTitle = 'a'"));
- q.addPrefetch(Artist.PAINTING_ARRAY_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
-
- DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- entity.getDataMap().setQuotingSQLIdentifiers(true);
-
- String charStart = unitAdapter.getIdentifiersStartQuote();
- String charEnd = unitAdapter.getIdentifiersEndQuote();
-
- String s = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- assertTrue(s.startsWith("SELECT DISTINCT "));
- int iFrom = s.indexOf(" FROM ");
- assertTrue(iFrom > 0);
- int artistName = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
- assertTrue(artistName > 0 && artistName < iFrom);
- int artistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
- assertTrue(artistId > 0 && artistId < iFrom);
- int dateOfBirth = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
- assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
- int estimatedPrice = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ESTIMATED_PRICE" + charEnd);
- assertTrue(estimatedPrice > 0 && estimatedPrice < iFrom);
- int paintingDescription = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "PAINTING_DESCRIPTION"
- + charEnd);
- assertTrue(paintingDescription > 0 && paintingDescription < iFrom);
- int paintingTitle = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "PAINTING_TITLE" + charEnd);
- assertTrue(paintingTitle > 0 && paintingTitle < iFrom);
- int artistIdT1 = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
- assertTrue(artistIdT1 > 0 && artistIdT1 < iFrom);
- int galleryId = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "GALLERY_ID" + charEnd);
- assertTrue(galleryId > 0 && galleryId < iFrom);
- int paintingId = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "PAINTING_ID" + charEnd);
- assertTrue(paintingId > 0 && paintingId < iFrom);
- int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t0" + charEnd);
- assertTrue(iArtist > iFrom);
- int iLeftJoin = s.indexOf("LEFT JOIN");
- assertTrue(iLeftJoin > iFrom);
- int iPainting = s.indexOf(charStart + "PAINTING" + charEnd + " " + charStart + "t1" + charEnd);
- assertTrue(iPainting > iLeftJoin);
- int iOn = s.indexOf(" ON ");
- assertTrue(iOn > iLeftJoin);
- int iArtistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
- assertTrue(iArtistId > iOn);
- int iArtistIdT1 = s
- .indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
- assertTrue(iArtistIdT1 > iOn);
- int i = s.indexOf("=", iLeftJoin);
- assertTrue(iArtistIdT1 > i || iArtistId > i);
- int iJoin = s.indexOf("JOIN");
- assertTrue(iJoin > iLeftJoin);
- int iPainting2 = s.indexOf(charStart + "PAINTING" + charEnd + " " + charStart + "t2" + charEnd);
- assertTrue(iPainting2 > iJoin);
- int iOn2 = s.indexOf(" ON ");
- assertTrue(iOn2 > iJoin);
- int iArtistId2 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iJoin);
- assertTrue(iArtistId2 > iOn2);
- int iArtistId2T2 = s.indexOf(charStart + "t2" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iJoin);
- assertTrue(iArtistId2T2 > iOn2);
- int i2 = s.indexOf("=", iJoin);
- assertTrue(iArtistId2T2 > i2 || iArtistId2 > i2);
- int iWhere = s.indexOf(" WHERE ");
- assertTrue(iWhere > iJoin);
-
- int paintingTitle2 = s.indexOf(charStart + "t2" + charEnd + "." + charStart + "PAINTING_TITLE" + charEnd
- + " = ?");
- assertTrue(paintingTitle2 > iWhere);
-
- } finally {
- DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
- entity.getDataMap().setQuotingSQLIdentifiers(false);
- }
- }
-
- @Test
- public void testCreateSqlStringWithQuoteSqlIdentifiers4() throws Exception {
-
- // query with to-one joint prefetches
- // and with QuoteSqlIdentifiers = true
- try {
- SelectQuery q = new SelectQuery(Painting.class);
- q.addPrefetch(Painting.TO_ARTIST_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
-
- DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
- entity.getDataMap().setQuotingSQLIdentifiers(true);
-
- String charStart = unitAdapter.getIdentifiersStartQuote();
- String charEnd = unitAdapter.getIdentifiersEndQuote();
-
- String s = new SelectTranslator(q, dataNode, connection).createSqlString();
-
- assertTrue(s.startsWith("SELECT "));
- int iFrom = s.indexOf(" FROM ");
- assertTrue(iFrom > 0);
-
- int paintingDescription = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "PAINTING_DESCRIPTION"
- + charEnd);
- assertTrue(paintingDescription > 0 && paintingDescription < iFrom);
- int paintingTitle = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "PAINTING_TITLE" + charEnd);
- assertTrue(paintingTitle > 0 && paintingTitle < iFrom);
- int artistIdT1 = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
- assertTrue(artistIdT1 > 0 && artistIdT1 < iFrom);
- int estimatedPrice = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ESTIMATED_PRICE" + charEnd);
- assertTrue(estimatedPrice > 0 && estimatedPrice < iFrom);
- int galleryId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "GALLERY_ID" + charEnd);
- assertTrue(galleryId > 0 && galleryId < iFrom);
- int paintingId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "PAINTING_ID" + charEnd);
- assertTrue(paintingId > 0 && paintingId < iFrom);
- int artistName = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_NAME" + charEnd);
- assertTrue(artistName > 0 && artistName < iFrom);
- int artistId = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd);
- assertTrue(artistId > 0 && artistId < iFrom);
- int dateOfBirth = s.indexOf(charStart + "t1" + charEnd + "." + charStart + "DATE_OF_BIRTH" + charEnd);
- assertTrue(dateOfBirth > 0 && dateOfBirth < iFrom);
- int iPainting = s.indexOf(charStart + "PAINTING" + charEnd + " " + charStart + "t0" + charEnd);
- assertTrue(iPainting > iFrom);
-
- int iLeftJoin = s.indexOf("LEFT JOIN");
- assertTrue(iLeftJoin > iFrom);
- int iArtist = s.indexOf(charStart + "ARTIST" + charEnd + " " + charStart + "t1" + charEnd);
- assertTrue(iArtist > iLeftJoin);
- int iOn = s.indexOf(" ON ");
- assertTrue(iOn > iLeftJoin);
- int iArtistId = s.indexOf(charStart + "t0" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
- assertTrue(iArtistId > iOn);
- int iArtistIdT1 = s
- .indexOf(charStart + "t1" + charEnd + "." + charStart + "ARTIST_ID" + charEnd, iLeftJoin);
- assertTrue(iArtistIdT1 > iOn);
- int i = s.indexOf("=", iLeftJoin);
- assertTrue(iArtistIdT1 > i || iArtistId > i);
-
- } finally {
- DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
- entity.getDataMap().setQuotingSQLIdentifiers(false);
- }
- }
-
- /**
- * Tests columns generated for a simple object query.
- */
- @Test
- public void testBuildResultColumns1() throws Exception {
- SelectQuery q = new SelectQuery(Painting.class);
- SelectTranslator tr = new SelectTranslator(q, dataNode, connection);
-
- List<?> columns = tr.buildResultColumns();
-
- // all DbAttributes must be included
- DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
- for (final DbAttribute a : entity.getAttributes()) {
- ColumnDescriptor c = new ColumnDescriptor(a, "t0");
- assertTrue("No descriptor for " + a + ", columns: " + columns, columns.contains(c));
- }
- }
-
- /**
- * Tests columns generated for an object query with joint prefetch.
- */
- @Test
- public void testBuildResultColumns2() throws Exception {
- SelectQuery q = new SelectQuery(Painting.class);
- q.addPrefetch(Painting.TO_ARTIST_PROPERTY).setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
- SelectTranslator tr = new SelectTranslator(q, dataNode, connection);
-
- List<?> columns = tr.buildResultColumns();
-
- // assert root entity columns
- DbEntity entity = context.getEntityResolver().getDbEntity("PAINTING");
- for (final DbAttribute a : entity.getAttributes()) {
- ColumnDescriptor c = new ColumnDescriptor(a, "t0");
- assertTrue("No descriptor for " + a + ", columns: " + columns, columns.contains(c));
- }
-
- // assert joined columns
- DbEntity joined = context.getEntityResolver().getDbEntity("ARTIST");
- for (final DbAttribute a : joined.getAttributes()) {
-
- // skip ARTIST PK, it is joined from painting
- if (Artist.ARTIST_ID_PK_COLUMN.equals(a.getName())) {
- continue;
- }
-
- ColumnDescriptor c = new ColumnDescriptor(a, "t1");
- c.setDataRowKey("toArtist." + a.getName());
- assertTrue("No descriptor for " + a + ", columns: " + columns, columns.contains(c));
- }
- }
-
-}
[7/7] cayenne git commit: CAY-2003 cdbimport doesn't work properly
with several includeTable tags
Posted by aa...@apache.org.
CAY-2003 cdbimport doesn't work properly with several includeTable tags
missing record in RELEASE-NOTES
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/a6d153a2
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/a6d153a2
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/a6d153a2
Branch: refs/heads/master
Commit: a6d153a282b51d0c911efd16ccf6100b1400cb9e
Parents: 81f4fb5
Author: aadamchik <aa...@apache.org>
Authored: Tue Apr 28 12:44:27 2015 +0200
Committer: aadamchik <aa...@apache.org>
Committed: Tue Apr 28 12:44:27 2015 +0200
----------------------------------------------------------------------
docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 +
1 file changed, 1 insertion(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a6d153a2/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 46dd250..2d270aa 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -31,6 +31,7 @@ CAY-1994 Modeler Migration Tool Shows No Changes
CAY-1997 Difference in NULL handling inside the path between PropertyUtils and DataObject.readNestedProperty
CAY-1998 Speeding up PropertyUtils
CAY-1999 Unneeded Property import for superclasses with no properties
+CAY-2003 cdbimport doesn't work properly with several includeTable tags
----------------------------------
Release: 4.0.M2