Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plan,table: fix select null value for table partition #7452

Merged
merged 6 commits into from Aug 23, 2018

Conversation

tiancaiamao
Copy link
Contributor

What problem does this PR solve?

set @@session.tidb_enable_table_partition = 1;
create table t1 (a int unsigned) partition by range (a)
(partition pnull values less than (0),
 partition p0 values less than (1),
 partition p1 values less than(2));

insert into t1 values (null),(0),(1);

select * from t1 where a is null;

should get

mysql> select * from t1 where a is null;
+------+
| a    |
+------+
| NULL |

But we get an empty result.

mysql> select * from t1 where a is null;
Empty set (0.00 sec)

The reason is that partition pruning works incorrectly, it prunes all the partitions.

What is changed and how it works?

The partition pruning condition of partition[0] is changed from

expr < value

to

expr < value or expr is null

Before this change, the partition pruning conditions in the failed example are:

a < 0 and a is null
0 < a < 1 and a is null
1 < a < 2 and a is null

The pruning result is an empty set.

After this change, the partition pruning conditions are:

(a < 0 or a is null) and a is null
0 < a < 1 and a is null
1 < a < 2 and a is null

The first partition remains, so we get the correct result.

Check List

Tests

  • Unit test

@zz-jason @coocood

@tiancaiamao tiancaiamao added the sig/planner SIG: Planner label Aug 21, 2018
@tiancaiamao tiancaiamao added this to In Progress in Table Partitioning via automation Aug 21, 2018
@tiancaiamao
Copy link
Contributor Author

/run-all-tests tidb-test=pr/606

}
}
if column == nil {
log.Warnf("partition pruning won't work on this expr:%s", pi.Expr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to log a warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without partition pruning, a query need to scan all partitions, we'd better warn the user about this.
For example,

create table t (id int) partition by range (mod(id, 5)) (partition ... )

The warning log is not frequent, it's happen when creating table, and normal expression will not print the warning.

@tiancaiamao
Copy link
Contributor Author

Check if the expression is null is more complex, the expression may be anything:

select * from t where a is null
select * from t where a is null or a > 6
select * from t where a is null and a > 6
select * from t where cast(a, as integer) > 3
select * from t where to_days(a) = 2016000;
...

Change partition expression range is much easier, for partition 1 and others:

expr < lower bound and expr < upper bound

while partition 0 condition is actually

expr < upper bound or expr is null

@coocood

@tiancaiamao
Copy link
Contributor Author

/run-common-test tidb-test=pr/606
/run-integration-common-test tidb-test=pr/606

fmt.Fprintf(&buf, " or ((%s) is null)", pi.Expr)

// Extracts the column of the partition expression, it will be used by partition prunning.
if tmp, err1 := expression.ParseSimpleExprWithTableInfo(ctx, pi.Expr, tblInfo); err1 == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err1 is not logged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If expression.ParseSimpleExprWithTableInfo(ctx, pi.Expr, tblInfo) meets error,
expression.ParseSimpleExprWithTableInfo(ctx, pi.Expr < xxx, tblInfo) will also meets error, and the error will be logged.

The err1 is not important, we just need to get a *expression.Column from a (maybe) string.

@coocood
Copy link
Member

coocood commented Aug 22, 2018

LGTM

@coocood coocood added the status/LGT1 Indicates that a PR has LGTM 1. label Aug 22, 2018
@tiancaiamao
Copy link
Contributor Author

PTAL @zz-jason

@tiancaiamao
Copy link
Contributor Author

PTAL @zz-jason

@@ -538,6 +538,17 @@ func (s *testPlanSuite) TestTablePartition(c *C) {
best: "Dual->Projection",
is: is1,
},
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these kind of tests can not show the detail about the filters, it's better to add some explain tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll port this one https://github.com/twitter/mysql/blob/master/mysql-test/t/partition_pruning.test
It will certainly cover more cases, but it's a file and I could not do it in this PR.

// UpperBounds: (x < y1); (x < y2); (x < y3)
type PartitionExpr struct {
Column *expression.Column
Ranges []expression.Expression
UpperBounds []expression.Expression
}

func generatePartitionExpr(tblInfo *model.TableInfo) (*PartitionExpr, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to add some ut for this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -129,6 +131,19 @@ func generatePartitionExpr(tblInfo *model.TableInfo) (*PartitionExpr, error) {

if i > 0 {
fmt.Fprintf(&buf, " and ((%s) >= (%s))", pi.Expr, pi.Definitions[i-1].LessThan[0])
} else {
// NULL will locates in the first partition, so its expression is (expr < value or expr is null).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/locates/locate/

// UpperBounds: (x < y1); (x < y2); (x < y3)
type PartitionExpr struct {
Column *expression.Column
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the newly added attribute.

@tiancaiamao
Copy link
Contributor Author

PTAL @zz-jason

Copy link
Member

@zz-jason zz-jason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tiancaiamao
Copy link
Contributor Author

/run-all-tests

@tiancaiamao tiancaiamao added status/LGT2 Indicates that a PR has LGTM 2. and removed status/LGT1 Indicates that a PR has LGTM 1. labels Aug 23, 2018
@coocood coocood merged commit 3ff237c into pingcap:master Aug 23, 2018
Table Partitioning automation moved this from In Progress to Done Aug 23, 2018
@tiancaiamao tiancaiamao deleted the select-null branch August 23, 2018 06:16
@zz-jason zz-jason moved this from Issue: Done to PR: Donw in Table Partitioning Apr 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sig/planner SIG: Planner status/LGT2 Indicates that a PR has LGTM 2.
Projects
No open projects
Table Partitioning
  
PR: Done
Development

Successfully merging this pull request may close these issues.

None yet

4 participants