Skip to content

Commit

Permalink
[Enhancement]Rewrite unnest(bitmap_to_array) to unnest_bitmap (backport
Browse files Browse the repository at this point in the history
#51036) (#52942)

Signed-off-by: before-Sunrise <[email protected]>
  • Loading branch information
before-Sunrise authored Nov 15, 2024
1 parent 5aca50a commit 4110988
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ public class FunctionSet {
public static final String SECONDS_SUB = "seconds_sub";
public static final String MILLISECONDS_ADD = "milliseconds_add";
public static final String MILLISECONDS_SUB = "milliseconds_sub";
// table function
public static final String UNNEST = "unnest";
public static final String UNNEST_BITMAP = "unnest_bitmap";

private static final Logger LOGGER = LogManager.getLogger(FunctionSet.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ public static void initBuiltins(FunctionSet functionSet) {
functionSet.addBuiltin(func);
}

TableFunction funcUnnestBitmap = new TableFunction(new FunctionName("unnest_bitmap"),
Lists.newArrayList("unnest_bitmap"), Lists.newArrayList(Type.BITMAP), Lists.newArrayList(Type.BIGINT));
TableFunction funcUnnestBitmap = new TableFunction(new FunctionName(FunctionSet.UNNEST_BITMAP),
Lists.newArrayList(FunctionSet.UNNEST_BITMAP), Lists.newArrayList(Type.BITMAP), Lists.newArrayList(Type.BIGINT));
functionSet.addBuiltin(funcUnnestBitmap);

for (Type type : Lists.newArrayList(Type.TINYINT, Type.SMALLINT, Type.INT, Type.BIGINT, Type.LARGEINT)) {
Expand Down
10 changes: 10 additions & 0 deletions fe/fe-core/src/main/java/com/starrocks/qe/SessionVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ public static MaterializedViewRewriteMode parse(String str) {
// regexp predicate is less efficient than like predicates.
public static final String LIKE_PREDICATE_CONSOLIDATE_MIN = "like_predicate_consolidate_min";

public static final String ENABLE_REWRITE_UNNEST_BITMAP_TO_ARRAY = "enable_rewrite_unnest_bitmap_to_array";

public static final List<String> DEPRECATED_VARIABLES = ImmutableList.<String>builder()
.add(CODEGEN_LEVEL)
.add(MAX_EXECUTION_TIME)
Expand Down Expand Up @@ -1626,6 +1628,10 @@ public Optional<Boolean> isFollowerForwardToLeaderOpt() {
@VarAttr(name = LIKE_PREDICATE_CONSOLIDATE_MIN)
private int likePredicateConsolidateMin = 2;


@VarAttr(name = ENABLE_REWRITE_UNNEST_BITMAP_TO_ARRAY)
private boolean enableRewriteUnnestBitmapToArray = true;

public int getExprChildrenLimit() {
return exprChildrenLimit;
}
Expand Down Expand Up @@ -3031,6 +3037,10 @@ public void setLikePredicateConsolidateMin(int value) {
this.likePredicateConsolidateMin = value;
}

public boolean isEnableRewriteUnnestBitmapToArray() {
return enableRewriteUnnestBitmapToArray;
}

// Serialize to thrift object
// used for rest api
public TQueryOptions toThrift() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.starrocks.sql.optimizer.rule.transformation.RewriteGroupingSetsByCTERule;
import com.starrocks.sql.optimizer.rule.transformation.RewriteMultiDistinctRule;
import com.starrocks.sql.optimizer.rule.transformation.RewriteSimpleAggToMetaScanRule;
import com.starrocks.sql.optimizer.rule.transformation.RewriteUnnestBitmapRule;
import com.starrocks.sql.optimizer.rule.transformation.SeparateProjectRule;
import com.starrocks.sql.optimizer.rule.transformation.SkewJoinOptimizeRule;
import com.starrocks.sql.optimizer.rule.transformation.SplitScanORToUnionRule;
Expand Down Expand Up @@ -438,6 +439,8 @@ private OptExpression logicalRuleRewrite(ConnectContext connectContext,
ruleRewriteIterative(tree, rootTaskContext, new RewriteSimpleAggToMetaScanRule());
ruleRewriteOnlyOnce(tree, rootTaskContext, new MinMaxCountOptOnScanRule());
ruleRewriteOnlyOnce(tree, rootTaskContext, new PartitionColumnValueOnlyOnScanRule());
// before MergeProjectWithChildRule, after INLINE_CTE and MergeApplyWithTableFunction
ruleRewriteIterative(tree, rootTaskContext, RewriteUnnestBitmapRule.getInstance());

// After this rule, we shouldn't generate logical project operator
ruleRewriteIterative(tree, rootTaskContext, new MergeProjectWithChildRule());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public enum RuleType {
TF_REWRITE_PARTITION_COLUMN_ONLY_AGG,
TF_REWRITE_SUM_BY_ASSOCIATIVE_RULE,
TF_REWRITE_COUNT_IF_RULE,
TF_REWRITE_UNNEST_BITMAP_RULE,

TF_INTERSECT_REORDER,
TF_INTERSECT_DISTINCT,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// 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
//
// https://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 com.starrocks.sql.optimizer.rule.transformation;

import com.google.common.collect.Lists;
import com.starrocks.analysis.Expr;
import com.starrocks.catalog.Function;
import com.starrocks.catalog.FunctionSet;
import com.starrocks.catalog.TableFunction;
import com.starrocks.catalog.Type;
import com.starrocks.common.Pair;
import com.starrocks.sql.optimizer.OptExpression;
import com.starrocks.sql.optimizer.OptimizerContext;
import com.starrocks.sql.optimizer.operator.OperatorType;
import com.starrocks.sql.optimizer.operator.logical.LogicalProjectOperator;
import com.starrocks.sql.optimizer.operator.logical.LogicalTableFunctionOperator;
import com.starrocks.sql.optimizer.operator.pattern.Pattern;
import com.starrocks.sql.optimizer.operator.scalar.CallOperator;
import com.starrocks.sql.optimizer.operator.scalar.ColumnRefOperator;
import com.starrocks.sql.optimizer.operator.scalar.ScalarOperator;
import com.starrocks.sql.optimizer.rule.RuleType;

import java.util.List;
import java.util.Map;

public class RewriteUnnestBitmapRule extends TransformationRule {
private static final RewriteUnnestBitmapRule INSTANCE = new RewriteUnnestBitmapRule();

private RewriteUnnestBitmapRule() {
super(RuleType.TF_REWRITE_UNNEST_BITMAP_RULE, Pattern.create(OperatorType.LOGICAL_TABLE_FUNCTION)
.addChildren(Pattern.create(OperatorType.LOGICAL_PROJECT)));
}

public static RewriteUnnestBitmapRule getInstance() {
return INSTANCE;
}

@Override
public boolean check(OptExpression input, OptimizerContext context) {
if (!context.getSessionVariable().isEnableRewriteUnnestBitmapToArray()) {
return false;
}

LogicalTableFunctionOperator tableFunctionOperator = (LogicalTableFunctionOperator) input.getOp();
if (tableFunctionOperator.getFn() != null &&
!tableFunctionOperator.getFn().functionName().equals(FunctionSet.UNNEST)) {
return false;
}

LogicalProjectOperator projectOperator = (LogicalProjectOperator) input.inputAt(0).getOp();

boolean existBitmapToArray = projectOperator.getColumnRefMap().values().stream().filter(scalarOperator -> {
if (scalarOperator instanceof CallOperator) {
CallOperator callOperator = (CallOperator) scalarOperator;
return callOperator.getFnName().equals(FunctionSet.BITMAP_TO_ARRAY);
}
return false;
}).count() == 1;

return existBitmapToArray;
}

@Override
public List<OptExpression> transform(OptExpression input, OptimizerContext context) {
LogicalTableFunctionOperator originalTableFunctionOperator = (LogicalTableFunctionOperator) input.getOp();
LogicalProjectOperator projectOperator = (LogicalProjectOperator) input.inputAt(0).getOp();

Map<ColumnRefOperator, ScalarOperator> columnRefMap = projectOperator.getColumnRefMap();
Pair<ColumnRefOperator, ScalarOperator> bitmapToArray = columnRefMap.entrySet().stream().filter(entry -> {
if (entry.getValue() instanceof CallOperator) {
CallOperator callOperator = (CallOperator) entry.getValue();
return callOperator.getFnName().equals(FunctionSet.BITMAP_TO_ARRAY);
}
return false;
}).map(entry -> new Pair<>(entry.getKey(), entry.getValue())).findFirst().get();

// if bitmap_to_array's output will be used by upper nodes, it's not safe to rewrite
if (originalTableFunctionOperator.getOuterColRefs().contains(bitmapToArray.first)) {
return Lists.newArrayList();
}

ColumnRefOperator bitmapColumn = bitmapToArray.second.getColumnRefs().get(0);
columnRefMap.remove(bitmapToArray.first);
columnRefMap.putIfAbsent(bitmapColumn, bitmapColumn);

TableFunction unnestBitmapFn =
(TableFunction) Expr.getBuiltinFunction(FunctionSet.UNNEST_BITMAP, new Type[] {Type.BITMAP},
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
List<Pair<ColumnRefOperator, ScalarOperator>> fnParamColumnProject =
Lists.newArrayList(Pair.create(bitmapColumn, bitmapColumn));

LogicalTableFunctionOperator newTableFunctionOperator =
new LogicalTableFunctionOperator(originalTableFunctionOperator.getFnResultColRefs(), unnestBitmapFn,
fnParamColumnProject, originalTableFunctionOperator.getOuterColRefs());

return Lists.newArrayList(OptExpression.create(newTableFunctionOperator, input.inputAt(0)));
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,14 @@ public void testTableFunctionAlias() throws Exception {

Assert.assertTrue(e.getMessage().contains("Not unique table/alias: 'table_function_unnest'"));
}

@Test
public void testRewrite() throws Exception {
String sql = "SELECT k1, unnest AS c3\n" +
" FROM test_agg,unnest(bitmap_to_array(b1)) ORDER BY k1 ASC, c3 ASC\n" +
"LIMIT 5;";
String plan = getFragmentPlan(sql);
assertContains(plan, "tableFunctionName: unnest_bitmap");
assertNotContains(plan, "bitmap_to_array");
}
}

0 comments on commit 4110988

Please sign in to comment.