From b403942491fc1feec0b84dbd85a8217de151bc71 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 20 Dec 2018 23:55:07 +0100 Subject: [PATCH] Query cache bug, which produces wrong results (#1584) --- src/test/java/org/tests/query/cache/Acl.java | 20 +++++++ .../query/cache/AclContainerRelation.java | 48 +++++++++++++++ .../java/org/tests/query/cache/Contract.java | 49 +++++++++++++++ .../org/tests/query/cache/ContractCosts.java | 34 +++++++++++ .../java/org/tests/query/cache/Main1.java | 23 ++++++++ .../java/org/tests/query/cache/Main2.java | 22 +++++++ .../java/org/tests/query/cache/Position.java | 32 ++++++++++ .../org/tests/query/cache/TestDistinct.java | 59 +++++++++++++++++++ 8 files changed, 287 insertions(+) create mode 100644 src/test/java/org/tests/query/cache/Acl.java create mode 100644 src/test/java/org/tests/query/cache/AclContainerRelation.java create mode 100644 src/test/java/org/tests/query/cache/Contract.java create mode 100644 src/test/java/org/tests/query/cache/ContractCosts.java create mode 100644 src/test/java/org/tests/query/cache/Main1.java create mode 100644 src/test/java/org/tests/query/cache/Main2.java create mode 100644 src/test/java/org/tests/query/cache/Position.java create mode 100644 src/test/java/org/tests/query/cache/TestDistinct.java diff --git a/src/test/java/org/tests/query/cache/Acl.java b/src/test/java/org/tests/query/cache/Acl.java new file mode 100644 index 000000000..5919ae13a --- /dev/null +++ b/src/test/java/org/tests/query/cache/Acl.java @@ -0,0 +1,20 @@ +package org.tests.query.cache; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Acl { + @Id + @GeneratedValue + protected Long id; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } +} \ No newline at end of file diff --git a/src/test/java/org/tests/query/cache/AclContainerRelation.java b/src/test/java/org/tests/query/cache/AclContainerRelation.java new file mode 100644 index 000000000..9038bb56f --- /dev/null +++ b/src/test/java/org/tests/query/cache/AclContainerRelation.java @@ -0,0 +1,48 @@ +package org.tests.query.cache; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; + +@Entity +public class AclContainerRelation { + @Id + @GeneratedValue + protected Long id; + + @ManyToOne(cascade = {}, fetch = FetchType.LAZY) + @NotNull + private Contract container; + + @ManyToOne(cascade = {}, fetch = FetchType.LAZY) + @NotNull + private Acl aclEntry; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public Contract getContainer() { + return container; + } + + public void setContainer(final Contract container) { + this.container = container; + } + + public Acl getAclEntry() { + return aclEntry; + } + + public void setAclEntry(final Acl aclEntry) { + this.aclEntry = aclEntry; + } + +} \ No newline at end of file diff --git a/src/test/java/org/tests/query/cache/Contract.java b/src/test/java/org/tests/query/cache/Contract.java new file mode 100644 index 000000000..db89b1cbe --- /dev/null +++ b/src/test/java/org/tests/query/cache/Contract.java @@ -0,0 +1,49 @@ +package org.tests.query.cache; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity +public class Contract { + @Id + @GeneratedValue + protected Long id; + + @OneToMany(cascade = CascadeType.ALL, mappedBy = "contract") + private List positions; + + @OneToMany(cascade = CascadeType.ALL, mappedBy = "container", fetch = FetchType.LAZY) + @Nonnull + private List aclEntries = new ArrayList<>(); + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public List getPositions() { + return positions; + } + + public void setPositions(final List positions) { + this.positions = positions; + } + + public List getAclEntries() { + return aclEntries; + } + + public void setAclEntries(final List aclEntries) { + this.aclEntries = aclEntries; + } +} \ No newline at end of file diff --git a/src/test/java/org/tests/query/cache/ContractCosts.java b/src/test/java/org/tests/query/cache/ContractCosts.java new file mode 100644 index 000000000..ed2655bc3 --- /dev/null +++ b/src/test/java/org/tests/query/cache/ContractCosts.java @@ -0,0 +1,34 @@ +package org.tests.query.cache; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; + +@Entity +public class ContractCosts { + @Id + @GeneratedValue + protected Long id; + + @NotNull + @ManyToOne(cascade = {}, optional = false) + private Position position; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public Position getPosition() { + return position; + } + + public void setPosition(final Position position) { + this.position = position; + } +} \ No newline at end of file diff --git a/src/test/java/org/tests/query/cache/Main1.java b/src/test/java/org/tests/query/cache/Main1.java new file mode 100644 index 000000000..1a092443c --- /dev/null +++ b/src/test/java/org/tests/query/cache/Main1.java @@ -0,0 +1,23 @@ +package org.tests.query.cache; + + +public class Main1 { + + public static void main(String[] args) { + TestDistinct test = new TestDistinct(); + test.testMissingUnique(); + // executed query: + // select t0.id, t0.position_id from contract_costs t0 + // join position t1 on t1.id = t0.position_id + // join contract t2 on t2.id = t1.contract_id + // left join acl_container_relation t3 on t3.container_id = t2.id + // where t3.acl_entry_id in (?, ? ) ; --bind(Array[2]={1,2}) + + // Test will fail: + // Expected size:<1> but was:<2> in: + // <[org.tests.query.cache.ContractCosts@1, org.tests.query.cache.ContractCosts@1]> + // at org.tests.query.cache.TestDistinct.testMissingUnique(TestDistinct.java:49) + // at org.tests.query.cache.Main1.main(Main1.java:7) + } + +} diff --git a/src/test/java/org/tests/query/cache/Main2.java b/src/test/java/org/tests/query/cache/Main2.java new file mode 100644 index 000000000..56a7ee0b1 --- /dev/null +++ b/src/test/java/org/tests/query/cache/Main2.java @@ -0,0 +1,22 @@ +package org.tests.query.cache; + +import io.ebean.Ebean; + +public class Main2 { + + public static void main(String[] args) { + TestDistinct test = new TestDistinct(); + Ebean.find(ContractCosts.class).where() + .in("position.contract.aclEntries.aclEntry.id",1L, 2L, 3L).findList(); // <-- will fix some cache issue? + test.testMissingUnique(); + + // executed query: + // select distinct t0.id, t0.position_id from contract_costs t0 + // join position u1 on u1.id = t0.position_id + // join contract u2 on u2.id = u1.contract_id + // join acl_container_relation u3 on u3.container_id = u2.id + // where u3.acl_entry_id in (?, ? ) ; --bind(Array[2]={1,2}) + + } + +} diff --git a/src/test/java/org/tests/query/cache/Position.java b/src/test/java/org/tests/query/cache/Position.java new file mode 100644 index 000000000..fc6a5edbc --- /dev/null +++ b/src/test/java/org/tests/query/cache/Position.java @@ -0,0 +1,32 @@ +package org.tests.query.cache; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Position { + @Id + @GeneratedValue + protected Long id; + + @ManyToOne(cascade = {}, optional = false) + private Contract contract; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public Contract getContract() { + return contract; + } + + public void setContract(final Contract contract) { + this.contract = contract; + } +} \ No newline at end of file diff --git a/src/test/java/org/tests/query/cache/TestDistinct.java b/src/test/java/org/tests/query/cache/TestDistinct.java new file mode 100644 index 000000000..164c70019 --- /dev/null +++ b/src/test/java/org/tests/query/cache/TestDistinct.java @@ -0,0 +1,59 @@ +package org.tests.query.cache; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.UUID; + +import org.junit.Test; + +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import io.ebean.ExpressionList; + +public class TestDistinct extends BaseTestCase { + /** + * The call of {@link #positionsQuery} {@link ExpressionList#findList()} causes the following use of {@link #costsQuery(UUID...)} to not + * use distinct and return the wrong number of results. + */ + @Test + public void testMissingUnique() { + Acl acl = new Acl(); + Ebean.save(acl); + Acl acl2 = new Acl(); + Ebean.save(acl2); + Contract contract = new Contract(); + AclContainerRelation rel1 = new AclContainerRelation(); + rel1.setAclEntry(acl); + rel1.setContainer(contract); + AclContainerRelation rel2 = new AclContainerRelation(); + rel2.setAclEntry(acl2); + rel2.setContainer(contract); + contract.getAclEntries().add(rel1); + contract.getAclEntries().add(rel2); + Position pos = new Position(); + pos.setContract(contract); + contract.getPositions().add(pos); + Ebean.save(contract); + ContractCosts cost = new ContractCosts(); + cost.setPosition(pos); + Ebean.save(cost); + + //costsQuery(acl.getId(), acl2.getId()).findCount(); + + // the between causes the error + List positions = positionsQuery(acl.getId()).findList(); + // the between causes the error + + System.out.println("The error, query without distinct:"); + List costs = costsQuery(acl.getId(), acl2.getId()).findList(); + assertThat(costs).hasSize(1); + } + + public ExpressionList positionsQuery(final Long aclId) { + return Ebean.find(Position.class).where().eq("contract.aclEntries.aclEntry.id", aclId); + } + + public ExpressionList costsQuery(final Long... aclId) { + return Ebean.find(ContractCosts.class).where().in("position.contract.aclEntries.aclEntry.id", aclId); + } +} \ No newline at end of file