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

types: convert string to MySQL BIT correctly #21310

Merged
merged 14 commits into from
Jan 18, 2021

Conversation

rebelice
Copy link
Contributor

What problem does this PR solve?

Issue Number: close #18681

Problem Summary:

LOAD DATA does not take effect on BIT. Because LOAD DATA regards all input as strings and TiDB cannot convert any string to bit correctly

What is changed and how it works?

What's Changed:

  • convert string to bit correctly
  • consider true, false, 0, 1

Related changes

  • Need to cherry-pick to the release branch

Check List

Tests

  • Unit test

Side effects

Release note

  • convert string to MySQL BIT correctly

@rebelice rebelice requested a review from a team as a code owner November 26, 2020 03:56
@rebelice rebelice requested review from SunRunAway and removed request for a team November 26, 2020 03:56
@rebelice
Copy link
Contributor Author

/label component/expression, sig/execution

@rebelice
Copy link
Contributor Author

/label needs-cherry-pick-4.0

@rebelice
Copy link
Contributor Author

/cc @ichn-hu, @lzmhhh123

Copy link
Contributor

@ichn-hu ichn-hu left a comment

Choose a reason for hiding this comment

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

I would suggest to add a intergration test for #18681 , see TestLoadData in executor/write_test.go

@XuHuaiyu XuHuaiyu added the type/bugfix This PR fixes a bug. label Dec 9, 2020
if target.Flen == 1 {
switch strings.ToLower(s) {
case "true", "1":
uintValue = 1
Copy link
Contributor

Choose a reason for hiding this comment

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

In mysql 8.0:

mysql > select true, b'1', cast(true as binary);
+------+------------+--------------------------------------------+
| true | b'1'       | cast(true as binary)                       |
+------+------------+--------------------------------------------+
|    1 | 0x01       | 0x31                                       |
+------+------------+--------------------------------------------+
1 row in set (0.00 sec)

Maybe we should handle it in another place.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This sentence of SQL has nothing to do with convert. It can be seen from the following that these values can be converted correctly, but they are returned as binary, so the output to MySQL Client may not be displayed.

MySQL [test]> select true, HEX(b'1'), HEX(cast(true as binary));
+------+-----------+---------------------------+
| TRUE | HEX(b'1') | HEX(cast(true as binary)) |
+------+-----------+---------------------------+
|    1 | 01        | 31                        |
+------+-----------+---------------------------+
1 row in set (0.001 sec)

MySQL [test]> select true, b'1', cast(true as binary);
+------+------+----------------------+
| TRUE | b'1' | cast(true as binary) |
+------+------+----------------------+
|    1 |     | 1                    |
+------+------+----------------------+
1 row in set (0.001 sec)

I think these are two different problems, and it's best to solve it in another PR

Comment on lines 2028 to 2032
_, err = tk.Exec("load data infile '/tmp/nonexistence.csv' into table load_data_test")
c.Assert(err, NotNil)
_, err = tk.Exec("load data local infile '/tmp/nonexistence.csv' replace into table load_data_test")
c.Assert(err, NotNil)
tk.MustExec("load data local infile '/tmp/nonexistence.csv' ignore into table load_data_test")
Copy link
Contributor

Choose a reason for hiding this comment

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

how are these tests relate to your change?

types/datum.go Outdated
func (d *Datum) convertStringToMysqlBit(sc *stmtctx.StatementContext) (uint64, error) {
bitStr, err := ParseBitStr(BinaryLiteral(d.b).ToString())
if err != nil {
// This is to be compatible with the previous processing method.
Copy link
Contributor

Choose a reason for hiding this comment

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

please be more precise with this comment, because once the PR get merged, no one will know what the "previous processing method" is.

types/datum.go Outdated
Comment on lines 1449 to 1450
// To solve issue #18681, we need consider true, false, 0, 1.
// More situations, we also consider b'0', b'1' and so on.
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this fix is not specific to 18681, and the code itself is self-explaining, if you want to add comment, it is better to find if there is a reference to MySQL's doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no mention of related behavior in the MySQL documentation, and I think there is nothing wrong with this comment

Copy link
Contributor

Choose a reason for hiding this comment

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

What I am saying is that this comment is not comprehensive enough if not redundant, you don't fix code because of an issue report, you fix code because you want proper behavior, that's why I am asking for a doc reference.

I am not against referencing to an issue here, but I would like a comment like this:

// For single bit value, we take string like "true", "1" as 1, and "false", "0" as 0,
// this behavior is not documented in MySQL, but it behaves so, for more information, see issue #18681

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

Comment on lines 2060 to 2063
tests := []testCase{
// data1 = nil, data2 != nil
{nil, []byte("true\tfalse\t0\t1\n"), []string{"1|0|0|1"}, nil, "Records: 1 Deleted: 0 Skipped: 0 Warnings: 0"},
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably you can consider intergrate this test into the following TestLoadData 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.

May not work because the table structure is not the same.

createSQL := `drop table if exists load_data_test;
create table load_data_test (a bit(1),b bit(1),c bit(1),d bit(1));`
tk.MustExec(createSQL)
tk.MustExec("load data local infile '/tmp/nonexistence.csv' ignore into table load_data_test")
Copy link
Contributor

Choose a reason for hiding this comment

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

this line is still irrelevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this is initialization.

types/datum.go Outdated
Comment on lines 1449 to 1450
// To solve issue #18681, we need consider true, false, 0, 1.
// More situations, we also consider b'0', b'1' and so on.
Copy link
Contributor

Choose a reason for hiding this comment

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

What I am saying is that this comment is not comprehensive enough if not redundant, you don't fix code because of an issue report, you fix code because you want proper behavior, that's why I am asking for a doc reference.

I am not against referencing to an issue here, but I would like a comment like this:

// For single bit value, we take string like "true", "1" as 1, and "false", "0" as 0,
// this behavior is not documented in MySQL, but it behaves so, for more information, see issue #18681

@rebelice rebelice requested a review from ichn-hu January 4, 2021 09:38
@ti-srebot ti-srebot added the status/LGT1 Indicates that a PR has LGTM 1. label Jan 5, 2021
@ti-srebot
Copy link
Contributor

@rebelice merge failed.

@lzmhhh123
Copy link
Contributor

/merge

@ti-srebot
Copy link
Contributor

/run-all-tests

@ti-srebot
Copy link
Contributor

@rebelice merge failed.

@rebelice
Copy link
Contributor Author

rebelice commented Jan 5, 2021

/run-all-tests

@rebelice
Copy link
Contributor Author

rebelice commented Jan 6, 2021

/run-all-tests

@rebelice
Copy link
Contributor Author

rebelice commented Jan 6, 2021

/run-unit-test

@rebelice
Copy link
Contributor Author

rebelice commented Jan 6, 2021

/run-all-tests

@XuHuaiyu
Copy link
Contributor

/merge

@ti-srebot
Copy link
Contributor

/run-all-tests

@ti-srebot ti-srebot merged commit ca93c7f into pingcap:master Jan 18, 2021
ti-srebot pushed a commit to ti-srebot/tidb that referenced this pull request Jan 18, 2021
@ti-srebot
Copy link
Contributor

cherry pick to release-4.0 in PR #22420

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/expression sig/execution SIG execution status/can-merge Indicates a PR has been approved by a committer. status/LGT2 Indicates that a PR has LGTM 2. type/bugfix This PR fixes a bug.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

load data can't load value 0/1/true/false for bit(1)
5 participants