Files
tidb/pkg/server/internal/column/convert.go

100 lines
3.6 KiB
Go

// Copyright 2023 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 column
import (
"github.com/pingcap/tidb/pkg/parser/charset"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/planner/core/resolve"
"github.com/pingcap/tidb/pkg/types"
)
// ConvertColumnInfo converts `*ast.ResultField` to `*Info`
func ConvertColumnInfo(fld *resolve.ResultField) (ci *Info) {
ci = &Info{
Name: fld.ColumnAsName.O,
OrgName: fld.Column.Name.O,
Table: fld.TableAsName.O,
Schema: fld.DBName.O,
Flag: uint16(fld.Column.GetFlag()),
Charset: uint16(mysql.CharsetNameToID(fld.Column.GetCharset())),
Type: fld.Column.GetType(),
DefaultValue: fld.Column.GetDefaultValue(),
}
if fld.EmptyOrgName {
ci.OrgName = ""
}
if fld.Table != nil {
ci.OrgTable = fld.Table.Name.O
}
if fld.Column.GetFlen() != types.UnspecifiedLength {
ci.ColumnLength = uint32(fld.Column.GetFlen())
if fld.Column.GetType() == mysql.TypeNewDecimal {
// Consider the negative sign.
ci.ColumnLength++
if fld.Column.GetDecimal() > types.DefaultFsp {
// Consider the decimal point.
ci.ColumnLength++
}
} else if types.IsString(fld.Column.GetType()) ||
fld.Column.GetType() == mysql.TypeEnum || fld.Column.GetType() == mysql.TypeSet { // issue #18870
// Fix issue #4540.
// The flen is a hint, not a precise value, so most client will not use the value.
// But we found in rare MySQL client, like Navicat for MySQL(version before 12) will truncate
// the `show create table` result. To fix this case, we must use a large enough flen to prevent
// the truncation, in MySQL, it will multiply bytes length by a multiple based on character set.
// For examples:
// * latin, the multiple is 1
// * gb2312, the multiple is 2
// * Utf-8, the multiple is 3
// * utf8mb4, the multiple is 4
// We used to check non-string types to avoid the truncation problem in some MySQL
// client such as Navicat. Now we only allow string type enter this branch.
charsetDesc, err := charset.GetCharsetInfo(fld.Column.GetCharset())
if err != nil {
ci.ColumnLength *= 4
} else {
ci.ColumnLength *= uint32(charsetDesc.Maxlen)
}
}
} else {
// FIX https://github.com/pingcap/tidb/issues/60503
// MySQL will always output a usable length
// while we cannot output the same length, we can give a default flen
// it is not placed before two branches above, because default flen here
// is already large enough, multiply or anything else may overflow them
clen, _ := mysql.GetDefaultFieldLengthAndDecimal(fld.Column.GetType())
ci.ColumnLength = uint32(clen)
}
if fld.Column.GetDecimal() == types.UnspecifiedLength {
if fld.Column.GetType() == mysql.TypeDuration {
ci.Decimal = uint8(types.DefaultFsp)
} else {
ci.Decimal = mysql.NotFixedDec
}
} else {
ci.Decimal = uint8(fld.Column.GetDecimal())
}
// Keep things compatible for old clients.
// Refer to mysql-server/sql/protocol.cc send_result_set_metadata()
if ci.Type == mysql.TypeVarchar {
ci.Type = mysql.TypeVarString
}
return
}