From 9756c9133ed019e92ce9bae2c3e7fdc4299c5ec7 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sat, 19 Aug 2023 13:46:45 +1200 Subject: [PATCH 1/4] Improve existing test for filterMany using raw with separate SQL query for filterMany --- .../test/java/org/tests/query/TestQueryFilterMany.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java b/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java index 75d158c88..0ebf2bdc6 100644 --- a/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java +++ b/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java @@ -134,18 +134,22 @@ public class TestQueryFilterMany extends BaseTestCase { } @Test - public void test_with_findOne() { - + public void test_with_findOne_rawSeparateQuery() { ResetBasicData.reset(); + LoggedSql.start(); Customer customer = DB.find(Customer.class) .setMaxRows(1) .order().asc("id") .fetch("orders") - .filterMany("orders").raw("1 = 0") + .filterMany("orders").raw("orderDate is not null") .findOne(); assertThat(customer).isNotNull(); + List sql = LoggedSql.stop(); + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("from o_customer t0 order by"); + assertThat(sql.get(1)).contains("from o_order t0 join o_customer t1 on t1.id = t0.kcustomer_id where"); } @Test From 8a4024ecfcdb95f51221f4dbd30fdd4ac375ea92 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sat, 19 Aug 2023 13:54:38 +1200 Subject: [PATCH 2/4] FIX for filterMany with raw expression with single SQL query Fix for the case where the filterMany is not executed on a separate query but instead part of the origin query. The fix is to use the appropriate DeployParser and also fix the path alias like ${} -> ${contacts} --- .../server/query/CQueryPredicates.java | 22 ++++++++++++++-- .../server/query/CQueryPredicatesTest.java | 26 +++++++++++++++++++ .../org/tests/query/TestQueryFilterMany.java | 18 +++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java index 78b54d56d..99ab30224 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java @@ -10,6 +10,7 @@ import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.DeployParser; +import io.ebeaninternal.server.deploy.DeployPropertyParser; import io.ebeaninternal.server.expression.DefaultExpressionRequest; import io.ebeaninternal.server.persist.Binder; import io.ebeaninternal.server.querydefn.OrmQueryProperties; @@ -198,9 +199,10 @@ public final class CQueryPredicates { OrmQueryProperties chunk = query.detail().getChunk(manyProperty.name(), false); SpiExpressionList filterManyExpr = chunk.getFilterMany(); if (filterManyExpr != null) { - this.filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr); + DeployPropertyParser parser = manyProperty.targetDescriptor().parser(); + this.filterMany = new DefaultExpressionRequest(request, parser, binder, filterManyExpr); if (buildSql) { - dbFilterMany = filterMany.buildSql(); + dbFilterMany = filterManyPaths(manyProperty.name(), filterMany.buildSql()); } } } @@ -216,6 +218,22 @@ public final class CQueryPredicates { } } + static String filterManyPaths(String prefix, String raw) { + final StringBuilder sb = new StringBuilder(raw.length() + 50); + int lastPos = 0; + int nextPos = raw.indexOf("${"); + while (nextPos > -1) { + sb.append(raw.substring(lastPos, nextPos)).append("${").append(prefix); + if (raw.charAt(nextPos + 2) != '}') { + sb.append('.'); + } + lastPos = nextPos + 2; + nextPos = raw.indexOf("${", nextPos + 2); + } + sb.append(raw.substring(lastPos)); + return sb.toString(); + } + /** * Replace the table alias place-holders. */ diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java new file mode 100644 index 000000000..5a355b933 --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java @@ -0,0 +1,26 @@ +package io.ebeaninternal.server.query; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CQueryPredicatesTest { + + @Test + void filterManyPaths_one() { + String result = CQueryPredicates.filterManyPaths("contacts", "${}first is not null"); + assertThat(result).isEqualTo("${contacts}first is not null"); + } + + @Test + void filterManyPaths_nested() { + String result = CQueryPredicates.filterManyPaths("contacts", "${address}city"); + assertThat(result).isEqualTo("${contacts.address}city"); + } + + @Test + void filterManyPaths_2() { + String result = CQueryPredicates.filterManyPaths("contacts", "${}first ${}last"); + assertThat(result).isEqualTo("${contacts}first ${contacts}last"); + } +} diff --git a/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java b/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java index 0ebf2bdc6..dffe3a72e 100644 --- a/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java +++ b/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java @@ -152,6 +152,24 @@ public class TestQueryFilterMany extends BaseTestCase { assertThat(sql.get(1)).contains("from o_order t0 join o_customer t1 on t1.id = t0.kcustomer_id where"); } + @Test + public void test_with_findOne_rawSameQuery() { + ResetBasicData.reset(); + + LoggedSql.start(); + var result = DB.find(Customer.class) + .order().asc("id") + .fetch("orders") + .filterMany("orders").raw("orderDate is not null") + .findList(); + + assertThat(result).isNotEmpty(); + List sql = LoggedSql.stop(); + assertThat(sql).hasSize(1); + assertThat(sql.get(0)).contains("from o_customer t0 left join o_order t1"); + assertThat(sql.get(0)).contains("where t1.order_date is not null"); + } + @Test public void test_with_findOneOrEmpty() { From defb35b25838c90c2a045e926981e25ad64bc43a Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 20 Aug 2023 23:32:55 +1200 Subject: [PATCH 3/4] FIX for filterMany with raw expression with single SQL query Fix for the case where the filterMany is not executed on a separate query but instead part of the origin query. The fix is to use the appropriate DeployParser and also fix the path alias like ${} -> ${contacts} --- .../server/expression/RawExpression.java | 36 ++++++++++++++++++- .../server/query/CQueryPredicates.java | 21 ++--------- .../server/expression/RawExpressionTest.java | 18 ++++++++++ .../server/query/CQueryPredicatesTest.java | 26 -------------- 4 files changed, 55 insertions(+), 46 deletions(-) delete mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index afe6572a8..db64b2ea7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -2,6 +2,9 @@ package io.ebeaninternal.server.expression; import io.ebeaninternal.api.*; import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.deploy.BeanProperty; +import io.ebeaninternal.server.deploy.BeanPropertyAssoc; +import io.ebeaninternal.server.deploy.DeployPropertyParser; import io.ebeaninternal.server.persist.MultiValueWrapper; import java.io.IOException; @@ -11,6 +14,7 @@ final class RawExpression extends NonPrepareExpression { final String sql; final Object[] values; + private String prefixPath; RawExpression(String sql, Object[] values) { this.sql = sql; @@ -51,9 +55,39 @@ final class RawExpression extends NonPrepareExpression { } } + @Override + public void prefixProperty(String path) { + this.prefixPath = path; + } + @Override public void addSql(SpiExpressionRequest request) { - request.parse(sql); + if (prefixPath == null) { + request.parse(sql); + } else { + BeanDescriptor descriptor = request.descriptor(); + BeanProperty beanProperty = descriptor.findPropertyFromPath(prefixPath); + BeanPropertyAssoc bpa = (BeanPropertyAssoc)beanProperty; + DeployPropertyParser parser = bpa.targetDescriptor().parser(); + request.append(filterManyPaths(prefixPath, parser.parse(sql))); + } + } + + + static String filterManyPaths(String prefix, String raw) { + final StringBuilder sb = new StringBuilder(raw.length() + 50); + int lastPos = 0; + int nextPos = raw.indexOf("${"); + while (nextPos > -1) { + sb.append(raw.substring(lastPos, nextPos)).append("${").append(prefix); + if (raw.charAt(nextPos + 2) != '}') { + sb.append('.'); + } + lastPos = nextPos + 2; + nextPos = raw.indexOf("${", nextPos + 2); + } + sb.append(raw.substring(lastPos)); + return sb.toString(); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java index 99ab30224..822c148db 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java @@ -199,10 +199,9 @@ public final class CQueryPredicates { OrmQueryProperties chunk = query.detail().getChunk(manyProperty.name(), false); SpiExpressionList filterManyExpr = chunk.getFilterMany(); if (filterManyExpr != null) { - DeployPropertyParser parser = manyProperty.targetDescriptor().parser(); - this.filterMany = new DefaultExpressionRequest(request, parser, binder, filterManyExpr); + this.filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr); if (buildSql) { - dbFilterMany = filterManyPaths(manyProperty.name(), filterMany.buildSql()); + dbFilterMany = filterMany.buildSql(); } } } @@ -218,22 +217,6 @@ public final class CQueryPredicates { } } - static String filterManyPaths(String prefix, String raw) { - final StringBuilder sb = new StringBuilder(raw.length() + 50); - int lastPos = 0; - int nextPos = raw.indexOf("${"); - while (nextPos > -1) { - sb.append(raw.substring(lastPos, nextPos)).append("${").append(prefix); - if (raw.charAt(nextPos + 2) != '}') { - sb.append('.'); - } - lastPos = nextPos + 2; - nextPos = raw.indexOf("${", nextPos + 2); - } - sb.append(raw.substring(lastPos)); - return sb.toString(); - } - /** * Replace the table alias place-holders. */ diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index c87e46c47..47d550b15 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -74,4 +74,22 @@ public class RawExpressionTest extends BaseExpressionTest { query.queryBindKey(bindValuesKey); return bindValuesKey; } + + @Test + void filterManyPaths_one() { + String result = RawExpression.filterManyPaths("contacts", "${}first is not null"); + assertThat(result).isEqualTo("${contacts}first is not null"); + } + + @Test + void filterManyPaths_nested() { + String result = RawExpression.filterManyPaths("contacts", "${address}city"); + assertThat(result).isEqualTo("${contacts.address}city"); + } + + @Test + void filterManyPaths_2() { + String result = RawExpression.filterManyPaths("contacts", "${}first ${}last"); + assertThat(result).isEqualTo("${contacts}first ${contacts}last"); + } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java deleted file mode 100644 index 5a355b933..000000000 --- a/ebean-core/src/test/java/io/ebeaninternal/server/query/CQueryPredicatesTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.ebeaninternal.server.query; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class CQueryPredicatesTest { - - @Test - void filterManyPaths_one() { - String result = CQueryPredicates.filterManyPaths("contacts", "${}first is not null"); - assertThat(result).isEqualTo("${contacts}first is not null"); - } - - @Test - void filterManyPaths_nested() { - String result = CQueryPredicates.filterManyPaths("contacts", "${address}city"); - assertThat(result).isEqualTo("${contacts.address}city"); - } - - @Test - void filterManyPaths_2() { - String result = CQueryPredicates.filterManyPaths("contacts", "${}first ${}last"); - assertThat(result).isEqualTo("${contacts}first ${contacts}last"); - } -} From 1933beed9b8b89817f241fc80e39ac00807e7a4d Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 20 Aug 2023 23:52:10 +1200 Subject: [PATCH 4/4] Tidy unused import in CQueryPredicates --- .../java/io/ebeaninternal/server/query/CQueryPredicates.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java index 822c148db..78b54d56d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPredicates.java @@ -10,7 +10,6 @@ import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.DeployParser; -import io.ebeaninternal.server.deploy.DeployPropertyParser; import io.ebeaninternal.server.expression.DefaultExpressionRequest; import io.ebeaninternal.server.persist.Binder; import io.ebeaninternal.server.querydefn.OrmQueryProperties;