Skip to content

Commit 3bc94b4

Browse files
authored
planner: push necessary predicates without virtual column down through UnionScan (#54985) (#54991)
close #54870
1 parent 9ddccb6 commit 3bc94b4

File tree

3 files changed

+83
-9
lines changed

3 files changed

+83
-9
lines changed

pkg/planner/core/integration_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2322,6 +2322,68 @@ func TestIssue48257(t *testing.T) {
23222322
))
23232323
}
23242324

2325+
func TestIssue54870(t *testing.T) {
2326+
store := testkit.CreateMockStore(t)
2327+
tk := testkit.NewTestKit(t, store)
2328+
2329+
tk.MustExec("use test")
2330+
tk.MustExec(`create table t (id int,
2331+
deleted_at datetime(3) NOT NULL DEFAULT '1970-01-01 01:00:01.000',
2332+
is_deleted tinyint(1) GENERATED ALWAYS AS ((deleted_at > _utf8mb4'1970-01-01 01:00:01.000')) VIRTUAL NOT NULL,
2333+
key k(id, is_deleted))`)
2334+
tk.MustExec(`begin`)
2335+
tk.MustExec(`insert into t (id, deleted_at) values (1, now())`)
2336+
tk.MustHavePlan(`select 1 from t where id=1 and is_deleted=true`, "IndexRangeScan")
2337+
}
2338+
2339+
func TestIssue53951(t *testing.T) {
2340+
store := testkit.CreateMockStore(t)
2341+
tk := testkit.NewTestKit(t, store)
2342+
tk.MustExec("use test")
2343+
tk.MustExec(`CREATE TABLE gholla_dummy1 (
2344+
id varchar(10) NOT NULL,
2345+
mark int,
2346+
deleted_at datetime(3) NOT NULL DEFAULT '1970-01-01 01:00:01.000',
2347+
account_id varchar(10) NOT NULL,
2348+
metastore_id varchar(10) NOT NULL,
2349+
is_deleted tinyint(1) GENERATED ALWAYS AS ((deleted_at > _utf8mb4'1970-01-01 01:00:01.000')) VIRTUAL NOT NULL,
2350+
PRIMARY KEY (account_id,metastore_id,id),
2351+
KEY isDeleted_accountId_metastoreId (is_deleted,account_id,metastore_id)
2352+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`)
2353+
tk.MustExec(`CREATE TABLE gholla_dummy2 (
2354+
id varchar(10) NOT NULL,
2355+
mark int,
2356+
deleted_at datetime(3) NOT NULL DEFAULT '1970-01-01 01:00:01.000',
2357+
account_id varchar(10) NOT NULL,
2358+
metastore_id varchar(10) NOT NULL,
2359+
is_deleted tinyint(1) GENERATED ALWAYS AS ((deleted_at > _utf8mb4'1970-01-01 01:00:01.000')) VIRTUAL NOT NULL,
2360+
PRIMARY KEY (account_id,metastore_id,id),
2361+
KEY isDeleted_accountId_metastoreId (is_deleted,account_id,metastore_id)
2362+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; `)
2363+
tk.MustExec(`INSERT INTO gholla_dummy1 (id,mark,deleted_at,account_id,metastore_id) VALUES ('ABC', 1, '1970-01-01 01:00:01.000', 'ABC', 'ABC');`)
2364+
tk.MustExec(`INSERT INTO gholla_dummy2 (id,mark,deleted_at,account_id,metastore_id) VALUES ('ABC', 1, '1970-01-01 01:00:01.000', 'ABC', 'ABC');`)
2365+
tk.MustExec(`start transaction;`)
2366+
tk.MustExec(`update gholla_dummy2 set deleted_at = NOW(), mark=2 where account_id = 'ABC' and metastore_id = 'ABC' and id = 'ABC';`)
2367+
tk.MustQuery(`select
2368+
/*+ INL_JOIN(g1, g2) */
2369+
g1.account_id,
2370+
g2.mark
2371+
from
2372+
gholla_dummy1 g1 FORCE INDEX(isDeleted_accountId_metastoreId)
2373+
STRAIGHT_JOIN
2374+
gholla_dummy2 g2 FORCE INDEX (PRIMARY)
2375+
ON
2376+
g1.account_id = g2.account_id AND
2377+
g1.metastore_id = g2.metastore_id AND
2378+
g1.id = g2.id
2379+
WHERE
2380+
g1.account_id = 'ABC' AND
2381+
g1.metastore_id = 'ABC' AND
2382+
g1.is_deleted = FALSE AND
2383+
g2.is_deleted = FALSE;`).Check(testkit.Rows()) // empty result, no error
2384+
tk.MustExec(`rollback`)
2385+
}
2386+
23252387
func TestIssue52472(t *testing.T) {
23262388
store := testkit.CreateMockStore(t)
23272389
tk := testkit.NewTestKit(t, store)

pkg/planner/core/rule_predicate_push_down.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,22 @@ func (p *LogicalSelection) PredicatePushDown(predicates []expression.Expression,
124124

125125
// PredicatePushDown implements LogicalPlan PredicatePushDown interface.
126126
func (p *LogicalUnionScan) PredicatePushDown(predicates []expression.Expression, opt *util.LogicalOptimizeOp) ([]expression.Expression, LogicalPlan) {
127+
var predicatesWithVCol, predicatesWithoutVCol []expression.Expression
128+
// predicates with virtual columns can't be pushed down to TiKV/TiFlash so they'll be put into a Projection
129+
// below the UnionScan, but the current UnionScan doesn't support placing Projection below it, see #53951.
130+
for _, expr := range predicates {
131+
if expression.ContainVirtualColumn([]expression.Expression{expr}) {
132+
predicatesWithVCol = append(predicatesWithVCol, expr)
133+
} else {
134+
predicatesWithoutVCol = append(predicatesWithoutVCol, expr)
135+
}
136+
}
137+
predicates = predicatesWithoutVCol
127138
retainedPredicates, _ := p.children[0].PredicatePushDown(predicates, opt)
128139
p.conditions = make([]expression.Expression, 0, len(predicates))
129140
p.conditions = append(p.conditions, predicates...)
130141
// The conditions in UnionScan is only used for added rows, so parent Selection should not be removed.
142+
retainedPredicates = append(retainedPredicates, predicatesWithVCol...)
131143
return retainedPredicates, p
132144
}
133145

tests/integrationtest/r/explain_generate_column_substitute.result

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -573,15 +573,15 @@ begin;
573573
delete from t2 where c_decimal > c_double/2 order by c_int, c_str, c_double, c_decimal limit 1;
574574
desc format='brief' select t2.c_enum from t2,t1 where t1.c_int - 1 = t2.c_int - 1 order by t2.c_enum;
575575
id estRows task access object operator info
576-
Sort 12487.50 root explain_generate_column_substitute.t2.c_enum
577-
└─HashJoin 12487.50 root inner join, equal:[eq(minus(explain_generate_column_substitute.t1.c_int, 1), minus(explain_generate_column_substitute.t2.c_int, 1))]
578-
├─IndexReader(Build) 9990.00 root index:IndexFullScan
579-
│ └─IndexFullScan 9990.00 cop[tikv] table:t1, index:expression_index_2(`c_int` - 1) keep order:false, stats:pseudo
580-
└─Projection(Probe) 10000.00 root explain_generate_column_substitute.t2.c_enum, minus(explain_generate_column_substitute.t2.c_int, 1), explain_generate_column_substitute.t2._tidb_rowid
581-
└─UnionScan 8000.00 root not(isnull(minus(explain_generate_column_substitute.t2.c_int, 1)))
582-
└─Selection 8000.00 root not(isnull(minus(explain_generate_column_substitute.t2.c_int, 1)))
583-
└─TableReader 10000.00 root data:TableFullScan
584-
└─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo
576+
Sort 10000.00 root explain_generate_column_substitute.t2.c_enum
577+
└─HashJoin 10000.00 root inner join, equal:[eq(minus(explain_generate_column_substitute.t1.c_int, 1), minus(explain_generate_column_substitute.t2.c_int, 1))]
578+
├─Selection(Build) 8000.00 root not(isnull(minus(explain_generate_column_substitute.t2.c_int, 1)))
579+
│ └─Projection 10000.00 root explain_generate_column_substitute.t2.c_enum, minus(explain_generate_column_substitute.t2.c_int, 1), explain_generate_column_substitute.t2._tidb_rowid
580+
│ └─UnionScan 10000.00 root
581+
│ └─TableReader 10000.00 root data:TableFullScan
582+
└─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo
583+
└─IndexReader(Probe) 9990.00 root index:IndexFullScan
584+
└─IndexFullScan 9990.00 cop[tikv] table:t1, index:expression_index_2(`c_int` - 1) keep order:false, stats:pseudo
585585
select t2.c_enum from t2,t1 where t1.c_int - 1 = t2.c_int - 1 order by t2.c_enum;
586586
c_enum
587587
orange

0 commit comments

Comments
 (0)