This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch 5.0-FIX-CAY-2844-joint-prefetch+qualifier in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 2b989f6a85d3cec78a63c1d1e0ce2ac80414f471 Author: Nikita Timofeev <[email protected]> AuthorDate: Mon Feb 19 16:28:42 2024 +0400 DRAFT: CAY-2844 joint prefetch with qualifier --- .../translator/select/PrefetchNodeStage.java | 12 +++++-- .../access/translator/select/TableTree.java | 10 ++++-- .../access/translator/select/TableTreeNode.java | 11 ++++++- .../translator/select/TableTreeQualifierStage.java | 38 +++++++++++----------- .../translator/select/TranslatorContext.java | 11 +++++++ 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java index e4d010c71..836bbaca8 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java @@ -26,6 +26,7 @@ import org.apache.cayenne.exp.path.CayennePath; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.exp.parser.ASTDbPath; +import org.apache.cayenne.exp.parser.ASTPath; import org.apache.cayenne.exp.path.CayennePathSegment; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; @@ -81,9 +82,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); CayennePath dbPath = dbPrefetch.getPath(); DbEntity dbEntity = objEntity.getDbEntity(); + Expression targetQualifier = targetRel.getTargetEntity().getDeclaredQualifier(); CayennePath fullPath = CayennePath.EMPTY_PATH.withMarker(CayennePath.PREFETCH_MARKER); @@ -94,13 +97,16 @@ class PrefetchNodeStage implements TranslationStage { } fullPath = fullPath.dot(c); - context.getTableTree().addJoinTable(fullPath, rel, JoinType.LEFT_OUTER); + if(targetQualifier != null && c == dbPath.last()) { + targetQualifier = targetRel.getTargetEntity().translateToDbPath(targetQualifier); + context.getTableTree().addJoinTable(fullPath.withModifier(CayennePath.PREFETCH), rel, JoinType.LEFT_OUTER, targetQualifier); + } else { + context.getTableTree().addJoinTable(fullPath.withModifier(CayennePath.PREFETCH), 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(dbPath.withMarker(CayennePath.PREFETCH_MARKER)); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java index 43fbbda33..fe3f5fe26 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.path.CayennePath; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbRelationship; @@ -57,11 +58,16 @@ class TableTree { } void addJoinTable(CayennePath 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/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java index b4a09117a..c2b68563b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java +++ b/cayenne/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.exp.path.CayennePath; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbRelationship; @@ -38,20 +39,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 = CayennePath.EMPTY_PATH; this.entity = entity; this.tableAlias = tableAlias; this.relationship = null; this.joinType = null; + this.additionalQualifier = null; } - TableTreeNode(CayennePath path, DbRelationship relationship, String tableAlias, JoinType joinType) { + TableTreeNode(CayennePath path, DbRelationship relationship, String tableAlias, JoinType joinType, Expression additionalQualifier) { this.attributePath = path; this.entity = relationship.getTargetEntity(); this.tableAlias = tableAlias; this.relationship = relationship; this.joinType = joinType; + this.additionalQualifier = additionalQualifier; } public CayennePath getAttributePath() { @@ -73,4 +78,8 @@ class TableTreeNode { public DbRelationship getRelationship() { return relationship; } + + public Expression getAdditionalQualifier() { + return additionalQualifier; + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java index ba7eaec7b..f75390398 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java @@ -37,29 +37,29 @@ class TableTreeQualifierStage implements TranslationStage { @Override public void perform(TranslatorContext context) { context.getTableTree().visit(node -> { - Expression dbQualifier = node.getEntity().getQualifier(); - if (dbQualifier != null) { - CayennePath pathToRoot = node.getAttributePath(); - dbQualifier = dbQualifier.transform(input -> { - if (input instanceof ASTPath) { - CayennePath path = pathToRoot.dot(((ASTPath) input).getPath()); - 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; + } + + CayennePath pathToRoot = node.getAttributePath(); + dbQualifier = dbQualifier.transform(input -> { + if (input instanceof ASTPath) { + CayennePath path = pathToRoot.dot(((ASTPath) input).getPath()); + return new ASTDbPath(path); + } + return input; + }); + Node translatedQualifier = context.getQualifierTranslator().translate(dbQualifier); + context.appendQualifierNode(translatedQualifier); + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java index 0c9894549..237cfac07 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java @@ -42,6 +42,9 @@ import org.apache.cayenne.map.SQLResult; import org.apache.cayenne.query.Ordering; 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. * @@ -291,6 +294,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; }
