From 1a2aba3f56df9c4d58e3e18f1498693492115b66 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 11 Aug 2021 08:54:40 +0200 Subject: [PATCH] Distinguish between exists and in subquery --- .../server/autotune/service/BaseQueryTuner.java | 3 ++- .../main/java/io/ebeaninternal/api/SpiEbeanServer.java | 3 ++- .../src/main/java/io/ebeaninternal/api/SpiQuery.java | 9 +++++++-- .../io/ebeaninternal/server/core/DefaultServer.java | 4 ++-- .../server/expression/ExistsQueryExpression.java | 3 ++- .../server/expression/InQueryExpression.java | 3 ++- .../java/io/ebeaninternal/server/query/SqlTree.java | 1 + .../io/ebeaninternal/server/query/SqlTreeBuilder.java | 10 +++++++++- .../server/querydefn/DefaultOrmQuery.java | 5 +++-- .../java/io/ebeaninternal/api/TDSpiEbeanServer.java | 6 +++--- .../org/tests/query/cancel/SqlQueryCancelTest.java | 8 ++++---- 11 files changed, 37 insertions(+), 18 deletions(-) diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java index af969fd53..b93be5cc8 100644 --- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java +++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java @@ -146,7 +146,8 @@ public class BaseQueryTuner { case ID_LIST: case UPDATE: case DELETE: - case SUBQUERY: + case SQ_EXISTS: + case SQ_IN: return false; default: // not using autoTune when explicitly loading the l2 bean cache diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java index daf6418d0..fae60f49d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java @@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.meta.MetricVisitor; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.core.SpiResultSet; import io.ebeaninternal.server.core.timezone.DataTimeZone; import io.ebeaninternal.server.deploy.BeanDescriptor; @@ -149,7 +150,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect /** * Compile a query. */ - CQuery compileQuery(Query query, Transaction t); + CQuery compileQuery(Type type, Query query, Transaction t); /** * Execute the findId's query but without copying the query. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 5ba0b6eeb..12a25fed8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -92,9 +92,14 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod COUNT(FIND_COUNT, "findCount"), /** - * A subquery used as part of a where clause. + * A subquery used as part of an exists where clause. */ - SUBQUERY(FIND_SUBQUERY, "subquery"), + SQ_EXISTS(FIND_SUBQUERY, "sqExists"), + + /** + * A subquery used as part of an in where clause. + */ + SQ_IN(FIND_SUBQUERY, "sqIn"), /** * Delete query. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 4f0eed00b..c52befdcc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -526,8 +526,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { * Compile a query. Only valid for ORM queries. */ @Override - public CQuery compileQuery(Query query, Transaction t) { - SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t); + public CQuery compileQuery(Type type, Query query, Transaction t) { + SpiOrmQueryRequest qr = createQueryRequest(type, query, t); OrmQueryRequest orm = (OrmQueryRequest) qr; return cqueryEngine.buildQuery(orm); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index 4ba939066..71ca665fe 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -9,6 +9,7 @@ import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiExpressionValidation; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.query.CQuery; @@ -82,7 +83,7 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress */ protected CQuery compileSubQuery(BeanQueryRequest queryRequest) { SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer(); - return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction()); + return ebeanServer.compileQuery(Type.SQ_EXISTS, subQuery, queryRequest.getTransaction()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 48647d0c2..a2d6d787c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -6,6 +6,7 @@ import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.query.CQuery; import java.io.IOException; @@ -69,7 +70,7 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor private CQuery compileSubQuery(BeanQueryRequest queryRequest) { SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer(); - return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction()); + return ebeanServer.compileQuery(Type.SQ_IN, subQuery, queryRequest.getTransaction()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java index 1befa650b..5f548fd59 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java @@ -101,6 +101,7 @@ class SqlTree { * Return the String for the actual SQL. */ String getSelectSql() { + assert selectSql != null : "selectSql was null"; return selectSql; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 06465355c..a344244c4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -102,7 +102,11 @@ public final class SqlTreeBuilder { this.query = request.getQuery(); this.temporalMode = SpiQuery.TemporalMode.of(query); this.disableLazyLoad = query.isDisableLazyLoading(); - this.subQuery = Type.SUBQUERY == query.getType() || Type.ID_LIST == query.getType() || Type.DELETE == query.getType() || query.isCountDistinct(); + this.subQuery = Type.SQ_EXISTS == query.getType() + || Type.SQ_IN == query.getType() + || Type.ID_LIST == query.getType() + || Type.DELETE == query.getType() + || query.isCountDistinct(); this.includeJoin = query.getM2mIncludeJoin(); this.manyWhereJoins = query.getManyWhereJoins(); this.queryDetail = query.getDetail(); @@ -146,6 +150,10 @@ public final class SqlTreeBuilder { if (rawSql) { return "Not Used"; } + if (query.getType() ==Type.SQ_EXISTS) { + // effective query is "where exists (select 1 from ...)" + return "1"; + } rootNode.appendSelect(ctx, subQuery); return trimComma(ctx.getContent()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index db3a605ea..dea84fb80 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -565,7 +565,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { * Limit the number of fetch joins to Many properties, mark as query joins as needed. */ private void markQueryJoins() { - detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type != Type.ATTRIBUTE && type != Type.SUBQUERY); + detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), + type != Type.ATTRIBUTE && type != Type.SQ_EXISTS && type != Type.SQ_IN); } private boolean isAllowOneManyFetch() { @@ -578,7 +579,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { @Override public void setDefaultSelectClause() { - if (type != Type.ATTRIBUTE && type != Type.SUBQUERY) { + if (type != Type.ATTRIBUTE && type != Type.SQ_EXISTS && type != Type.SQ_IN) { detail.setDefaultSelectClause(beanDescriptor); } else if (!detail.hasSelectClause()) { // explicit empty select when single attribute query on non-root fetch path diff --git a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java index b09a0f54e..cb7e613a3 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java +++ b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java @@ -45,6 +45,7 @@ import io.ebean.plugin.Property; import io.ebean.plugin.SpiServer; import io.ebean.text.csv.CsvReader; import io.ebean.text.json.JsonContext; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.core.SpiResultSet; import io.ebeaninternal.server.core.timezone.DataTimeZone; import io.ebeaninternal.server.deploy.BeanDescriptor; @@ -54,7 +55,6 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent; import javax.persistence.OptimisticLockException; import javax.persistence.PersistenceException; import javax.sql.DataSource; -import java.lang.reflect.Type; import java.time.Clock; import java.util.Collection; import java.util.Collections; @@ -256,7 +256,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public CQuery compileQuery(Query query, Transaction t) { + public CQuery compileQuery(Type type, Query query, Transaction t) { return null; } @@ -316,7 +316,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public boolean isSupportedType(Type genericType) { + public boolean isSupportedType(java.lang.reflect.Type genericType) { return false; } diff --git a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java index 9fc3d6b57..5fedc53e0 100644 --- a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java +++ b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java @@ -113,8 +113,8 @@ public class SqlQueryCancelTest extends BaseTestCase { doCancelOrmAtBegin(Query::findOne); doCancelOrmAtBegin(q -> q.setMaxRows(1000).findPagedList().getList()); // untested doCancelOrmAtBegin(Query::findSet); - doCancelOrmAtBegin(Query::findSingleAttribute); - doCancelOrmAtBegin(Query::findSingleAttributeList); + doCancelOrmAtBegin(q -> q.select("name").findSingleAttribute()); + doCancelOrmAtBegin(q -> q.select("name").findSingleAttributeList()); doCancelOrmAtBegin(Query::findStream); // testDuringRun(Query::findVersions); // EBasic has no history support, but it should work if @History is added @@ -138,8 +138,8 @@ public class SqlQueryCancelTest extends BaseTestCase { // findOne cannot be tested, as H2 does the cancel check every 128 rows only doCancelOrmDuringRun(q -> q.setMaxRows(1000).findPagedList().getList()); // untested doCancelOrmDuringRun(Query::findSet); - doCancelOrmDuringRun(Query::findSingleAttribute); - doCancelOrmDuringRun(Query::findSingleAttributeList); + doCancelOrmDuringRun(q -> q.select("name").findSingleAttribute()); + doCancelOrmDuringRun(q -> q.select("name").findSingleAttributeList()); doCancelOrmDuringRun(Query::findStream); // testDuringRun(Query::findVersions); // EBasic has no history support, but it should work if @History is added