Skip to content

Commit

Permalink
Improve LIKE pushdown for complex expression
Browse files Browse the repository at this point in the history
  • Loading branch information
ssheikin committed Sep 27, 2024
1 parent 0d17374 commit e8ebf9b
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.trino.plugin.base.expression.ConnectorExpressionRewriter;
import io.trino.plugin.base.expression.ConnectorExpressionRule.RewriteContext;
import io.trino.plugin.base.mapping.IdentifierMapping;
import io.trino.plugin.clickhouse.expression.RewriteLike;
import io.trino.plugin.clickhouse.expression.RewriteStringComparison;
import io.trino.plugin.clickhouse.expression.RewriteStringIn;
import io.trino.plugin.jdbc.BaseJdbcClient;
Expand Down Expand Up @@ -231,6 +232,7 @@ public ClickHouseClient(
.addStandardRules(this::quoted)
.add(new RewriteStringComparison())
.add(new RewriteStringIn())
.add(new RewriteLike())
.map("$not(value: boolean)").to("NOT value")
.build();
this.aggregateFunctionRewriter = new AggregateFunctionRewriter<>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Licensed 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 io.trino.plugin.clickhouse.expression;

import com.google.common.collect.ImmutableList;
import io.trino.matching.Capture;
import io.trino.matching.Captures;
import io.trino.matching.Pattern;
import io.trino.plugin.base.expression.ConnectorExpressionRule;
import io.trino.plugin.jdbc.QueryParameter;
import io.trino.plugin.jdbc.expression.ParameterizedExpression;
import io.trino.spi.expression.Call;
import io.trino.spi.expression.ConnectorExpression;
import io.trino.spi.expression.Variable;
import io.trino.spi.type.CharType;
import io.trino.spi.type.VarcharType;

import java.util.Optional;

import static io.trino.matching.Capture.newCapture;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.argument;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.argumentCount;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.call;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.expression;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.functionName;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.type;
import static io.trino.plugin.base.expression.ConnectorExpressionPatterns.variable;
import static io.trino.plugin.clickhouse.ClickHouseClient.supportsPushdown;
import static io.trino.spi.expression.StandardFunctions.LIKE_FUNCTION_NAME;
import static io.trino.spi.type.BooleanType.BOOLEAN;
import static java.lang.String.format;

public class RewriteLike
implements ConnectorExpressionRule<Call, ParameterizedExpression>
{
private static final Capture<Variable> LIKE_VALUE = newCapture();
private static final Capture<ConnectorExpression> LIKE_PATTERN = newCapture();
private static final Pattern<Call> PATTERN = call()
.with(functionName().equalTo(LIKE_FUNCTION_NAME))
.with(type().equalTo(BOOLEAN))
.with(argumentCount().equalTo(2))
.with(argument(0).matching(variable()
.with(type().matching(type -> type instanceof CharType || type instanceof VarcharType))
.matching((Variable variable, RewriteContext<ParameterizedExpression> context) -> supportsPushdown(variable, context))
.capturedAs(LIKE_VALUE)))
.with(argument(1).matching(expression()
.with(type().matching(type -> type instanceof CharType || type instanceof VarcharType))
.matching((ConnectorExpression expression, RewriteContext<ParameterizedExpression> context) ->
!(expression instanceof Variable variable) || supportsPushdown(variable, context))
.capturedAs(LIKE_PATTERN)));

@Override
public Pattern<Call> getPattern()
{
return PATTERN;
}

@Override
public Optional<ParameterizedExpression> rewrite(Call expression, Captures captures, RewriteContext<ParameterizedExpression> context)
{
Optional<ParameterizedExpression> value = context.defaultRewrite(captures.get(LIKE_VALUE));
if (value.isEmpty()) {
return Optional.empty();
}
Optional<ParameterizedExpression> pattern = context.defaultRewrite(captures.get(LIKE_PATTERN));
if (pattern.isEmpty()) {
return Optional.empty();
}

ImmutableList<QueryParameter> parameters = ImmutableList.<QueryParameter>builder()
.addAll(value.get().parameters())
.addAll(pattern.get().parameters())
.build();
return Optional.of(new ParameterizedExpression(
format("%s LIKE %s", value.get().expression(), pattern.get().expression()),
parameters));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,25 @@ a_enum_2 Enum('hello', 'world', 'a', 'b', 'c'))
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string NOT IN ('a', 'b')" + withConnectorExpression)).isFullyPushedDown();
assertThat(query(smallDomainCompactionThreshold, "SELECT some_column FROM " + table.getName() + " WHERE a_string NOT IN ('a', 'b')")).isNotFullyPushedDown(FilterNode.class);
assertThat(query(smallDomainCompactionThreshold, "SELECT some_column FROM " + table.getName() + " WHERE a_string NOT IN ('a', 'b')" + withConnectorExpression)).isNotFullyPushedDown(FilterNode.class);

assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE 'b'")).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE 'b'" + withConnectorExpression)).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE 'b%'")).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE 'b%'" + withConnectorExpression)).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE '%b'")).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE '%b'" + withConnectorExpression)).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE '%b%'")).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE '%b%'" + withConnectorExpression)).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_enum_1 LIKE '%b%'")).isNotFullyPushedDown(FilterNode.class);
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_enum_1 LIKE '%b%'" + withConnectorExpression)).isNotFullyPushedDown(FilterNode.class);
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE a_string_alias")).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE a_string_alias" + withConnectorExpression)).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE a_enum_1")).isNotFullyPushedDown(FilterNode.class);
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE a_enum_1" + withConnectorExpression)).isNotFullyPushedDown(FilterNode.class);
assertThat(query(convertToVarchar, "SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE unsupported_1")).isNotFullyPushedDown(FilterNode.class);
assertThat(query(convertToVarchar, "SELECT some_column FROM " + table.getName() + " WHERE a_string LIKE unsupported_1" + withConnectorExpression)).isNotFullyPushedDown(FilterNode.class);
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string NOT LIKE '%b%'")).isFullyPushedDown();
assertThat(query("SELECT some_column FROM " + table.getName() + " WHERE a_string NOT LIKE '%b%'" + withConnectorExpression)).isFullyPushedDown();
}
}

Expand Down

0 comments on commit e8ebf9b

Please sign in to comment.