*: Support the statement of "create table ... like" (#2707)

This commit is contained in:
Lynn
2017-02-22 23:57:04 +08:00
committed by Ewan Chou
parent f7c95f3f42
commit 61868f44dc
9 changed files with 130 additions and 3 deletions

View File

@ -397,6 +397,7 @@ type CreateTableStmt struct {
IfNotExists bool
Table *TableName
ReferTable *TableName
Cols []*ColumnDef
Constraints []*Constraint
Options []*TableOption
@ -414,6 +415,13 @@ func (n *CreateTableStmt) Accept(v Visitor) (Node, bool) {
return n, false
}
n.Table = node.(*TableName)
if n.ReferTable != nil {
node, ok = n.ReferTable.Accept(v)
if !ok {
return n, false
}
n.ReferTable = node.(*TableName)
}
for i, val := range n.Cols {
node, ok = val.Accept(v)
if !ok {

View File

@ -100,6 +100,7 @@ type DDL interface {
DropSchema(ctx context.Context, schema model.CIStr) error
CreateTable(ctx context.Context, ident ast.Ident, cols []*ast.ColumnDef,
constrs []*ast.Constraint, options []*ast.TableOption) error
CreateTableWithLike(ctx context.Context, ident, referIdent ast.Ident) error
DropTable(ctx context.Context, tableIdent ast.Ident) (err error)
CreateIndex(ctx context.Context, tableIdent ast.Ident, unique bool, indexName model.CIStr,
columnNames []*ast.IndexColName) error

View File

@ -571,6 +571,45 @@ func (d *ddl) buildTableInfo(tableName model.CIStr, cols []*table.Column, constr
return
}
func (d *ddl) CreateTableWithLike(ctx context.Context, ident, referIdent ast.Ident) error {
is := d.GetInformationSchema()
_, ok := is.SchemaByName(referIdent.Schema)
if !ok {
return infoschema.ErrTableNotExists.GenByArgs(referIdent.Schema, referIdent.Name)
}
referTbl, err := is.TableByName(referIdent.Schema, referIdent.Name)
if err != nil {
return infoschema.ErrTableNotExists.GenByArgs(referIdent.Schema, referIdent.Name)
}
schema, ok := is.SchemaByName(ident.Schema)
if !ok {
return infoschema.ErrDatabaseNotExists.GenByArgs(ident.Schema)
}
if is.TableExists(ident.Schema, ident.Name) {
return infoschema.ErrTableExists.GenByArgs(ident)
}
tblInfo := *referTbl.Meta()
tblInfo.Name = ident.Name
tblInfo.AutoIncID = 0
tblInfo.ForeignKeys = nil
tblInfo.ID, err = d.genGlobalID()
if err != nil {
return errors.Trace(err)
}
job := &model.Job{
SchemaID: schema.ID,
TableID: tblInfo.ID,
Type: model.ActionCreateTable,
BinlogInfo: &model.HistoryInfo{},
Args: []interface{}{tblInfo},
}
err = d.doDDLJob(ctx, job)
err = d.callHookOnChanged(err)
return errors.Trace(err)
}
func (d *ddl) CreateTable(ctx context.Context, ident ast.Ident, colDefs []*ast.ColumnDef,
constraints []*ast.Constraint, options []*ast.TableOption) (err error) {
is := d.GetInformationSchema()

View File

@ -894,6 +894,55 @@ func (s *testDBSuite) TestUpdateMultipleTable(c *C) {
tk.MustQuery("select * from t1").Check(testkit.Rows("8 1 9", "8 2 9"))
}
func (s *testDBSuite) TestCreateTableWithLike(c *C) {
defer testleak.AfterTest(c)
store, err := tidb.NewStore("memory://create_table_like")
c.Assert(err, IsNil)
s.tk = testkit.NewTestKit(c, store)
_, err = tidb.BootstrapSession(store)
c.Assert(err, IsNil)
// for the same database
s.tk.MustExec("use test")
s.tk.MustExec("create table tt(id int primary key)")
s.tk.MustExec("create table t (c1 int not null auto_increment, c2 int, constraint cc foreign key (c2) references tt(id), primary key(c1)) auto_increment = 10")
s.tk.MustExec("insert into t set c2=1")
s.tk.MustExec("create table t1 like test.t")
s.tk.MustExec("insert into t1 set c2=11")
s.tk.MustQuery("select * from t").Check(testkit.Rows("10 1"))
s.tk.MustQuery("select * from t1").Check(testkit.Rows("1 11"))
ctx := s.tk.Se.(context.Context)
is := sessionctx.GetDomain(ctx).InfoSchema()
tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1"))
c.Assert(err, IsNil)
tblInfo := tbl.Meta()
c.Assert(tblInfo.ForeignKeys, IsNil)
c.Assert(tblInfo.PKIsHandle, Equals, true)
col := tblInfo.Columns[0]
hasNotNull := tmysql.HasNotNullFlag(col.Flag)
c.Assert(hasNotNull, IsTrue)
// for different databases
s.tk.MustExec("create database test1")
s.tk.MustExec("use test1")
s.tk.MustExec("create table t1 like test.t")
s.tk.MustExec("insert into t1 set c2=11")
s.tk.MustQuery("select * from t1").Check(testkit.Rows("1 11"))
is = sessionctx.GetDomain(ctx).InfoSchema()
tbl, err = is.TableByName(model.NewCIStr("test1"), model.NewCIStr("t1"))
c.Assert(err, IsNil)
c.Assert(tbl.Meta().ForeignKeys, IsNil)
// for failure cases
failSQL := fmt.Sprintf("create table t1 like test_not_exist.t")
s.testErrorCode(c, failSQL, tmysql.ErrNoSuchTable)
failSQL = fmt.Sprintf("create table t1 like test.t_not_exist")
s.testErrorCode(c, failSQL, tmysql.ErrNoSuchTable)
failSQL = fmt.Sprintf("create table test_not_exis.t1 like test.t")
s.testErrorCode(c, failSQL, tmysql.ErrBadDB)
failSQL = fmt.Sprintf("create table t1 like test.t")
s.testErrorCode(c, failSQL, tmysql.ErrTableExists)
}
func (s *testDBSuite) TestTruncateTable(c *C) {
defer testleak.AfterTest(c)
store, err := tidb.NewStore("memory://truncate_table")

View File

@ -146,7 +146,13 @@ func (e *DDLExec) executeCreateDatabase(s *ast.CreateDatabaseStmt) error {
func (e *DDLExec) executeCreateTable(s *ast.CreateTableStmt) error {
ident := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}
err := sessionctx.GetDomain(e.ctx).DDL().CreateTable(e.ctx, ident, s.Cols, s.Constraints, s.Options)
var err error
if s.ReferTable == nil {
err = sessionctx.GetDomain(e.ctx).DDL().CreateTable(e.ctx, ident, s.Cols, s.Constraints, s.Options)
} else {
referIdent := ast.Ident{Schema: s.ReferTable.Schema, Name: s.ReferTable.Name}
err = sessionctx.GetDomain(e.ctx).DDL().CreateTableWithLike(e.ctx, ident, referIdent)
}
if terror.ErrorEqual(err, infoschema.ErrTableExists) {
if s.IfNotExists {
return nil

View File

@ -1610,6 +1610,14 @@ CreateTableStmt:
Options: $8.([]*ast.TableOption),
}
}
| "CREATE" "TABLE" IfNotExists TableName "LIKE" TableName
{
$$ = &ast.CreateTableStmt{
Table: $4.(*ast.TableName),
ReferTable: $6.(*ast.TableName),
IfNotExists: $3.(bool),
}
}
DefaultKwdOpt:
{}

View File

@ -1152,15 +1152,17 @@ func (s *testParserSuite) TestDDL(c *C) {
union_name varbinary(52) NOT NULL,
union_id int(11) DEFAULT '0',
PRIMARY KEY (union_name)) ENGINE=MyISAM DEFAULT CHARSET=binary;`, true},
// create table with multiple index options
// Create table with multiple index options.
{`create table t (c int, index ci (c) USING BTREE COMMENT "123");`, true},
// for default value
{"CREATE TABLE sbtest (id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, k integer UNSIGNED DEFAULT '0' NOT NULL, c char(120) DEFAULT '' NOT NULL, pad char(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) )", true},
{"create table test (create_date TIMESTAMP NOT NULL COMMENT '创建日期 create date' DEFAULT now());", true},
{"create table ts (t int, v timestamp(3) default CURRENT_TIMESTAMP(3));", true},
// Create table with primary key name.
{"create table if not exists `t` (`id` int not null auto_increment comment '消息ID', primary key `pk_id` (`id`) );", true},
// Create table with like.
{"create table a like b", true},
{"create table if not exists a like b", true},
// for alter table
{"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED", true},

View File

@ -1445,6 +1445,13 @@ func (s *testPlanSuite) TestVisitInfo(c *C) {
{mysql.CreatePriv, "test", "t", ""},
},
},
{
sql: "create table t1 like t",
ans: []visitInfo{
{mysql.CreatePriv, "test", "t1", ""},
{mysql.SelectPriv, "test", "t", ""},
},
},
{
sql: "create database test",
ans: []visitInfo{

View File

@ -700,6 +700,13 @@ func (b *planBuilder) buildDDL(node ast.DDLNode) Plan {
db: v.Table.Schema.L,
table: v.Table.Name.L,
})
if v.ReferTable != nil {
b.visitInfo = append(b.visitInfo, visitInfo{
privilege: mysql.SelectPriv,
db: v.ReferTable.Schema.L,
table: v.ReferTable.Name.L,
})
}
case *ast.DropDatabaseStmt:
b.visitInfo = append(b.visitInfo, visitInfo{
privilege: mysql.DropPriv,