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 1124cb9de CAY-2844 Joint prefetch doesn't use ObjEntity qualifier
1124cb9de is described below

commit 1124cb9deca380f68449a1cdc50f86e71eb96f54
Author: Nikita Timofeev <stari...@gmail.com>
AuthorDate: Tue Mar 5 13:26:51 2024 +0400

    CAY-2844 Joint prefetch doesn't use ObjEntity qualifier
    
    (cherry picked from commit 55acf42bcd29f3d663f5e343d20e8f681c856c43)
---
 RELEASE-NOTES.txt                                  |  1 +
 .../access/translator/select/DbPathProcessor.java  |  5 +-
 .../access/translator/select/PathProcessor.java    | 11 ++--
 .../translator/select/PrefetchNodeStage.java       | 19 +++++-
 .../access/translator/select/TableTree.java        | 12 +++-
 .../access/translator/select/TableTreeNode.java    | 11 +++-
 .../translator/select/TableTreeQualifierStage.java | 50 ++++++++-------
 .../translator/select/TranslatorContext.java       | 11 ++++
 .../org/apache/cayenne/CDOQualifiedEntitiesIT.java | 72 ++++++++++++++++++++++
 9 files changed, 151 insertions(+), 41 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index dbad3137f..cd90a551c 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -23,6 +23,7 @@ CAY-2815 Incorrect translation of aliased expression
 CAY-2838 Vertical Inheritance: Problem setting db attribute to null via 
flattened path
 CAY-2840 Vertical Inheritance: Missing subclass attributes with joint prefetch
 CAY-2841 Multi column ColumnSelect with SHARED_CACHE fails after 1st select
+CAY-2844 Joint prefetch doesn't use ObjEntity qualifier
 
 ----------------------------------
 Release: 4.2
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
index 104299df0..c01bd0582 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
@@ -30,7 +30,7 @@ import org.apache.cayenne.map.JoinType;
  */
 class DbPathProcessor extends PathProcessor<DbEntity> {
 
-    private boolean flattenedPath;
+    private final boolean flattenedPath;
 
     DbPathProcessor(TranslatorContext context, DbEntity entity, String 
parentPath, boolean flattenedPath) {
         super(context, entity);
@@ -47,6 +47,9 @@ class DbPathProcessor extends PathProcessor<DbEntity> {
 
     @Override
     protected void processNormalAttribute(String next) {
+        if(next.startsWith("p:")) {
+            next = next.substring(2);
+        }
         DbAttribute dbAttribute = entity.getAttribute(next);
         if (dbAttribute != null) {
             processAttribute(dbAttribute);
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
index 8c68ebf57..3786eac13 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
@@ -21,7 +21,6 @@ package org.apache.cayenne.access.translator.select;
 
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.Embeddable;
 import org.apache.cayenne.map.Entity;
 
 import java.util.ArrayList;
@@ -61,6 +60,9 @@ abstract class PathProcessor<T extends Entity> implements 
PathTranslationResult
     }
 
     public PathTranslationResult process(String path) {
+        if(path.startsWith("p:")) {
+            currentDbPath.insert(0, "p:");
+        }
         PathComponents components = new PathComponents(path);
         String[] rawComponents = components.getAll();
         for (int i = 0; i < rawComponents.length; i++) {
@@ -111,18 +113,13 @@ abstract class PathProcessor<T extends Entity> implements 
PathTranslationResult
         return Optional.of(relationship);
     }
 
-    @Override
-    public Optional<Embeddable> getEmbeddable() {
-        return Optional.empty();
-    }
-
     @Override
     public String getFinalPath() {
         return currentDbPath.toString();
     }
 
     protected void appendCurrentPath(String nextSegment) {
-        if (currentDbPath.length() > 0) {
+        if (currentDbPath.length() > 0 && 
currentDbPath.charAt(currentDbPath.length() - 1) != ':') {
             currentDbPath.append('.');
         }
         currentDbPath.append(nextSegment);
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
index 924a6e916..f85c0a465 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
@@ -78,9 +78,11 @@ class PrefetchNodeStage implements TranslationStage {
 
         for(PrefetchTreeNode node : prefetch.adjacentJointNodes()) {
             Expression prefetchExp = ExpressionFactory.pathExp(node.getPath());
+            ObjRelationship targetRel = (ObjRelationship) 
prefetchExp.evaluate(objEntity);
             ASTDbPath dbPrefetch = (ASTDbPath) 
objEntity.translateToDbPath(prefetchExp);
             final String dbPath = dbPrefetch.getPath();
             DbEntity dbEntity = objEntity.getDbEntity();
+            Expression targetQualifier = 
context.getResolver().getClassDescriptor(targetRel.getTargetEntityName()).getEntityInheritanceTree().qualifierForEntityAndSubclasses();
 
             PathComponents components = new PathComponents(dbPath);
             StringBuilder fullPath = new StringBuilder();
@@ -92,13 +94,17 @@ class PrefetchNodeStage implements TranslationStage {
                 if(fullPath.length() > 0) {
                     fullPath.append('.');
                 }
-                context.getTableTree().addJoinTable("p:" + 
fullPath.append(c).toString(), rel, JoinType.LEFT_OUTER);
+                fullPath.append(c);
+                if(targetQualifier != null && c.equals(components.getLast())) {
+                    targetQualifier = 
translateToPrefetchQualifier(targetRel.getTargetEntity(), targetQualifier);
+                    context.getTableTree().addJoinTable("p:" + fullPath, rel, 
JoinType.LEFT_OUTER, targetQualifier);
+                } else {
+                    context.getTableTree().addJoinTable("p:" + fullPath, rel, 
JoinType.LEFT_OUTER);
+                }
                 dbEntity = rel.getTargetEntity();
             }
 
-            ObjRelationship targetRel = (ObjRelationship) 
prefetchExp.evaluate(objEntity);
             ClassDescriptor prefetchClassDescriptor = 
context.getResolver().getClassDescriptor(targetRel.getTargetEntityName());
-
             DescriptorColumnExtractor columnExtractor = new 
DescriptorColumnExtractor(context, prefetchClassDescriptor);
             columnExtractor.extract("p:" + dbPath);
 
@@ -115,6 +121,13 @@ class PrefetchNodeStage implements TranslationStage {
         }
     }
 
+    Expression translateToPrefetchQualifier(ObjEntity entity, Expression 
targetQualifier) {
+        Expression expression = entity.translateToDbPath(targetQualifier);
+        return expression.transform(o -> o instanceof ASTDbPath
+                ? ExpressionFactory.dbPathExp("p:" + ((ASTDbPath) o).getPath())
+                : o);
+    }
+
     private void processPrefetchQuery(TranslatorContext context) {
         Select<?> select = context.getQuery().unwrap();
         if(!(select instanceof PrefetchSelectQuery)) {
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
index 0816ea8b3..7b04a4464 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import java.util.stream.Collectors;
 
 import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.JoinType;
@@ -44,7 +45,7 @@ class TableTree {
     private final Map<String, TableTreeNode> tableNodes;
     private final TableTree parentTree;
 
-    private TableTreeNode rootNode;
+    private final TableTreeNode rootNode;
 
     private int tableAliasSequence;
 
@@ -56,11 +57,16 @@ class TableTree {
     }
 
     void addJoinTable(String path, DbRelationship relationship, JoinType 
joinType) {
-        if (tableNodes.get(path) != null) {
+        addJoinTable(path, relationship, joinType, null);
+    }
+
+    void addJoinTable(String path, DbRelationship relationship, JoinType 
joinType, Expression additionalQualifier) {
+        TableTreeNode treeNode = tableNodes.get(path);
+        if (treeNode != null) {
             return;
         }
 
-        TableTreeNode node = new TableTreeNode(path, relationship, 
nextTableAlias(), joinType);
+        TableTreeNode node = new TableTreeNode(path, relationship, 
nextTableAlias(), joinType, additionalQualifier);
         tableNodes.put(path, node);
     }
 
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
index ac8ae9dcc..4ab196fbb 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
@@ -19,6 +19,7 @@
 
 package org.apache.cayenne.access.translator.select;
 
+import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.JoinType;
@@ -37,20 +38,24 @@ class TableTreeNode {
     // relationship that connects this node with parent (or null if this is 
root)
     private final DbRelationship relationship;
 
+    private final Expression additionalQualifier;
+
     TableTreeNode(DbEntity entity, String tableAlias) {
         this.attributePath = new PathComponents("");
         this.entity = entity;
         this.tableAlias = tableAlias;
         this.relationship = null;
         this.joinType = null;
+        this.additionalQualifier = null;
     }
 
-    TableTreeNode(String path, DbRelationship relationship, String tableAlias, 
JoinType joinType) {
+    TableTreeNode(String path, DbRelationship relationship, String tableAlias, 
JoinType joinType, Expression additionalQualifier) {
         this.attributePath = new PathComponents(path);
         this.entity = relationship.getTargetEntity();
         this.tableAlias = tableAlias;
         this.relationship = relationship;
         this.joinType = joinType;
+        this.additionalQualifier = additionalQualifier;
     }
 
     public PathComponents getAttributePath() {
@@ -72,4 +77,8 @@ class TableTreeNode {
     public DbRelationship getRelationship() {
         return relationship;
     }
+
+    public Expression getAdditionalQualifier() {
+        return additionalQualifier;
+    }
 }
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 d805eb727..53b84ae07 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
@@ -19,15 +19,11 @@
 
 package org.apache.cayenne.access.translator.select;
 
-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 static org.apache.cayenne.access.sqlbuilder.SQLBuilder.exp;
-import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.node;
-
 /**
  * @since 4.2
  */
@@ -36,32 +32,34 @@ class TableTreeQualifierStage implements TranslationStage {
     @Override
     public void perform(TranslatorContext context) {
         context.getTableTree().visit(node -> {
-            Expression dbQualifier = node.getEntity().getQualifier();
-            if (dbQualifier != null) {
-                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;
-                });
-                Node rootQualifier = context.getQualifierNode();
-                Node translatedQualifier = 
context.getQualifierTranslator().translate(dbQualifier);
-                if (rootQualifier != null) {
-                    NodeBuilder expressionNodeBuilder = 
exp(node(rootQualifier)).and(node(translatedQualifier));
-                    context.setQualifierNode(expressionNodeBuilder.build());
-                } else {
-                    context.setQualifierNode(translatedQualifier);
-                }
-            }
+            appendQualifier(context, node, node.getEntity().getQualifier());
+            appendQualifier(context, node, node.getAdditionalQualifier());
         });
 
         if(context.getQualifierNode() != null) {
             context.getSelectBuilder().where(context.getQualifierNode());
         }
     }
+
+    private static void appendQualifier(TranslatorContext context, 
TableTreeNode node, Expression dbQualifier) {
+        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;
+        });
+        Node translatedQualifier = 
context.getQualifierTranslator().translate(dbQualifier);
+        context.appendQualifierNode(translatedQualifier);
+    }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
index 702bc3a51..2032cc58f 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
@@ -39,6 +39,9 @@ import org.apache.cayenne.map.EntityResult;
 import org.apache.cayenne.map.SQLResult;
 import org.apache.cayenne.query.QueryMetadata;
 
+import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.exp;
+import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.node;
+
 /**
  * Context that holds all data necessary for query translation as well as a 
result of that translation.
  *
@@ -262,6 +265,14 @@ public class TranslatorContext implements 
SQLGenerationContext {
         this.qualifierNode = qualifierNode;
     }
 
+    void appendQualifierNode(Node qualifierNode) {
+        if(this.qualifierNode == null) {
+            this.qualifierNode = qualifierNode;
+        } else {
+            this.qualifierNode = 
exp(node(this.qualifierNode)).and(node(qualifierNode)).build();
+        }
+    }
+
     Node getQualifierNode() {
         return qualifierNode;
     }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
index bfe2b6836..18a0890de 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
@@ -128,6 +128,78 @@ public class CDOQualifiedEntitiesIT extends ServerCase {
         }
     }
 
+    @Test
+    public void testJointPrefetchToMany() throws Exception {
+        if (accessStackAdapter.supportsNullBoolean()) {
+
+            createReadToManyDataSet();
+
+            List<Qualified1> roots = ObjectSelect.query(Qualified1.class)
+                    .prefetch(Qualified1.QUALIFIED2S.joint())
+                    .select(context);
+
+            assertEquals(1, roots.size());
+
+            Qualified1 root = roots.get(0);
+
+            assertEquals("OX1", root.getName());
+
+            List<Qualified2> related = root.getQualified2s();
+            assertEquals(1, related.size());
+
+            Qualified2 r = related.get(0);
+            assertEquals("OY1", r.getName());
+        }
+    }
+
+    @Test
+    public void testDisjointPrefetchToMany() throws Exception {
+        if (accessStackAdapter.supportsNullBoolean()) {
+
+            createReadToManyDataSet();
+
+            List<Qualified1> roots = ObjectSelect.query(Qualified1.class)
+                    .prefetch(Qualified1.QUALIFIED2S.disjoint())
+                    .select(context);
+
+            assertEquals(1, roots.size());
+
+            Qualified1 root = roots.get(0);
+
+            assertEquals("OX1", root.getName());
+
+            List<Qualified2> related = root.getQualified2s();
+            assertEquals(1, related.size());
+
+            Qualified2 r    = related.get(0);
+            assertEquals("OY1", r.getName());
+        }
+    }
+
+    @Test
+    public void testDisjointByIdPrefetchToMany() throws Exception {
+        if (accessStackAdapter.supportsNullBoolean()) {
+
+            createReadToManyDataSet();
+
+            List<Qualified1> roots = ObjectSelect.query(Qualified1.class)
+                    .prefetch(Qualified1.QUALIFIED2S.disjointById())
+                    .select(context);
+
+            assertEquals(1, roots.size());
+
+            Qualified1 root = roots.get(0);
+
+            assertEquals("OX1", root.getName());
+
+            List<Qualified2> related = root.getQualified2s();
+            assertEquals(1, related.size());
+
+            Qualified2 r = related.get(0);
+            assertEquals("OY1", r.getName());
+        }
+    }
+
     @Test
     public void testReadToOne() throws Exception {
         if (accessStackAdapter.supportsNullBoolean()) {

Reply via email to