Skip to content

Commit a148775

Browse files
authored
table: fix the issue that the default value for BIT column is wrong | tidb-test=pr/2426 (pingcap#57303) (pingcap#57358)
close pingcap#57301, close pingcap#57312
1 parent 7362832 commit a148775

File tree

8 files changed

+85
-43
lines changed

8 files changed

+85
-43
lines changed

pkg/ddl/add_column.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import (
4848
"github.com/pingcap/tidb/pkg/util/collate"
4949
"github.com/pingcap/tidb/pkg/util/dbterror"
5050
"github.com/pingcap/tidb/pkg/util/hack"
51+
"github.com/pingcap/tidb/pkg/util/intest"
5152
"go.uber.org/zap"
5253
)
5354

@@ -1225,6 +1226,21 @@ func checkColumnDefaultValue(ctx exprctx.BuildContext, col *table.Column, value
12251226
}
12261227
}
12271228
}
1229+
if value != nil && col.GetType() == mysql.TypeBit {
1230+
v, ok := value.(string)
1231+
if !ok {
1232+
return hasDefaultValue, value, types.ErrInvalidDefault.GenWithStackByArgs(col.Name.O)
1233+
}
1234+
1235+
uintVal, err := types.BinaryLiteral(v).ToInt(ctx.GetEvalCtx().TypeCtx())
1236+
if err != nil {
1237+
return hasDefaultValue, value, types.ErrInvalidDefault.GenWithStackByArgs(col.Name.O)
1238+
}
1239+
intest.Assert(col.GetFlen() > 0 && col.GetFlen() <= 64)
1240+
if col.GetFlen() < 64 && uintVal >= 1<<(uint64(col.GetFlen())) {
1241+
return hasDefaultValue, value, types.ErrInvalidDefault.GenWithStackByArgs(col.Name.O)
1242+
}
1243+
}
12281244
return hasDefaultValue, value, nil
12291245
}
12301246

pkg/executor/test/writetest/write_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,13 @@ func TestIssue18681(t *testing.T) {
230230
tk := testkit.NewTestKit(t, store)
231231
tk.MustExec("use test")
232232
createSQL := `drop table if exists load_data_test;
233-
create table load_data_test (a bit(1),b bit(1),c bit(1),d bit(1));`
233+
create table load_data_test (a bit(1),b bit(1),c bit(1),d bit(1),e bit(32),f bit(1));`
234234
tk.MustExec(createSQL)
235235
loadSQL := "load data local infile '/tmp/nonexistence.csv' ignore into table load_data_test"
236236
ctx := tk.Session().(sessionctx.Context)
237237

238238
deleteSQL := "delete from load_data_test"
239-
selectSQL := "select bin(a), bin(b), bin(c), bin(d) from load_data_test;"
239+
selectSQL := "select bin(a), bin(b), bin(c), bin(d), bin(e), bin(f) from load_data_test;"
240240
levels := ctx.GetSessionVars().StmtCtx.ErrLevels()
241241
levels[errctx.ErrGroupDupKey] = errctx.LevelWarn
242242
levels[errctx.ErrGroupBadNull] = errctx.LevelWarn
@@ -249,7 +249,7 @@ func TestIssue18681(t *testing.T) {
249249
}()
250250
sc.SetTypeFlags(oldTypeFlags.WithIgnoreTruncateErr(true))
251251
tests := []testCase{
252-
{[]byte("true\tfalse\t0\t1\n"), []string{"1|0|0|1"}, "Records: 1 Deleted: 0 Skipped: 0 Warnings: 0"},
252+
{[]byte("true\tfalse\t0\t1\tb'1'\tb'1'\n"), []string{"1|1|1|1|1100010001001110011000100100111|1"}, "Records: 1 Deleted: 0 Skipped: 0 Warnings: 5"},
253253
}
254254
checkCases(tests, loadSQL, t, tk, ctx, selectSQL, deleteSQL)
255255
require.Equal(t, uint16(0), sc.WarningCount())

pkg/types/datum.go

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,38 +1705,13 @@ func (d *Datum) ConvertToMysqlYear(ctx Context, target *FieldType) (Datum, error
17051705
return ret, errors.Trace(err)
17061706
}
17071707

1708-
func (d *Datum) convertStringToMysqlBit(ctx Context) (uint64, error) {
1709-
bitStr, err := ParseBitStr(BinaryLiteral(d.b).ToString())
1710-
if err != nil {
1711-
// It cannot be converted to bit type, so we need to convert it to int type.
1712-
return BinaryLiteral(d.b).ToInt(ctx)
1713-
}
1714-
return bitStr.ToInt(ctx)
1715-
}
1716-
17171708
func (d *Datum) convertToMysqlBit(ctx Context, target *FieldType) (Datum, error) {
17181709
var ret Datum
17191710
var uintValue uint64
17201711
var err error
17211712
switch d.k {
1722-
case KindBytes:
1713+
case KindString, KindBytes:
17231714
uintValue, err = BinaryLiteral(d.b).ToInt(ctx)
1724-
case KindString:
1725-
// For single bit value, we take string like "true", "1" as 1, and "false", "0" as 0,
1726-
// this behavior is not documented in MySQL, but it behaves so, for more information, see issue #18681
1727-
s := BinaryLiteral(d.b).ToString()
1728-
if target.GetFlen() == 1 {
1729-
switch strings.ToLower(s) {
1730-
case "true", "1":
1731-
uintValue = 1
1732-
case "false", "0":
1733-
uintValue = 0
1734-
default:
1735-
uintValue, err = d.convertStringToMysqlBit(ctx)
1736-
}
1737-
} else {
1738-
uintValue, err = d.convertStringToMysqlBit(ctx)
1739-
}
17401715
case KindInt64:
17411716
// if input kind is int64 (signed), when trans to bit, we need to treat it as unsigned
17421717
d.k = KindUint64

pkg/types/datum_test.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -517,22 +517,35 @@ func prepareCompareDatums() ([]Datum, []Datum) {
517517

518518
func TestStringToMysqlBit(t *testing.T) {
519519
tests := []struct {
520-
a Datum
521-
out []byte
520+
a Datum
521+
out []byte
522+
flen int
523+
truncated bool
522524
}{
523-
{NewStringDatum("true"), []byte{1}},
524-
{NewStringDatum("false"), []byte{0}},
525-
{NewStringDatum("1"), []byte{1}},
526-
{NewStringDatum("0"), []byte{0}},
527-
{NewStringDatum("b'1'"), []byte{1}},
528-
{NewStringDatum("b'0'"), []byte{0}},
529-
}
530-
tp := NewFieldType(mysql.TypeBit)
531-
tp.SetFlen(1)
525+
{NewStringDatum("true"), []byte{1}, 1, true},
526+
{NewStringDatum("true"), []byte{0x74, 0x72, 0x75, 0x65}, 32, false},
527+
{NewStringDatum("false"), []byte{0x1}, 1, true},
528+
{NewStringDatum("false"), []byte{0x66, 0x61, 0x6c, 0x73, 0x65}, 40, false},
529+
{NewStringDatum("1"), []byte{1}, 1, true},
530+
{NewStringDatum("1"), []byte{0x31}, 8, false},
531+
{NewStringDatum("0"), []byte{1}, 1, true},
532+
{NewStringDatum("0"), []byte{0x30}, 8, false},
533+
{NewStringDatum("b'1'"), []byte{0x62, 0x27, 0x31, 0x27}, 32, false},
534+
{NewStringDatum("b'0'"), []byte{0x62, 0x27, 0x30, 0x27}, 32, false},
535+
}
532536
for _, tt := range tests {
533-
bin, err := tt.a.convertToMysqlBit(DefaultStmtNoWarningContext, tp)
534-
require.NoError(t, err)
535-
require.Equal(t, tt.out, bin.b)
537+
t.Run(fmt.Sprintf("%s %d %t", tt.a.GetString(), tt.flen, tt.truncated), func(t *testing.T) {
538+
tp := NewFieldType(mysql.TypeBit)
539+
tp.SetFlen(tt.flen)
540+
541+
bin, err := tt.a.convertToMysqlBit(DefaultStmtNoWarningContext, tp)
542+
if tt.truncated {
543+
require.Contains(t, err.Error(), "Data Too Long")
544+
} else {
545+
require.NoError(t, err)
546+
}
547+
require.Equal(t, tt.out, bin.b)
548+
})
536549
}
537550
}
538551

tests/integrationtest/r/ddl/column.result

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,15 @@ t CREATE TABLE `t` (
8484
`a` decimal(10,0) DEFAULT NULL,
8585
`b` decimal(10,0) DEFAULT NULL
8686
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
87+
drop table if exists t;
88+
create table t(a bit(2) default b'111');
89+
Error 1067 (42000): Invalid default value for 'a'
90+
create table t(a bit(65) default b'111');
91+
Error 1439 (42000): Display width out of range for column 'a' (max = 64)
92+
create table t(a bit(64) default b'1111111111111111111111111111111111111111111111111111111111111111');
93+
drop table t;
94+
create table t(a bit(3) default b'111');
95+
drop table t;
96+
create table t(a bit(3) default b'000111');
97+
drop table t;
98+
create table t(a bit(32) default b'1111111111111111111111111111111');

tests/integrationtest/r/table/tables.result

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ select count(distinct(_tidb_rowid>>48)) from shard_t;
2424
count(distinct(_tidb_rowid>>48))
2525
3
2626
set @@tidb_shard_allocate_step=default;
27+
drop table if exists t;
28+
create table t(a bit(32) default b'1100010001001110011000100100111');
29+
insert into t values ();
30+
select hex(a) from t;
31+
hex(a)
32+
62273127

tests/integrationtest/t/ddl/column.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,17 @@ alter table t1 add column d date default '2024-10';
4444
drop table if exists t;
4545
create table t(a decimal(0,0), b decimal(0));
4646
show create table t;
47+
48+
# TestTooLongDefaultValueForBit
49+
drop table if exists t;
50+
-- error 1067
51+
create table t(a bit(2) default b'111');
52+
-- error 1439
53+
create table t(a bit(65) default b'111');
54+
create table t(a bit(64) default b'1111111111111111111111111111111111111111111111111111111111111111');
55+
drop table t;
56+
create table t(a bit(3) default b'111');
57+
drop table t;
58+
create table t(a bit(3) default b'000111');
59+
drop table t;
60+
create table t(a bit(32) default b'1111111111111111111111111111111');

tests/integrationtest/t/table/tables.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ insert into shard_t values (12);
2222
select count(distinct(_tidb_rowid>>48)) from shard_t;
2323

2424
set @@tidb_shard_allocate_step=default;
25+
26+
# TestInsertBitDefaultValue
27+
drop table if exists t;
28+
create table t(a bit(32) default b'1100010001001110011000100100111');
29+
insert into t values ();
30+
select hex(a) from t;

0 commit comments

Comments
 (0)