mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge pull request #55 from FOCONIS/fix-db2-select-distinct-lob
Fix select distinct lob fields with db2
This commit is contained in:
@@ -1160,7 +1160,11 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
public boolean isLob() {
|
||||
return lob;
|
||||
}
|
||||
|
||||
|
||||
public boolean isDbLob() {
|
||||
return lob || dbType == DbPlatformType.JSON && dbLength == 0;
|
||||
}
|
||||
|
||||
public static boolean isLobType(int type) {
|
||||
switch (type) {
|
||||
case Types.CLOB:
|
||||
|
||||
+5
@@ -62,4 +62,9 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase {
|
||||
ctx.appendParseSelect(parsedFormula, alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDbLob() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -752,6 +752,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).
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,11 @@ public interface STreeProperty extends ScalarDataReader<Object> {
|
||||
* Return true if the property is the Id.
|
||||
*/
|
||||
boolean isId();
|
||||
|
||||
/**
|
||||
* Returns true, if this is a lob property.
|
||||
*/
|
||||
boolean isDbLob();
|
||||
|
||||
/**
|
||||
* Return true if the property is an embedded type.
|
||||
|
||||
@@ -35,6 +35,7 @@ public final class SqlTreeBuilder {
|
||||
private final CQueryPredicates predicates;
|
||||
private final boolean subQuery;
|
||||
private final boolean distinctOnPlatform;
|
||||
private final boolean distinctNoLobs;
|
||||
/**
|
||||
* Property if resultSet contains master and detail rows.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,4 +92,9 @@ interface SqlTreeNode {
|
||||
* Add dependent tables to the given set.
|
||||
*/
|
||||
void dependentTables(Set<String> tables);
|
||||
|
||||
/**
|
||||
* Unselect lobs (for distinct queries on DB2 and Oracle).
|
||||
*/
|
||||
default void unselectLobs() {};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Mode;
|
||||
@@ -15,6 +17,7 @@ import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -36,8 +39,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
/**
|
||||
* Set to true if this is a partial object fetch.
|
||||
*/
|
||||
private final boolean partialObject;
|
||||
private final STreeProperty[] properties;
|
||||
private boolean partialObject;
|
||||
private STreeProperty[] properties;
|
||||
/**
|
||||
* Extra where clause added by Where annotation on associated many.
|
||||
*/
|
||||
@@ -700,4 +703,32 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unselectLobs() {
|
||||
if (children != null) {
|
||||
for (SqlTreeNode child : children) {
|
||||
child.unselectLobs();
|
||||
}
|
||||
}
|
||||
if (hasLob()) {
|
||||
List<STreeProperty> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +75,7 @@ create table migtest_e_basic (
|
||||
constraint ck_migtest_e_basic_status2 check ( status2 in ('N','A','I')),
|
||||
constraint pk_migtest_e_basic primary key (id)
|
||||
);
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest2 on migtest_e_basic(indextest2) exclude null keys;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest6 on migtest_e_basic(indextest6) exclude null keys;
|
||||
|
||||
create table migtest_e_enum (
|
||||
@@ -131,7 +129,6 @@ create table migtest_e_ref (
|
||||
name varchar(127) not null,
|
||||
constraint pk_migtest_e_ref primary key (id)
|
||||
);
|
||||
-- alterTableAddUniqueConstraint
|
||||
alter table migtest_e_ref add constraint uq_migtest_e_ref_name unique (name);
|
||||
|
||||
create table migtest_e_softdelete (
|
||||
|
||||
@@ -75,9 +75,7 @@ create table migtest_e_basic (
|
||||
constraint ck_migtest_e_basic_status2 check ( status2 in ('N','A','I')),
|
||||
constraint pk_migtest_e_basic primary key (id)
|
||||
);
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest2 on migtest_e_basic(indextest2) exclude null keys;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest6 on migtest_e_basic(indextest6) exclude null keys;
|
||||
|
||||
create table migtest_e_enum (
|
||||
@@ -131,7 +129,6 @@ create table migtest_e_ref (
|
||||
name varchar(127) not null,
|
||||
constraint pk_migtest_e_ref primary key (id)
|
||||
);
|
||||
-- alterTableAddUniqueConstraint
|
||||
alter table migtest_e_ref add constraint uq_migtest_e_ref_name unique (name);
|
||||
|
||||
create table migtest_e_softdelete (
|
||||
|
||||
@@ -67,7 +67,6 @@ alter table migtest_e_basic alter column status2 drop not null;
|
||||
call sysproc.admin_cmd('reorg table migtest_e_basic') /* reorg #1 */;
|
||||
-- db2 does not support parial null indices :( - so we have to clean;
|
||||
update migtest_e_basic set status = 'N' where id = 1;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_description on migtest_e_basic(description) exclude null keys;
|
||||
|
||||
insert into migtest_e_user (id) select distinct user_id from migtest_e_basic;
|
||||
@@ -111,13 +110,9 @@ if exists (select indname from syscat.indexes where indschema = current_schema a
|
||||
execute stmt;
|
||||
end if;
|
||||
end$$;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_status_indextest1 on migtest_e_basic(status,indextest1) exclude null keys;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_name on migtest_e_basic(name) exclude null keys;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest4 on migtest_e_basic(indextest4) exclude null keys;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest5 on migtest_e_basic(indextest5) exclude null keys;
|
||||
delimiter $$
|
||||
begin
|
||||
|
||||
@@ -5,7 +5,6 @@ create table migtest_e_ref (
|
||||
name varchar(127) not null,
|
||||
constraint pk_migtest_e_ref primary key (id)
|
||||
);
|
||||
-- alterTableAddUniqueConstraint
|
||||
alter table migtest_e_ref add constraint uq_migtest_e_ref_name unique (name);
|
||||
|
||||
delimiter $$
|
||||
@@ -156,9 +155,7 @@ if exists (select indname from syscat.indexes where indschema = current_schema a
|
||||
end if;
|
||||
end$$;
|
||||
call sysproc.admin_cmd('reorg table migtest_e_basic') /* reorg #3 */;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest2 on migtest_e_basic(indextest2) exclude null keys;
|
||||
-- alterTableAddUniqueConstraint
|
||||
create unique index uq_migtest_e_basic_indextest6 on migtest_e_basic(indextest6) exclude null keys;
|
||||
delimiter $$
|
||||
begin
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
997100585, 1.0__initial.sql
|
||||
855187652, 1.1.sql
|
||||
1580812656, 1.0__initial.sql
|
||||
312838996, 1.1.sql
|
||||
1091886546, 1.2__dropsFor_1.1.sql
|
||||
-364921922, 1.3.sql
|
||||
-1041850370, 1.3.sql
|
||||
2020621716, 1.4__dropsFor_1.3.sql
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ 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.Test;
|
||||
@@ -10,6 +12,7 @@ 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;
|
||||
|
||||
@@ -19,12 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
public class TestJsonMapBasic extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void whereManyPredicate() {
|
||||
|
||||
if (!isPostgres()) {
|
||||
// testing postgres specific select distinct on clause
|
||||
return;
|
||||
}
|
||||
@ForPlatform(Platform.POSTGRES)
|
||||
public void whereManyPredicatePg() {
|
||||
|
||||
EBasicJsonMap bean = new EBasicJsonMap();
|
||||
bean.setName("own1");
|
||||
@@ -33,28 +32,60 @@ public class TestJsonMapBasic extends BaseTestCase {
|
||||
|
||||
DB.save(bean);
|
||||
|
||||
Query<EBasicJsonMap> query1 = DB.find(EBasicJsonMap.class)
|
||||
.fetch("details")
|
||||
.where().startsWith("details.name", "detail")
|
||||
.query();
|
||||
Query<EBasicJsonMap> query1 = DB.find(EBasicJsonMap.class).fetch("details").where()
|
||||
.startsWith("details.name", "detail").query();
|
||||
|
||||
query1.findList();
|
||||
|
||||
assertThat(query1.getGeneratedSql()).contains("select distinct on (t0.id, t1.id) ");
|
||||
|
||||
Query<EBasicJsonMap> query2 = DB.find(EBasicJsonMap.class)
|
||||
.where().startsWith("details.name", "detail")
|
||||
.query();
|
||||
Query<EBasicJsonMap> query2 = DB.find(EBasicJsonMap.class).where().startsWith("details.name", "detail").query();
|
||||
query2.findList();
|
||||
|
||||
assertThat(query2.getGeneratedSql()).contains("select distinct on (t0.id) ");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ForPlatform(Platform.DB2)
|
||||
public void whereManyPredicateDb2() {
|
||||
|
||||
EBasicJsonMap bean = new EBasicJsonMap();
|
||||
Map<String, Object> 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);
|
||||
try {
|
||||
Query<EBasicJsonMap> query1 = DB.find(EBasicJsonMap.class).select("*").fetch("details").where()
|
||||
.startsWith("details.name", "db2-detail").query();
|
||||
|
||||
List<EBasicJsonMap> 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<EBasicJsonMap> 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");
|
||||
} finally {
|
||||
// temporär hier, sollte die ganze Testklasse aufräumen
|
||||
DB.delete(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateDelete() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":18,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
//String s1 = "{\"docId\":19,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
// String s1 =
|
||||
// "{\"docId\":19,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
Map<String, Object> content = EJson.parseObject(s0);
|
||||
|
||||
@@ -117,10 +148,7 @@ public class TestJsonMapBasic extends BaseTestCase {
|
||||
|
||||
LoggedSql.start();
|
||||
|
||||
final int rows = DB.update(EBasicJsonMap.class)
|
||||
.set("content", content1)
|
||||
.where().eq("id", bean.getId())
|
||||
.update();
|
||||
final int rows = DB.update(EBasicJsonMap.class).set("content", content1).where().eq("id", bean.getId()).update();
|
||||
|
||||
final List<String> sql = LoggedSql.stop();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user