Skip to content

Commit c70031d

Browse files
authored
schemadiff small internal refactor: formalizing column charset/collation (#16239)
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
1 parent 8685b9e commit c70031d

File tree

2 files changed

+82
-96
lines changed

2 files changed

+82
-96
lines changed

go/vt/schemadiff/column.go

Lines changed: 70 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,66 @@ func NewModifyColumnDiffByDefinition(definition *sqlparser.ColumnDefinition) *Mo
7171
}
7272

7373
type ColumnDefinitionEntity struct {
74-
columnDefinition *sqlparser.ColumnDefinition
74+
columnDefinition *sqlparser.ColumnDefinition
75+
tableCharsetCollate *charsetCollate
76+
Env *Environment
7577
}
7678

77-
func NewColumnDefinitionEntity(c *sqlparser.ColumnDefinition) *ColumnDefinitionEntity {
78-
return &ColumnDefinitionEntity{columnDefinition: c}
79+
func NewColumnDefinitionEntity(env *Environment, c *sqlparser.ColumnDefinition, tableCharsetCollate *charsetCollate) *ColumnDefinitionEntity {
80+
return &ColumnDefinitionEntity{
81+
columnDefinition: c,
82+
tableCharsetCollate: tableCharsetCollate,
83+
Env: env,
84+
}
85+
}
86+
87+
func (c *ColumnDefinitionEntity) Clone() *ColumnDefinitionEntity {
88+
clone := &ColumnDefinitionEntity{
89+
columnDefinition: sqlparser.Clone(c.columnDefinition),
90+
tableCharsetCollate: c.tableCharsetCollate,
91+
Env: c.Env,
92+
}
93+
return clone
94+
}
95+
96+
// SetExplicitCharsetCollate enriches this column definition with collation and charset. Those may be
97+
// already present, or perhaps just one of them is present (in which case we use the one to populate the other),
98+
// or both might be missing, in which case we use the table's charset/collation.
99+
func (c *ColumnDefinitionEntity) SetExplicitCharsetCollate() error {
100+
if !c.IsTextual() {
101+
return nil
102+
}
103+
// We will now denormalize the columns charset & collate as needed (if empty, populate from table.)
104+
// Normalizing _this_ column definition:
105+
if c.columnDefinition.Type.Charset.Name != "" && c.columnDefinition.Type.Options.Collate == "" {
106+
// Charset defined without collation. Assign the default collation for that charset.
107+
collation := c.Env.CollationEnv().DefaultCollationForCharset(c.columnDefinition.Type.Charset.Name)
108+
if collation == collations.Unknown {
109+
return &UnknownColumnCharsetCollationError{Column: c.columnDefinition.Name.String(), Charset: c.tableCharsetCollate.charset}
110+
}
111+
c.columnDefinition.Type.Options.Collate = c.Env.CollationEnv().LookupName(collation)
112+
}
113+
if c.columnDefinition.Type.Charset.Name == "" && c.columnDefinition.Type.Options.Collate != "" {
114+
// Column has explicit collation but no charset. We can infer the charset from the collation.
115+
collationID := c.Env.CollationEnv().LookupByName(c.columnDefinition.Type.Options.Collate)
116+
charset := c.Env.CollationEnv().LookupCharsetName(collationID)
117+
if charset == "" {
118+
return &UnknownColumnCollationCharsetError{Column: c.columnDefinition.Name.String(), Collation: c.columnDefinition.Type.Options.Collate}
119+
}
120+
c.columnDefinition.Type.Charset.Name = charset
121+
}
122+
if c.columnDefinition.Type.Charset.Name == "" {
123+
// Still nothing? Assign the table's charset/collation.
124+
c.columnDefinition.Type.Charset.Name = c.tableCharsetCollate.charset
125+
if c.columnDefinition.Type.Options.Collate = c.tableCharsetCollate.collate; c.columnDefinition.Type.Options.Collate == "" {
126+
collation := c.Env.CollationEnv().DefaultCollationForCharset(c.tableCharsetCollate.charset)
127+
if collation == collations.Unknown {
128+
return &UnknownColumnCharsetCollationError{Column: c.columnDefinition.Name.String(), Charset: c.tableCharsetCollate.charset}
129+
}
130+
c.columnDefinition.Type.Options.Collate = c.Env.CollationEnv().LookupName(collation)
131+
}
132+
}
133+
return nil
79134
}
80135

81136
// ColumnDiff compares this table statement with another table statement, and sees what it takes to
@@ -98,100 +153,22 @@ func (c *ColumnDefinitionEntity) ColumnDiff(
98153
env *Environment,
99154
tableName string,
100155
other *ColumnDefinitionEntity,
101-
t1cc *charsetCollate,
102-
t2cc *charsetCollate,
103156
hints *DiffHints,
104157
) (*ModifyColumnDiff, error) {
158+
cClone := c // not real clone yet
159+
otherClone := other // not real clone yet
105160
if c.IsTextual() || other.IsTextual() {
106-
// We will now denormalize the columns charset & collate as needed (if empty, populate from table.)
107-
// Normalizing _this_ column definition:
108-
if c.columnDefinition.Type.Charset.Name != "" && c.columnDefinition.Type.Options.Collate == "" {
109-
// Charset defined without collation. Assign the default collation for that charset.
110-
collation := env.CollationEnv().DefaultCollationForCharset(c.columnDefinition.Type.Charset.Name)
111-
if collation == collations.Unknown {
112-
return nil, &UnknownColumnCharsetCollationError{Column: c.columnDefinition.Name.String(), Charset: t1cc.charset}
113-
}
114-
defer func() {
115-
c.columnDefinition.Type.Options.Collate = ""
116-
}()
117-
c.columnDefinition.Type.Options.Collate = env.CollationEnv().LookupName(collation)
118-
}
119-
if c.columnDefinition.Type.Charset.Name == "" && c.columnDefinition.Type.Options.Collate != "" {
120-
// Column has explicit collation but no charset. We can infer the charset from the collation.
121-
collationID := env.CollationEnv().LookupByName(c.columnDefinition.Type.Options.Collate)
122-
charset := env.CollationEnv().LookupCharsetName(collationID)
123-
if charset == "" {
124-
return nil, &UnknownColumnCollationCharsetError{Column: c.columnDefinition.Name.String(), Collation: c.columnDefinition.Type.Options.Collate}
125-
}
126-
defer func() {
127-
c.columnDefinition.Type.Charset.Name = ""
128-
}()
129-
c.columnDefinition.Type.Charset.Name = charset
130-
}
131-
if c.columnDefinition.Type.Charset.Name == "" {
132-
// Still nothing? Assign the table's charset/collation.
133-
defer func() {
134-
c.columnDefinition.Type.Charset.Name = ""
135-
c.columnDefinition.Type.Options.Collate = ""
136-
}()
137-
c.columnDefinition.Type.Charset.Name = t1cc.charset
138-
if c.columnDefinition.Type.Options.Collate == "" {
139-
defer func() {
140-
c.columnDefinition.Type.Options.Collate = ""
141-
}()
142-
c.columnDefinition.Type.Options.Collate = t1cc.collate
143-
}
144-
if c.columnDefinition.Type.Options.Collate = t1cc.collate; c.columnDefinition.Type.Options.Collate == "" {
145-
collation := env.CollationEnv().DefaultCollationForCharset(t1cc.charset)
146-
if collation == collations.Unknown {
147-
return nil, &UnknownColumnCharsetCollationError{Column: c.columnDefinition.Name.String(), Charset: t1cc.charset}
148-
}
149-
c.columnDefinition.Type.Options.Collate = env.CollationEnv().LookupName(collation)
150-
}
161+
cClone = c.Clone()
162+
if err := cClone.SetExplicitCharsetCollate(); err != nil {
163+
return nil, err
151164
}
152-
// Normalizing _the other_ column definition:
153-
if other.columnDefinition.Type.Charset.Name != "" && other.columnDefinition.Type.Options.Collate == "" {
154-
// Charset defined without collation. Assign the default collation for that charset.
155-
collation := env.CollationEnv().DefaultCollationForCharset(other.columnDefinition.Type.Charset.Name)
156-
if collation == collations.Unknown {
157-
return nil, &UnknownColumnCharsetCollationError{Column: other.columnDefinition.Name.String(), Charset: t2cc.charset}
158-
}
159-
defer func() {
160-
other.columnDefinition.Type.Options.Collate = ""
161-
}()
162-
other.columnDefinition.Type.Options.Collate = env.CollationEnv().LookupName(collation)
163-
}
164-
if other.columnDefinition.Type.Charset.Name == "" && other.columnDefinition.Type.Options.Collate != "" {
165-
// Column has explicit collation but no charset. We can infer the charset from the collation.
166-
collationID := env.CollationEnv().LookupByName(other.columnDefinition.Type.Options.Collate)
167-
charset := env.CollationEnv().LookupCharsetName(collationID)
168-
if charset == "" {
169-
return nil, &UnknownColumnCollationCharsetError{Column: other.columnDefinition.Name.String(), Collation: other.columnDefinition.Type.Options.Collate}
170-
}
171-
defer func() {
172-
other.columnDefinition.Type.Charset.Name = ""
173-
}()
174-
other.columnDefinition.Type.Charset.Name = charset
175-
}
176-
177-
if other.columnDefinition.Type.Charset.Name == "" {
178-
// Still nothing? Assign the table's charset/collation.
179-
defer func() {
180-
other.columnDefinition.Type.Charset.Name = ""
181-
other.columnDefinition.Type.Options.Collate = ""
182-
}()
183-
other.columnDefinition.Type.Charset.Name = t2cc.charset
184-
if other.columnDefinition.Type.Options.Collate = t2cc.collate; other.columnDefinition.Type.Options.Collate == "" {
185-
collation := env.CollationEnv().DefaultCollationForCharset(t2cc.charset)
186-
if collation == collations.Unknown {
187-
return nil, &UnknownColumnCharsetCollationError{Column: other.columnDefinition.Name.String(), Charset: t2cc.charset}
188-
}
189-
other.columnDefinition.Type.Options.Collate = env.CollationEnv().LookupName(collation)
190-
}
165+
otherClone = other.Clone()
166+
if err := otherClone.SetExplicitCharsetCollate(); err != nil {
167+
return nil, err
191168
}
192169
}
193170

194-
if sqlparser.Equals.RefOfColumnDefinition(c.columnDefinition, other.columnDefinition) {
171+
if sqlparser.Equals.RefOfColumnDefinition(cClone.columnDefinition, otherClone.columnDefinition) {
195172
return nil, nil
196173
}
197174

@@ -204,11 +181,11 @@ func (c *ColumnDefinitionEntity) ColumnDiff(
204181
}
205182
switch hints.EnumReorderStrategy {
206183
case EnumReorderStrategyReject:
207-
otherEnumValuesMap := getEnumValuesMap(other.columnDefinition.Type.EnumValues)
208-
for ordinal, enumValue := range c.columnDefinition.Type.EnumValues {
184+
otherEnumValuesMap := getEnumValuesMap(otherClone.columnDefinition.Type.EnumValues)
185+
for ordinal, enumValue := range cClone.columnDefinition.Type.EnumValues {
209186
if otherOrdinal, ok := otherEnumValuesMap[enumValue]; ok {
210187
if ordinal != otherOrdinal {
211-
return nil, &EnumValueOrdinalChangedError{Table: tableName, Column: c.columnDefinition.Name.String(), Value: enumValue, Ordinal: ordinal, NewOrdinal: otherOrdinal}
188+
return nil, &EnumValueOrdinalChangedError{Table: tableName, Column: cClone.columnDefinition.Name.String(), Value: enumValue, Ordinal: ordinal, NewOrdinal: otherOrdinal}
212189
}
213190
}
214191
}

go/vt/schemadiff/table.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,15 @@ func NewCreateTableEntity(env *Environment, c *sqlparser.CreateTable) (*CreateTa
454454
return entity, nil
455455
}
456456

457+
func (c *CreateTableEntity) ColumnDefinitionEntities() []*ColumnDefinitionEntity {
458+
cc := getTableCharsetCollate(c.Env, &c.CreateTable.TableSpec.Options)
459+
entities := make([]*ColumnDefinitionEntity, len(c.CreateTable.TableSpec.Columns))
460+
for i := range c.CreateTable.TableSpec.Columns {
461+
entities[i] = NewColumnDefinitionEntity(c.Env, c.CreateTable.TableSpec.Columns[i], cc)
462+
}
463+
return entities
464+
}
465+
457466
// normalize cleans up the table definition:
458467
// - setting names to all keys
459468
// - table option case (upper/lower/special)
@@ -1731,11 +1740,11 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable,
17311740
t2ColName := t2Col.Name.Lowered()
17321741
// we know that column exists in both tables
17331742
t1Col := t1ColumnsMap[t2ColName]
1734-
t1ColEntity := NewColumnDefinitionEntity(t1Col.col)
1735-
t2ColEntity := NewColumnDefinitionEntity(t2Col)
1743+
t1ColEntity := NewColumnDefinitionEntity(c.Env, t1Col.col, t1cc)
1744+
t2ColEntity := NewColumnDefinitionEntity(c.Env, t2Col, t2cc)
17361745

17371746
// check diff between before/after columns:
1738-
modifyColumnDiff, err := t1ColEntity.ColumnDiff(c.Env, c.Name(), t2ColEntity, t1cc, t2cc, hints)
1747+
modifyColumnDiff, err := t1ColEntity.ColumnDiff(c.Env, c.Name(), t2ColEntity, hints)
17391748
if err != nil {
17401749
return err
17411750
}

0 commit comments

Comments
 (0)