This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push: new 69d754c24 Fix split expressions for db relationships new 598eb371e Merge pull request #540 from m-dzianishchyts/CAY-2764 69d754c24 is described below commit 69d754c24be25506fcf8f0ccb64d179cecf5e005 Author: Mikhail Dzianishchyts <mikhail.dzianishch...@gmail.com> AuthorDate: Mon Nov 21 02:58:17 2022 +0300 Fix split expressions for db relationships --- .../access/translator/select/DbPathProcessor.java | 1 + .../org/apache/cayenne/exp/ExpressionFactory.java | 13 +- .../apache/cayenne/exp/ExpressionFactoryIT.java | 308 ++++++++++++--------- 3 files changed, 186 insertions(+), 136 deletions(-) diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java index 48fdcee2c..c1336b5ff 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java @@ -70,6 +70,7 @@ class DbPathProcessor extends PathProcessor<DbEntity> { throw new IllegalStateException("Non-relationship aliased path part: " + alias); } + entity = relationship.getTargetEntity(); processRelationship(relationship); } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java index 8f50603f7..b17775411 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java @@ -79,6 +79,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Function; /** * Helper class to build expressions. @@ -330,6 +331,14 @@ public class ExpressionFactory { return new ASTTrue(); } + Function<Object, ASTPath> pathProvider; + if (path.startsWith(ASTDbPath.DB_PREFIX)) { + pathProvider = ASTDbPath::new; + path = path.substring(ASTDbPath.DB_PREFIX.length()); + } else { + pathProvider = ASTObjPath::new; + } + int split = path.indexOf(SPLIT_SEPARATOR); List<Expression> matches = new ArrayList<>(values.length); @@ -355,13 +364,13 @@ public class ExpressionFactory { String aliasedPath = beforeSplit + alias + afterSplit; i++; - ASTPath pathExp = new ASTObjPath(aliasedPath); + ASTPath pathExp = pathProvider.apply(aliasedPath); pathExp.setPathAliases(Collections.singletonMap(alias, splitChunk)); matches.add(new ASTEqual(pathExp, value)); } } else { for (Object value : values) { - matches.add(new ASTEqual(new ASTObjPath(path), value)); + matches.add(new ASTEqual(pathProvider.apply(path), value)); } } diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryIT.java index c22b71d46..22408c316 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryIT.java @@ -19,21 +19,11 @@ package org.apache.cayenne.exp; -import static org.apache.cayenne.exp.ExpressionFactory.exp; -import static org.apache.cayenne.exp.ExpressionFactory.greaterExp; -import static org.apache.cayenne.exp.FunctionExpressionFactory.lengthExp; -import static org.apache.cayenne.exp.FunctionExpressionFactory.substringExp; -import static org.apache.cayenne.exp.FunctionExpressionFactory.trimExp; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.List; - import org.apache.cayenne.ObjectContext; import org.apache.cayenne.di.Inject; import org.apache.cayenne.query.ObjectSelect; 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.UnitDbAdapter; import org.apache.cayenne.unit.di.server.CayenneProjects; @@ -41,130 +31,180 @@ import org.apache.cayenne.unit.di.server.ServerCase; import org.apache.cayenne.unit.di.server.UseServerRuntime; import org.junit.Test; +import java.util.List; + +import static org.apache.cayenne.exp.ExpressionFactory.exp; +import static org.apache.cayenne.exp.ExpressionFactory.greaterExp; +import static org.apache.cayenne.exp.FunctionExpressionFactory.lengthExp; +import static org.apache.cayenne.exp.FunctionExpressionFactory.substringExp; +import static org.apache.cayenne.exp.FunctionExpressionFactory.trimExp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) public class ExpressionFactoryIT extends ServerCase { - @Inject - private ObjectContext context; - - @Inject - private UnitDbAdapter accessStackAdapter; - - // CAY-416 - @Test - public void testCollectionMatch() { - Artist artist = context.newObject(Artist.class); - artist.setArtistName("artist"); - Painting p1 = context.newObject(Painting.class), - p2 = context.newObject(Painting.class), - p3 = context.newObject(Painting.class); - p1.setPaintingTitle("p1"); - p2.setPaintingTitle("p2"); - p3.setPaintingTitle("p3"); - artist.addToPaintingArray(p1); - artist.addToPaintingArray(p2); - - context.commitChanges(); - - assertTrue(ExpressionFactory.matchExp("paintingArray", p1).match(artist)); - assertFalse(ExpressionFactory.matchExp("paintingArray", p3).match(artist)); - assertTrue(ExpressionFactory.noMatchExp("paintingArray", p1).match(artist)); // changed to align with SQL - assertTrue(ExpressionFactory.noMatchExp("paintingArray", p3).match(artist)); - - assertTrue(ExpressionFactory.matchExp("paintingArray.paintingTitle", "p1").match(artist)); - assertFalse(ExpressionFactory.matchExp("paintingArray.paintingTitle", "p3").match(artist)); - assertTrue(ExpressionFactory.noMatchExp("paintingArray.paintingTitle", "p1").match(artist)); // changed to align with SQL - assertTrue(ExpressionFactory.noMatchExp("paintingArray.paintingTitle", "p3").match(artist)); - - assertTrue(ExpressionFactory.inExp("paintingTitle", "p1").match(p1)); - assertFalse(ExpressionFactory.notInExp("paintingTitle", "p3").match(p3)); - } - - @Test - public void testIn() { - Artist a1 = context.newObject(Artist.class); - a1.setArtistName("a1"); - Painting p1 = context.newObject(Painting.class); - p1.setPaintingTitle("p1"); - Painting p2 = context.newObject(Painting.class); - p2.setPaintingTitle("p2"); - a1.addToPaintingArray(p1); - a1.addToPaintingArray(p2); - - Expression in = ExpressionFactory.inExp("paintingArray", p1); - assertTrue(in.match(a1)); - } - - @Test - public void testEscapeCharacter() { - if(!accessStackAdapter.supportsEscapeInLike()) { - return; - } - - Artist a1 = context.newObject(Artist.class); - a1.setArtistName("A_1"); - Artist a2 = context.newObject(Artist.class); - a2.setArtistName("A_2"); - context.commitChanges(); - - Expression ex1 = ExpressionFactory.likeIgnoreCaseDbExp("ARTIST_NAME", "A*_1", '*'); - List<Artist> artists = ObjectSelect.query(Artist.class, ex1).select(context); - assertEquals(1, artists.size()); - - Expression ex2 = ExpressionFactory.likeExp("artistName", "A*_2", '*'); - artists = ObjectSelect.query(Artist.class, ex2).select(context); - assertEquals(1, artists.size()); - } - - @Test - public void testContains_Escape() { - - if(!accessStackAdapter.supportsEscapeInLike()) { - return; - } - - Artist a1 = context.newObject(Artist.class); - a1.setArtistName("MA_1X"); - Artist a2 = context.newObject(Artist.class); - a2.setArtistName("CA%2Y"); - context.commitChanges(); - - Expression ex1 = ExpressionFactory.containsExp(Artist.ARTIST_NAME.getName(), "A_1"); - List<Artist> artists = ObjectSelect.query(Artist.class, ex1).select(context); - assertEquals(1, artists.size()); - - Expression ex2 = ExpressionFactory.containsExp(Artist.ARTIST_NAME.getName(), "A%2"); - artists = ObjectSelect.query(Artist.class, ex2).select(context); - assertEquals(1, artists.size()); - } - - @Test - public void testDifferentExpressionAPI() { - List<Artist> res; - - // First version via expression string - Expression exp1 = exp( - "length(substring(artistName, 1, 3)) > length(trim(artistName))" - ); - res = ObjectSelect.query(Artist.class, exp1).select(context); - assertEquals(0, res.size()); - - // Second version via FunctionExpressionFactory API - Expression exp2 = greaterExp( - lengthExp(substringExp(Artist.ARTIST_NAME.getExpression(), 1, 3)), - lengthExp(trimExp(Artist.ARTIST_NAME.getExpression())) - ); - res = ObjectSelect.query(Artist.class, exp2).select(context); - assertEquals(0, res.size()); - - // Third version via Property API - Expression exp3 = Artist.ARTIST_NAME.substring(1, 3).length() - .gt(Artist.ARTIST_NAME.trim().length()); - res = ObjectSelect.query(Artist.class, exp3).select(context); - assertEquals(0, res.size()); - - // Check that all expressions are equal - assertEquals(exp1, exp2); - assertEquals(exp3, exp3); - } + @Inject + private ObjectContext context; + + @Inject + private UnitDbAdapter accessStackAdapter; + + // CAY-416 + @Test + public void testCollectionMatch() { + Artist artist = context.newObject(Artist.class); + artist.setArtistName("artist"); + Painting p1 = context.newObject(Painting.class), + p2 = context.newObject(Painting.class), + p3 = context.newObject(Painting.class); + p1.setPaintingTitle("p1"); + p2.setPaintingTitle("p2"); + p3.setPaintingTitle("p3"); + artist.addToPaintingArray(p1); + artist.addToPaintingArray(p2); + + context.commitChanges(); + + assertTrue(ExpressionFactory.matchExp("paintingArray", p1).match(artist)); + assertFalse(ExpressionFactory.matchExp("paintingArray", p3).match(artist)); + assertTrue(ExpressionFactory.noMatchExp("paintingArray", p1).match(artist)); // changed to align with SQL + assertTrue(ExpressionFactory.noMatchExp("paintingArray", p3).match(artist)); + + assertTrue(ExpressionFactory.matchExp("paintingArray.paintingTitle", "p1").match(artist)); + assertFalse(ExpressionFactory.matchExp("paintingArray.paintingTitle", "p3").match(artist)); + assertTrue(ExpressionFactory.noMatchExp("paintingArray.paintingTitle", "p1") + .match(artist)); // changed to align with SQL + assertTrue(ExpressionFactory.noMatchExp("paintingArray.paintingTitle", "p3").match(artist)); + + assertTrue(ExpressionFactory.inExp("paintingTitle", "p1").match(p1)); + assertFalse(ExpressionFactory.notInExp("paintingTitle", "p3").match(p3)); + } + + @Test + public void testIn() { + Artist a1 = context.newObject(Artist.class); + a1.setArtistName("a1"); + Painting p1 = context.newObject(Painting.class); + p1.setPaintingTitle("p1"); + Painting p2 = context.newObject(Painting.class); + p2.setPaintingTitle("p2"); + a1.addToPaintingArray(p1); + a1.addToPaintingArray(p2); + + Expression in = ExpressionFactory.inExp("paintingArray", p1); + assertTrue(in.match(a1)); + } + + @Test + public void testEscapeCharacter() { + if (!accessStackAdapter.supportsEscapeInLike()) { + return; + } + + Artist a1 = context.newObject(Artist.class); + a1.setArtistName("A_1"); + Artist a2 = context.newObject(Artist.class); + a2.setArtistName("A_2"); + context.commitChanges(); + + Expression ex1 = ExpressionFactory.likeIgnoreCaseDbExp("ARTIST_NAME", "A*_1", '*'); + List<Artist> artists = ObjectSelect.query(Artist.class, ex1).select(context); + assertEquals(1, artists.size()); + + Expression ex2 = ExpressionFactory.likeExp("artistName", "A*_2", '*'); + artists = ObjectSelect.query(Artist.class, ex2).select(context); + assertEquals(1, artists.size()); + } + + @Test + public void testContains_Escape() { + + if (!accessStackAdapter.supportsEscapeInLike()) { + return; + } + + Artist a1 = context.newObject(Artist.class); + a1.setArtistName("MA_1X"); + Artist a2 = context.newObject(Artist.class); + a2.setArtistName("CA%2Y"); + context.commitChanges(); + + Expression ex1 = ExpressionFactory.containsExp(Artist.ARTIST_NAME.getName(), "A_1"); + List<Artist> artists = ObjectSelect.query(Artist.class, ex1).select(context); + assertEquals(1, artists.size()); + + Expression ex2 = ExpressionFactory.containsExp(Artist.ARTIST_NAME.getName(), "A%2"); + artists = ObjectSelect.query(Artist.class, ex2).select(context); + assertEquals(1, artists.size()); + } + + @Test + public void testSplitExpressions() { + Artist artist1 = context.newObject(Artist.class), + artist2 = context.newObject(Artist.class); + artist1.setArtistName("a1"); + artist2.setArtistName("a2"); + + Painting p1 = context.newObject(Painting.class), + p2 = context.newObject(Painting.class), + p3 = context.newObject(Painting.class); + p1.setPaintingTitle("p1"); + p2.setPaintingTitle("p2"); + p3.setPaintingTitle("p3"); + + Gallery g1 = context.newObject(Gallery.class), + g2 = context.newObject(Gallery.class); + g1.setGalleryName("g1"); + g2.setGalleryName("g2"); + + artist1.addToPaintingArray(p1); + artist1.addToPaintingArray(p2); + artist2.addToPaintingArray(p3); + + g1.addToPaintingArray(p1); + g1.addToPaintingArray(p3); + g2.addToPaintingArray(p2); + + context.commitChanges(); + + List<Artist> objArtists = ObjectSelect.query(Artist.class) + .where(ExpressionFactory.matchAllExp("|paintingArray.toGallery.galleryName", "g1", "g2")) + .select(context); + List<Artist> dbArtists = ObjectSelect.query(Artist.class) + .where(ExpressionFactory.matchAllExp("db:|paintingArray.toGallery.GALLERY_NAME", "g1", "g2")) + .select(context); + assertEquals(objArtists, dbArtists); + } + + @Test + public void testDifferentExpressionAPI() { + List<Artist> res; + + // First version via expression string + Expression exp1 = exp( + "length(substring(artistName, 1, 3)) > length(trim(artistName))" + ); + res = ObjectSelect.query(Artist.class, exp1).select(context); + assertEquals(0, res.size()); + + // Second version via FunctionExpressionFactory API + Expression exp2 = greaterExp( + lengthExp(substringExp(Artist.ARTIST_NAME.getExpression(), 1, 3)), + lengthExp(trimExp(Artist.ARTIST_NAME.getExpression())) + ); + res = ObjectSelect.query(Artist.class, exp2).select(context); + assertEquals(0, res.size()); + + // Third version via Property API + Expression exp3 = Artist.ARTIST_NAME.substring(1, 3).length() + .gt(Artist.ARTIST_NAME.trim().length()); + res = ObjectSelect.query(Artist.class, exp3).select(context); + assertEquals(0, res.size()); + + // Check that all expressions are equal + assertEquals(exp1, exp2); + assertEquals(exp3, exp3); + } }