Repository: cayenne
Updated Branches:
  refs/heads/master 9c07e1826 -> 739d02ed5


CAY-2412 test case + warning about limit with joint prefetch


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/739d02ed
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/739d02ed
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/739d02ed

Branch: refs/heads/master
Commit: 739d02ed5f143148b4437894febfc3ede9aff7d8
Parents: 9c07e18
Author: Nikita Timofeev <stari...@gmail.com>
Authored: Tue Mar 6 17:43:29 2018 +0300
Committer: Nikita Timofeev <stari...@gmail.com>
Committed: Tue Mar 6 17:43:29 2018 +0300

----------------------------------------------------------------------
 .../select/DefaultSelectTranslator.java         |  68 ++++++++++++
 .../test/java/org/apache/cayenne/Cay2412IT.java | 110 +++++++++++++++++++
 2 files changed, 178 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/739d02ed/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 b1da173..b391a16 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
@@ -42,6 +42,7 @@ 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.PrefetchProcessor;
 import org.apache.cayenne.query.PrefetchSelectQuery;
 import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.query.Query;
@@ -55,6 +56,8 @@ 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.sql.Types;
 import java.util.ArrayList;
@@ -72,6 +75,8 @@ import java.util.Set;
  */
 public class DefaultSelectTranslator extends QueryAssembler implements 
SelectTranslator {
 
+       private static final Logger logger = 
LoggerFactory.getLogger(SelectTranslator.class);
+
        protected static final int[] UNSUPPORTED_DISTINCT_TYPES = { Types.BLOB, 
Types.CLOB, Types.NCLOB,
                        Types.LONGVARBINARY, Types.LONGVARCHAR, 
Types.LONGNVARCHAR };
 
@@ -110,6 +115,8 @@ public class DefaultSelectTranslator extends QueryAssembler 
implements SelectTra
         */
        AddJoinListener joinListener;
 
+       JointPrefetchChecker jointPrefetchChecker = new JointPrefetchChecker();
+
 
        public DefaultSelectTranslator(Query query, DbAdapter adapter, 
EntityResolver entityResolver) {
                super(query, adapter, entityResolver);
@@ -129,6 +136,8 @@ public class DefaultSelectTranslator extends QueryAssembler 
implements SelectTra
        @Override
        protected void doTranslate() {
 
+               checkLimitAndJointPrefetch();
+
                DataMap dataMap = queryMetadata.getDataMap();
                JoinStack joins = getJoinStack();
 
@@ -249,6 +258,22 @@ public class DefaultSelectTranslator extends 
QueryAssembler implements SelectTra
        }
 
        /**
+        * Warn user in case query uses both limit and joint prefetch, as we 
don't support this combination.
+        */
+       private void checkLimitAndJointPrefetch() {
+               if(queryMetadata.getFetchLimit() == 0 && 
queryMetadata.getFetchOffset() == 0) {
+                       return;
+               }
+
+               
if(!jointPrefetchChecker.haveJointNode(queryMetadata.getPrefetchTree())) {
+                       return;
+               }
+
+               logger.warn("Query uses both limit and joint prefetch, this 
most probably will lead to incorrect result. " +
+                               "Either use disjointById prefetch or get full 
result set.");
+       }
+
+       /**
         * Allows subclasses to insert their own dialect of DISTINCT statement 
to
         * improve performance.
         *
@@ -920,4 +945,47 @@ public class DefaultSelectTranslator extends 
QueryAssembler implements SelectTra
        interface AddJoinListener {
                void joinAdded();
        }
+
+       private static class JointPrefetchChecker implements PrefetchProcessor {
+               private boolean haveJointNode;
+
+               public JointPrefetchChecker() {
+               }
+
+               public boolean haveJointNode(PrefetchTreeNode prefetchTree) {
+                       haveJointNode = false;
+                       prefetchTree.traverse(this);
+                       return haveJointNode;
+               }
+
+               @Override
+        public boolean startPhantomPrefetch(PrefetchTreeNode node) {
+            return true;
+        }
+
+               @Override
+        public boolean startDisjointPrefetch(PrefetchTreeNode node) {
+            return true;
+        }
+
+               @Override
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode 
prefetchTreeNode) {
+            return true;
+        }
+
+               @Override
+        public boolean startJointPrefetch(PrefetchTreeNode node) {
+            haveJointNode = true;
+            return false;
+        }
+
+               @Override
+        public boolean startUnknownPrefetch(PrefetchTreeNode node) {
+            return true;
+        }
+
+               @Override
+        public void finishPrefetch(PrefetchTreeNode node) {
+        }
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/739d02ed/cayenne-server/src/test/java/org/apache/cayenne/Cay2412IT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/Cay2412IT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/Cay2412IT.java
new file mode 100644
index 0000000..44788a1
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/Cay2412IT.java
@@ -0,0 +1,110 @@
+/*****************************************************************
+ *   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;
+
+import java.sql.Types;
+
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+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.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @since 4.1
+ */
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class Cay2412IT extends ServerCase {
+
+    @Inject
+    DataContext context;
+
+    @Inject
+    private DBHelper dbHelper;
+
+    @Before
+    public void prepareData() throws Exception {
+        TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH");
+        tArtist.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.DATE);
+        tArtist.insert(1, "artist1", new 
java.sql.Date(System.currentTimeMillis()));
+
+        TableHelper tGallery = new TableHelper(dbHelper, "GALLERY");
+        tGallery.setColumns("GALLERY_ID", "GALLERY_NAME");
+        tGallery.insert(1, "tate modern");
+
+        TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING");
+        tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", 
"GALLERY_ID", "ESTIMATED_PRICE");
+        for (int i = 1; i <= 3; i++) {
+            tPaintings.insert(i, "painting" + i, 1, 1, 22 - i);
+        }
+    }
+
+    @Ignore("selectFirst() call corrupts object state in context, and because 
of cache it's also returned for unrelated query")
+    @Test
+    public void testJoinPrefetch() {
+        Artist artist0 = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTING_ARRAY.joint())
+                .localCache("test")
+                .selectOne(context);
+        assertEquals(3, artist0.getPaintingArray().size());
+
+        Artist artist1 = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTING_ARRAY.joint())
+                .selectFirst(context);
+        assertEquals(1, artist1.getPaintingArray().size()); // <-- wrong 
assertion, but expected
+
+        Artist artist2 = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTING_ARRAY.joint())
+                .localCache("test")
+                .selectOne(context);
+        assertEquals(3, artist2.getPaintingArray().size()); // <-- assertion 
failure here, got 1 instead of 3
+    }
+
+    @Test
+    public void testDisjointByIdPrefetch() {
+        Artist artist0 = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTING_ARRAY.disjointById())
+                .localCache("test")
+                .selectOne(context);
+        assertEquals(3, artist0.getPaintingArray().size());
+
+        Artist artist1 = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTING_ARRAY.disjointById())
+                .selectFirst(context);
+        assertEquals(3, artist1.getPaintingArray().size());
+
+        Artist artist2 = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTING_ARRAY.disjointById())
+                .localCache("test")
+                .selectOne(context);
+        assertEquals(3, artist2.getPaintingArray().size());
+    }
+
+}

Reply via email to