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);
+               }
+       }
 }

Reply via email to