You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2018/03/21 13:28:36 UTC

[2/4] cayenne git commit: CAY-2412 test case + warning about limit with joint prefetch

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/26330d4c
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/26330d4c
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/26330d4c

Branch: refs/heads/STABLE-4.0
Commit: 26330d4cb1edfa66f2c02e32462c85b5101edd6b
Parents: a16d7c4
Author: Nikita Timofeev <st...@gmail.com>
Authored: Tue Mar 6 17:38:54 2018 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Tue Mar 6 17:38:54 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/26330d4c/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 e455901..d933a39 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.
 	 *
@@ -925,4 +950,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/26330d4c/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..26b6049
--- /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 testJoinPrefetchWithLimitBeforeFullSelect() {
+        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 testDisjointByIdPrefetchWithLimitBeforeFullSelect() {
+        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());
+    }
+
+}