[BugFix] Fix mv rewrite bugs with char/varchar implicit cast (backport #63659) (#63835)

Signed-off-by: shuming.li <ming.moriarty@gmail.com>
Co-authored-by: shuming.li <ming.moriarty@gmail.com>
This commit is contained in:
mergify[bot] 2025-10-09 15:37:00 +08:00 committed by GitHub
parent 868b8287de
commit 6d8f208910
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 225 additions and 6 deletions

View File

@ -630,17 +630,17 @@ public final class AggregatedMaterializedViewRewriter extends MaterializedViewRe
// add projection to make sure that the output columns keep the same with the origin query
if (queryAgg.getProjection() == null) {
for (int i = 0; i < originalGroupKeys.size(); i++) {
newProjection.put(originalGroupKeys.get(i), newGroupByKeyColumnRefs.get(i));
addIntoProjection(newProjection, originalGroupKeys.get(i), newGroupByKeyColumnRefs.get(i));
}
for (Map.Entry<ColumnRefOperator, CallOperator> entry : queryAgg.getAggregations().entrySet()) {
newProjection.put(entry.getKey(), queryColumnRefToScalarMap.get(entry.getKey()));
addIntoProjection(newProjection, entry.getKey(), queryColumnRefToScalarMap.get(entry.getKey()));
}
} else {
Map<ColumnRefOperator, ScalarOperator> originalMap = queryAgg.getProjection().getColumnRefMap();
ReplaceColumnRefRewriter rewriter = new ReplaceColumnRefRewriter(queryColumnRefToScalarMap);
for (Map.Entry<ColumnRefOperator, ScalarOperator> entry : originalMap.entrySet()) {
ScalarOperator rewritten = rewriter.rewrite(entry.getValue());
newProjection.put(entry.getKey(), rewritten);
addIntoProjection(newProjection, entry.getKey(), rewritten);
}
}
Projection projection = new Projection(newProjection);
@ -751,7 +751,7 @@ public final class AggregatedMaterializedViewRewriter extends MaterializedViewRe
ColumnRefOperator outerProject = context.getQueryRefFactory()
.create(copyProject, copyProject.getType(), copyProject.isNullable());
newProjection.put(outerProject, copyProject);
addIntoProjection(newProjection, outerProject, copyProject);
newAggregations.put(innerAgg, realAggregate);
// replace original projection
@ -851,7 +851,7 @@ public final class AggregatedMaterializedViewRewriter extends MaterializedViewRe
aggColRef.isNullable());
aggregateMapping.put(aggColRef, newAggColRef);
rewrittens.put(newAggColRef, newAggregate);
newProjection.put(newAggColRef, genRollupProject(aggCall, newAggColRef, hasGroupByKeys));
addIntoProjection(newProjection, newAggColRef, genRollupProject(aggCall, newAggColRef, hasGroupByKeys));
}
return rewrittens;

View File

@ -68,6 +68,7 @@ import com.starrocks.sql.optimizer.operator.logical.LogicalScanOperator;
import com.starrocks.sql.optimizer.operator.logical.LogicalUnionOperator;
import com.starrocks.sql.optimizer.operator.logical.LogicalViewScanOperator;
import com.starrocks.sql.optimizer.operator.scalar.BinaryPredicateOperator;
import com.starrocks.sql.optimizer.operator.scalar.CastOperator;
import com.starrocks.sql.optimizer.operator.scalar.ColumnRefOperator;
import com.starrocks.sql.optimizer.operator.scalar.ConstantOperator;
import com.starrocks.sql.optimizer.operator.scalar.IsNullPredicateOperator;
@ -3052,4 +3053,24 @@ public class MaterializedViewRewriter implements IMaterializedViewRewriter {
}
return projection;
}
/**
* Add columnRefOperator and scalarOperator into newProjection and ensure their type is the same.
*/
protected void addIntoProjection(Map<ColumnRefOperator, ScalarOperator> newProjection,
ColumnRefOperator columnRefOperator,
ScalarOperator scalarOperator) {
// Ensure columnRefOperator's type is exactly the same as scalarOperator's type,
// This can happen when mv and the query's type are different but they are the same columns, such as:
// query: char(4)
// mv : varchar(-1)
// TODO: may it's safe to remove the cast if the type is compatible
if (!columnRefOperator.getType().getPrimitiveType().equals(scalarOperator.getType().getPrimitiveType())) {
// add cast if type is not the same
ScalarOperator newScalarOperator = new CastOperator(columnRefOperator.getType(), scalarOperator, true);
newProjection.put(columnRefOperator, newScalarOperator);
} else {
newProjection.put(columnRefOperator, scalarOperator);
}
}
}

View File

@ -43,6 +43,7 @@ import com.starrocks.sql.optimizer.operator.logical.LogicalScanOperator;
import com.starrocks.sql.optimizer.operator.physical.PhysicalScanOperator;
import com.starrocks.sql.optimizer.operator.scalar.ColumnRefOperator;
import com.starrocks.sql.plan.PlanTestBase;
import com.starrocks.thrift.TExplainLevel;
import mockit.Mock;
import mockit.MockUp;
import org.junit.jupiter.api.Assertions;
@ -981,6 +982,36 @@ public class MvRewriteTest extends MVTestBase {
}
}
@Test
public void testDictWithMVRewrite() throws Exception {
FeConstants.USE_MOCK_DICT_MANAGER = true;
starRocksAssert.withTable("CREATE TABLE supplier_char(" +
" s_suppkey INTEGER NOT NULL,\n" +
" s_name CHAR(25) NOT NULL,\n" +
" s_address CHAR(40), \n" +
" s_nationkey INTEGER NOT NULL,\n" +
" s_phone CHAR(15) NOT NULL,\n" +
" s_acctbal double NOT NULL,\n" +
" s_comment CHAR(101) NOT NULL,\n" +
" pad char(1) NOT NULL)\n" +
"DUPLICATE KEY(`s_suppkey`)\n" +
"DISTRIBUTED BY HASH(`s_suppkey`) BUCKETS 1\n" +
"PROPERTIES (\n" +
"\"replication_num\" = \"1\"" +
");");
starRocksAssert.withMaterializedView("CREATE MATERIALIZED VIEW test_mv1\n" +
"DISTRIBUTED BY RANDOM\n" +
"PROPERTIES (\n" +
"\"replication_num\" = \"1\"\n" +
")\n" +
"AS select s_nationkey, s_name, bitmap_agg(s_suppkey), sum(s_nationkey) from supplier_char group by " +
"s_nationkey, s_name;");
String query = "select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name;";
String plan = getFragmentPlan(query, TExplainLevel.COSTS);
PlanTestBase.assertContains(plan, "dict_col=s_name");
FeConstants.USE_MOCK_DICT_MANAGER = false;
}
@Test
public void testPkFk() throws SQLException {
starRocksAssert.withMTables(List.of(

View File

@ -283,6 +283,10 @@ public class PlanTestNoneDBBase {
}
public String getFragmentPlan(String sql, String traceModule) throws Exception {
return getFragmentPlan(sql, TExplainLevel.NORMAL, traceModule);
}
public String getFragmentPlan(String sql, TExplainLevel tExplainLevel, String traceModule) throws Exception {
Pair<String, Pair<ExecPlan, String>> result =
UtFrameUtils.getFragmentPlanWithTrace(connectContext, sql, traceModule);
Pair<ExecPlan, String> execPlanWithQuery = result.second;
@ -290,7 +294,7 @@ public class PlanTestNoneDBBase {
if (!Strings.isNullOrEmpty(traceLog)) {
System.out.println(traceLog);
}
return execPlanWithQuery.first.getExplainString(TExplainLevel.NORMAL);
return execPlanWithQuery.first.getExplainString(tExplainLevel);
}
public String getLogicalFragmentPlan(String sql) throws Exception {

View File

@ -0,0 +1,108 @@
-- name: test_mv_rewrite_bugfix3
create database db_${uuid0};
-- result:
-- !result
use db_${uuid0};
-- result:
-- !result
CREATE TABLE supplier_char(
s_suppkey INTEGER NOT NULL,
s_name CHAR(25) NOT NULL,
s_address CHAR(40),
s_nationkey INTEGER NOT NULL,
s_phone CHAR(15) NOT NULL,
s_acctbal double NOT NULL,
s_comment CHAR(101) NOT NULL,
pad char(1) NOT NULL)
DUPLICATE KEY(`s_suppkey`)
DISTRIBUTED BY HASH(`s_suppkey`) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
);
-- result:
-- !result
INSERT INTO supplier_char VALUES (1, 'name1', 'address1', 1, 'phone1', 1.0, 'comment1', 'p'),
(2, 'name2', 'address2', 2, 'phone2', 2.0, 'comment2', 'p'),
(3, 'name3', 'address3', 3, 'phone3', 3.0, 'comment3', 'p'),
(4, 'name4', 'address4', 4, 'phone4', 4.0, 'comment4', 'p'),
(5, 'name5', 'address5', 5, 'phone5', 5.0, 'comment5', 'p'),
(6, 'name6', 'address6', 6, 'phone6', 6.0, 'comment6', 'p'),
(7, 'name7', 'address7', 7, 'phone7', 7.0, 'comment7', 'p'),
(8, 'name8', 'address8', 8, 'phone8', 8.0, 'comment8', 'p'),
(9, 'name9', 'address9', 9, 'phone9', 9.0, 'comment9', 'p'),
(10, 'name10', 'address10', 10, 'phone10', 10.0, 'comment10', 'p');
-- result:
-- !result
CREATE MATERIALIZED VIEW test_mv1
DISTRIBUTED BY RANDOM
PROPERTIES (
"replication_num" = "1"
)
AS select s_nationkey, s_name, bitmap_agg(s_suppkey), sum(s_nationkey) from supplier_char group by
s_nationkey, s_name;
-- result:
-- !result
REFRESH MATERIALIZED VIEW test_mv1 WITH SYNC MODE;
[UC] analyze full table supplier_char;
-- result:
db_611c78b427b640e2b3e00e6241768903.supplier_char analyze status OK
-- !result
[UC] analyze full table test_mv1;
-- result:
db_611c78b427b640e2b3e00e6241768903.test_mv1 analyze status OK
-- !result
function: wait_global_dict_ready('s_name', 'supplier_char')
-- result:
-- !result
function: wait_global_dict_ready('s_name', 'test_mv1')
-- result:
-- !result
select s_nationkey, s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_nationkey, s_name order by 1,2;
-- result:
1 name1 1 1
2 name2 1 2
3 name3 1 3
4 name4 1 4
5 name5 1 5
6 name6 1 6
7 name7 1 7
8 name8 1 8
9 name9 1 9
10 name10 1 10
-- !result
select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name order by 1,2;
-- result:
name1 1 1
name10 1 10
name2 1 2
name3 1 3
name4 1 4
name5 1 5
name6 1 6
name7 1 7
name8 1 8
name9 1 9
-- !result
select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name having sum(s_nationkey) > 10 order by 1,2;
-- result:
-- !result
function: print_hit_materialized_views("select s_nationkey, s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_nationkey, s_name order by 1,2;")
-- result:
test_mv1
-- !result
function: print_hit_materialized_views("select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name order by 1,2;")
-- result:
test_mv1
-- !result
function: print_hit_materialized_views("select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name having sum(s_nationkey) > 10 order by 1,2;")
-- result:
test_mv1
-- !result
drop materialized view test_mv1;
-- result:
-- !result
drop database db_${uuid0};
-- result:
-- !result

View File

@ -0,0 +1,55 @@
-- name: test_mv_rewrite_bugfix3
create database db_${uuid0};
use db_${uuid0};
CREATE TABLE supplier_char(
s_suppkey INTEGER NOT NULL,
s_name CHAR(25) NOT NULL,
s_address CHAR(40),
s_nationkey INTEGER NOT NULL,
s_phone CHAR(15) NOT NULL,
s_acctbal double NOT NULL,
s_comment CHAR(101) NOT NULL,
pad char(1) NOT NULL)
DUPLICATE KEY(`s_suppkey`)
DISTRIBUTED BY HASH(`s_suppkey`) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
);
INSERT INTO supplier_char VALUES (1, 'name1', 'address1', 1, 'phone1', 1.0, 'comment1', 'p'),
(2, 'name2', 'address2', 2, 'phone2', 2.0, 'comment2', 'p'),
(3, 'name3', 'address3', 3, 'phone3', 3.0, 'comment3', 'p'),
(4, 'name4', 'address4', 4, 'phone4', 4.0, 'comment4', 'p'),
(5, 'name5', 'address5', 5, 'phone5', 5.0, 'comment5', 'p'),
(6, 'name6', 'address6', 6, 'phone6', 6.0, 'comment6', 'p'),
(7, 'name7', 'address7', 7, 'phone7', 7.0, 'comment7', 'p'),
(8, 'name8', 'address8', 8, 'phone8', 8.0, 'comment8', 'p'),
(9, 'name9', 'address9', 9, 'phone9', 9.0, 'comment9', 'p'),
(10, 'name10', 'address10', 10, 'phone10', 10.0, 'comment10', 'p');
CREATE MATERIALIZED VIEW test_mv1
DISTRIBUTED BY RANDOM
PROPERTIES (
"replication_num" = "1"
)
AS select s_nationkey, s_name, bitmap_agg(s_suppkey), sum(s_nationkey) from supplier_char group by
s_nationkey, s_name;
REFRESH MATERIALIZED VIEW test_mv1 WITH SYNC MODE;
[UC] analyze full table supplier_char;
[UC] analyze full table test_mv1;
function: wait_global_dict_ready('s_name', 'supplier_char')
function: wait_global_dict_ready('s_name', 'test_mv1')
select s_nationkey, s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_nationkey, s_name order by 1,2;
select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name order by 1,2;
select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name having sum(s_nationkey) > 10 order by 1,2;
function: print_hit_materialized_views("select s_nationkey, s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_nationkey, s_name order by 1,2;")
function: print_hit_materialized_views("select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name order by 1,2;")
function: print_hit_materialized_views("select s_name, count(distinct s_suppkey), sum(s_nationkey) from supplier_char group by s_name having sum(s_nationkey) > 10 order by 1,2;")
drop materialized view test_mv1;
drop database db_${uuid0};