You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/12/07 14:50:14 UTC

[groovy] branch master updated: Tweak the optimizer of GINQ further

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f27c001  Tweak the optimizer of GINQ further
f27c001 is described below

commit f27c001ebcfa5b7099c1076f4da2812dc91422be
Author: Daniel Sun <su...@apache.org>
AuthorDate: Mon Dec 7 22:49:52 2020 +0800

    Tweak the optimizer of GINQ further
---
 .../apache/groovy/ginq/dsl/GinqAstOptimizer.groovy | 61 ++++++++++-----
 .../test/org/apache/groovy/ginq/GinqTest.groovy    | 90 +++++++++++++++++++++-
 2 files changed, 131 insertions(+), 20 deletions(-)

diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstOptimizer.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstOptimizer.groovy
index c892388..90f9f6b 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstOptimizer.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstOptimizer.groovy
@@ -27,6 +27,7 @@ import org.apache.groovy.ginq.dsl.expression.SelectExpression
 import org.apache.groovy.ginq.dsl.expression.WhereExpression
 import org.codehaus.groovy.ast.expr.ArgumentListExpression
 import org.codehaus.groovy.ast.expr.BinaryExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
 import org.codehaus.groovy.ast.expr.Expression
 import org.codehaus.groovy.ast.expr.ExpressionTransformer
 import org.codehaus.groovy.ast.expr.ListExpression
@@ -107,28 +108,43 @@ class GinqAstOptimizer extends GinqAstBaseVisitor {
 
         WhereExpression whereExpression = ginqExpression.whereExpression
         if (whereExpression) {
-            Map<String, List<Expression>> conditionsToOptimize = [:]
-            List<Expression> candidatesToOptimize = findCandidatesToOptimize(whereExpression)
+            transformFromClause(whereExpression, optimizingAliasList, allAliasList, optimizingDataSourceExpressionList)
+            transformWhereClause(whereExpression, ginqExpression)
+        }
 
-            candidatesToOptimize.stream()
-                    .forEach(e -> collectConditionsToOptimize(e, allAliasList, optimizingAliasList, conditionsToOptimize))
+        return null
+    }
 
-            transformFromClause(conditionsToOptimize, optimizingAliasList, optimizingDataSourceExpressionList)
+    private void transformWhereClause(WhereExpression whereExpression, GinqExpression ginqExpression) {
+        List<Expression> candidates = findCandidatesToOptimize(whereExpression)
+        List<Expression> nonOptimizedCandidates = candidates.grep { Expression e ->
+            if (e instanceof ConstantExpression && e.value) {
+                return false
+            }
 
-            List<Expression> candidates = findCandidatesToOptimize(whereExpression)
-            List<Expression> nonOptimizedCandidates = candidates.grep { Expression e ->
-                Boolean optimize = e.getNodeMetaData(TO_OPTIMIZE)
-                return null == optimize || !optimize
+            if (e instanceof BinaryExpression && e.leftExpression instanceof ConstantExpression && e.rightExpression instanceof ConstantExpression) {
+                try {
+                    def result = new GroovyShell().evaluate("$e.leftExpression.text $e.operation.text $e.rightExpression.text")
+                    if (result) {
+                        return false
+                    }
+                } catch (ignored) {
+                }
             }
 
-            if (nonOptimizedCandidates) {
-                whereExpression.filterExpr = contructFilterExpr(nonOptimizedCandidates)
-            } else {
-                ginqExpression.whereExpression = null
+            Boolean optimize = e.getNodeMetaData(TO_OPTIMIZE)
+            if (null == optimize || !optimize) {
+                return true
             }
+
+            return false
         }
 
-        return null
+        if (nonOptimizedCandidates) {
+            whereExpression.filterExpr = constructFilterExpr(nonOptimizedCandidates)
+        } else {
+            ginqExpression.whereExpression = null
+        }
     }
 
     private List<Expression> findCandidatesToOptimize(WhereExpression whereExpression) {
@@ -166,14 +182,20 @@ class GinqAstOptimizer extends GinqAstBaseVisitor {
         return candidatesToOptimize
     }
     static boolean isCandidate(Expression expression) {
-        if (expression instanceof BinaryExpression && expression.operation.type in [Types.LOGICAL_AND, Types.LOGICAL_OR]) {
+        if (expression instanceof BinaryExpression && expression.operation.type in LOGICAL_OP_TYPE_LIST) {
             return false
         }
 
         return true
     }
 
-    private void transformFromClause(LinkedHashMap<String, List<Expression>> conditionsToOptimize, List<String> optimizingAliasList, List<DataSourceExpression> optimizingDataSourceExpressionList) {
+    private void transformFromClause(WhereExpression whereExpression, List<String> optimizingAliasList, List<String> allAliasList, List<DataSourceExpression> optimizingDataSourceExpressionList) {
+        Map<String, List<Expression>> conditionsToOptimize = [:]
+        List<Expression> candidatesToOptimize = findCandidatesToOptimize(whereExpression)
+
+        candidatesToOptimize.stream()
+                .forEach(e -> collectConditionsToOptimize(e, allAliasList, optimizingAliasList, conditionsToOptimize))
+
         conditionsToOptimize.forEach((String alias, List<Expression> conditions) -> {
             if (!optimizingAliasList.contains(alias)) return
 
@@ -192,7 +214,7 @@ class GinqAstOptimizer extends GinqAstBaseVisitor {
                 contructedGinqExpression.fromExpression =
                         new FromExpression(new VariableExpression(constructedAlias), dataSourceExpression.dataSourceExpr)
                 contructedGinqExpression.whereExpression =
-                        new WhereExpression(contructFilterExpr(transformedConditions))
+                        new WhereExpression(constructFilterExpr(transformedConditions))
                 contructedGinqExpression.selectExpression =
                         new SelectExpression(
                                 new ArgumentListExpression(
@@ -219,7 +241,7 @@ class GinqAstOptimizer extends GinqAstBaseVisitor {
         })).getExpression(0)
     }
 
-    private Expression contructFilterExpr(List<Expression> conditions) {
+    private Expression constructFilterExpr(List<Expression> conditions) {
         if (!conditions) throw new IllegalArgumentException("The argument `conditions` should not be empty")
         if (1 == conditions.size()) return conditions[0]
 
@@ -228,7 +250,7 @@ class GinqAstOptimizer extends GinqAstBaseVisitor {
         }
 
         def condition = conditions[0]
-        def remainingCondition = contructFilterExpr(conditions[1..-1])
+        def remainingCondition = constructFilterExpr(conditions[1..-1])
         return new BinaryExpression(condition, new Token(Types.LOGICAL_AND, '&&', -1, -1), remainingCondition)
     }
 
@@ -264,5 +286,6 @@ class GinqAstOptimizer extends GinqAstBaseVisitor {
         }
     }
 
+    private static final List<Integer> LOGICAL_OP_TYPE_LIST = [Types.LOGICAL_AND, Types.LOGICAL_OR]
     private static final String TO_OPTIMIZE = "TO_OPTIMIZE"
 }
diff --git a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
index 010b37b..0ad87f7 100644
--- a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
+++ b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
@@ -3485,6 +3485,94 @@ class GinqTest {
             def hello() {
                 def c = {
                     from n1 in nums1
+                    innerjoin n2 in nums2 on n1 == n2
+                    where n1 > 1 && n2 <= 3 && true
+                    select n1, n2
+                }
+                return
+            }
+        '''
+        def sourceUnit
+        def ast = new CompilationUnit().tap {
+            sourceUnit = addSource 'hello.groovy', code
+            compile Phases.CONVERSION
+        }.ast
+
+        MethodNode methodNode = ast.classes[0].methods.grep(e -> e.name == 'hello')[0]
+        ExpressionStatement delcareStatement = ((BlockStatement) methodNode.getCode()).getStatements()[0]
+        DeclarationExpression declarationExpression = delcareStatement.getExpression()
+        ClosureExpression closureException = declarationExpression.rightExpression
+
+        GinqAstBuilder ginqAstBuilder = new GinqAstBuilder(sourceUnit)
+        closureException.code.visit(ginqAstBuilder)
+        GinqExpression ginqExpression = ginqAstBuilder.getGinqExpression()
+
+        GinqAstOptimizer ginqAstOptimizer = new GinqAstOptimizer()
+        ginqAstOptimizer.visitGinqExpression(ginqExpression)
+        assert null == ginqExpression.whereExpression
+
+        assert ginqExpression.fromExpression.dataSourceExpr instanceof GinqExpression
+        BinaryExpression contructedFilterExpr1 = ((GinqExpression) ginqExpression.fromExpression.dataSourceExpr).whereExpression.filterExpr
+        assert Types.COMPARE_GREATER_THAN == contructedFilterExpr1.operation.type
+        assert '1' == contructedFilterExpr1.rightExpression.text
+
+        assert ginqExpression.joinExpressionList[0].dataSourceExpr instanceof GinqExpression
+        BinaryExpression contructedFilterExpr2 = ((GinqExpression) ginqExpression.joinExpressionList[0].dataSourceExpr).whereExpression.filterExpr
+        assert Types.COMPARE_LESS_THAN_EQUAL == contructedFilterExpr2.operation.type
+        assert '3' == contructedFilterExpr2.rightExpression.text
+    }
+
+    @Test
+    @CompileDynamic
+    void "testGinq - optimize - 3"() {
+        def code = '''
+            def hello() {
+                def c = {
+                    from n1 in nums1
+                    innerjoin n2 in nums2 on n1 == n2
+                    where n1 > 1 && n2 <= 3 && 1 < 2 && 3 == 3 && 4 > 3
+                    select n1, n2
+                }
+                return
+            }
+        '''
+        def sourceUnit
+        def ast = new CompilationUnit().tap {
+            sourceUnit = addSource 'hello.groovy', code
+            compile Phases.CONVERSION
+        }.ast
+
+        MethodNode methodNode = ast.classes[0].methods.grep(e -> e.name == 'hello')[0]
+        ExpressionStatement delcareStatement = ((BlockStatement) methodNode.getCode()).getStatements()[0]
+        DeclarationExpression declarationExpression = delcareStatement.getExpression()
+        ClosureExpression closureException = declarationExpression.rightExpression
+
+        GinqAstBuilder ginqAstBuilder = new GinqAstBuilder(sourceUnit)
+        closureException.code.visit(ginqAstBuilder)
+        GinqExpression ginqExpression = ginqAstBuilder.getGinqExpression()
+
+        GinqAstOptimizer ginqAstOptimizer = new GinqAstOptimizer()
+        ginqAstOptimizer.visitGinqExpression(ginqExpression)
+        assert null == ginqExpression.whereExpression
+
+        assert ginqExpression.fromExpression.dataSourceExpr instanceof GinqExpression
+        BinaryExpression contructedFilterExpr1 = ((GinqExpression) ginqExpression.fromExpression.dataSourceExpr).whereExpression.filterExpr
+        assert Types.COMPARE_GREATER_THAN == contructedFilterExpr1.operation.type
+        assert '1' == contructedFilterExpr1.rightExpression.text
+
+        assert ginqExpression.joinExpressionList[0].dataSourceExpr instanceof GinqExpression
+        BinaryExpression contructedFilterExpr2 = ((GinqExpression) ginqExpression.joinExpressionList[0].dataSourceExpr).whereExpression.filterExpr
+        assert Types.COMPARE_LESS_THAN_EQUAL == contructedFilterExpr2.operation.type
+        assert '3' == contructedFilterExpr2.rightExpression.text
+    }
+
+    @Test
+    @CompileDynamic
+    void "testGinq - optimize - 4"() {
+        def code = '''
+            def hello() {
+                def c = {
+                    from n1 in nums1
                     leftjoin n2 in nums2 on n1 == n2
                     where n1 > 1 && n2 <= 3
                     select n1, n2
@@ -3523,7 +3611,7 @@ class GinqTest {
     }
 
     @Test
-    void "testGinq - optimize - 3"() {
+    void "testGinq - optimize - 5"() {
         assertScript '''
 // tag::ginq_optimize_01[]
             assert [[2, 2]] == GQ(optimize:false) {