From b9ba6b2db5c57835172e99e33b2ab2cbc609a81d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 26 Jan 2022 16:28:30 +0100 Subject: [PATCH 1/2] DB2: Adapted Noemi's patch to new ebean version --- .../server/query/SqlTreeBuilder.java | 6 ++++ .../server/query/SqlTreeNode.java | 2 ++ .../server/query/SqlTreeNodeBean.java | 33 +++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) 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 0ee056430..ed687aff8 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 @@ -53,6 +53,7 @@ public final class SqlTreeBuilder { private final SpiQuery.TemporalMode temporalMode; private SqlTreeNode rootNode; private boolean sqlDistinct; + private final boolean distinctNoLobs; /** * Construct for RawSql query. @@ -65,6 +66,7 @@ public final class SqlTreeBuilder { this.query = null; this.subQuery = false; this.distinctOnPlatform = false; + this.distinctNoLobs = false; this.queryDetail = queryDetail; this.predicates = predicates; this.temporalMode = SpiQuery.TemporalMode.CURRENT; @@ -97,6 +99,7 @@ public final class SqlTreeBuilder { this.predicates = predicates; this.alias = new SqlTreeAlias(request.baseTableAlias(), temporalMode); this.distinctOnPlatform = builder.isPlatformDistinctOn(); + this.distinctNoLobs = builder.isPlatformDistinctNoLobs(); String fromForUpdate = builder.fromForUpdate(query); CQueryHistorySupport historySupport = builder.getHistorySupport(query); CQueryDraftSupport draftSupport = builder.getDraftSupport(query); @@ -266,6 +269,9 @@ public final class SqlTreeBuilder { if (joinList != null) { joinList.add(selectNode); } + if (sqlDistinct && distinctNoLobs) { + selectNode.unselectLobs(); + } return selectNode; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java index 54632d9ca..d4d22908c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java @@ -78,4 +78,6 @@ interface SqlTreeNode { * Create the loader for this node. */ SqlTreeLoad createLoad(); + + default void unselectLobs() {}; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java index 4836fa3fd..6214137c1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -26,8 +26,8 @@ class SqlTreeNodeBean implements SqlTreeNode { /** * Set to true if this is a partial object fetch. */ - final boolean partialObject; - final STreeProperty[] properties; + boolean partialObject; + STreeProperty[] properties; /** * Extra where clause added by Where annotation on associated many. */ @@ -399,4 +399,33 @@ class SqlTreeNodeBean implements SqlTreeNode { } return false; } + + + @Override + public void unselectLobs() { + if (children != null) { + for (SqlTreeNode child : children) { + child.unselectLobs(); + } + } + if (hasLob()) { + List lst = new ArrayList<>(); + for (STreeProperty prop : properties) { + if (!prop.isDbLob()) { + lst.add(prop); + } + } + properties = lst.toArray(new STreeProperty[0]); + partialObject = true; + } + } + + private boolean hasLob() { + for (STreeProperty prop : properties) { + if (prop.isDbLob()) { + return true; + } + } + return false; + } } From 69a753fc4f78958f409e8514dcbe44eabbc866eb Mon Sep 17 00:00:00 2001 From: Noemi Szemenyei Date: Fri, 21 Jan 2022 14:30:06 +0100 Subject: [PATCH 2/2] DB2:Unselect LOBs when making a distinct select --- .../server/deploy/BeanProperty.java | 21 +++++++- .../DynamicPropertyAggregationFormula.java | 5 ++ .../server/query/CQueryBuilder.java | 4 ++ .../server/query/STreeProperty.java | 5 ++ .../server/query/SqlTreeNode.java | 8 ++- .../java/org/tests/json/TestJsonMapBasic.java | 54 +++++++++++++++---- 6 files changed, 85 insertions(+), 12 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index bbee58e60..72379b232 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -1160,7 +1160,26 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { public boolean isLob() { return lob; } - + + /** + * Returns true if this isLob() or the type will effectively map to a lob. + */ + public boolean isDbLob() { + if (lob) { + return true; + } + switch (dbType) { + case DbPlatformType.JSON: + case DbPlatformType.JSONB: + return dbLength == 0; // must be analog to DbPlatformTypeMapping.lookup + case DbPlatformType.JSONBlob: + case DbPlatformType.JSONClob: + return true; + default: + return false; + } + } + public static boolean isLobType(int type) { switch (type) { case Types.CLOB: diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java index 970145632..ec1c7035b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DynamicPropertyAggregationFormula.java @@ -62,4 +62,9 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase { ctx.appendParseSelect(parsedFormula, alias); } + @Override + public boolean isDbLob() { + return false; + } + } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 1ad0dae58..1d32ce994 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -760,6 +760,10 @@ final class CQueryBuilder { return dbPlatform.isPlatform(Platform.POSTGRES); } + boolean isPlatformDistinctNoLobs() { + return dbPlatform.isPlatform(Platform.DB2); // CHECKME: Also oracle? + } + /** * Return the 'for update' FROM hint (sql server). */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/STreeProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/STreeProperty.java index 0981d5ac4..c13126858 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/STreeProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/STreeProperty.java @@ -28,6 +28,11 @@ public interface STreeProperty extends ScalarDataReader { * Return true if the property is the Id. */ boolean isId(); + + /** + * Returns true, if this is a lob property from db-perspective. + */ + boolean isDbLob(); /** * Return true if the property is an embedded type. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java index d4d22908c..3d50a9377 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java @@ -78,6 +78,10 @@ interface SqlTreeNode { * Create the loader for this node. */ SqlTreeLoad createLoad(); - - default void unselectLobs() {}; + + /** + * Unselect lobs (for distinct queries on DB2 and Oracle). + */ + default void unselectLobs() { + }; } diff --git a/ebean-test/src/test/java/org/tests/json/TestJsonMapBasic.java b/ebean-test/src/test/java/org/tests/json/TestJsonMapBasic.java index e186de792..1c1cebb43 100644 --- a/ebean-test/src/test/java/org/tests/json/TestJsonMapBasic.java +++ b/ebean-test/src/test/java/org/tests/json/TestJsonMapBasic.java @@ -3,13 +3,18 @@ package org.tests.json; import io.ebean.BaseTestCase; import io.ebean.DB; import io.ebean.Query; +import io.ebean.annotation.ForPlatform; +import io.ebean.annotation.Platform; import io.ebean.test.LoggedSql; import io.ebean.text.json.EJson; + +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.tests.model.json.EBasicJsonMap; import org.tests.model.json.EBasicJsonMapDetail; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,15 +23,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TestJsonMapBasic extends BaseTestCase { - @Test - public void whereManyPredicate() { + private final EBasicJsonMap bean = new EBasicJsonMap();; - if (!isPostgres()) { - // testing postgres specific select distinct on clause - return; + @AfterEach + void cleanup() { + if (bean != null && bean.getId() != null) { + DB.delete(EBasicJsonMap.class, bean.getId()); } + } + + @Test + @ForPlatform(Platform.POSTGRES) + public void whereManyPredicatePg() { - EBasicJsonMap bean = new EBasicJsonMap(); bean.setName("own1"); bean.getDetails().add(new EBasicJsonMapDetail("detail1")); bean.getDetails().add(new EBasicJsonMapDetail("detail2")); @@ -50,6 +59,36 @@ public class TestJsonMapBasic extends BaseTestCase { assertThat(query2.getGeneratedSql()).contains("select distinct on (t0.id) "); } + @Test + @ForPlatform(Platform.DB2) + public void whereManyPredicateDb2() { + + Map m1 = new HashMap<>(); + m1.put("foo", "bar"); + bean.setContent(m1); + bean.setName("own1"); + bean.getDetails().add(new EBasicJsonMapDetail("db2-detail1")); + bean.getDetails().add(new EBasicJsonMapDetail("db2-detail2")); + + DB.save(bean); + Query query1 = DB.find(EBasicJsonMap.class).select("*").fetch("details").where() + .startsWith("details.name", "db2-detail").query(); + + List lst = query1.findList(); + + assertThat(query1.getGeneratedSql()).contains("select distinct t0.id, t0.name, t0.version,") + .doesNotContain("content"); + assertThat(lst).hasSize(1); + assertThat(lst.get(0).getContent()).containsEntry("foo", "bar"); + + Query query2 = DB.find(EBasicJsonMap.class).where().startsWith("details.name", "db2-detail") + .query(); + query2.findList(); + + assertThat(query2.getGeneratedSql()).contains("select distinct t0.id, t0.name, t0.version from"); + + } + @Test public void testInsertUpdateDelete() throws IOException { @@ -58,7 +97,6 @@ public class TestJsonMapBasic extends BaseTestCase { Map content = EJson.parseObject(s0); - EBasicJsonMap bean = new EBasicJsonMap(); bean.setName("one"); bean.setContent(content); @@ -106,7 +144,6 @@ public class TestJsonMapBasic extends BaseTestCase { String s0 = "{\"docId\":22,\"contentId\":\"initialDoc\"}"; Map content = EJson.parseObject(s0); - EBasicJsonMap bean = new EBasicJsonMap(); bean.setName("one"); bean.setContent(content); @@ -133,6 +170,5 @@ public class TestJsonMapBasic extends BaseTestCase { assertThat(content2.get("contentId")).isEqualTo("updatedDoc222"); assertThat(content2.get("docId")).isEqualTo(222L); - DB.delete(found); } }