1 Star 0 Fork 0

DaMeng/Atlas

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
diff.go 22.08 KB
一键复制 编辑 原始数据 按行查看 历史
DaMeng 提交于 2024-10-24 15:32 +08:00 . :art:修改mod名称
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
// Copyright 2021-present The Atlas Authors. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package mysql
import (
"encoding/hex"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
"gitee.com/damengde/atlas/sql/internal/sqlx"
"gitee.com/damengde/atlas/sql/schema"
)
// DefaultDiff provides basic diffing capabilities for MySQL dialects.
// Note, it is recommended to call Open, create a new Driver and use its
// Differ when a database connection is available.
var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{conn: noConn}}
// A diff provides a MySQL implementation for sqlx.DiffDriver.
type diff struct {
*conn
// charset to collation mapping.
// See, internal directory.
ch2co, co2ch struct {
sync.Once
v map[string]string
err error
}
}
// SchemaAttrDiff returns a changeset for migrating schema attributes from one state to the other.
func (d *diff) SchemaAttrDiff(from, to *schema.Schema) []schema.Change {
var (
topAttr []schema.Attr
changes []schema.Change
)
if from.Realm != nil {
topAttr = from.Realm.Attrs
}
// Charset change.
if change := d.charsetChange(from.Attrs, topAttr, to.Attrs); change != noChange {
changes = append(changes, change)
}
// Collation change.
if change := d.collationChange(from.Attrs, topAttr, to.Attrs); change != noChange {
changes = append(changes, change)
}
return changes
}
// SchemaObjectDiff returns a changeset for migrating schema objects from
// one state to the other.
func (*diff) SchemaObjectDiff(_, _ *schema.Schema) ([]schema.Change, error) {
return nil, nil
}
// TableAttrDiff returns a changeset for migrating table attributes from one state to the other.
func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
var changes []schema.Change
if change := d.autoIncChange(from.Attrs, to.Attrs); change != noChange {
changes = append(changes, change)
}
if change := sqlx.CommentDiff(from.Attrs, to.Attrs); change != nil {
changes = append(changes, change)
}
if change := d.charsetChange(from.Attrs, from.Schema.Attrs, to.Attrs); change != noChange {
changes = append(changes, change)
}
if change := d.collationChange(from.Attrs, from.Schema.Attrs, to.Attrs); change != noChange {
changes = append(changes, change)
}
if change := d.engineChange(from.Attrs, to.Attrs); change != noChange {
changes = append(changes, change)
}
if !d.SupportsCheck() && sqlx.Has(to.Attrs, &schema.Check{}) {
return nil, fmt.Errorf("version %q does not support CHECK constraints", d.V)
}
// For MariaDB, we skip JSON CHECK constraints that were created by the databases,
// or by Atlas for older versions. These CHECK constraints (inlined on the columns)
// also cannot be dropped using "DROP CONSTRAINTS", but can be modified and dropped
// using "MODIFY COLUMN".
var checks []schema.Change
for _, c := range sqlx.CheckDiff(from, to, func(c1, c2 *schema.Check) bool {
return enforced(c1.Attrs) == enforced(c2.Attrs)
}) {
drop, ok := c.(*schema.DropCheck)
if !ok || !strings.HasPrefix(drop.C.Expr, "json_valid") {
checks = append(checks, c)
continue
}
// Generated CHECK have the form of "json_valid(`<column>`)"
// and named as the column.
if _, ok := to.Column(drop.C.Name); !ok {
checks = append(checks, c)
}
}
return append(changes, checks...), nil
}
// ColumnChange returns the schema changes (if any) for migrating one column to the other.
func (d *diff) ColumnChange(fromT *schema.Table, from, to *schema.Column) (schema.ChangeKind, error) {
change := sqlx.CommentChange(from.Attrs, to.Attrs)
if from.Type.Null != to.Type.Null {
change |= schema.ChangeNull
}
changed, err := d.typeChanged(from, to)
if err != nil {
return schema.NoChange, err
}
if changed {
change |= schema.ChangeType
}
if changed, err = d.defaultChanged(from, to); err != nil {
return schema.NoChange, err
}
if changed {
change |= schema.ChangeDefault
}
if changed, err = d.generatedChanged(from, to); err != nil {
return schema.NoChange, err
}
if changed {
change |= schema.ChangeGenerated
}
if changed, err = d.columnCharsetChanged(fromT, from, to); err != nil {
return schema.NoChange, err
}
if changed {
change |= schema.ChangeCharset
}
if changed, err = d.columnCollateChanged(fromT, from, to); err != nil {
return schema.NoChange, err
}
if changed {
change |= schema.ChangeCollate
}
return change, nil
}
// IsGeneratedIndexName reports if the index name was generated by the database.
func (d *diff) IsGeneratedIndexName(_ *schema.Table, idx *schema.Index) bool {
// Auto-generated index names for functional/expression indexes. See.
// mysql-server/sql/sql_table.cc#add_functional_index_to_create_list
const f = "functional_index"
switch {
case d.SupportsIndexExpr() && idx.Name == f:
return true
case d.SupportsIndexExpr() && strings.HasPrefix(idx.Name+"_", f):
i, err := strconv.ParseInt(strings.TrimLeft(idx.Name, idx.Name+"_"), 10, 64)
return err == nil && i > 1
case len(idx.Parts) == 0 || idx.Parts[0].C == nil:
return false
}
// Unnamed INDEX or UNIQUE constraints are named by
// the first index-part (as column or part of it).
// For example, "c", "c_2", "c_3", etc.
switch name := idx.Parts[0].C.Name; {
case idx.Name == name:
return true
case strings.HasPrefix(idx.Name, name+"_"):
i, err := strconv.ParseInt(strings.TrimPrefix(idx.Name, name+"_"), 10, 64)
return err == nil && i > 1
default:
return false
}
}
// IndexAttrChanged reports if the index attributes were changed.
func (*diff) IndexAttrChanged(from, to []schema.Attr) bool {
if indexType(from).T != indexType(to).T {
return true
}
var (
fromP, toP IndexParser
fromHas, toHas = sqlx.Has(from, &fromP), sqlx.Has(to, &toP)
)
return fromHas != toHas || (fromHas && fromP.P != toP.P)
}
// IndexPartAttrChanged reports if the index-part attributes (collation or prefix) were changed.
func (*diff) IndexPartAttrChanged(fromI, toI *schema.Index, i int) bool {
var s1, s2 SubPart
return sqlx.Has(fromI.Parts[i].Attrs, &s1) != sqlx.Has(toI.Parts[i].Attrs, &s2) || s1.Len != s2.Len
}
// ReferenceChanged reports if the foreign key referential action was changed.
func (*diff) ReferenceChanged(from, to schema.ReferenceOption) bool {
// According to MySQL docs, foreign key constraints are checked
// immediately, so NO ACTION is the same as RESTRICT. Specifying
// RESTRICT (or NO ACTION) is the same as omitting the ON DELETE
// or ON UPDATE clause.
if from == "" || from == schema.Restrict {
from = schema.NoAction
}
if to == "" || to == schema.Restrict {
to = schema.NoAction
}
return from != to
}
// Normalize implements the sqlx.Normalizer interface.
func (d *diff) Normalize(from, to *schema.Table) error {
indexes := make([]*schema.Index, 0, len(from.Indexes))
for _, idx := range from.Indexes {
// MySQL requires that foreign key columns be indexed; Therefore, if the child
// table is defined on non-indexed columns, an index is automatically created
// to satisfy the constraint.
// Therefore, if no such key was defined on the desired state, the diff will
// recommend dropping it on migration. Therefore, we fix it by dropping it from
// the current state manually.
if _, ok := to.Index(idx.Name); ok || !keySupportsFK(from, idx) {
indexes = append(indexes, idx)
}
}
from.Indexes = indexes
// In case the "current" state was inspected (or loaded) with the collation/charset attributes,
// but there are not found on the desired state, detect what are the default settings for the
// desired state of the table (based on database default) to avoid proposing unnecessary changes.
if sqlx.Has(from.Attrs, &schema.Collation{}) {
if err := d.defaultCollate(&to.Attrs); err != nil {
return err
}
}
if sqlx.Has(from.Attrs, &schema.Charset{}) {
if err := d.defaultCharset(&to.Attrs); err != nil {
return err
}
}
return nil
}
// FindTable implements the DiffDriver.TableFinder method in order to provide
// tables lookup that respect the "lower_case_table_names" system variable.
func (d *diff) FindTable(s *schema.Schema, name string) (*schema.Table, error) {
switch d.lcnames {
// In mode 0: tables are stored as specified, and comparisons are case-sensitive.
case 0:
t, ok := s.Table(name)
if !ok {
return nil, &schema.NotExistError{Err: fmt.Errorf("table %q was not found", name)}
}
return t, nil
// In mode 1: the table are stored in lowercase, but they are still
// returned on inspection, because comparisons are not case-sensitive.
// In mode 2: the tables are stored as given but compared in lowercase.
// This option is not supported by Linux-based systems.
case 1, 2:
var matches []*schema.Table
for _, t := range s.Tables {
if strings.ToLower(name) == strings.ToLower(t.Name) {
matches = append(matches, t)
}
}
switch n := len(matches); n {
case 0:
return nil, &schema.NotExistError{Err: fmt.Errorf("table %q was not found", name)}
case 1:
return matches[0], nil
default:
return nil, fmt.Errorf("%d matches found for table %q", n, name)
}
default:
return nil, fmt.Errorf("unsupported 'lower_case_table_names' mode: %d", d.lcnames)
}
}
// collationChange returns the schema change for migrating the collation if
// it was changed, and it is not the default attribute inherited from its parent.
func (*diff) collationChange(from, top, to []schema.Attr) schema.Change {
var fromC, topC, toC schema.Collation
switch fromHas, topHas, toHas := sqlx.Has(from, &fromC), sqlx.Has(top, &topC), sqlx.Has(to, &toC); {
case !fromHas && !toHas:
case !fromHas:
return &schema.AddAttr{
A: &toC,
}
case !toHas:
// There is no way to DROP a COLLATE that was configured on the table,
// and it is not the default. Therefore, we use ModifyAttr and give it
// the inherited (and default) collation from schema or server.
if topHas && fromC.V != topC.V {
return &schema.ModifyAttr{
From: &fromC,
To: &topC,
}
}
case fromC.V != toC.V:
return &schema.ModifyAttr{
From: &fromC,
To: &toC,
}
}
return noChange
}
// engineChange returns the schema change for migrating the table engine in case
// it was changed.
func (*diff) engineChange(from, to []schema.Attr) schema.Change {
var fromE, toE Engine
switch fromHas, toHas := sqlx.Has(from, &fromE), sqlx.Has(to, &toE); {
// Both engines are defined but different.
case fromHas && toHas && strings.ToLower(fromE.V) != strings.ToLower(toE.V):
return &schema.ModifyAttr{
From: &fromE,
To: &toE,
}
// If the engine attribute has been removed from the desired state (e.g., HCL), and the current state
// is not the default, we change the engine to InnoDB (the default for MySQL, unless configured otherwise).
case fromHas && !toHas && !fromE.Default && strings.ToLower(fromE.V) != strings.ToLower(EngineInnoDB):
return &schema.ModifyAttr{
From: &fromE,
To: &Engine{V: EngineInnoDB, Default: true},
}
// In case the engine attribute was added to the desired state (e.g., HCL)
// and it is not the default, we modify the engine to the desired value.
case !fromHas && toHas && !toE.Default && strings.ToLower(fromE.V) != strings.ToLower(EngineInnoDB):
return &schema.ModifyAttr{
From: &Engine{V: EngineInnoDB, Default: true},
To: &toE,
}
}
return noChange
}
// charsetChange returns the schema change for migrating the collation if
// it was changed, and it is not the default attribute inherited from its parent.
func (*diff) charsetChange(from, top, to []schema.Attr) schema.Change {
var fromC, topC, toC schema.Charset
switch fromHas, topHas, toHas := sqlx.Has(from, &fromC), sqlx.Has(top, &topC), sqlx.Has(to, &toC); {
case !fromHas && !toHas:
case !fromHas:
return &schema.AddAttr{
A: &toC,
}
case !toHas:
// There is no way to DROP a CHARSET that was configured on the table,
// and it is not the default. Therefore, we use ModifyAttr and give it
// the inherited (and default) collation from schema or server.
if topHas && fromC.V != topC.V {
return &schema.ModifyAttr{
From: &fromC,
To: &topC,
}
}
case fromC.V != toC.V:
return &schema.ModifyAttr{
From: &fromC,
To: &toC,
}
}
return noChange
}
// columnCharsetChange indicates if there is a change to the column charset.
func (d *diff) columnCharsetChanged(fromT *schema.Table, from, to *schema.Column) (bool, error) {
if err := d.defaultCharset(&to.Attrs); err != nil {
return false, err
}
var (
fromC, topC, toC schema.Charset
fromHas, topHas, toHas = sqlx.Has(from.Attrs, &fromC), sqlx.Has(fromT.Attrs, &topC), sqlx.Has(to.Attrs, &toC)
)
// Column was updated with custom CHARSET that was dropped.
// Hence, we should revert to the one defined on the table.
return fromHas && !toHas && topHas && fromC.V != topC.V ||
// Custom CHARSET was added to the column. Hence,
// Does not match the one defined in the table.
!fromHas && toHas && topHas && toC.V != topC.V ||
// CHARSET was explicitly changed.
fromHas && toHas && fromC.V != toC.V, nil
}
// columnCollateChanged indicates if there is a change to the column charset.
func (d *diff) columnCollateChanged(fromT *schema.Table, from, to *schema.Column) (bool, error) {
if err := d.defaultCollate(&to.Attrs); err != nil {
return false, err
}
var (
fromC, topC, toC schema.Collation
fromHas, topHas, toHas = sqlx.Has(from.Attrs, &fromC), sqlx.Has(fromT.Attrs, &topC), sqlx.Has(to.Attrs, &toC)
)
// Column was updated with custom COLLATE that was dropped.
// Hence, we should revert to the one defined on the table.
return fromHas && !toHas && topHas && fromC.V != topC.V ||
// Custom COLLATE was added to the column. Hence,
// Does not match the one defined in the table.
!fromHas && toHas && topHas && toC.V != topC.V ||
// COLLATE was explicitly changed.
fromHas && toHas && fromC.V != toC.V, nil
}
// autoIncChange returns the schema change for changing the AUTO_INCREMENT
// attribute in case it is not the default.
func (*diff) autoIncChange(from, to []schema.Attr) schema.Change {
var fromA, toA AutoIncrement
switch fromHas, toHas := sqlx.Has(from, &fromA), sqlx.Has(to, &toA); {
// Ignore if the AUTO_INCREMENT attribute was dropped from the desired schema.
case fromHas && !toHas:
// The AUTO_INCREMENT exists in the desired schema, and may not exist in the inspected one.
// This can happen because older versions of MySQL (< 8.0) stored the AUTO_INCREMENT counter
// in main memory (not persistent), and the value is reset on process restart for empty tables.
case toA.V > 1 && toA.V > fromA.V:
// Suggest a diff only if the desired value is greater than the inspected one,
// because this attribute cannot be maintained in users schema and used to set
// up only the initial value.
return &schema.ModifyAttr{
From: &fromA,
To: &toA,
}
}
return noChange
}
// indexType returns the index type from its attribute.
// The default type is BTREE if no type was specified.
func indexType(attr []schema.Attr) *IndexType {
t := &IndexType{T: IndexTypeBTree}
if sqlx.Has(attr, t) {
t.T = strings.ToUpper(t.T)
}
return t
}
// enforced returns the ENFORCED attribute for the CHECK
// constraint. A CHECK is ENFORCED if not state otherwise.
func enforced(attr []schema.Attr) bool {
if e := (Enforced{}); sqlx.Has(attr, &e) {
return e.V
}
return true
}
// noChange describes a zero change.
var noChange struct{ schema.Change }
func (d *diff) typeChanged(from, to *schema.Column) (bool, error) {
fromT, toT := from.Type.Type, to.Type.Type
if fromT == nil || toT == nil {
return false, fmt.Errorf("mysql: missing type information for column %q", from.Name)
}
if reflect.TypeOf(fromT) != reflect.TypeOf(toT) {
return true, nil
}
var changed bool
switch fromT := fromT.(type) {
case *BitType, *schema.BinaryType, *schema.BoolType, *schema.DecimalType, *schema.FloatType,
*schema.JSONType, *schema.StringType, *schema.SpatialType, *schema.TimeType, *schema.UUIDType:
ft, err := FormatType(fromT)
if err != nil {
return false, err
}
tt, err := FormatType(toT)
if err != nil {
return false, err
}
changed = ft != tt
case *schema.EnumType:
toT := toT.(*schema.EnumType)
changed = !sqlx.ValuesEqual(fromT.Values, toT.Values)
case *schema.IntegerType:
toT := toT.(*schema.IntegerType)
// MySQL v8.0.19 dropped both display-width
// and zerofill from the information schema.
if d.SupportsDisplayWidth() {
ft, _, _, err := parseColumn(fromT.T)
if err != nil {
return false, err
}
tt, _, _, err := parseColumn(toT.T)
if err != nil {
return false, err
}
fromT.T, toT.T = ft[0], tt[0]
}
changed = fromT.T != toT.T || fromT.Unsigned != toT.Unsigned
case *SetType:
toT := toT.(*SetType)
changed = !sqlx.ValuesEqual(fromT.Values, toT.Values)
default:
return false, &sqlx.UnsupportedTypeError{Type: fromT}
}
return changed, nil
}
// defaultChanged reports if the default value of a column was changed.
func (d *diff) defaultChanged(from, to *schema.Column) (bool, error) {
d1, ok1 := sqlx.DefaultValue(from)
d2, ok2 := sqlx.DefaultValue(to)
if ok1 != ok2 {
return true, nil
}
if d1 == d2 {
return false, nil
}
switch from.Type.Type.(type) {
case *schema.BinaryType:
a, err1 := binValue(d1)
b, err2 := binValue(d2)
if err1 != nil || err2 != nil {
return true, nil
}
return !equalsStringValues(a, b), nil
case *schema.BoolType:
a, err1 := boolValue(d1)
b, err2 := boolValue(d2)
if err1 == nil && err2 == nil {
return a != b, nil
}
return false, nil
case *schema.IntegerType:
return !d.equalIntValues(d1, d2), nil
case *schema.FloatType, *schema.DecimalType:
return !d.equalFloatValues(d1, d2), nil
case *schema.EnumType, *SetType, *schema.StringType:
return !equalsStringValues(d1, d2), nil
case *schema.TimeType:
x1 := strings.ToLower(strings.Trim(d1, "' ()"))
x2 := strings.ToLower(strings.Trim(d2, "' ()"))
return !equalsStringValues(x1, x2), nil
default:
x1 := strings.Trim(d1, "'")
x2 := strings.Trim(d2, "'")
return x1 != x2, nil
}
}
// generatedChanged reports if the generated expression of a column was changed.
func (*diff) generatedChanged(from, to *schema.Column) (bool, error) {
var (
fromX, toX schema.GeneratedExpr
fromHas, toHas = sqlx.Has(from.Attrs, &fromX), sqlx.Has(to.Attrs, &toX)
)
if !fromHas && !toHas || fromHas && toHas && sqlx.MayWrap(fromX.Expr) == sqlx.MayWrap(toX.Expr) && storedOrVirtual(fromX.Type) == storedOrVirtual(toX.Type) {
return false, nil
}
// Checking validity of the change is done
// by the planner (checkChangeGenerated).
return true, nil
}
// equalIntValues report if the 2 int default values are ~equal.
// Note that default expression are not supported atm.
func (d *diff) equalIntValues(x1, x2 string) bool {
x1 = strings.ToLower(strings.Trim(x1, "' "))
x2 = strings.ToLower(strings.Trim(x2, "' "))
if x1 == x2 {
return true
}
d1, err := strconv.ParseInt(x1, 10, 64)
if err != nil {
// Numbers are rounded down to their nearest integer.
f, err := strconv.ParseFloat(x1, 64)
if err != nil {
return false
}
d1 = int64(f)
}
d2, err := strconv.ParseInt(x2, 10, 64)
if err != nil {
// Numbers are rounded down to their nearest integer.
f, err := strconv.ParseFloat(x2, 64)
if err != nil {
return false
}
d2 = int64(f)
}
return d1 == d2
}
// equalFloatValues report if the 2 float default values are ~equal.
// Note that default expression are not supported atm.
func (d *diff) equalFloatValues(x1, x2 string) bool {
x1 = strings.ToLower(strings.Trim(x1, "' "))
x2 = strings.ToLower(strings.Trim(x2, "' "))
if x1 == x2 {
return true
}
d1, err := strconv.ParseFloat(x1, 64)
if err != nil {
return false
}
d2, err := strconv.ParseFloat(x2, 64)
if err != nil {
return false
}
return d1 == d2
}
// equalsStringValues report if the 2 string default values are
// equal after dropping their quotes.
func equalsStringValues(x1, x2 string) bool {
a, err1 := sqlx.Unquote(x1)
b, err2 := sqlx.Unquote(x2)
return a == b && err1 == nil && err2 == nil
}
// boolValue returns the MySQL boolean value from the given string (if it is known).
func boolValue(x string) (bool, error) {
switch x {
case "1", "'1'", "TRUE", "true":
return true, nil
case "0", "'0'", "FALSE", "false":
return false, nil
default:
return false, fmt.Errorf("mysql: unknown value: %q", x)
}
}
// binValue returns the MySQL binary value from the given string (if it is known).
func binValue(x string) (string, error) {
if !isHex(x) {
return x, nil
}
d, err := hex.DecodeString(x[2:])
if err != nil {
return x, err
}
return string(d), nil
}
// keySupportsFK reports if the index key was created automatically by MySQL
// to support the constraint. See sql/sql_table.cc#find_fk_supporting_key.
func keySupportsFK(t *schema.Table, idx *schema.Index) bool {
if _, ok := t.ForeignKey(idx.Name); ok {
return true
}
search:
for _, fk := range t.ForeignKeys {
if len(fk.Columns) != len(idx.Parts) {
continue
}
for i, c := range fk.Columns {
if idx.Parts[i].C == nil || idx.Parts[i].C.Name != c.Name {
continue search
}
}
return true
}
return false
}
// defaultCollate appends the default COLLATE to the attributes in case a
// custom character-set was defined for the element and the COLLATE was not.
func (d *diff) defaultCollate(attrs *[]schema.Attr) error {
var charset schema.Charset
if !sqlx.Has(*attrs, &charset) || sqlx.Has(*attrs, &schema.Collation{}) {
return nil
}
d.ch2co.Do(func() {
d.ch2co.v, d.ch2co.err = d.CharsetToCollate(d.ExecQuerier)
})
if d.ch2co.err != nil {
return d.ch2co.err
}
if v, ok := d.ch2co.v[charset.V]; ok {
// If charset is known, use its default collation.
schema.ReplaceOrAppend(attrs, &schema.Collation{V: v})
}
return nil
}
// defaultCharset appends the default CHARSET to the attributes in case a
// custom collation was defined for the element and the CHARSET was not.
func (d *diff) defaultCharset(attrs *[]schema.Attr) error {
var collate schema.Collation
if !sqlx.Has(*attrs, &collate) || sqlx.Has(*attrs, &schema.Charset{}) {
return nil
}
d.co2ch.Do(func() {
d.co2ch.v, d.co2ch.err = d.CollateToCharset(d.ExecQuerier)
})
if d.co2ch.err != nil {
return d.co2ch.err
}
if v, ok := d.co2ch.v[collate.V]; ok {
// If collation is known, use its default charset.
schema.ReplaceOrAppend(attrs, &schema.Charset{V: v})
}
return nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/damengde/atlas.git
git@gitee.com:damengde/atlas.git
damengde
atlas
Atlas
v0.0.3

搜索帮助