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

ddl, types: convert the binary default value to proper encoding #31196

Merged
merged 10 commits into from
Dec 31, 2021
23 changes: 15 additions & 8 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,13 +902,20 @@ func getDefaultValue(ctx sessionctx.Context, col *table.Column, c *ast.ColumnOpt
}

if v.Kind() == types.KindBinaryLiteral || v.Kind() == types.KindMysqlBit {
if tp == mysql.TypeBit ||
tp == mysql.TypeString || tp == mysql.TypeVarchar || tp == mysql.TypeVarString ||
tp == mysql.TypeBlob || tp == mysql.TypeLongBlob || tp == mysql.TypeMediumBlob || tp == mysql.TypeTinyBlob ||
tp == mysql.TypeJSON || tp == mysql.TypeEnum || tp == mysql.TypeSet {
// For BinaryLiteral / string fields, when getting default value we cast the value into BinaryLiteral{}, thus we return
// its raw string content here.
return v.GetBinaryLiteral().ToString(), false, nil
if types.IsTypeBlob(tp) || tp == mysql.TypeJSON {
// BLOB/TEXT/JSON column cannot have a default value.
// Skip the unnecessary decode procedure.
return v.GetString(), false, err
}
if tp == mysql.TypeBit || tp == mysql.TypeString || tp == mysql.TypeVarchar ||
tp == mysql.TypeVarString || tp == mysql.TypeEnum || tp == mysql.TypeSet {
// For BinaryLiteral or bit fields, we decode the default value to utf8 string.
str, err := v.GetBinaryStringDecoded(nil, col.Charset)
if err != nil {
// Overwrite the decoding error with invalid default value error.
err = ErrInvalidDefaultValue.GenWithStackByArgs(col.Name.O)
}
return str, false, err
}
// For other kind of fields (e.g. INT), we supply its integer as string value.
value, err := v.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx)
Expand Down Expand Up @@ -3937,7 +3944,7 @@ func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.Colu
hasDefaultValue := false
value, isSeqExpr, err := getDefaultValue(ctx, col, option)
if err != nil {
return hasDefaultValue, errors.Trace(err)
return false, errors.Trace(err)
}
if isSeqExpr {
if err := checkSequenceDefaultValue(col); err != nil {
Expand Down
61 changes: 61 additions & 0 deletions ddl/integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2021 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ddl_test

import (
"fmt"
"testing"

"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/util/collate"
)

func TestDefaultValueIsBinaryString(t *testing.T) {
collate.SetCharsetFeatEnabledForTest(true)
defer collate.SetCharsetFeatEnabledForTest(false)
store, clean := testkit.CreateMockStore(t)
defer clean()
tests := []struct {
colTp string
defVal string
result string
}{
{"char(10) charset gbk", "0xC4E3BAC3", "你好"},
{"char(10) charset gbk", "'好'", "好"},
{"varchar(10) charset gbk", "0xC4E3BAC3", "你好"},
{"char(10) charset utf8mb4", "0xE4BDA0E5A5BD", "你好"},
{"char(10) charset utf8mb4", "0b111001001011100010010110111001111001010110001100", "世界"},
{"bit(48)", "0xE4BDA0E5A5BD", "你好"},
{"enum('你好')", "0xE4BDA0E5A5BD", "你好"},
{"set('你好')", "0xE4BDA0E5A5BD", "你好"},
}
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test;")
for _, tt := range tests {
tk.MustExec("drop table if exists t;")
template := "create table t (a %s default %s);"
tk.MustExec(fmt.Sprintf(template, tt.colTp, tt.defVal))
tk.MustExec("insert into t values (default);")
tk.MustQuery("select a from t;").Check(testkit.Rows(tt.result))
}

// Test invalid default value.
tk.MustExec("drop table if exists t;")
// 0xE4BDA0E5A5BD81 is an invalid utf-8 string.
tk.MustGetErrMsg("create table t (a char(20) charset utf8mb4 default 0xE4BDA0E5A5BD81);",
"[ddl:1067]Invalid default value for 'a'")
tk.MustGetErrMsg("create table t (a blob default 0xE4BDA0E5A5BD81);",
"[ddl:1101]BLOB/TEXT/JSON column 'a' can't have a default value")
}
3 changes: 3 additions & 0 deletions types/datum.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ func (d *Datum) GetStringWithCheck(sc *stmtctx.StatementContext, chs string) (st

func findEncoding(sc *stmtctx.StatementContext, chs string) (enc charset.Encoding, skip bool) {
enc = charset.FindEncoding(chs)
if sc == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

When sc is nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

return enc, false
}
if enc.Tp() == charset.EncodingTpUTF8 && sc.SkipUTF8Check ||
enc.Tp() == charset.EncodingTpASCII && sc.SkipASCIICheck {
return nil, true
Expand Down