This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch STABLE-4.2
in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/STABLE-4.2 by this push:
new 9f051bca2 CAY-2863 DbEntity qualifiers are no longer applied to JOIN
conditions
9f051bca2 is described below
commit 9f051bca2b29a716195da506484de7038ea6b179
Author: Nikita Timofeev <[email protected]>
AuthorDate: Mon Nov 10 17:39:54 2025 +0400
CAY-2863 DbEntity qualifiers are no longer applied to JOIN conditions
---
RELEASE-NOTES.txt | 1 +
.../translator/select/TableTreeQualifierStage.java | 24 +++--------
.../access/translator/select/TableTreeStage.java | 35 ++++++++++++++++
.../select/DefaultSelectTranslatorIT.java | 47 ++++++++++++++++++----
4 files changed, 81 insertions(+), 26 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 6bc91d05c..1ee950b7c 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -21,6 +21,7 @@ CAY-2905 Upgrade Gradle to 8.14
Bug Fixes:
CAY-2836 ObjectSelect.selectCount() throws if a query contains ordering
+CAY-2863 DbEntity qualifiers are no longer applied to JOIN conditions
CAY-2883 License and notice templates are not processed by the Gradle build
CAY-2885 Modeler: DbImport fails to load DB schema view
CAY-2887 Expressions: Incorrect serialization to string of numeric literals
diff --git
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
index 53b84ae07..ca26d6cb3 100644
---
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
+++
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
@@ -21,8 +21,6 @@ package org.apache.cayenne.access.translator.select;
import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.exp.parser.ASTDbPath;
-import org.apache.cayenne.exp.parser.ASTPath;
/**
* @since 4.2
@@ -32,8 +30,11 @@ class TableTreeQualifierStage implements TranslationStage {
@Override
public void perform(TranslatorContext context) {
context.getTableTree().visit(node -> {
- appendQualifier(context, node, node.getEntity().getQualifier());
- appendQualifier(context, node, node.getAdditionalQualifier());
+ if(node.getRelationship() == null) {
+ // translate only root qualifier here, joined tables are
processed in the `TableTreeStage`
+ appendQualifier(context, node,
node.getEntity().getQualifier());
+ appendQualifier(context, node, node.getAdditionalQualifier());
+ }
});
if(context.getQualifierNode() != null) {
@@ -45,20 +46,7 @@ class TableTreeQualifierStage implements TranslationStage {
if (dbQualifier == null) {
return;
}
-
- String pathToRoot = node.getAttributePath().getPath();
- dbQualifier = dbQualifier.transform(input -> {
- if (input instanceof ASTPath) {
- String path = ((ASTPath) input).getPath();
- // here we are not only marking path as prefetch, but changing
ObjPath to DB
- // (without conversion, as it's a convenience option to allow
path without "db:" in the Modeler)
- if(!pathToRoot.isEmpty()) {
- path = pathToRoot + '.' + path;
- }
- return new ASTDbPath(path);
- }
- return input;
- });
+ dbQualifier = TableTreeStage.translateToDbPath(node, dbQualifier);
Node translatedQualifier =
context.getQualifierTranslator().translate(dbQualifier);
context.appendQualifierNode(translatedQualifier);
}
diff --git
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
index 25511f3ed..aa32bce03 100644
---
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
+++
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
@@ -24,6 +24,10 @@ import java.util.List;
import org.apache.cayenne.access.sqlbuilder.ExpressionNodeBuilder;
import org.apache.cayenne.access.sqlbuilder.JoinNodeBuilder;
import org.apache.cayenne.access.sqlbuilder.NodeBuilder;
+import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.exp.parser.ASTPath;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbJoin;
@@ -74,6 +78,37 @@ class TableTreeStage implements TranslationStage {
}
}
+ // append entity qualifiers
+ expressionNodeBuilder = appendQualifier(expressionNodeBuilder,
context, node, node.getEntity().getQualifier());
+ expressionNodeBuilder = appendQualifier(expressionNodeBuilder,
context, node, node.getAdditionalQualifier());
return expressionNodeBuilder;
}
+
+ private static ExpressionNodeBuilder appendQualifier(ExpressionNodeBuilder
joinBuilder,
+ TranslatorContext context,
+ TableTreeNode node,
+ Expression dbQualifier) {
+ if (dbQualifier == null) {
+ return joinBuilder;
+ }
+
+ dbQualifier = translateToDbPath(node, dbQualifier);
+ Node translatedQualifier =
context.getQualifierTranslator().translate(dbQualifier);
+ return joinBuilder.and(() -> translatedQualifier);
+ }
+
+ static Expression translateToDbPath(TableTreeNode node, Expression
dbQualifier) {
+ String pathToRoot = node.getAttributePath().getPath();
+ dbQualifier = dbQualifier.transform(input -> {
+ if (input instanceof ASTPath) {
+ String path = ((ASTPath) input).getPath();
+ if(!pathToRoot.isEmpty()) {
+ path = pathToRoot + '.' + path;
+ }
+ return new ASTDbPath(path);
+ }
+ return input;
+ });
+ return dbQualifier;
+ }
}
diff --git
a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
index d06f4d07b..69653fe5a 100644
---
a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
+++
b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslatorIT.java
@@ -150,14 +150,18 @@ public class DefaultSelectTranslatorIT extends ServerCase
{
// do some simple assertions to make sure all parts are
in
assertNotNull(generatedSql);
assertTrue(generatedSql.startsWith("SELECT "));
- assertTrue(generatedSql.indexOf(" FROM ") > 0);
- if (generatedSql.contains("RTRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME)
=") > generatedSql.indexOf("RTRIM("));
- } else if (generatedSql.contains("TRIM")) {
- assertTrue(generatedSql.indexOf("ARTIST_NAME)
=") > generatedSql.indexOf("TRIM("));
- } else {
- assertTrue(generatedSql.indexOf("ARTIST_NAME
=") > 0);
- }
+
+ int iFrom = generatedSql.indexOf(" FROM ");
+ int iPaintingTable = generatedSql.indexOf(" PAINTING ");
+ int iArtistTable = generatedSql.indexOf(" ARTIST ");
+ int iName = generatedSql.indexOf("ARTIST_NAME =");
+ int iOrder = generatedSql.indexOf(" ORDER");
+
+ assertTrue(iFrom > 0);
+ assertTrue(iPaintingTable > iFrom);
+ assertTrue(iArtistTable > iPaintingTable);
+ assertTrue(iName > iArtistTable);
+ assertTrue(iOrder > iName);
} finally {
entity.setQualifier(null);
@@ -856,4 +860,31 @@ public class DefaultSelectTranslatorIT extends ServerCase {
int totalJoins = translator.getContext().getTableCount() - 1;
assertEquals(4, totalJoins);
}
+
+ @Test
+ public void testDbEntityQualifier_JoinQuery() throws Exception {
+
+ final DbEntity entity =
context.getEntityResolver().getDbEntity("ARTIST");
+ entity.setQualifier(ExpressionFactory.exp("ARTIST_NAME =
'Should be on JOIN condition and not WHERE'"));
+
+ ObjectSelect<Painting> q = ObjectSelect.query(Painting.class)
+ .where
+ (
+
Painting.TO_ARTIST.dot(Artist.DATE_OF_BIRTH).eq(new java.sql.Date(1, 0, 1))
+
.orExp(Painting.TO_GALLERY.dot(Gallery.GALLERY_NAME).like("G%"))
+ );
+
+ // If the DbEntity qualifier is set on the WHERE condition then
the OR expression will fail to find matches
+
+ SelectTranslator transl = new DefaultSelectTranslator(q,
dataNode.getAdapter(), dataNode.getEntityResolver());
+ try {
+ String generatedSql = transl.getSql();
+ int whereNdx = generatedSql.indexOf(" WHERE ");
+ int joinNdx = generatedSql.indexOf(" JOIN ARTIST ");
+ assertTrue(generatedSql.substring(joinNdx,
whereNdx).indexOf("ARTIST_NAME") > 0); // Should be in JOIN condition
+ assertTrue(generatedSql.indexOf("ARTIST_NAME",
whereNdx) < 0); // Should not be part of WHERE
+ } finally {
+ entity.setQualifier(null);
+ }
+ }
}