You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2024/04/24 07:59:12 UTC

(doris) branch master updated: [Feat](nereids) add expression rewrite rule LikeToEqualRewrite (#33803)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new fc9423ffee3 [Feat](nereids) add expression rewrite rule LikeToEqualRewrite (#33803)
fc9423ffee3 is described below

commit fc9423ffee3970b1875c6f1eae41a6a126e97d4b
Author: feiniaofeiafei <53...@users.noreply.github.com>
AuthorDate: Wed Apr 24 15:59:00 2024 +0800

    [Feat](nereids) add expression rewrite rule LikeToEqualRewrite (#33803)
    
    like expressions without fuzzy matching are rewritten into equivalent expressions
---
 .../rules/expression/ExpressionOptimization.java   |   4 +-
 .../rules/expression/rules/LikeToEqualRewrite.java |  70 ++++++++++
 .../expression/rules/LikeToEqualRewriteTest.java   | 147 +++++++++++++++++++++
 .../like_to_equal_to_rewrite.out                   |  24 ++++
 .../like_to_equal_to_rewrite.groovy                |  53 ++++++++
 5 files changed, 297 insertions(+), 1 deletion(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionOptimization.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionOptimization.java
index b3bb18163ea..abf57057601 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionOptimization.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionOptimization.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.rules.expression.rules.CaseWhenToIf;
 import org.apache.doris.nereids.rules.expression.rules.DateFunctionRewrite;
 import org.apache.doris.nereids.rules.expression.rules.DistinctPredicatesRule;
 import org.apache.doris.nereids.rules.expression.rules.ExtractCommonFactorRule;
+import org.apache.doris.nereids.rules.expression.rules.LikeToEqualRewrite;
 import org.apache.doris.nereids.rules.expression.rules.NullSafeEqualToEqual;
 import org.apache.doris.nereids.rules.expression.rules.OrToIn;
 import org.apache.doris.nereids.rules.expression.rules.SimplifyComparisonPredicate;
@@ -51,7 +52,8 @@ public class ExpressionOptimization extends ExpressionRewrite {
                 ArrayContainToArrayOverlap.INSTANCE,
                 CaseWhenToIf.INSTANCE,
                 TopnToMax.INSTANCE,
-                NullSafeEqualToEqual.INSTANCE
+                NullSafeEqualToEqual.INSTANCE,
+                LikeToEqualRewrite.INSTANCE
             )
     );
     private static final ExpressionRuleExecutor EXECUTOR = new ExpressionRuleExecutor(OPTIMIZE_REWRITE_RULES);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/LikeToEqualRewrite.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/LikeToEqualRewrite.java
new file mode 100644
index 00000000000..e2836204cdc
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/LikeToEqualRewrite.java
@@ -0,0 +1,70 @@
+// 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.doris.nereids.rules.expression.rules;
+
+import org.apache.doris.nereids.rules.expression.ExpressionPatternMatcher;
+import org.apache.doris.nereids.rules.expression.ExpressionPatternRuleFactory;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Like;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * LikeToEqualRewrite
+ */
+public class LikeToEqualRewrite implements ExpressionPatternRuleFactory {
+    public static LikeToEqualRewrite INSTANCE = new LikeToEqualRewrite();
+
+    @Override
+    public List<ExpressionPatternMatcher<? extends Expression>> buildRules() {
+        return ImmutableList.of(
+                matchesType(Like.class).then(LikeToEqualRewrite::rewriteLikeToEqual)
+        );
+    }
+
+    private static Expression rewriteLikeToEqual(Like like) {
+        Expression left = like.child(0);
+        Expression right = like.child(1);
+        if (!(right instanceof VarcharLiteral)) {
+            return like;
+        }
+        String str = ((VarcharLiteral) right).value;
+        StringBuilder sb = new StringBuilder();
+        int len = str.length();
+        char escapeChar = '\\';
+        for (int i = 0; i < len;) {
+            char c = str.charAt(i);
+            if (c == escapeChar && (i + 1) < len
+                    && (str.charAt(i + 1) == '%' || str.charAt(i + 1) == '_' || str.charAt(i + 1) == escapeChar)) {
+                sb.append(str.charAt(i + 1));
+                i += 2;
+            } else {
+                if (c == '%' || c == '_') {
+                    return like;
+                }
+                sb.append(c);
+                i++;
+            }
+        }
+        return new EqualTo(left, new VarcharLiteral(sb.toString()));
+    }
+}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/LikeToEqualRewriteTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/LikeToEqualRewriteTest.java
new file mode 100644
index 00000000000..463efc1a224
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/LikeToEqualRewriteTest.java
@@ -0,0 +1,147 @@
+// 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.doris.nereids.rules.expression.rules;
+
+import org.apache.doris.nereids.rules.expression.ExpressionRewriteTestHelper;
+import org.apache.doris.nereids.rules.expression.ExpressionRuleExecutor;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Like;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+import org.apache.doris.nereids.types.StringType;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Test;
+
+public class LikeToEqualRewriteTest extends ExpressionRewriteTestHelper {
+    @Test
+    public void testLikeRegularConst() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, str));
+    }
+
+    @Test
+    public void testLikeEscapePercentSignConst() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\%");
+        VarcharLiteral strForEqual = new VarcharLiteral("abc%");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, strForEqual));
+    }
+
+    @Test
+    public void testLikeEscapeUnderlineConst() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\_");
+        VarcharLiteral strForEqual = new VarcharLiteral("abc_");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, strForEqual));
+    }
+
+    @Test
+    public void testLikeNotEscapePercentSignConst() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc%");
+        assertRewrite(new Like(slot, str), new Like(slot, str));
+    }
+
+    @Test
+    public void testLikeNotEscapeUnderlineConst() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc_");
+        assertRewrite(new Like(slot, str), new Like(slot, str));
+    }
+
+    @Test
+    public void testLikeTwoEscape() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\\\");
+        VarcharLiteral strForEqual = new VarcharLiteral("abc\\");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, strForEqual));
+    }
+
+    @Test
+    public void testLikeThreeEscape() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\\\\\");
+        VarcharLiteral strForEqual = new VarcharLiteral("abc\\\\");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, strForEqual));
+    }
+
+    @Test
+    public void testLikeFourEscape() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\\\\\\\");
+        VarcharLiteral strForEqual = new VarcharLiteral("abc\\\\");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, strForEqual));
+    }
+
+    @Test
+    public void testLikeTwoEscapeAndPercentSign() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\\\%");
+        assertRewrite(new Like(slot, str), new Like(slot, str));
+    }
+
+    @Test
+    public void testLikeThreeEscapeAndPercentSign() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\\\\\%");
+        VarcharLiteral strForEqual = new VarcharLiteral("abc\\%");
+        assertRewrite(new Like(slot, str), new EqualTo(slot, strForEqual));
+    }
+
+    @Test
+    public void testLikeFourEscapeAndPercentSign() {
+        executor = new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(LikeToEqualRewrite.INSTANCE)
+        ));
+        SlotReference slot = new SlotReference("a", StringType.INSTANCE, true);
+        VarcharLiteral str = new VarcharLiteral("abc\\\\\\\\%");
+        assertRewrite(new Like(slot, str), new Like(slot, str));
+    }
+}
diff --git a/regression-test/data/nereids_rules_p0/like_to_equal_to_rewrite/like_to_equal_to_rewrite.out b/regression-test/data/nereids_rules_p0/like_to_equal_to_rewrite/like_to_equal_to_rewrite.out
new file mode 100644
index 00000000000..50a9a1981c8
--- /dev/null
+++ b/regression-test/data/nereids_rules_p0/like_to_equal_to_rewrite/like_to_equal_to_rewrite.out
@@ -0,0 +1,24 @@
+-- This file is automatically generated. You should know what you did if you want to edit this
+-- !test_regular --
+abc	1
+
+-- !test_const_percent_sign --
+abc%	1
+
+-- !test_underline --
+abc_	1
+
+-- !test_backslash1 --
+abc\\	1
+
+-- !test_backslash2 --
+abc\\	1
+
+-- !test_backslash_percent_sign_odd --
+abc\\%	1
+
+-- !test_backslash_percent_sign_even_match --
+abc\\\\%	1
+
+-- !test_backslash_percent_sign_even_cannot_match --
+
diff --git a/regression-test/suites/nereids_rules_p0/like_to_equal_to_rewrite/like_to_equal_to_rewrite.groovy b/regression-test/suites/nereids_rules_p0/like_to_equal_to_rewrite/like_to_equal_to_rewrite.groovy
new file mode 100644
index 00000000000..b42b0159db4
--- /dev/null
+++ b/regression-test/suites/nereids_rules_p0/like_to_equal_to_rewrite/like_to_equal_to_rewrite.groovy
@@ -0,0 +1,53 @@
+// 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.
+
+suite("like_to_equal_to_rewrite") {
+    sql "SET enable_nereids_planner=true"
+    sql "SET enable_fallback_to_original_planner=false"
+    sql "drop table if exists mal_test_to_equal_to"
+    sql """
+        create table mal_test_to_equal_to(a varchar(10), b int) 
+        distributed by hash(a) buckets 32
+        properties(
+        "replication_allocation"="tag.location.default: 1"
+        );"""
+
+    sql "insert into mal_test_to_equal_to values('abc',1);"
+    qt_test_regular "select * from mal_test_to_equal_to where a like 'abc';"
+
+    sql "truncate table mal_test_to_equal_to;"
+    sql "insert into mal_test_to_equal_to values('abc%',1);"
+    qt_test_const_percent_sign "select * from mal_test_to_equal_to where a like 'abc\\%';"
+
+    sql "truncate table mal_test_to_equal_to;"
+    sql "insert into mal_test_to_equal_to values('abc_',1);"
+    qt_test_underline """select * from mal_test_to_equal_to where a like 'abc\\_';"""
+
+    sql "truncate table mal_test_to_equal_to;"
+    sql "insert into mal_test_to_equal_to values('abc\\\\',1);"
+    qt_test_backslash1 "select * from mal_test_to_equal_to where a like 'abc\\\\\\\\';"
+    qt_test_backslash2 "select * from mal_test_to_equal_to where a like 'abc\\\\';"
+
+    sql "truncate table mal_test_to_equal_to;"
+    sql "insert into mal_test_to_equal_to values('abc\\\\%',1);"
+    qt_test_backslash_percent_sign_odd "select * from mal_test_to_equal_to where a like 'abc\\\\\\\\\\%';"
+
+    sql "truncate table mal_test_to_equal_to;"
+    sql "insert into mal_test_to_equal_to values('abc\\\\\\%',1);"
+    qt_test_backslash_percent_sign_even_match "select * from mal_test_to_equal_to where a like 'abc\\\\\\\\\\\\\\\\\\\\%';"
+    qt_test_backslash_percent_sign_even_cannot_match "select * from mal_test_to_equal_to where a like 'abc\\\\\\\\\\\\\\\\\\\\\\\\%';"
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org