Repository: cayenne Updated Branches: refs/heads/master 8c4f1ad0f -> 32bdcda54
CAY-2272 ColumnSelect: methods to manually control DISTINCT clause Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/32bdcda5 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/32bdcda5 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/32bdcda5 Branch: refs/heads/master Commit: 32bdcda545077bf6867fbc7e856109ae14fc01b1 Parents: 8c4f1ad Author: Nikita Timofeev <stari...@gmail.com> Authored: Tue Mar 21 15:51:33 2017 +0300 Committer: Nikita Timofeev <stari...@gmail.com> Committed: Tue Mar 21 15:51:33 2017 +0300 ---------------------------------------------------------------------- .../cayenne/lifecycle/id/StringIdQuery.java | 5 ++ .../apache/cayenne/access/DataDomainQuery.java | 5 ++ .../cayenne/access/DataDomainQueryAction.java | 13 +++-- .../access/ObjectsFromDataRowsQuery.java | 5 ++ .../cayenne/access/jdbc/SelectAction.java | 3 +- .../select/DefaultSelectTranslator.java | 13 +++-- .../apache/cayenne/query/BaseQueryMetadata.java | 8 +++ .../org/apache/cayenne/query/ColumnSelect.java | 22 ++++++++ .../cayenne/query/DefaultQueryMetadata.java | 8 +++ .../org/apache/cayenne/query/QueryMetadata.java | 5 ++ .../cayenne/query/QueryMetadataProxy.java | 5 ++ .../org/apache/cayenne/query/SelectQuery.java | 8 +++ .../cayenne/query/SelectQueryMetadata.java | 16 ++++++ .../apache/cayenne/query/ColumnSelectIT.java | 58 +++++++++++++++++++- .../apache/cayenne/query/ColumnSelectTest.java | 18 ++++++ .../apache/cayenne/query/MockQueryMetadata.java | 5 ++ docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + 17 files changed, 184 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java ---------------------------------------------------------------------- diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java index 2b28f44..56c500f 100644 --- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java +++ b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java @@ -238,6 +238,11 @@ public class StringIdQuery implements Query { public int getStatementFetchSize() { return QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT; } + + @Override + public boolean isSuppressingDistinct() { + return false; + } }; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java index 0488065..7d20356 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQuery.java @@ -160,4 +160,9 @@ class DataDomainQuery implements Query, QueryMetadata { public int getStatementFetchSize() { return 0; } + + @Override + public boolean isSuppressingDistinct() { + return false; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java index 6e9b9f8..7e00cca 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java @@ -769,11 +769,14 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver { } } } - Set<List<?>> seen = new HashSet<>(mainRows.size()); - Iterator<Object[]> it = mainRows.iterator(); - while (it.hasNext()) { - if (!seen.add(Arrays.asList(it.next()))) { - it.remove(); + + if(!metadata.isSuppressingDistinct()) { + Set<List<?>> seen = new HashSet<>(mainRows.size()); + Iterator<Object[]> it = mainRows.iterator(); + while (it.hasNext()) { + if (!seen.add(Arrays.asList(it.next()))) { + it.remove(); + } } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java index ee23454..f0f319e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectsFromDataRowsQuery.java @@ -160,4 +160,9 @@ class ObjectsFromDataRowsQuery implements Query, QueryMetadata { public int getStatementFetchSize() { return 0; } + + @Override + public boolean isSuppressingDistinct() { + return false; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/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 ce2a22b..8001067 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 @@ -180,7 +180,8 @@ public class SelectAction extends BaseSQLAction { } private <T> ResultIterator<T> forSuppressedDistinct(ResultIterator<T> iterator, SelectTranslator translator) { - if (!translator.isSuppressingDistinct()) { + if (!translator.isSuppressingDistinct() || + queryMetadata.isSuppressingDistinct()) { return iterator; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/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 index 42ac0b8..61aec97 100644 --- 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 @@ -153,12 +153,13 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra // 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; + suppressingDistinct = queryMetadata.isSuppressingDistinct(); + if(!suppressingDistinct) { + for (ColumnDescriptor column : resultColumns) { + if (isUnsupportedForDistinct(column.getJdbcType())) { + suppressingDistinct = true; + break; + } } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java index 8c523f5..92b7835 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java @@ -517,4 +517,12 @@ class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable prefetchTree.removePath(prefetch); } } + + /** + * @since 4.0 + */ + @Override + public boolean isSuppressingDistinct() { + return false; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java index 69494b8..b0f708c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java @@ -62,6 +62,8 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> { // package private for tests boolean singleColumn = true; private Expression having; + boolean distinct; + boolean suppressDistinct; protected ColumnSelect() { super(); @@ -93,6 +95,8 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> { replacement.setColumns(columns); replacement.setHavingQualifier(having); replacement.setCanReturnScalarValue(singleColumn); + replacement.setDistinct(distinct); + replacement.setSuppressDistinct(suppressDistinct); return replacement; } @@ -286,6 +290,24 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> { return this; } + /** + * Explicitly request distinct in query. + */ + public ColumnSelect<T> distinct() { + this.suppressDistinct = false; + this.distinct = true; + return this; + } + + /** + * Explicitly suppress distinct in query. + */ + public ColumnSelect<T> suppressDistinct() { + this.suppressDistinct = true; + this.distinct = false; + return this; + } + private void setActiveExpression(Expression exp) { if(havingExpressionIsActive) { having = exp; http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java index 10dff3c..47aa751 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/DefaultQueryMetadata.java @@ -163,4 +163,12 @@ class DefaultQueryMetadata implements QueryMetadata { public int getStatementFetchSize() { return QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT; } + + /** + * @since 4.0 + */ + @Override + public boolean isSuppressingDistinct() { + return false; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java index 15a3057..3de4737 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadata.java @@ -257,4 +257,9 @@ public interface QueryMetadata { * @since 3.0 */ int getStatementFetchSize(); + + /** + * @since 4.0 + */ + boolean isSuppressingDistinct(); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java index 53ec1bf..259aa51 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/QueryMetadataProxy.java @@ -143,4 +143,9 @@ public class QueryMetadataProxy implements QueryMetadata { public int getStatementFetchSize() { return mdDelegate.getStatementFetchSize(); } + + @Override + public boolean isSuppressingDistinct() { + return mdDelegate.isSuppressingDistinct(); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java index 97153cc..ff9660e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java @@ -614,6 +614,14 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery, } /** + * Sets <code>distinct</code> property that determines whether this query + * returns distinct row. + */ + public void setSuppressDistinct(boolean suppressDistinct) { + this.metaData.setSuppressingDistinct(suppressDistinct); + } + + /** * Adds one or more aliases for the qualifier expression path. Aliases serve * to instruct Cayenne to generate separate sets of joins for overlapping * paths, that maybe needed for complex conditions. An example of an http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java index 158fbea..cb4d195 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java @@ -59,6 +59,7 @@ class SelectQueryMetadata extends BaseQueryMetadata { Map<String, String> pathSplitAliases; boolean isSingleResultSetMapping; + boolean suppressingDistinct; @Override void copyFromInfo(QueryMetadata info) { @@ -383,4 +384,19 @@ class SelectQueryMetadata extends BaseQueryMetadata { public boolean isSingleResultSetMapping() { return isSingleResultSetMapping; } + + /** + * @since 4.0 + */ + @Override + public boolean isSuppressingDistinct() { + return suppressingDistinct; + } + + /** + * @since 4.0 + */ + public void setSuppressingDistinct(boolean suppressingDistinct) { + this.suppressingDistinct = suppressingDistinct; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java index 02102c5..2d79d29 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java @@ -51,6 +51,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -75,7 +76,7 @@ public class ColumnSelectIT extends ServerCase { // Format: d/m/YY private static final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US); - private TableHelper tArtist; + private TableHelper tArtist, tPaintings; @Before public void createArtistsDataSet() throws Exception { @@ -95,7 +96,7 @@ public class ColumnSelectIT extends ServerCase { tGallery.setColumns("GALLERY_ID", "GALLERY_NAME"); tGallery.insert(1, "tate modern"); - TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING"); + tPaintings = new TableHelper(dbHelper, "PAINTING"); tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID", "ESTIMATED_PRICE"); for (int i = 1; i <= 20; i++) { tPaintings.insert(i, "painting" + i, i % 5 + 1, 1, 22 - i); @@ -839,4 +840,57 @@ public class ColumnSelectIT extends ServerCase { assertEquals(4, result.size()); } + /* + * Test distinct() / suppressDistinct() methods + */ + + @Test + public void testExplicitDistinct() throws Exception { + tArtist.insert(21, "artist1", null); + + List<String> result = ObjectSelect + .columnQuery(Artist.class, Artist.ARTIST_NAME) + .select(context); + assertEquals(21, result.size()); + + List<String> result2 = ObjectSelect + .columnQuery(Artist.class, Artist.ARTIST_NAME) + .suppressDistinct() + .select(context); + assertEquals(result, result2); + + result = ObjectSelect + .columnQuery(Artist.class, Artist.ARTIST_NAME) + .distinct() + .select(context); + assertEquals(20, result.size()); + } + + + @Test + public void testSuppressDistinct() throws Exception { + // create non unique artist name / painting name pair + tArtist.insert(21, "artist1", null); + tPaintings.insert(22, "painting10", 21, 1, 23); + + List<Object[]> result = ObjectSelect + .columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.PAINTING_ARRAY.dot(Painting.PAINTING_TITLE)) + .select(context); + assertEquals(21, result.size()); + + List<Object[]> result2 = ObjectSelect + .columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.PAINTING_ARRAY.dot(Painting.PAINTING_TITLE)) + .distinct() + .select(context); + assertEquals(result.size(), result2.size()); + for(int i=0; i<result.size(); i++) { + assertArrayEquals(result.get(i), result2.get(i)); + } + + result = ObjectSelect + .columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.PAINTING_ARRAY.dot(Painting.PAINTING_TITLE)) + .suppressDistinct() + .select(context); + assertEquals(22, result.size()); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java index 188455f..eb09967 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java @@ -237,4 +237,22 @@ public class ColumnSelectTest { assertEquals(properties, q.getColumns()); } + @Test + public void testDistinct() { + ColumnSelect<Artist> q = new ColumnSelect<>(); + + assertFalse(q.distinct); + assertFalse(q.suppressDistinct); + + q.distinct(); + + assertTrue(q.distinct); + assertFalse(q.suppressDistinct); + + q.suppressDistinct(); + + assertFalse(q.distinct); + assertTrue(q.suppressDistinct); + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java b/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java index 80c18d1..1b7df00 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/MockQueryMetadata.java @@ -120,4 +120,9 @@ public class MockQueryMetadata implements QueryMetadata { public int getStatementFetchSize() { return 0; } + + @Override + public boolean isSuppressingDistinct() { + return false; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/32bdcda5/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 7c88496..d658bae 100644 --- a/docs/doc/src/main/resources/RELEASE-NOTES.txt +++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt @@ -22,6 +22,7 @@ CAY-2259 QueryCache: support for referencing type-safe caches CAY-2269 Add support for date/time components extraction in expression functions CAY-2270 Update function support in expression parser CAY-2271 ColumnSelect: support for prefetch and limit +CAY-2272 ColumnSelect: methods to manually control DISTINCT clause Bug Fixes: