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 <2607934+shlomi-noach@users.noreply.github.com> * 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 <2607934+shlomi-noach@users.noreply.github.com> * minor update to test Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * adding test for user defined AUTO_INCREMENT statement * Generated column as part of UNIQUE (or PRIMARY) KEY Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * skip analysis of generated column data type in unique key Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * All MySQL DBs limited to max 3 concurrent/idle connections Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * hooks: reporting GH_OST_ETA_SECONDS. ETA stored as part of migration context Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * GH_OST_ETA_NANOSECONDS Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * N/A denoted by negative value Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * ETAUnknown constant Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * Convering enum to varchar Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * test: not null Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * first attempt at setting enum-to-string right Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * fix insert query Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * store enum values, use when populating Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * apply EnumValues to mapped column Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * fix compilation error Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * gofmt Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
This commit is contained in:
parent
f19f101610
commit
9bc508f068
@ -187,6 +187,10 @@ func (this *Inspector) inspectOriginalAndGhostTables() (err error) {
|
|||||||
if column.Name == mappedColumn.Name && column.Type == sql.DateTimeColumnType && mappedColumn.Type == sql.TimestampColumnType {
|
if column.Name == mappedColumn.Name && column.Type == sql.DateTimeColumnType && mappedColumn.Type == sql.TimestampColumnType {
|
||||||
this.migrationContext.MappedSharedColumns.SetConvertDatetimeToTimestamp(column.Name, this.migrationContext.ApplierTimeZone)
|
this.migrationContext.MappedSharedColumns.SetConvertDatetimeToTimestamp(column.Name, this.migrationContext.ApplierTimeZone)
|
||||||
}
|
}
|
||||||
|
if column.Name == mappedColumn.Name && column.Type == sql.EnumColumnType && mappedColumn.Charset != "" {
|
||||||
|
this.migrationContext.MappedSharedColumns.SetEnumToTextConversion(column.Name)
|
||||||
|
this.migrationContext.MappedSharedColumns.SetEnumValues(column.Name, column.EnumValues)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, column := range this.migrationContext.UniqueKey.Columns.Columns() {
|
for _, column := range this.migrationContext.UniqueKey.Columns.Columns() {
|
||||||
@ -590,6 +594,7 @@ func (this *Inspector) applyColumnTypes(databaseName, tableName string, columnsL
|
|||||||
}
|
}
|
||||||
if strings.HasPrefix(columnType, "enum") {
|
if strings.HasPrefix(columnType, "enum") {
|
||||||
column.Type = sql.EnumColumnType
|
column.Type = sql.EnumColumnType
|
||||||
|
column.EnumValues = sql.ParseEnumValues(m.GetString("COLUMN_TYPE"))
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(columnType, "binary") {
|
if strings.HasPrefix(columnType, "binary") {
|
||||||
column.Type = sql.BinaryColumnType
|
column.Type = sql.BinaryColumnType
|
||||||
|
@ -38,6 +38,8 @@ func buildColumnsPreparedValues(columns *ColumnList) []string {
|
|||||||
var token string
|
var token string
|
||||||
if column.timezoneConversion != nil {
|
if column.timezoneConversion != nil {
|
||||||
token = fmt.Sprintf("convert_tz(?, '%s', '%s')", column.timezoneConversion.ToTimezone, "+00:00")
|
token = fmt.Sprintf("convert_tz(?, '%s', '%s')", column.timezoneConversion.ToTimezone, "+00:00")
|
||||||
|
} else if column.enumToTextConversion {
|
||||||
|
token = fmt.Sprintf("ELT(?, %s)", column.EnumValues)
|
||||||
} else if column.Type == JSONColumnType {
|
} else if column.Type == JSONColumnType {
|
||||||
token = "convert(? using utf8mb4)"
|
token = "convert(? using utf8mb4)"
|
||||||
} else {
|
} else {
|
||||||
@ -108,6 +110,8 @@ func BuildSetPreparedClause(columns *ColumnList) (result string, err error) {
|
|||||||
var setToken string
|
var setToken string
|
||||||
if column.timezoneConversion != nil {
|
if column.timezoneConversion != nil {
|
||||||
setToken = fmt.Sprintf("%s=convert_tz(?, '%s', '%s')", EscapeName(column.Name), column.timezoneConversion.ToTimezone, "+00:00")
|
setToken = fmt.Sprintf("%s=convert_tz(?, '%s', '%s')", EscapeName(column.Name), column.timezoneConversion.ToTimezone, "+00:00")
|
||||||
|
} else if column.enumToTextConversion {
|
||||||
|
setToken = fmt.Sprintf("%s=ELT(?, %s)", EscapeName(column.Name), column.EnumValues)
|
||||||
} else if column.Type == JSONColumnType {
|
} else if column.Type == JSONColumnType {
|
||||||
setToken = fmt.Sprintf("%s=convert(? using utf8mb4)", EscapeName(column.Name))
|
setToken = fmt.Sprintf("%s=convert(? using utf8mb4)", EscapeName(column.Name))
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +33,7 @@ var (
|
|||||||
// ALTER TABLE tbl something
|
// ALTER TABLE tbl something
|
||||||
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)\s+(.*$)`),
|
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)\s+(.*$)`),
|
||||||
}
|
}
|
||||||
|
enumValuesRegexp = regexp.MustCompile("^enum[(](.*)[)]$")
|
||||||
)
|
)
|
||||||
|
|
||||||
type AlterTableParser struct {
|
type AlterTableParser struct {
|
||||||
@ -205,3 +206,10 @@ func (this *AlterTableParser) HasExplicitTable() bool {
|
|||||||
func (this *AlterTableParser) GetAlterStatementOptions() string {
|
func (this *AlterTableParser) GetAlterStatementOptions() string {
|
||||||
return this.alterStatementOptions
|
return this.alterStatementOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseEnumValues(enumColumnType string) string {
|
||||||
|
if submatch := enumValuesRegexp.FindStringSubmatch(enumColumnType); len(submatch) > 0 {
|
||||||
|
return submatch[1]
|
||||||
|
}
|
||||||
|
return enumColumnType
|
||||||
|
}
|
||||||
|
@ -322,3 +322,21 @@ func TestParseAlterStatementExplicitTable(t *testing.T) {
|
|||||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b", "add index idx(i)"}))
|
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b", "add index idx(i)"}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseEnumValues(t *testing.T) {
|
||||||
|
{
|
||||||
|
s := "enum('red','green','blue','orange')"
|
||||||
|
values := ParseEnumValues(s)
|
||||||
|
test.S(t).ExpectEquals(values, "'red','green','blue','orange'")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
s := "('red','green','blue','orange')"
|
||||||
|
values := ParseEnumValues(s)
|
||||||
|
test.S(t).ExpectEquals(values, "('red','green','blue','orange')")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
s := "zzz"
|
||||||
|
values := ParseEnumValues(s)
|
||||||
|
test.S(t).ExpectEquals(values, "zzz")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,11 +37,12 @@ type Column struct {
|
|||||||
IsUnsigned bool
|
IsUnsigned bool
|
||||||
Charset string
|
Charset string
|
||||||
Type ColumnType
|
Type ColumnType
|
||||||
|
EnumValues string
|
||||||
|
timezoneConversion *TimezoneConversion
|
||||||
|
enumToTextConversion bool
|
||||||
// add Octet length for binary type, fix bytes with suffix "00" get clipped in mysql binlog.
|
// add Octet length for binary type, fix bytes with suffix "00" get clipped in mysql binlog.
|
||||||
// https://github.com/github/gh-ost/issues/909
|
// https://github.com/github/gh-ost/issues/909
|
||||||
BinaryOctetLength uint
|
BinaryOctetLength uint
|
||||||
timezoneConversion *TimezoneConversion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Column) convertArg(arg interface{}, isUniqueKeyColumn bool) interface{} {
|
func (this *Column) convertArg(arg interface{}, isUniqueKeyColumn bool) interface{} {
|
||||||
@ -198,6 +199,18 @@ func (this *ColumnList) HasTimezoneConversion(columnName string) bool {
|
|||||||
return this.GetColumn(columnName).timezoneConversion != nil
|
return this.GetColumn(columnName).timezoneConversion != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ColumnList) SetEnumToTextConversion(columnName string) {
|
||||||
|
this.GetColumn(columnName).enumToTextConversion = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ColumnList) IsEnumToTextConversion(columnName string) bool {
|
||||||
|
return this.GetColumn(columnName).enumToTextConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ColumnList) SetEnumValues(columnName string, enumValues string) {
|
||||||
|
this.GetColumn(columnName).EnumValues = enumValues
|
||||||
|
}
|
||||||
|
|
||||||
func (this *ColumnList) String() string {
|
func (this *ColumnList) String() string {
|
||||||
return strings.Join(this.Names(), ",")
|
return strings.Join(this.Names(), ",")
|
||||||
}
|
}
|
||||||
|
26
localtests/enum-to-varchar/create.sql
Normal file
26
localtests/enum-to-varchar/create.sql
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
drop table if exists gh_ost_test;
|
||||||
|
create table gh_ost_test (
|
||||||
|
id int auto_increment,
|
||||||
|
i int not null,
|
||||||
|
e enum('red', 'green', 'blue', 'orange') null default null collate 'utf8_bin',
|
||||||
|
primary key(id)
|
||||||
|
) auto_increment=1;
|
||||||
|
|
||||||
|
insert into gh_ost_test values (null, 7, 'red');
|
||||||
|
|
||||||
|
drop event if exists gh_ost_test;
|
||||||
|
delimiter ;;
|
||||||
|
create event gh_ost_test
|
||||||
|
on schedule every 1 second
|
||||||
|
starts current_timestamp
|
||||||
|
ends current_timestamp + interval 60 second
|
||||||
|
on completion not preserve
|
||||||
|
enable
|
||||||
|
do
|
||||||
|
begin
|
||||||
|
insert into gh_ost_test values (null, 11, 'red');
|
||||||
|
insert into gh_ost_test values (null, 13, 'green');
|
||||||
|
insert into gh_ost_test values (null, 17, 'blue');
|
||||||
|
set @last_insert_id := last_insert_id();
|
||||||
|
update gh_ost_test set e='orange' where id = @last_insert_id;
|
||||||
|
end ;;
|
1
localtests/enum-to-varchar/extra_args
Normal file
1
localtests/enum-to-varchar/extra_args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--alter="change e e varchar(32) not null default ''"
|
Loading…
Reference in New Issue
Block a user