package org.tests.query.orderby;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import org.tests.model.basic.Customer;
import org.tests.model.basic.MRole;
import org.tests.model.basic.MUser;
import org.tests.model.basic.MUserType;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOrderByWithDistinct extends BaseTestCase {
@Test
public void testOrderByValidation() {
ResetBasicData.reset();
Query<Customer> query = Ebean.find(Customer.class)
.where()
.eq("junk", "blah")
.eq("name", "jim")
.orderBy("id desc,path.that.does.not.exist,contacts.group.name asc");
Set<String> unknownProperties = query.validate();
assertThat(unknownProperties).isNotEmpty();
assertThat(unknownProperties).hasSize(2);
assertThat(unknownProperties).contains("junk", "path.that.does.not.exist");
}
@Test
public void test() {
/*
* Original conversation:
* https://groups.google.com/forum/?fromgroups=#!topic/ebean/uuvi1btdCDQ%5B1-25-false%5D
*
* This test exposes what may be a general problem with columns required by the order by phrase being omitted from the select.
* I'm not sure this exposes all causes of the problem.
*/
MUserType ut = new MUserType("md");
Ebean.save(ut);
MUser user1 = new MUser("one");
user1.setUserType(ut);
Ebean.save(user1);
MUser user2 = new MUser("two");
user2.setUserType(ut);
Ebean.save(user2);
MRole roleA = new MRole("A");
Ebean.save(roleA);
MRole roleB = new MRole("B");
Ebean.save(roleB);
user1.addRole(roleA);
Ebean.save(user1);
user2.addRole(roleB);
Ebean.save(user2);
Query<MUser> query = Ebean.find(MUser.class)
.fetch("userType", "name")
.where()
.eq("roles.roleName", "A")
.orderBy("userType.name, userName");
List<MUser> list = query.findList();
// select distinct t0.userid c0, t0.user_name c1, t1.id c2, t1.name c3
// from muser t0
// left join muser_type t1 on t1.id = t0.user_type_id
// join mrole_muser u1z_ on u1z_.muser_userid = t0.userid
// join mrole u1 on u1.roleid = u1z_.mrole_roleid
// where u1.role_name = ?
// order by t1.name, t0.user_name; --bind(A)
Assert.assertEquals(1, list.size());
Assert.assertEquals(user1, list.get(0));
String generatedSql = query.getGeneratedSql();
if (isPostgres()) {
assertThat(generatedSql).contains("select distinct on (t1.name, t0.user_name, t0.userid) t0.userid"); // using distinct
} else {
assertThat(generatedSql).contains("select distinct t0.userid"); // using distinct
}
assertThat(generatedSql).contains("order by t1.name,"); // name in order by
assertThat(generatedSql).contains("t1.name");// name in select
// repeat with slight variation, not sure this really produces a different execution path
// this problem also manifests when autofetch eliminates properties from the select that aren't used in the objects
// still need them to be present for purpose of order by
// so here I'm "simulating" a scenario where autofetch has dropped userType.name
query = Ebean.find(MUser.class)
.setAutoTune(false)
.select("userName")
.fetch("userType", "name")
.where()
.eq("roles.roleName", "A")
.orderBy("userType.name");
list = query.findList();
Assert.assertEquals(1, list.size());
Assert.assertEquals(user1, list.get(0));
// select distinct t0.userid c0, t0.user_name c1, t1.id c2, t1.name c3
// from muser t0
// left join muser_type t1 on t1.id = t0.user_type_id
// join mrole_muser u1z_ on u1z_.muser_userid = t0.userid
// join mrole u1 on u1.roleid = u1z_.mrole_roleid
// where u1.role_name = ?
// order by t1.name; --bind(A)
generatedSql = query.getGeneratedSql();
if (isPostgres()) {
assertThat(generatedSql).contains("select distinct on (t1.name, t0.userid) t0.userid"); // using distinct
} else {
assertThat(generatedSql).contains("select distinct t0.userid"); // using distinct
}
assertThat(generatedSql).contains("order by t1.name"); // name in order by
assertThat(generatedSql).contains("t1.name");// name in select
}
}