This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
commit 800bb3d4ba8c82898dbe2a542b8e2014de039dba Author: feiniaofeiafei <53502832+feiniaofeia...@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-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org