Skip to content

Commit 9f051bc

Browse files
committed
CAY-2863 DbEntity qualifiers are no longer applied to JOIN conditions
1 parent 6ef8cfc commit 9f051bc

File tree

4 files changed

+81
-26
lines changed

4 files changed

+81
-26
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ CAY-2905 Upgrade Gradle to 8.14
2121
Bug Fixes:
2222

2323
CAY-2836 ObjectSelect.selectCount() throws if a query contains ordering
24+
CAY-2863 DbEntity qualifiers are no longer applied to JOIN conditions
2425
CAY-2883 License and notice templates are not processed by the Gradle build
2526
CAY-2885 Modeler: DbImport fails to load DB schema view
2627
CAY-2887 Expressions: Incorrect serialization to string of numeric literals

cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121

2222
import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
2323
import org.apache.cayenne.exp.Expression;
24-
import org.apache.cayenne.exp.parser.ASTDbPath;
25-
import org.apache.cayenne.exp.parser.ASTPath;
2624

2725
/**
2826
* @since 4.2
@@ -32,8 +30,11 @@ class TableTreeQualifierStage implements TranslationStage {
3230
@Override
3331
public void perform(TranslatorContext context) {
3432
context.getTableTree().visit(node -> {
35-
appendQualifier(context, node, node.getEntity().getQualifier());
36-
appendQualifier(context, node, node.getAdditionalQualifier());
33+
if(node.getRelationship() == null) {
34+
// translate only root qualifier here, joined tables are processed in the `TableTreeStage`
35+
appendQualifier(context, node, node.getEntity().getQualifier());
36+
appendQualifier(context, node, node.getAdditionalQualifier());
37+
}
3738
});
3839

3940
if(context.getQualifierNode() != null) {
@@ -45,20 +46,7 @@ private static void appendQualifier(TranslatorContext context, TableTreeNode nod
4546
if (dbQualifier == null) {
4647
return;
4748
}
48-
49-
String pathToRoot = node.getAttributePath().getPath();
50-
dbQualifier = dbQualifier.transform(input -> {
51-
if (input instanceof ASTPath) {
52-
String path = ((ASTPath) input).getPath();
53-
// here we are not only marking path as prefetch, but changing ObjPath to DB
54-
// (without conversion, as it's a convenience option to allow path without "db:" in the Modeler)
55-
if(!pathToRoot.isEmpty()) {
56-
path = pathToRoot + '.' + path;
57-
}
58-
return new ASTDbPath(path);
59-
}
60-
return input;
61-
});
49+
dbQualifier = TableTreeStage.translateToDbPath(node, dbQualifier);
6250
Node translatedQualifier = context.getQualifierTranslator().translate(dbQualifier);
6351
context.appendQualifierNode(translatedQualifier);
6452
}

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
import org.apache.cayenne.access.sqlbuilder.ExpressionNodeBuilder;
2525
import org.apache.cayenne.access.sqlbuilder.JoinNodeBuilder;
2626
import org.apache.cayenne.access.sqlbuilder.NodeBuilder;
27+
import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
28+
import org.apache.cayenne.exp.Expression;
29+
import org.apache.cayenne.exp.parser.ASTDbPath;
30+
import org.apache.cayenne.exp.parser.ASTPath;
2731
import org.apache.cayenne.map.DbAttribute;
2832
import org.apache.cayenne.map.DbJoin;
2933

@@ -74,6 +78,37 @@ private NodeBuilder getJoinExpression(TranslatorContext context, TableTreeNode n
7478
}
7579
}
7680

81+
// append entity qualifiers
82+
expressionNodeBuilder = appendQualifier(expressionNodeBuilder, context, node, node.getEntity().getQualifier());
83+
expressionNodeBuilder = appendQualifier(expressionNodeBuilder, context, node, node.getAdditionalQualifier());
7784
return expressionNodeBuilder;
7885
}
86+
87+
private static ExpressionNodeBuilder appendQualifier(ExpressionNodeBuilder joinBuilder,
88+
TranslatorContext context,
89+
TableTreeNode node,
90+
Expression dbQualifier) {
91+
if (dbQualifier == null) {
92+
return joinBuilder;
93+
}
94+
95+
dbQualifier = translateToDbPath(node, dbQualifier);
96+
Node translatedQualifier = context.getQualifierTranslator().translate(dbQualifier);
97+
return joinBuilder.and(() -> translatedQualifier);
98+
}
99+
100+
static Expression translateToDbPath(TableTreeNode node, Expression dbQualifier) {
101+
String pathToRoot = node.getAttributePath().getPath();
102+
dbQualifier = dbQualifier.transform(input -> {
103+
if (input instanceof ASTPath) {
104+
String path = ((ASTPath) input).getPath();
105+
if(!pathToRoot.isEmpty()) {
106+
path = pathToRoot + '.' + path;
107+
}
108+
return new ASTDbPath(path);
109+
}
110+
return input;
111+
});
112+
return dbQualifier;
113+
}
79114
}

cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,18 @@ public void testDbEntityQualifier_OuterJoin() throws Exception {
150150
// do some simple assertions to make sure all parts are in
151151
assertNotNull(generatedSql);
152152
assertTrue(generatedSql.startsWith("SELECT "));
153-
assertTrue(generatedSql.indexOf(" FROM ") > 0);
154-
if (generatedSql.contains("RTRIM")) {
155-
assertTrue(generatedSql.indexOf("ARTIST_NAME) =") > generatedSql.indexOf("RTRIM("));
156-
} else if (generatedSql.contains("TRIM")) {
157-
assertTrue(generatedSql.indexOf("ARTIST_NAME) =") > generatedSql.indexOf("TRIM("));
158-
} else {
159-
assertTrue(generatedSql.indexOf("ARTIST_NAME =") > 0);
160-
}
153+
154+
int iFrom = generatedSql.indexOf(" FROM ");
155+
int iPaintingTable = generatedSql.indexOf(" PAINTING ");
156+
int iArtistTable = generatedSql.indexOf(" ARTIST ");
157+
int iName = generatedSql.indexOf("ARTIST_NAME =");
158+
int iOrder = generatedSql.indexOf(" ORDER");
159+
160+
assertTrue(iFrom > 0);
161+
assertTrue(iPaintingTable > iFrom);
162+
assertTrue(iArtistTable > iPaintingTable);
163+
assertTrue(iName > iArtistTable);
164+
assertTrue(iOrder > iName);
161165

162166
} finally {
163167
entity.setQualifier(null);
@@ -856,4 +860,31 @@ public void testAliasedJoins_FlattenedRelationship() {
856860
int totalJoins = translator.getContext().getTableCount() - 1;
857861
assertEquals(4, totalJoins);
858862
}
863+
864+
@Test
865+
public void testDbEntityQualifier_JoinQuery() throws Exception {
866+
867+
final DbEntity entity = context.getEntityResolver().getDbEntity("ARTIST");
868+
entity.setQualifier(ExpressionFactory.exp("ARTIST_NAME = 'Should be on JOIN condition and not WHERE'"));
869+
870+
ObjectSelect<Painting> q = ObjectSelect.query(Painting.class)
871+
.where
872+
(
873+
Painting.TO_ARTIST.dot(Artist.DATE_OF_BIRTH).eq(new java.sql.Date(1, 0, 1))
874+
.orExp(Painting.TO_GALLERY.dot(Gallery.GALLERY_NAME).like("G%"))
875+
);
876+
877+
// If the DbEntity qualifier is set on the WHERE condition then the OR expression will fail to find matches
878+
879+
SelectTranslator transl = new DefaultSelectTranslator(q, dataNode.getAdapter(), dataNode.getEntityResolver());
880+
try {
881+
String generatedSql = transl.getSql();
882+
int whereNdx = generatedSql.indexOf(" WHERE ");
883+
int joinNdx = generatedSql.indexOf(" JOIN ARTIST ");
884+
assertTrue(generatedSql.substring(joinNdx, whereNdx).indexOf("ARTIST_NAME") > 0); // Should be in JOIN condition
885+
assertTrue(generatedSql.indexOf("ARTIST_NAME", whereNdx) < 0); // Should not be part of WHERE
886+
} finally {
887+
entity.setQualifier(null);
888+
}
889+
}
859890
}

0 commit comments

Comments
 (0)