Skip to content

Commit

Permalink
CAY-2389 DbEntity qualifier with DbPath expression translates into wr…
Browse files Browse the repository at this point in the history
…ong SQL
  • Loading branch information
stariy95 committed Dec 13, 2017
1 parent 38f37d7 commit 41dd56c
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 17 deletions.
Expand Up @@ -25,8 +25,7 @@
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.parser.ASTDbPath;
import org.apache.cayenne.exp.parser.ASTObjPath;
import org.apache.cayenne.exp.parser.SimpleNode;
import org.apache.cayenne.exp.parser.ASTPath;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
Expand Down Expand Up @@ -211,8 +210,8 @@ class JoinedDbEntityQualifierTransformer implements Function<Object, Object> {
}

public Object apply(Object input) {
if (input instanceof ASTObjPath) {
return new ASTDbPath(pathToRoot.toString() + ((SimpleNode) input).getOperand(0));
if (input instanceof ASTPath) {
return new ASTDbPath(pathToRoot.toString() + ((ASTPath) input).getPath());
}
return input;
}
Expand Down
Expand Up @@ -19,11 +19,17 @@
package org.apache.cayenne;

import org.apache.cayenne.di.Inject;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.query.ObjectSelect;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.qualified.Qualified1;
import org.apache.cayenne.testdo.qualified.Qualified2;
import org.apache.cayenne.testdo.qualified.Qualified3;
import org.apache.cayenne.testdo.qualified.Qualified4;
import org.apache.cayenne.unit.UnitDbAdapter;
import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
Expand Down Expand Up @@ -51,23 +57,28 @@ public class CDOQualifiedEntitiesIT extends ServerCase {

private TableHelper tQualified1;
private TableHelper tQualified2;
private TableHelper tQualified3;
private TableHelper tQualified4;

@Before
public void setUp() throws Exception {
int bool = accessStackAdapter.supportsBoolean() ? Types.BOOLEAN : Types.INTEGER;

tQualified1 = new TableHelper(dbHelper, "TEST_QUALIFIED1");
tQualified1.setColumns("ID", "NAME", "DELETED").setColumnTypes(
Types.INTEGER,
Types.VARCHAR,
bool);

tQualified2 = new TableHelper(dbHelper, "TEST_QUALIFIED2");
tQualified2.setColumns("ID", "NAME", "DELETED", "QUALIFIED1_ID").setColumnTypes(
Types.INTEGER,
Types.VARCHAR,
bool,
Types.INTEGER);
tQualified1 = new TableHelper(dbHelper, "TEST_QUALIFIED1")
.setColumns("ID", "NAME", "DELETED")
.setColumnTypes(Types.INTEGER, Types.VARCHAR, bool);

tQualified2 = new TableHelper(dbHelper, "TEST_QUALIFIED2")
.setColumns("ID", "NAME", "DELETED", "QUALIFIED1_ID")
.setColumnTypes(Types.INTEGER, Types.VARCHAR, bool, Types.INTEGER);

tQualified3 = new TableHelper(dbHelper, "TEST_QUALIFIED3")
.setColumns("ID", "NAME", "DELETED")
.setColumnTypes(Types.INTEGER, Types.VARCHAR, bool);

tQualified4 = new TableHelper(dbHelper, "TEST_QUALIFIED4")
.setColumns("ID", "NAME", "DELETED", "QUALIFIED3_ID")
.setColumnTypes(Types.INTEGER, Types.VARCHAR, bool, Types.INTEGER);
}

private void createReadToManyDataSet() throws Exception {
Expand All @@ -88,6 +99,14 @@ private void createReadToOneDataSet() throws Exception {
tQualified2.insert(1, "OY1", null, 2);
}

private void createJoinDataSet() throws Exception {
tQualified3.insert(1, "O1", null);
tQualified3.insert(2, "O2", accessStackAdapter.supportsBoolean() ? true : 1);

tQualified4.insert(1, "SHOULD_SELECT", null, 1);
tQualified4.insert(2, "SHOULD_NOT_SELECT", null, 2);
}

@Test
public void testReadToMany() throws Exception {
if (accessStackAdapter.supportsNullBoolean()) {
Expand Down Expand Up @@ -125,7 +144,43 @@ public void testReadToOne() throws Exception {
assertEquals("OY1", root.getName());

Qualified1 target = root.getQualified1();
assertNull("" + target, target);
assertNull(target);
}
}

@Test
public void joinWithQualifier() throws Exception {
createJoinDataSet();

List<Qualified4> result = ObjectSelect.query(Qualified4.class)
.where(Qualified4.QUALIFIED3.dot(Qualified3.NAME).like("O%"))
.select(context);

assertEquals(1, result.size());
assertEquals("SHOULD_SELECT", result.get(0).getName());
}

@Test
public void joinWithCustomDbQualifier() throws Exception {
createJoinDataSet();

DbEntity entity1 = context.getEntityResolver().getDbEntity("TEST_QUALIFIED3");
DbEntity entity2 = context.getEntityResolver().getDbEntity("TEST_QUALIFIED4");
Expression oldExpression1 = entity1.getQualifier();
Expression oldExpression2 = entity2.getQualifier();
try {
entity1.setQualifier(ExpressionFactory.matchDbExp("DELETED", null));
entity2.setQualifier(ExpressionFactory.matchDbExp("DELETED", null));

List<Qualified4> result = ObjectSelect.query(Qualified4.class)
.where(Qualified4.QUALIFIED3.dot(Qualified3.NAME).like("O%"))
.select(context);

assertEquals(1, result.size());
assertEquals("SHOULD_SELECT", result.get(0).getName());
} finally {
entity1.setQualifier(oldExpression1);
entity2.setQualifier(oldExpression2);
}
}
}
@@ -0,0 +1,9 @@
package org.apache.cayenne.testdo.qualified;

import org.apache.cayenne.testdo.qualified.auto._Qualified3;

public class Qualified3 extends _Qualified3 {

private static final long serialVersionUID = 1L;

}
@@ -0,0 +1,9 @@
package org.apache.cayenne.testdo.qualified;

import org.apache.cayenne.testdo.qualified.auto._Qualified4;

public class Qualified4 extends _Qualified4 {

private static final long serialVersionUID = 1L;

}
@@ -0,0 +1,110 @@
package org.apache.cayenne.testdo.qualified.auto;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

import org.apache.cayenne.BaseDataObject;
import org.apache.cayenne.exp.Property;
import org.apache.cayenne.testdo.qualified.Qualified4;

/**
* Class _Qualified3 was generated by Cayenne.
* It is probably a good idea to avoid changing this class manually,
* since it may be overwritten next time code is regenerated.
* If you need to make any customizations, please use subclass.
*/
public abstract class _Qualified3 extends BaseDataObject {

private static final long serialVersionUID = 1L;

public static final String ID_PK_COLUMN = "ID";

public static final Property<String> NAME = Property.create("name", String.class);
public static final Property<List<Qualified4>> QUALIFIED4S = Property.create("qualified4s", List.class);

protected String name;

protected Object qualified4s;

public void setName(String name) {
beforePropertyWrite("name", this.name, name);
this.name = name;
}

public String getName() {
beforePropertyRead("name");
return this.name;
}

public void addToQualified4s(Qualified4 obj) {
addToManyTarget("qualified4s", obj, true);
}

public void removeFromQualified4s(Qualified4 obj) {
removeToManyTarget("qualified4s", obj, true);
}

@SuppressWarnings("unchecked")
public List<Qualified4> getQualified4s() {
return (List<Qualified4>)readProperty("qualified4s");
}

@Override
public Object readPropertyDirectly(String propName) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch(propName) {
case "name":
return this.name;
case "qualified4s":
return this.qualified4s;
default:
return super.readPropertyDirectly(propName);
}
}

@Override
public void writePropertyDirectly(String propName, Object val) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch (propName) {
case "name":
this.name = (String)val;
break;
case "qualified4s":
this.qualified4s = val;
break;
default:
super.writePropertyDirectly(propName, val);
}
}

private void writeObject(ObjectOutputStream out) throws IOException {
writeSerialized(out);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
readSerialized(in);
}

@Override
protected void writeState(ObjectOutputStream out) throws IOException {
super.writeState(out);
out.writeObject(this.name);
out.writeObject(this.qualified4s);
}

@Override
protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
super.readState(in);
this.name = (String)in.readObject();
this.qualified4s = in.readObject();
}

}
@@ -0,0 +1,104 @@
package org.apache.cayenne.testdo.qualified.auto;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.apache.cayenne.BaseDataObject;
import org.apache.cayenne.exp.Property;
import org.apache.cayenne.testdo.qualified.Qualified3;

/**
* Class _Qualified4 was generated by Cayenne.
* It is probably a good idea to avoid changing this class manually,
* since it may be overwritten next time code is regenerated.
* If you need to make any customizations, please use subclass.
*/
public abstract class _Qualified4 extends BaseDataObject {

private static final long serialVersionUID = 1L;

public static final String ID_PK_COLUMN = "ID";

public static final Property<String> NAME = Property.create("name", String.class);
public static final Property<Qualified3> QUALIFIED3 = Property.create("qualified3", Qualified3.class);

protected String name;

protected Object qualified3;

public void setName(String name) {
beforePropertyWrite("name", this.name, name);
this.name = name;
}

public String getName() {
beforePropertyRead("name");
return this.name;
}

public void setQualified3(Qualified3 qualified3) {
setToOneTarget("qualified3", qualified3, true);
}

public Qualified3 getQualified3() {
return (Qualified3)readProperty("qualified3");
}

@Override
public Object readPropertyDirectly(String propName) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch(propName) {
case "name":
return this.name;
case "qualified3":
return this.qualified3;
default:
return super.readPropertyDirectly(propName);
}
}

@Override
public void writePropertyDirectly(String propName, Object val) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch (propName) {
case "name":
this.name = (String)val;
break;
case "qualified3":
this.qualified3 = val;
break;
default:
super.writePropertyDirectly(propName, val);
}
}

private void writeObject(ObjectOutputStream out) throws IOException {
writeSerialized(out);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
readSerialized(in);
}

@Override
protected void writeState(ObjectOutputStream out) throws IOException {
super.writeState(out);
out.writeObject(this.name);
out.writeObject(this.qualified3);
}

@Override
protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
super.readState(in);
this.name = (String)in.readObject();
this.qualified3 = in.readObject();
}

}
2 changes: 2 additions & 0 deletions cayenne-server/src/test/resources/cayenne-qualified.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<domain xmlns="http://cayenne.apache.org/schema/10/domain"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd"
project-version="10">
<map name="qualified"/>
</domain>

0 comments on commit 41dd56c

Please sign in to comment.