You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2019/05/04 15:01:04 UTC

[cayenne] 02/04: Add DbEntity qualifiers to main query qualifier instead of join clause

This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit 932037f80adade89f49df5e58f3f482a48b32bc4
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Sat May 4 17:15:00 2019 +0300

    Add DbEntity qualifiers to main query qualifier instead of join clause
---
 .../translator/select/DefaultSelectTranslator.java |  1 +
 .../select/QualifierTranslationStage.java          | 21 +------
 .../access/translator/select/TableTree.java        | 14 ++++-
 .../translator/select/TableTreeQualifierStage.java | 67 ++++++++++++++++++++++
 .../access/translator/select/TableTreeStage.java   | 25 --------
 .../translator/select/TranslatorContext.java       | 10 ++++
 .../select/QualifierTranslationStageTest.java      | 10 +---
 7 files changed, 94 insertions(+), 54 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
index 1213c38..fee28d8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
@@ -48,6 +48,7 @@ public class DefaultSelectTranslator implements SelectTranslator {
             new DistinctStage(),
             new LimitOffsetStage(),
             new ColumnDescriptorStage(),
+            new TableTreeQualifierStage(),
             new TableTreeStage(),
             new SQLResultStage(),
             new SQLGenerationStage()
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslationStage.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslationStage.java
index c4d2ba1..386c0e3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslationStage.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslationStage.java
@@ -49,26 +49,7 @@ class QualifierTranslationStage implements TranslationStage {
             }
         }
 
-        // Attaching root Db entity's qualifier
-        DbEntity dbEntity = context.getMetadata().getDbEntity();
-        if (dbEntity != null) {
-            Expression dbQualifier = dbEntity.getQualifier();
-            if (dbQualifier != null) {
-                dbQualifier = dbQualifier.transform(node -> {
-                    if (node instanceof ASTObjPath) {
-                        return new ASTDbPath(((SimpleNode) node).getOperand(0));
-                    }
-                    return node;
-                });
-
-                expression = expression == null ? dbQualifier : expression.andExp(dbQualifier);
-            }
-        }
-
         Node qualifierNode = translator.translate(expression);
-
-        if(qualifierNode != null) {
-            context.getSelectBuilder().where(qualifierNode);
-        }
+        context.setQualifierNode(qualifierNode);
     }
 }
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 5082c4e..6eb7a8d 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
@@ -21,6 +21,7 @@ package org.apache.cayenne.access.translator.select;
 
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.map.DbEntity;
@@ -97,8 +98,17 @@ class TableTree {
 
     public void visit(TableNodeVisitor visitor) {
         visitor.visit(rootNode);
-        for(TableTreeNode node : tableNodes.values()) {
-            visitor.visit(node);
+
+        // as we can spawn new nodes while processing existing,
+        // we need multiple iterations until all rows are processed
+        int initialSize = 0;
+        int currentSize = tableNodes.size();
+        while(initialSize != currentSize) {
+            tableNodes.values().stream().skip(initialSize)
+                    .collect(Collectors.toList()) // copy collection in case of concurrent modification in visitor
+                    .forEach(visitor::visit);
+            initialSize = currentSize;
+            currentSize = tableNodes.size();
         }
     }
 
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
new file mode 100644
index 0000000..1d00543
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
@@ -0,0 +1,67 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+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
+ */
+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);
+                }
+            }
+        });
+
+        if(context.getQualifierNode() != null) {
+            context.getSelectBuilder().where(context.getQualifierNode());
+        }
+    }
+}
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 9ad1588..c4ee949 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,10 +24,6 @@ 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;
 
@@ -45,7 +41,6 @@ class TableTreeStage implements TranslationStage {
             if(node.getRelationship() != null) {
                 tableNode = getJoin(node, tableNode).on(getJoinExpression(context, node));
             }
-
             context.getSelectBuilder().from(tableNode);
         });
     }
@@ -79,26 +74,6 @@ class TableTreeStage implements TranslationStage {
             }
         }
 
-        expressionNodeBuilder = attachTargetQualifier(context, node, expressionNodeBuilder);
-
-        return expressionNodeBuilder;
-    }
-
-    private ExpressionNodeBuilder attachTargetQualifier(TranslatorContext context, TableTreeNode node, ExpressionNodeBuilder expressionNodeBuilder) {
-        Expression dbQualifier = node.getRelationship().getTargetEntity().getQualifier();
-        if (dbQualifier != null) {
-            String pathToRoot = node.getAttributePath().getPath();
-            dbQualifier = dbQualifier.transform(input -> input instanceof ASTPath
-                    ? new ASTDbPath(pathToRoot + '.' + ((ASTPath) input).getPath())
-                    : input
-            );
-            Node translatedQualifier = context.getQualifierTranslator().translate(dbQualifier);
-            if (expressionNodeBuilder != null) {
-                expressionNodeBuilder = expressionNodeBuilder.and(node(translatedQualifier));
-            } else {
-                expressionNodeBuilder = exp(node(translatedQualifier));
-            }
-        }
         return expressionNodeBuilder;
     }
 }
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 bf89ceb..25c1687 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
@@ -87,6 +87,8 @@ public class TranslatorContext implements SQLGenerationContext {
     // List of SQL tree nodes that describe resulting rows of this query
     private final List<ResultNodeDescriptor> resultNodeList;
 
+    // resulting qualifier for this query ('where' qualifier and qualifiers from entities)
+    private Node qualifierNode;
     // if true SQL generation stage will be skipped, needed for nested queries translation
     private boolean skipSQLGeneration;
     // translated SQL string
@@ -256,6 +258,14 @@ public class TranslatorContext implements SQLGenerationContext {
         return rootEntityResult;
     }
 
+    void setQualifierNode(Node qualifierNode) {
+        this.qualifierNode = qualifierNode;
+    }
+
+    Node getQualifierNode() {
+        return qualifierNode;
+    }
+
     enum DescriptorType {
         ROOT,
         PREFETCH,
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslationStageTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslationStageTest.java
index 9b56c81..cc628f8 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslationStageTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/QualifierTranslationStageTest.java
@@ -62,19 +62,15 @@ public class QualifierTranslationStageTest {
         QualifierTranslationStage stage = new QualifierTranslationStage();
         stage.perform(context);
 
-        Node select = context.getSelectBuilder().build();
+        assertNotNull(context.getQualifierNode());
 
-        // Content of "select" node:
+        // Content of "Qualifier" node:
         //
-        //      Where
-        //        |
         //   OpExpression
         //    /        \
         // Column     Value
 
-        assertEquals(1, select.getChildrenCount());
-        assertThat(select.getChild(0), instanceOf(WhereNode.class));
-        Node op = select.getChild(0).getChild(0);
+        Node op = context.getQualifierNode();
         assertThat(op, instanceOf(OpExpressionNode.class));
         assertEquals(">=", ((OpExpressionNode)op).getOp());
         assertEquals(2, op.getChildrenCount());