Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
The ddl-executor is a golang library that can parse and execute MySQL DDL statements.
The library maintains schema structures in memory, for examples: creates a new schema structure when a CREATE statement executed, modifys a schema structure when a ALTER statement executed.

# What can it be used for ?
# What can it be used for?
This library may be used for DDL analysis, binlog stream's schema tracking (like [`binlog_row_metadata=FULL`](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_row_metadata) in MySQL 8) and so on.

# Usage
Expand Down Expand Up @@ -45,7 +45,7 @@ This library use TiDB 's parser to parse MySQL statement to generate AST(abstrac
* ALTER DATABASE


# What statements it supports ?
# What statements does it support?
This library support 99% MySQL DDL statements.The ddl-executor can execute statements same as MySQL 5.7 identically, such as complicated statement like this:
```
# -----------------------------------------------
Expand Down Expand Up @@ -81,15 +81,15 @@ drop table t1;
```
> Those statements above come from MySQL' s test suit, and is part of our compatibility test cases.

# What statements it doesn't support ?
# What statements are not supported?
Some DDL statement that are infrequent:
* ALTER with 'convert charset': ALTER TABLE t1 CONVERT TO CHARACTER SET latin1;
* ALTER with 'order by': ALTER TABLE t1 add column new_col int, ORDER BY payoutid, bandid;
* DDL with geo types: ALTER TABLE t1 ADD b GEOMETRY, ADD c POINT, ADD SPATIAL INDEX(b);
* CREATE TABLE with 'SELECT' clause;
* Some others unfrequent statement we don't know now;

Those statements above will raise error when executing whit this library.
Those statements above will raise error when executing with this library.

# Compatibility tests
You can have a look on 'github.com/bytewatch/ddl-executor/compatibility_test', which is a cmd line tool to test compatibility between this library and MySQL.
Expand Down
72 changes: 37 additions & 35 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,49 +498,51 @@ func (o *Executor) enterDropIndexStmt(stmt *ast.DropIndexStmt) error {
}

func (o *Executor) enterRenameTableStmt(stmt *ast.RenameTableStmt) error {
oldDatabaseName := o.getSqlName(stmt.OldTable.Schema)
oldTableName := o.getSqlName(stmt.OldTable.Name)
newDatabaseName := o.getSqlName(stmt.NewTable.Schema)
newTableName := o.getSqlName(stmt.NewTable.Name)
for _, value := range stmt.TableToTables {
oldDatabaseName := o.getSqlName(value.OldTable.Schema)
oldTableName := o.getSqlName(value.OldTable.Name)
newDatabaseName := o.getSqlName(value.NewTable.Schema)
newTableName := o.getSqlName(value.NewTable.Name)

oldDatabaseDef, err := o.getSpecifiedOrDefaultDb(oldDatabaseName)
if err != nil {
return err
}
tableDef := oldDatabaseDef.cloneTable(oldTableName)
if tableDef == nil {
return ErrNoSuchTable.Gen(oldDatabaseDef.Name, oldTableName)
}

if newDatabaseName == oldDatabaseName && newTableName == oldTableName {
// Nothing to do
return nil
}

if newDatabaseName != oldDatabaseName {
// The new table name is in another database
newDatabaseDef, err := o.getSpecifiedOrDefaultDb(newDatabaseName)
oldDatabaseDef, err := o.getSpecifiedOrDefaultDb(oldDatabaseName)
if err != nil {
return err
}
tableDef := oldDatabaseDef.cloneTable(oldTableName)
if tableDef == nil {
return ErrNoSuchTable.Gen(oldDatabaseDef.Name, oldTableName)
}

if newDatabaseDef.findTable(newTableName) != nil {
return ErrTableExists.Gen(newTableName)
if newDatabaseName == oldDatabaseName && newTableName == oldTableName {
// Nothing to do
continue
}

oldDatabaseDef.dropTable(oldTableName)
tableDef.Name = newTableName
newDatabaseDef.setTable(newTableName, tableDef)
if newDatabaseName != oldDatabaseName {
// The new table name is in another database
newDatabaseDef, err := o.getSpecifiedOrDefaultDb(newDatabaseName)
if err != nil {
return err
}

} else {
// The new table name is still in the original database
if oldDatabaseDef.findTable(newTableName) != nil &&
newTableName != oldTableName {
return ErrTableExists.Gen(newTableName)
}
oldDatabaseDef.dropTable(oldTableName)
tableDef.Name = newTableName
oldDatabaseDef.setTable(newTableName, tableDef)
if newDatabaseDef.findTable(newTableName) != nil {
return ErrTableExists.Gen(newTableName)
}

oldDatabaseDef.dropTable(oldTableName)
tableDef.Name = newTableName
newDatabaseDef.setTable(newTableName, tableDef)

} else {
// The new table name is still in the original database
if oldDatabaseDef.findTable(newTableName) != nil &&
newTableName != oldTableName {
return ErrTableExists.Gen(newTableName)
}
oldDatabaseDef.dropTable(oldTableName)
tableDef.Name = newTableName
oldDatabaseDef.setTable(newTableName, tableDef)
}
}

return nil
Expand Down
100 changes: 85 additions & 15 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestCreateTable(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -181,7 +181,7 @@ func TestCreateTableWithLike(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -220,7 +220,7 @@ func TestAlterTableAddColumn(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -288,7 +288,7 @@ func TestAlterTableAddColumnWithPos(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -347,7 +347,7 @@ func TestAlterTableDropColumn(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -411,7 +411,7 @@ func TestAlterTableAddIndex(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -490,7 +490,7 @@ func TestAlterTableAddIndexLowerCase(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "ID",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -562,7 +562,7 @@ func TestAlterTableDropIndex(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_MUL,
Charset: "",
Expand Down Expand Up @@ -626,7 +626,7 @@ func TestAlterTableModifyColumn(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_NONE,
Charset: "",
Expand Down Expand Up @@ -694,7 +694,7 @@ func TestAlterTableModifyColumnWithPos(t *testing.T) {
})
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_NONE,
Charset: "",
Expand Down Expand Up @@ -734,7 +734,7 @@ func TestAlterTableChangeColumn(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_NONE,
Charset: "",
Expand Down Expand Up @@ -796,7 +796,7 @@ func TestAlterTableRenameTable(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_MUL,
Charset: "",
Expand Down Expand Up @@ -885,7 +885,7 @@ func TestCreateIndex(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_MUL,
Charset: "",
Expand Down Expand Up @@ -947,7 +947,7 @@ func TestRenameTable(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Expand Down Expand Up @@ -980,6 +980,76 @@ func TestRenameTable(t *testing.T) {

}

func TestRenameTableMulti(t *testing.T) {
var err error
expectedDef := &TableDef{
Name: "test2",
Database: "test",
Charset: "gbk",
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Unsigned: true,
Nullable: false,
})
expectedDefTest4 := &TableDef{
Name: "test4",
Database: "test",
Charset: "gbk",
}
expectedDefTest4.Columns = append(expectedDefTest4.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
InnerType: TypeLong,
Key: IndexType_PRI,
Charset: "",
Unsigned: true,
Nullable: false,
})
executor := NewExecutor(NewDefaultConfig())
err = executor.Exec(`
create database test;
create table test.test1(
id int unsigned auto_increment,
primary key (id)
) CHARACTER SET gbk;
create table test.test3(
id int(10) unsigned auto_increment,
primary key (id)
) CHARACTER SET gbk;`)
require.Nil(t, err)

err = executor.Exec(`
use test;
rename table test1 to test.test2, test3 to test4;`)
require.Nil(t, err)

tableDef, err := executor.GetTableDef("test", "test1")
// It should be failed because test.test1 doesn't exist
require.NotNil(t, err)

tableDef, err = executor.GetTableDef("test", "test3")
// It should be failed because test.test3 doesn't exist
require.NotNil(t, err)

tableDef, err = executor.GetTableDef("test", "test2")
require.Nil(t, err)

tableDef.Indices = nil
require.Equal(t, expectedDef, tableDef)

tableDef, err = executor.GetTableDef("test", "test4")
require.Nil(t, err)

tableDef.Indices = nil
require.Equal(t, expectedDefTest4, tableDef)

}

func TestLowerCaseTableNames(t *testing.T) {
var err error
executor := NewExecutor(NewDefaultConfig())
Expand Down Expand Up @@ -1030,7 +1100,7 @@ func TestDropIndex(t *testing.T) {
}
expectedDef.Columns = append(expectedDef.Columns, &ColumnDef{
Name: "id",
Type: "int(10) unsigned",
Type: "int(11) unsigned",
InnerType: TypeLong,
Key: IndexType_NONE,
Charset: "",
Expand Down