Query cache bug, which produces wrong results (#1584)

This commit is contained in:
Roland Praml
2018-12-21 11:55:07 +13:00
committed by Rob Bygrave
parent baa3909c63
commit b403942491
8 changed files with 287 additions and 0 deletions
+20
View File
@@ -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;
}
}
@@ -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;
}
}
+49
View File
@@ -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<Position> positions;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "container", fetch = FetchType.LAZY)
@Nonnull
private List<AclContainerRelation> aclEntries = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public List<Position> getPositions() {
return positions;
}
public void setPositions(final List<Position> positions) {
this.positions = positions;
}
public List<AclContainerRelation> getAclEntries() {
return aclEntries;
}
public void setAclEntries(final List<AclContainerRelation> aclEntries) {
this.aclEntries = aclEntries;
}
}
+34
View File
@@ -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;
}
}
+23
View File
@@ -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)
}
}
+22
View File
@@ -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})
}
}
+32
View File
@@ -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;
}
}
+59
View File
@@ -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<Position> positions = positionsQuery(acl.getId()).findList();
// the between causes the error
System.out.println("The error, query without distinct:");
List<ContractCosts> costs = costsQuery(acl.getId(), acl2.getId()).findList();
assertThat(costs).hasSize(1);
}
public ExpressionList<Position> positionsQuery(final Long aclId) {
return Ebean.find(Position.class).where().eq("contract.aclEntries.aclEntry.id", aclId);
}
public ExpressionList<ContractCosts> costsQuery(final Long... aclId) {
return Ebean.find(ContractCosts.class).where().in("position.contract.aclEntries.aclEntry.id", aclId);
}
}