Skip to content

Commit 4702c36

Browse files
committed
CAY-2863 DbEntity qualifiers are no longer applied to JOIN conditions
1 parent a69c2d9 commit 4702c36

File tree

6 files changed

+55
-17
lines changed

6 files changed

+55
-17
lines changed

cayenne/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ protected void processNormalAttribute(String next) {
6161
return;
6262
}
6363

64+
// special case when the path should be processed in the context of the current join clause
65+
if(TableTree.CURRENT_ALIAS.equals(next)) {
66+
entity = context.getTableTree().nonNullActiveNode().getEntity();
67+
appendCurrentPath(next);
68+
return;
69+
}
70+
6471
throw new IllegalStateException("Unable to resolve path: " + currentDbPath.toString() + "." + next);
6572
}
6673

cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,9 @@ private Node processPathTranslationResult(Expression node, Expression parentNode
296296
return new EmptyNode();
297297
} else {
298298
String alias = context.getTableTree().aliasForPath(result.getLastAttributePath());
299+
// special case when the path should be processed in the context of the current join clause
299300
if(TableTree.CURRENT_ALIAS.equals(alias)) {
300-
alias = node.getPathAliases().get(TableTree.CURRENT_ALIAS);
301+
alias = context.getTableTree().nonNullActiveNode().getTableAlias();
301302
}
302303
return table(alias).column(result.getLastAttribute()).build();
303304
}

cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
class TableTree {
3838

3939
public static final String CURRENT_ALIAS = "__current_table_alias__";
40+
public static final CayennePath CURRENT_ALIAS_PATH = CayennePath.of("__current_table_alias__");
4041

4142
/**
4243
* Tables mapped by db path it's spawned by.
@@ -50,6 +51,7 @@ class TableTree {
5051
private final TableTree parentTree;
5152
private final TableTreeNode rootNode;
5253

54+
private TableTreeNode activeNode;
5355
private int tableAliasSequence;
5456

5557
TableTree(DbEntity root, TableTree parentTree) {
@@ -64,10 +66,6 @@ void addJoinTable(CayennePath path, DbRelationship relationship, JoinType joinTy
6466
}
6567

6668
void addJoinTable(CayennePath path, DbRelationship relationship, JoinType joinType, Expression additionalQualifier) {
67-
// skip adding new node if we are resolving table tree itself
68-
if(path.marker() == CayennePath.TABLE_TREE_MARKER) {
69-
return;
70-
}
7169
TableTreeNode treeNode = tableNodes.get(path);
7270
if (treeNode != null) {
7371
return;
@@ -78,13 +76,13 @@ void addJoinTable(CayennePath path, DbRelationship relationship, JoinType joinTy
7876
}
7977

8078
String aliasForPath(CayennePath attributePath) {
81-
// should be resolved dynamically by the caller
82-
if(attributePath.marker() == CayennePath.TABLE_TREE_MARKER) {
83-
return CURRENT_ALIAS;
84-
}
8579
if(attributePath.isEmpty()) {
8680
return rootNode.getTableAlias();
8781
}
82+
// should be resolved dynamically by the caller
83+
if(CURRENT_ALIAS_PATH.equals(attributePath)) {
84+
return CURRENT_ALIAS;
85+
}
8886
TableTreeNode node = tableNodes.get(attributePath);
8987
if (node == null) {
9088
throw new CayenneRuntimeException("No table for attribute '%s' found", attributePath);
@@ -100,6 +98,17 @@ String nextTableAlias() {
10098
return 't' + String.valueOf(tableAliasSequence++);
10199
}
102100

101+
TableTreeNode nonNullActiveNode() {
102+
if(activeNode == null) {
103+
throw new CayenneRuntimeException("No active TableTree node found");
104+
}
105+
return activeNode;
106+
}
107+
108+
void setActiveNode(TableTreeNode activeNode) {
109+
this.activeNode = activeNode;
110+
}
111+
103112
public int getNodeCount() {
104113
return tableNodes.size() + 1;
105114
}

cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ private static ExpressionNodeBuilder appendQualifier(ExpressionNodeBuilder joinB
9595
}
9696

9797
dbQualifier = translateToDbPath(node, dbQualifier);
98+
// mark table tree node as current to process qualifier
99+
context.getTableTree().setActiveNode(node);
98100
Node translatedQualifier = context.getQualifierTranslator().translate(dbQualifier);
101+
context.getTableTree().setActiveNode(null);
99102
return joinBuilder.and(() -> translatedQualifier);
100103
}
101104

@@ -104,9 +107,14 @@ static Expression translateToDbPath(TableTreeNode node, Expression dbQualifier)
104107
dbQualifier = dbQualifier.transform(input -> {
105108
// here we are not only marking path, but changing ObjPath to DB
106109
if (input instanceof ASTPath) {
107-
ASTDbPath dbPath = new ASTDbPath(pathToRoot.dot(((ASTPath) input).getPath()).withMarker(CayennePath.TABLE_TREE_MARKER));
108-
dbPath.setPathAliases(Map.of(TableTree.CURRENT_ALIAS, node.getTableAlias()));
109-
return dbPath;
110+
// we do not really care about the parent path, as we do not need to join any new table here.
111+
// so we must tell the path processor that we are processing exactly this table
112+
// TODO: should check qualifiers via related tables if that is even the thing
113+
CayennePath path = ((ASTPath) input).getPath();
114+
if(!pathToRoot.isEmpty()) {
115+
path = TableTree.CURRENT_ALIAS_PATH.dot(path);
116+
}
117+
return new ASTDbPath(path);
110118
}
111119
return input;
112120
});

cayenne/src/main/java/org/apache/cayenne/exp/path/CayennePath.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ public interface CayennePath extends Iterable<CayennePathSegment>, Serializable
5757
*/
5858
int PREFETCH_MARKER = 1;
5959

60-
/**
61-
* Marker denotes paths inside tree resolution logic
62-
*/
63-
int TABLE_TREE_MARKER = 2;
64-
6560
/**
6661
* Constant value for an empty path
6762
*/

cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,13 @@ private void createReadToOneDataSet() throws Exception {
101101
private void createJoinDataSet() throws Exception {
102102
tQualified3.insert(1, "O1", null);
103103
tQualified3.insert(2, "O2", accessStackAdapter.supportsBoolean() ? true : 1);
104+
tQualified3.insert(3, "11", null);
105+
tQualified3.insert(4, "12", accessStackAdapter.supportsBoolean() ? true : 1);
104106

105107
tQualified4.insert(1, "SHOULD_SELECT", null, 1);
106108
tQualified4.insert(2, "SHOULD_NOT_SELECT", null, 2);
109+
tQualified4.insert(3, "SHOULD_SELECT_TOO", null, 3);
110+
tQualified4.insert(4, "SHOULD_NOT_SELECT_TOO", null, 4);
107111
}
108112

109113
@Test
@@ -229,6 +233,20 @@ public void joinWithQualifier() throws Exception {
229233
assertEquals("SHOULD_SELECT", result.get(0).getName());
230234
}
231235

236+
@Test
237+
public void joinWithQualifierAndAliases() throws Exception {
238+
createJoinDataSet();
239+
240+
List<Qualified4> result = ObjectSelect.query(Qualified4.class)
241+
.where(Qualified4.QUALIFIED3.alias("a1").dot(Qualified3.NAME).like("O%"))
242+
.or(Qualified4.QUALIFIED3.alias("a2").dot(Qualified3.NAME).like("1%"))
243+
.select(context);
244+
245+
assertEquals(2, result.size());
246+
assertEquals("SHOULD_SELECT", result.get(0).getName());
247+
assertEquals("SHOULD_SELECT_TOO", result.get(1).getName());
248+
}
249+
232250
@Test
233251
public void joinWithCustomDbQualifier() throws Exception {
234252
createJoinDataSet();

0 commit comments

Comments
 (0)