This is an automated email from the ASF dual-hosted git repository. abulatski pushed a commit to branch STABLE-4.1 in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/STABLE-4.1 by this push: new d3940e9 CAY-2553 Wrong disjoint prefetch query qualifier d3940e9 is described below commit d3940e9df874507dccf8f9862a505e0337e934a6 Author: Arseni Bulatski <ancars...@gmail.com> AuthorDate: Tue Jun 11 15:30:05 2019 +0300 CAY-2553 Wrong disjoint prefetch query qualifier --- RELEASE-NOTES.txt | 1 + .../main/java/org/apache/cayenne/map/DbEntity.java | 130 +++-------------- .../apache/cayenne/exp/TranslateExpressionIT.java | 159 +++++++++++++++++++++ .../java/org/apache/cayenne/map/DbEntityIT.java | 8 +- 4 files changed, 181 insertions(+), 117 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 144aa21..2f397d4 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -14,6 +14,7 @@ Date: Bug Fixes: +CAY-2553 Wrong disjoint prefetch query qualifier CAY-2573 DI field injection is triggered when creating sql Driver CAY-2582 Double insert of manyToMany relationship mapped to Set CAY-2584 Crypto: can't use ColumnSelect with encrypted columns 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 0626655..c341aa6 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,16 @@ package org.apache.cayenne.map; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Function; + import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.configuration.ConfigurationNode; @@ -37,18 +47,6 @@ import org.apache.cayenne.util.CayenneMapEntry; import org.apache.cayenne.util.Util; import org.apache.cayenne.util.XMLEncoder; -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.TreeMap; -import java.util.function.Function; - /** * A DbEntity is a mapping descriptor that defines a structure of a database * table. @@ -676,113 +674,17 @@ 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<>(); - - // 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 Util.join(finalPath, Entity.PATH_SEPARATOR); - } - // case (1) - if (path.equals(relationshipPath)) { - - LinkedList<String> finalPath = new LinkedList<>(); - 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 Util.join(finalPath, Entity.PATH_SEPARATOR); - } - - // 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<>(); - while (relationshipIt.hasNext() && pathIt.hasNext()) { + 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); } + finalPath.add(path); return Util.join(finalPath, Entity.PATH_SEPARATOR); } 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); } }