You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by ab...@apache.org on 2019/06/11 12:15:54 UTC

[cayenne] branch STABLE-4.0 updated: CAY-2553 Wrong disjoint prefetch query qualifier

This is an automated email from the ASF dual-hosted git repository.

abulatski pushed a commit to branch STABLE-4.0
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/STABLE-4.0 by this push:
     new d92f17d  CAY-2553 Wrong disjoint prefetch query qualifier
d92f17d is described below

commit d92f17d19db51856f473de2bbd290f70777dc875
Author: Arseni Bulatski <an...@gmail.com>
AuthorDate: Tue Jun 11 15:14:34 2019 +0300

    CAY-2553 Wrong disjoint prefetch query qualifier
---
 RELEASE-NOTES.txt                                  |   1 +
 .../main/java/org/apache/cayenne/map/DbEntity.java | 135 +++--------------
 .../apache/cayenne/exp/TranslateExpressionIT.java  | 159 +++++++++++++++++++++
 .../java/org/apache/cayenne/map/DbEntityIT.java    |   8 +-
 4 files changed, 184 insertions(+), 119 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 40100e7..f1f7998 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -20,6 +20,7 @@ CAY-2570 Use MySQL adapter for latest versions of MariaDB
 Bug Fixes:
 
 CAY-2550 Modeler: ObjAttribute inspector modifies wrong columns in attribute table
+CAY-2553 Wrong disjoint prefetch query qualifier
 CAY-2559 Modeler: Warning dialog shows wrong information after changing target entity in dbRelationship
 CAY-2573 DI field injection is triggered when creating sql Driver
 CAY-2582 Double insert of manyToMany relationship mapped to Set
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
index 3c3af26..0499b8c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
@@ -19,6 +19,17 @@
 
 package org.apache.cayenne.map;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.configuration.ConfigurationNode;
@@ -38,17 +49,6 @@ import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.commons.collections.Transformer;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-
 /**
  * A DbEntity is a mapping descriptor that defines a structure of a database
  * table.
@@ -692,115 +692,18 @@ public class DbEntity extends Entity implements ConfigurationNode, DbEntityListe
 
         String translatePath(String path) {
 
-            // algorithm to determine the translated path:
-            // 0. If relationship has at least one to-many component, travel all
-            // the way
-            // back, and then all the way forward
-            // 1. If relationship path equals to input, travel one step back,
-            // and then one
-            // step forward.
-            // 2. If input completely includes relationship path, use input's
-            // remaining
-            // tail.
-            // 3. If relationship path and input have none or some leading
-            // components in
-            // common,
-            // (a) strip common leading part;
-            // (b) reverse the remaining relationship part;
-            // (c) append remaining input to the reversed remaining
-            // relationship.
-
-            // case (0)
-            if (toMany) {
-                PathComponentIterator pathIt = createPathIterator(path);
-                Iterator<CayenneMapEntry> relationshipIt = resolvePathComponents(relationshipPath);
-
-                // for inserts from the both ends use LinkedList
-                LinkedList<String> finalPath = new LinkedList<String>();
-
-                // append remainder of the relationship, reversing it
-                while (relationshipIt.hasNext()) {
-                    DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
-                    prependReversedPath(finalPath, nextDBR);
-                }
-
-                while (pathIt.hasNext()) {
-                    // components may be attributes or relationships
-                    PathComponent<Attribute, Relationship> component = pathIt.next();
-                    appendPath(finalPath, component);
-                }
-
-                return convertToPath(finalPath);
-            }
-            // case (1)
-            if (path.equals(relationshipPath)) {
-
-                LinkedList<String> finalPath = new LinkedList<String>();
-                PathComponentIterator pathIt = createPathIterator(path);
-
-                // just do one step back and one step forward to create correct
-                // joins...
-                // find last rel...
-                DbRelationship lastDBR = null;
-
-                while (pathIt.hasNext()) {
-                    // relationship path components must be DbRelationships
-                    lastDBR = (DbRelationship) pathIt.next().getRelationship();
-                }
-
-                if (lastDBR != null) {
-                    prependReversedPath(finalPath, lastDBR);
-                    appendPath(finalPath, lastDBR);
-                }
-
-                return convertToPath(finalPath);
-            }
-
-            // case (2)
-            String relationshipPathWithDot = relationshipPath + Entity.PATH_SEPARATOR;
-            if (path.startsWith(relationshipPathWithDot)) {
-                return path.substring(relationshipPathWithDot.length());
-            }
-
-            // case (3)
-            PathComponentIterator pathIt = createPathIterator(path);
-            Iterator<CayenneMapEntry> relationshipIt = resolvePathComponents(relationshipPath);
-
-            // for inserts from the both ends use LinkedList
-            LinkedList<String> finalPath = new LinkedList<String>();
-
-            while (relationshipIt.hasNext() && pathIt.hasNext()) {
+            LinkedList<String> finalPath = new LinkedList<>();
+            PathComponentIterator pathIt = createPathIterator(relationshipPath);
+            while (pathIt.hasNext()) {
                 // relationship path components must be DbRelationships
-                DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
-
-                // expression components may be attributes or relationships
-                PathComponent<Attribute, Relationship> component = pathIt.next();
-
-                if (nextDBR != component.getRelationship()) {
-                    // found split point
-                    // consume the last iteration components,
-                    // then break out to finish the iterators independently
-                    prependReversedPath(finalPath, nextDBR);
-                    appendPath(finalPath, component);
-                    break;
+                DbRelationship lastDBR = (DbRelationship) pathIt.next().getRelationship();
+                if(lastDBR != null) {
+                    prependReversedPath(finalPath, lastDBR);
                 }
-
-                break;
-            }
-
-            // append remainder of the relationship, reversing it
-            while (relationshipIt.hasNext()) {
-                DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
-                prependReversedPath(finalPath, nextDBR);
-            }
-
-            while (pathIt.hasNext()) {
-                // components may be attributes or relationships
-                PathComponent<Attribute, Relationship> component = pathIt.next();
-                appendPath(finalPath, component);
             }
 
-            return convertToPath(finalPath);
+            finalPath.add(path);
+            return Util.join(finalPath, Entity.PATH_SEPARATOR);
         }
 
         private String convertToPath(List<String> path) {
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/TranslateExpressionIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/TranslateExpressionIT.java
new file mode 100644
index 0000000..41bb991
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/TranslateExpressionIT.java
@@ -0,0 +1,159 @@
+/*****************************************************************
+ *   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.exp;
+
+import java.util.List;
+
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.ObjEntity;
+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.testdo.testmap.Gallery;
+import org.apache.cayenne.testdo.testmap.Painting;
+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.Test;
+
+import static org.junit.Assert.assertEquals;
+
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class TranslateExpressionIT extends ServerCase {
+
+    @Inject
+    private DataContext context;
+
+    @Inject
+    private DBHelper dbHelper;
+
+    @Before
+    public void createArtistsDataSet() throws Exception {
+        TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH");
+
+        long dateBase = System.currentTimeMillis();
+        for (int i = 1; i <= 20; i++) {
+            tArtist.insert(i, "artist" + i, new java.sql.Date(dateBase + 10000 * i));
+        }
+
+        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");
+        for (int i = 1; i <= 20; i++) {
+            tPaintings.insert(i, "painting" + i, i % 5 + 1, 1);
+        }
+    }
+
+    @Test
+    public void testPrefetchWithTranslatedExp() {
+        List<Painting> result = ObjectSelect.query(Painting.class)
+                .where(Painting.TO_ARTIST
+                        .dot(Artist.PAINTING_ARRAY)
+                        .dot(Painting.PAINTING_TITLE).like("painting7"))
+                .and(Painting.PAINTING_TITLE.like("painting2"))
+                .prefetch(Painting.TO_ARTIST.disjoint())
+                .select(context);
+        assertEquals(1, result.size());
+        assertEquals("artist3", result.get(0).getToArtist().getArtistName());
+    }
+
+    @Test
+    public void testPrefetchWithTheSamePrefetchAndQualifier() {
+        List<Painting> result = ObjectSelect.query(Painting.class)
+                .where(Painting.TO_GALLERY
+                        .dot(Gallery.PAINTING_ARRAY)
+                        .dot(Painting.PAINTING_TITLE)
+                        .eq("painting1"))
+                .and(Painting.PAINTING_TITLE.like("painting2"))
+                .prefetch(Painting.TO_GALLERY.disjoint())
+                .prefetch(Painting.TO_GALLERY.dot(Gallery.PAINTING_ARRAY).disjoint())
+                .select(context);
+        assertEquals(1, result.size());
+        assertEquals("painting2", result.get(0).getPaintingTitle());
+    }
+
+    @Test
+    public void testTranslateExpression() {
+        ObjEntity entity = context.getEntityResolver().getObjEntity("Painting");
+        Expression expression = ExpressionFactory.pathExp("toArtist.paintingArray");
+        Expression translatedExpression = entity
+                .translateToRelatedEntity(expression, "toArtist");
+        assertEquals(ExpressionFactory
+                        .dbPathExp("paintingArray.toArtist.paintingArray"),
+                translatedExpression);
+    }
+
+    @Test
+    public void testRelationshipPathEqualsToInput() {
+        ObjEntity entity = context.getEntityResolver().getObjEntity("Painting");
+        Expression expression = ExpressionFactory.pathExp("toArtist");
+        Expression translatedExpression = entity
+                .translateToRelatedEntity(expression, "toArtist");
+        assertEquals(ExpressionFactory.dbPathExp("paintingArray.toArtist"),
+                translatedExpression);
+    }
+
+    @Test
+    public void testRelationshipNoneLeadingParts() {
+        ObjEntity entity = context.getEntityResolver().getObjEntity("Painting");
+        Expression expression = ExpressionFactory.pathExp("toGallery");
+        Expression translatedExpression = entity
+                .translateToRelatedEntity(expression, "toArtist");
+        assertEquals(ExpressionFactory.dbPathExp("paintingArray.toGallery"),
+                translatedExpression);
+    }
+
+    @Test
+    public void testRelationshipSomeLeadingParts() {
+        ObjEntity entity = context.getEntityResolver().getObjEntity("Painting");
+        Expression expression = ExpressionFactory.pathExp("toGallery");
+        Expression translatedExpression = entity
+                .translateToRelatedEntity(expression, "toArtist.paintingArray.toGallery");
+        assertEquals(ExpressionFactory.dbPathExp("paintingArray.toArtist.paintingArray.toGallery"),
+                translatedExpression);
+    }
+
+    @Test
+    public void testCompQualifier() {
+        ObjEntity entity = context.getEntityResolver().getObjEntity("Painting");
+        Expression expression = ExpressionFactory.pathExp("toArtist.artistExhibitArray.toExhibit");
+        Expression translatedExpression = entity
+                .translateToRelatedEntity(expression, "toGallery");
+        assertEquals(ExpressionFactory.dbPathExp("paintingArray.toArtist.artistExhibitArray.toExhibit"),
+                translatedExpression);
+    }
+
+    @Test
+    public void testCompQualifierAndPref() {
+        ObjEntity entity = context.getEntityResolver().getObjEntity("Artist");
+        Expression expression = ExpressionFactory.pathExp("paintingArray.toGallery");
+        Expression translatedExpression = entity
+                .translateToRelatedEntity(expression, "artistExhibitArray.toExhibit");
+        assertEquals(ExpressionFactory.dbPathExp("artistExhibitArray.toArtist.paintingArray.toGallery"),
+                translatedExpression);
+    }
+}
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/map/DbEntityIT.java b/cayenne-server/src/test/java/org/apache/cayenne/map/DbEntityIT.java
index c0a886f..5afd955 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/map/DbEntityIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/map/DbEntityIT.java
@@ -19,6 +19,8 @@
 
 package org.apache.cayenne.map;
 
+import java.util.Collection;
+
 import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.exp.Expression;
@@ -29,8 +31,6 @@ import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.apache.cayenne.util.Util;
 import org.junit.Test;
 
-import java.util.Collection;
-
 import static org.junit.Assert.*;
 
 @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
@@ -270,6 +270,8 @@ public class DbEntityIT extends ServerCase {
         Expression e1 = ExpressionFactory.exp("db:toArtist.ARTIST_NAME = 'aa'");
         Expression translated = paintingE.translateToRelatedEntity(e1, "toArtist");
 
-        assertEquals("failure: " + translated, ExpressionFactory.exp("db:ARTIST_NAME = 'aa'"), translated);
+        assertEquals("failure: " + translated,
+                ExpressionFactory.exp("db:paintingArray.toArtist.ARTIST_NAME = 'aa'"),
+                translated);
     }
 }