Merge pull request #404 from github/drop-add-same-column
Drop, then add same column
This commit is contained in:
commit
d757539d3c
@ -181,6 +181,7 @@ type MigrationContext struct {
|
|||||||
UniqueKey *sql.UniqueKey
|
UniqueKey *sql.UniqueKey
|
||||||
SharedColumns *sql.ColumnList
|
SharedColumns *sql.ColumnList
|
||||||
ColumnRenameMap map[string]string
|
ColumnRenameMap map[string]string
|
||||||
|
DroppedColumnsMap map[string]bool
|
||||||
MappedSharedColumns *sql.ColumnList
|
MappedSharedColumns *sql.ColumnList
|
||||||
MigrationRangeMinValues *sql.ColumnValues
|
MigrationRangeMinValues *sql.ColumnValues
|
||||||
MigrationRangeMaxValues *sql.ColumnValues
|
MigrationRangeMaxValues *sql.ColumnValues
|
||||||
|
@ -662,7 +662,14 @@ func (this *Inspector) getSharedColumns(originalColumns, ghostColumns *sql.Colum
|
|||||||
}
|
}
|
||||||
sharedColumnNames := []string{}
|
sharedColumnNames := []string{}
|
||||||
for _, originalColumn := range originalColumns.Names() {
|
for _, originalColumn := range originalColumns.Names() {
|
||||||
|
isSharedColumn := false
|
||||||
if columnsInGhost[originalColumn] || columnsInGhost[columnRenameMap[originalColumn]] {
|
if columnsInGhost[originalColumn] || columnsInGhost[columnRenameMap[originalColumn]] {
|
||||||
|
isSharedColumn = true
|
||||||
|
}
|
||||||
|
if this.migrationContext.DroppedColumnsMap[originalColumn] {
|
||||||
|
isSharedColumn = false
|
||||||
|
}
|
||||||
|
if isSharedColumn {
|
||||||
sharedColumnNames = append(sharedColumnNames, originalColumn)
|
sharedColumnNames = append(sharedColumnNames, originalColumn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,6 +248,7 @@ func (this *Migrator) validateStatement() (err error) {
|
|||||||
}
|
}
|
||||||
log.Infof("Alter statement has column(s) renamed. gh-ost finds the following renames: %v; --approve-renamed-columns is given and so migration proceeds.", this.parser.GetNonTrivialRenames())
|
log.Infof("Alter statement has column(s) renamed. gh-ost finds the following renames: %v; --approve-renamed-columns is given and so migration proceeds.", this.parser.GetNonTrivialRenames())
|
||||||
}
|
}
|
||||||
|
this.migrationContext.DroppedColumnsMap = this.parser.DroppedColumnsMap()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,15 +14,18 @@ import (
|
|||||||
var (
|
var (
|
||||||
sanitizeQuotesRegexp = regexp.MustCompile("('[^']*')")
|
sanitizeQuotesRegexp = regexp.MustCompile("('[^']*')")
|
||||||
renameColumnRegexp = regexp.MustCompile(`(?i)\bchange\s+(column\s+|)([\S]+)\s+([\S]+)\s+`)
|
renameColumnRegexp = regexp.MustCompile(`(?i)\bchange\s+(column\s+|)([\S]+)\s+([\S]+)\s+`)
|
||||||
|
dropColumnRegexp = regexp.MustCompile(`(?i)\bdrop\s+(column\s+|)([\S]+)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
columnRenameMap map[string]string
|
columnRenameMap map[string]string
|
||||||
|
droppedColumns map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParser() *Parser {
|
func NewParser() *Parser {
|
||||||
return &Parser{
|
return &Parser{
|
||||||
columnRenameMap: make(map[string]string),
|
columnRenameMap: make(map[string]string),
|
||||||
|
droppedColumns: make(map[string]bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,10 +62,9 @@ func (this *Parser) sanitizeQuotesFromAlterStatement(alterStatement string) (str
|
|||||||
return strippedStatement
|
return strippedStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Parser) ParseAlterStatement(alterStatement string) (err error) {
|
func (this *Parser) parseAlterToken(alterToken string) (err error) {
|
||||||
alterTokens, _ := this.tokenizeAlterStatement(alterStatement)
|
{
|
||||||
for _, alterToken := range alterTokens {
|
// rename
|
||||||
alterToken = this.sanitizeQuotesFromAlterStatement(alterToken)
|
|
||||||
allStringSubmatch := renameColumnRegexp.FindAllStringSubmatch(alterToken, -1)
|
allStringSubmatch := renameColumnRegexp.FindAllStringSubmatch(alterToken, -1)
|
||||||
for _, submatch := range allStringSubmatch {
|
for _, submatch := range allStringSubmatch {
|
||||||
if unquoted, err := strconv.Unquote(submatch[2]); err == nil {
|
if unquoted, err := strconv.Unquote(submatch[2]); err == nil {
|
||||||
@ -71,10 +73,28 @@ func (this *Parser) ParseAlterStatement(alterStatement string) (err error) {
|
|||||||
if unquoted, err := strconv.Unquote(submatch[3]); err == nil {
|
if unquoted, err := strconv.Unquote(submatch[3]); err == nil {
|
||||||
submatch[3] = unquoted
|
submatch[3] = unquoted
|
||||||
}
|
}
|
||||||
|
|
||||||
this.columnRenameMap[submatch[2]] = submatch[3]
|
this.columnRenameMap[submatch[2]] = submatch[3]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// drop
|
||||||
|
allStringSubmatch := dropColumnRegexp.FindAllStringSubmatch(alterToken, -1)
|
||||||
|
for _, submatch := range allStringSubmatch {
|
||||||
|
if unquoted, err := strconv.Unquote(submatch[2]); err == nil {
|
||||||
|
submatch[2] = unquoted
|
||||||
|
}
|
||||||
|
this.droppedColumns[submatch[2]] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Parser) ParseAlterStatement(alterStatement string) (err error) {
|
||||||
|
alterTokens, _ := this.tokenizeAlterStatement(alterStatement)
|
||||||
|
for _, alterToken := range alterTokens {
|
||||||
|
alterToken = this.sanitizeQuotesFromAlterStatement(alterToken)
|
||||||
|
this.parseAlterToken(alterToken)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,3 +111,7 @@ func (this *Parser) GetNonTrivialRenames() map[string]string {
|
|||||||
func (this *Parser) HasNonTrivialRenames() bool {
|
func (this *Parser) HasNonTrivialRenames() bool {
|
||||||
return len(this.GetNonTrivialRenames()) > 0
|
return len(this.GetNonTrivialRenames()) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Parser) DroppedColumnsMap() map[string]bool {
|
||||||
|
return this.droppedColumns
|
||||||
|
}
|
||||||
|
@ -120,3 +120,42 @@ func TestSanitizeQuotesFromAlterStatement(t *testing.T) {
|
|||||||
test.S(t).ExpectEquals(strippedStatement, "change column i int ''")
|
test.S(t).ExpectEquals(strippedStatement, "change column i int ''")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAlterStatementDroppedColumns(t *testing.T) {
|
||||||
|
|
||||||
|
{
|
||||||
|
parser := NewParser()
|
||||||
|
statement := "drop column b"
|
||||||
|
err := parser.ParseAlterStatement(statement)
|
||||||
|
test.S(t).ExpectNil(err)
|
||||||
|
test.S(t).ExpectEquals(len(parser.droppedColumns), 1)
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["b"])
|
||||||
|
}
|
||||||
|
{
|
||||||
|
parser := NewParser()
|
||||||
|
statement := "drop column b, drop key c_idx, drop column `d`"
|
||||||
|
err := parser.ParseAlterStatement(statement)
|
||||||
|
test.S(t).ExpectNil(err)
|
||||||
|
test.S(t).ExpectEquals(len(parser.droppedColumns), 2)
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["b"])
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["d"])
|
||||||
|
}
|
||||||
|
{
|
||||||
|
parser := NewParser()
|
||||||
|
statement := "drop column b, drop key c_idx, drop column `d`, drop `e`, drop primary key, drop foreign key fk_1"
|
||||||
|
err := parser.ParseAlterStatement(statement)
|
||||||
|
test.S(t).ExpectNil(err)
|
||||||
|
test.S(t).ExpectEquals(len(parser.droppedColumns), 3)
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["b"])
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["d"])
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["e"])
|
||||||
|
}
|
||||||
|
{
|
||||||
|
parser := NewParser()
|
||||||
|
statement := "drop column b, drop bad statement, add column i int"
|
||||||
|
err := parser.ParseAlterStatement(statement)
|
||||||
|
test.S(t).ExpectNil(err)
|
||||||
|
test.S(t).ExpectEquals(len(parser.droppedColumns), 1)
|
||||||
|
test.S(t).ExpectTrue(parser.droppedColumns["b"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
30
localtests/drop-null-add-not-null/create.sql
Normal file
30
localtests/drop-null-add-not-null/create.sql
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
drop table if exists gh_ost_test;
|
||||||
|
create table gh_ost_test (
|
||||||
|
id int auto_increment,
|
||||||
|
c1 int null,
|
||||||
|
c2 int not null,
|
||||||
|
primary key (id)
|
||||||
|
) auto_increment=1;
|
||||||
|
|
||||||
|
insert into gh_ost_test values (null, null, 17);
|
||||||
|
insert into gh_ost_test values (null, null, 19);
|
||||||
|
|
||||||
|
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 ignore into gh_ost_test values (101, 11, 23);
|
||||||
|
insert ignore into gh_ost_test values (102, 13, 23);
|
||||||
|
insert into gh_ost_test values (null, 17, 23);
|
||||||
|
insert into gh_ost_test values (null, null, 29);
|
||||||
|
set @last_insert_id := last_insert_id();
|
||||||
|
-- update gh_ost_test set c2=c2+@last_insert_id where id=@last_insert_id order by id desc limit 1;
|
||||||
|
delete from gh_ost_test where id=1;
|
||||||
|
delete from gh_ost_test where c1=13; -- id=2
|
||||||
|
end ;;
|
1
localtests/drop-null-add-not-null/extra_args
Normal file
1
localtests/drop-null-add-not-null/extra_args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--alter="drop column c1, add column c1 int not null default 47"
|
1
localtests/drop-null-add-not-null/ghost_columns
Normal file
1
localtests/drop-null-add-not-null/ghost_columns
Normal file
@ -0,0 +1 @@
|
|||||||
|
c2
|
1
localtests/drop-null-add-not-null/orig_columns
Normal file
1
localtests/drop-null-add-not-null/orig_columns
Normal file
@ -0,0 +1 @@
|
|||||||
|
c2
|
Loading…
Reference in New Issue
Block a user