Skip to content

Commit 9bc508f

Browse files
authored
Enum to varchar (#963)
* v1.1.0 * WIP: copying AUTO_INCREMENT value to ghost table Initial commit: towards setting up a test suite Signed-off-by: Shlomi Noach <[email protected]> * greping for 'expect_table_structure' content * Adding simple test for 'expect_table_structure' scenario * adding tests for AUTO_INCREMENT value after row deletes. Should initially fail * clear event beforehand * parsing AUTO_INCREMENT from alter query, reading AUTO_INCREMENT from original table, applying AUTO_INCREMENT value onto ghost table if applicable and user has not specified AUTO_INCREMENT in alter statement * support GetUint64 Signed-off-by: Shlomi Noach <[email protected]> * minor update to test Signed-off-by: Shlomi Noach <[email protected]> * adding test for user defined AUTO_INCREMENT statement * Generated column as part of UNIQUE (or PRIMARY) KEY Signed-off-by: Shlomi Noach <[email protected]> * skip analysis of generated column data type in unique key Signed-off-by: Shlomi Noach <[email protected]> * All MySQL DBs limited to max 3 concurrent/idle connections Signed-off-by: Shlomi Noach <[email protected]> * hooks: reporting GH_OST_ETA_SECONDS. ETA stored as part of migration context Signed-off-by: Shlomi Noach <[email protected]> * GH_OST_ETA_NANOSECONDS Signed-off-by: Shlomi Noach <[email protected]> * N/A denoted by negative value Signed-off-by: Shlomi Noach <[email protected]> * ETAUnknown constant Signed-off-by: Shlomi Noach <[email protected]> * Convering enum to varchar Signed-off-by: Shlomi Noach <[email protected]> * test: not null Signed-off-by: Shlomi Noach <[email protected]> * first attempt at setting enum-to-string right Signed-off-by: Shlomi Noach <[email protected]> * fix insert query Signed-off-by: Shlomi Noach <[email protected]> * store enum values, use when populating Signed-off-by: Shlomi Noach <[email protected]> * apply EnumValues to mapped column Signed-off-by: Shlomi Noach <[email protected]> * fix compilation error Signed-off-by: Shlomi Noach <[email protected]> * gofmt Signed-off-by: Shlomi Noach <[email protected]>
1 parent f19f101 commit 9bc508f

File tree

7 files changed

+82
-7
lines changed

7 files changed

+82
-7
lines changed

go/logic/inspect.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ func (this *Inspector) inspectOriginalAndGhostTables() (err error) {
187187
if column.Name == mappedColumn.Name && column.Type == sql.DateTimeColumnType && mappedColumn.Type == sql.TimestampColumnType {
188188
this.migrationContext.MappedSharedColumns.SetConvertDatetimeToTimestamp(column.Name, this.migrationContext.ApplierTimeZone)
189189
}
190+
if column.Name == mappedColumn.Name && column.Type == sql.EnumColumnType && mappedColumn.Charset != "" {
191+
this.migrationContext.MappedSharedColumns.SetEnumToTextConversion(column.Name)
192+
this.migrationContext.MappedSharedColumns.SetEnumValues(column.Name, column.EnumValues)
193+
}
190194
}
191195

192196
for _, column := range this.migrationContext.UniqueKey.Columns.Columns() {
@@ -590,6 +594,7 @@ func (this *Inspector) applyColumnTypes(databaseName, tableName string, columnsL
590594
}
591595
if strings.HasPrefix(columnType, "enum") {
592596
column.Type = sql.EnumColumnType
597+
column.EnumValues = sql.ParseEnumValues(m.GetString("COLUMN_TYPE"))
593598
}
594599
if strings.HasPrefix(columnType, "binary") {
595600
column.Type = sql.BinaryColumnType

go/sql/builder.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ func buildColumnsPreparedValues(columns *ColumnList) []string {
3838
var token string
3939
if column.timezoneConversion != nil {
4040
token = fmt.Sprintf("convert_tz(?, '%s', '%s')", column.timezoneConversion.ToTimezone, "+00:00")
41+
} else if column.enumToTextConversion {
42+
token = fmt.Sprintf("ELT(?, %s)", column.EnumValues)
4143
} else if column.Type == JSONColumnType {
4244
token = "convert(? using utf8mb4)"
4345
} else {
@@ -108,6 +110,8 @@ func BuildSetPreparedClause(columns *ColumnList) (result string, err error) {
108110
var setToken string
109111
if column.timezoneConversion != nil {
110112
setToken = fmt.Sprintf("%s=convert_tz(?, '%s', '%s')", EscapeName(column.Name), column.timezoneConversion.ToTimezone, "+00:00")
113+
} else if column.enumToTextConversion {
114+
setToken = fmt.Sprintf("%s=ELT(?, %s)", EscapeName(column.Name), column.EnumValues)
111115
} else if column.Type == JSONColumnType {
112116
setToken = fmt.Sprintf("%s=convert(? using utf8mb4)", EscapeName(column.Name))
113117
} else {

go/sql/parser.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var (
3333
// ALTER TABLE tbl something
3434
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)\s+(.*$)`),
3535
}
36+
enumValuesRegexp = regexp.MustCompile("^enum[(](.*)[)]$")
3637
)
3738

3839
type AlterTableParser struct {
@@ -205,3 +206,10 @@ func (this *AlterTableParser) HasExplicitTable() bool {
205206
func (this *AlterTableParser) GetAlterStatementOptions() string {
206207
return this.alterStatementOptions
207208
}
209+
210+
func ParseEnumValues(enumColumnType string) string {
211+
if submatch := enumValuesRegexp.FindStringSubmatch(enumColumnType); len(submatch) > 0 {
212+
return submatch[1]
213+
}
214+
return enumColumnType
215+
}

go/sql/parser_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,21 @@ func TestParseAlterStatementExplicitTable(t *testing.T) {
322322
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b", "add index idx(i)"}))
323323
}
324324
}
325+
326+
func TestParseEnumValues(t *testing.T) {
327+
{
328+
s := "enum('red','green','blue','orange')"
329+
values := ParseEnumValues(s)
330+
test.S(t).ExpectEquals(values, "'red','green','blue','orange'")
331+
}
332+
{
333+
s := "('red','green','blue','orange')"
334+
values := ParseEnumValues(s)
335+
test.S(t).ExpectEquals(values, "('red','green','blue','orange')")
336+
}
337+
{
338+
s := "zzz"
339+
values := ParseEnumValues(s)
340+
test.S(t).ExpectEquals(values, "zzz")
341+
}
342+
}

go/sql/types.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,16 @@ type TimezoneConversion struct {
3333
}
3434

3535
type Column struct {
36-
Name string
37-
IsUnsigned bool
38-
Charset string
39-
Type ColumnType
40-
36+
Name string
37+
IsUnsigned bool
38+
Charset string
39+
Type ColumnType
40+
EnumValues string
41+
timezoneConversion *TimezoneConversion
42+
enumToTextConversion bool
4143
// add Octet length for binary type, fix bytes with suffix "00" get clipped in mysql binlog.
4244
// https://github.com/github/gh-ost/issues/909
43-
BinaryOctetLength uint
44-
timezoneConversion *TimezoneConversion
45+
BinaryOctetLength uint
4546
}
4647

4748
func (this *Column) convertArg(arg interface{}, isUniqueKeyColumn bool) interface{} {
@@ -198,6 +199,18 @@ func (this *ColumnList) HasTimezoneConversion(columnName string) bool {
198199
return this.GetColumn(columnName).timezoneConversion != nil
199200
}
200201

202+
func (this *ColumnList) SetEnumToTextConversion(columnName string) {
203+
this.GetColumn(columnName).enumToTextConversion = true
204+
}
205+
206+
func (this *ColumnList) IsEnumToTextConversion(columnName string) bool {
207+
return this.GetColumn(columnName).enumToTextConversion
208+
}
209+
210+
func (this *ColumnList) SetEnumValues(columnName string, enumValues string) {
211+
this.GetColumn(columnName).EnumValues = enumValues
212+
}
213+
201214
func (this *ColumnList) String() string {
202215
return strings.Join(this.Names(), ",")
203216
}

localtests/enum-to-varchar/create.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
drop table if exists gh_ost_test;
2+
create table gh_ost_test (
3+
id int auto_increment,
4+
i int not null,
5+
e enum('red', 'green', 'blue', 'orange') null default null collate 'utf8_bin',
6+
primary key(id)
7+
) auto_increment=1;
8+
9+
insert into gh_ost_test values (null, 7, 'red');
10+
11+
drop event if exists gh_ost_test;
12+
delimiter ;;
13+
create event gh_ost_test
14+
on schedule every 1 second
15+
starts current_timestamp
16+
ends current_timestamp + interval 60 second
17+
on completion not preserve
18+
enable
19+
do
20+
begin
21+
insert into gh_ost_test values (null, 11, 'red');
22+
insert into gh_ost_test values (null, 13, 'green');
23+
insert into gh_ost_test values (null, 17, 'blue');
24+
set @last_insert_id := last_insert_id();
25+
update gh_ost_test set e='orange' where id = @last_insert_id;
26+
end ;;

localtests/enum-to-varchar/extra_args

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--alter="change e e varchar(32) not null default ''"

0 commit comments

Comments
 (0)