Skip to content

Commit

Permalink
planner: play replay load restore the table with foreign key with rig…
Browse files Browse the repository at this point in the history
  • Loading branch information
ti-chi-bot authored Oct 17, 2024
1 parent c371f8e commit df753d1
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 23 deletions.
32 changes: 22 additions & 10 deletions pkg/domain/plan_replayer_dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,35 @@ func (tne *tableNameExtractor) getTablesAndViews() (map[tableNamePair]struct{},
r[tablePair] = struct{}{}
}
// if the table has a foreign key, we need to add the referenced table to the list
tblInfo, err := tne.is.TableByName(model.NewCIStr(tablePair.DBName), model.NewCIStr(tablePair.TableName))
err := findFK(tne.is, tablePair.DBName, tablePair.TableName, r)
if err != nil {
return nil, err
}
for _, fk := range tblInfo.Meta().ForeignKeys {
key := tableNamePair{
DBName: fk.RefSchema.L,
TableName: fk.RefTable.L,
IsView: false,
}
r[key] = struct{}{}
}
}
return r, nil
}

func (tne *tableNameExtractor) Enter(in ast.Node) (ast.Node, bool) {
func findFK(is infoschema.InfoSchema, dbName, tableName string, tableMap map[tableNamePair]struct{}) error {
tblInfo, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName))
if err != nil {
return err
}
for _, fk := range tblInfo.Meta().ForeignKeys {
key := tableNamePair{
DBName: fk.RefSchema.L,
TableName: fk.RefTable.L,
IsView: false,
}
tableMap[key] = struct{}{}
err := findFK(is, key.DBName, key.TableName, tableMap)
if err != nil {
return err
}
}
return nil
}

func (*tableNameExtractor) Enter(in ast.Node) (ast.Node, bool) {
if _, ok := in.(*ast.TableName); ok {
return in, true
}
Expand Down
37 changes: 26 additions & 11 deletions pkg/executor/plan_replayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,17 +481,9 @@ func (e *PlanReplayerLoadInfo) Update(data []byte) error {
}

// build schema and table first
for _, zipFile := range z.File {
if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) {
continue
}
path := strings.Split(zipFile.Name, "/")
if len(path) == 2 && strings.Compare(path[0], "schema") == 0 && zipFile.Mode().IsRegular() {
err = createSchemaAndItems(e.Ctx, zipFile)
if err != nil {
return err
}
}
err = e.createTable(z)
if err != nil {
return err
}

// set tiflash replica if exists
Expand Down Expand Up @@ -529,3 +521,26 @@ func (e *PlanReplayerLoadInfo) Update(data []byte) error {
}
return nil
}

func (e *PlanReplayerLoadInfo) createTable(z *zip.Reader) error {
origin := e.Ctx.GetSessionVars().ForeignKeyChecks
// We need to disable foreign key check when we create schema and tables.
// because the order of creating schema and tables is not guaranteed.
e.Ctx.GetSessionVars().ForeignKeyChecks = false
defer func() {
e.Ctx.GetSessionVars().ForeignKeyChecks = origin
}()
for _, zipFile := range z.File {
if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) {
continue
}
path := strings.Split(zipFile.Name, "/")
if len(path) == 2 && strings.Compare(path[0], "schema") == 0 && zipFile.Mode().IsRegular() {
err := createSchemaAndItems(e.Ctx, zipFile)
if err != nil {
return err
}
}
}
return nil
}
57 changes: 55 additions & 2 deletions pkg/server/handler/optimizor/plan_replayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ func prepareData4PlanReplayer(t *testing.T, client *testserverclient.TestServerC
tk.MustExec("create database planReplayer")
tk.MustExec("use planReplayer")
tk.MustExec("create table t(a int)")
tk.MustExec("CREATE TABLE authors (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100) NOT NULL,email VARCHAR(100) UNIQUE NOT NULL);")
tk.MustExec("CREATE TABLE books (id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(200) NOT NULL,publication_date DATE NOT NULL,author_id INT,FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE);")
err = h.HandleDDLEvent(<-h.DDLEventCh())
require.NoError(t, err)
tk.MustExec("insert into t values(1), (2), (3), (4)")
Expand Down Expand Up @@ -247,7 +249,7 @@ func prepareData4PlanReplayer(t *testing.T, client *testserverclient.TestServerC
return filename, filename3
}

func TestIssue56458(t *testing.T) {
func TestPlanReplayerWithMultiForeignKey(t *testing.T) {
store := testkit.CreateMockStore(t)
dom, err := session.GetDomain(store)
require.NoError(t, err)
Expand Down Expand Up @@ -283,17 +285,60 @@ func TestIssue56458(t *testing.T) {
"meta.txt",
"schema/planreplayer.t.schema.txt",
"schema/planreplayer.v.schema.txt",
"schema/planreplayer2.t.schema.txt",
"schema/schema_meta.txt",
"session_bindings.sql",
"sql/sql0.sql",
"sql_meta.toml",
"stats/planreplayer.t.json",
"stats/planreplayer.v.json",
"stats/planreplayer2.t.json",
"statsMem/planreplayer.t.txt",
"statsMem/planreplayer.v.txt",
"statsMem/planreplayer2.t.txt",
"table_tiflash_replica.txt",
"variables.toml",
}, filesInReplayer)

// 3. check plan replayer load
// 3-1. write the plan replayer file from manual command to a file
path := "/tmp/plan_replayer.zip"
fp, err := os.Create(path)
require.NoError(t, err)
require.NotNil(t, fp)
defer func() {
require.NoError(t, fp.Close())
require.NoError(t, os.Remove(path))
}()

_, err = io.Copy(fp, bytes.NewReader(body))
require.NoError(t, err)
require.NoError(t, fp.Sync())

// 3-2. connect to tidb and use PLAN REPLAYER LOAD to load this file
db, err := sql.Open("mysql", client.GetDSN(func(config *mysql.Config) {
config.AllowAllFiles = true
}))
require.NoError(t, err, "Error connecting")
defer func() {
err := db.Close()
require.NoError(t, err)
}()
tk := testkit.NewDBTestKit(t, db)
tk.MustExec("use planReplayer")
tk.MustExec("drop table planReplayer.t")
tk.MustExec("drop table planReplayer2.t")
tk.MustExec("drop table planReplayer.v")
tk.MustExec(`plan replayer load "/tmp/plan_replayer.zip"`)

// 3-3. check whether binding takes effect
tk.MustExec(`select a, b from t where a in (1, 2, 3)`)
rows := tk.MustQuery("select @@last_plan_from_binding")
require.True(t, rows.Next(), "unexpected data")
var count int64
err = rows.Scan(&count)
require.NoError(t, err)
require.Equal(t, int64(1), count)
}

func TestIssue43192(t *testing.T) {
Expand Down Expand Up @@ -402,11 +447,19 @@ func prepareData4Issue56458(t *testing.T, client *testserverclient.TestServerCli
tk := testkit.NewDBTestKit(t, db)

tk.MustExec("create database planReplayer")
tk.MustExec("create database planReplayer2")
tk.MustExec("use planReplayer")

tk.MustExec("CREATE TABLE v(id INT PRIMARY KEY AUTO_INCREMENT);")
tk.MustExec("create table t(a int, b int, INDEX ia (a), INDEX ib (b), author_id int, FOREIGN KEY (author_id) REFERENCES v(id) ON DELETE CASCADE);")
err = h.HandleDDLEvent(<-h.DDLEventCh())
require.NoError(t, err)
tk.MustExec("create table planReplayer2.t(a int, b int, INDEX ia (a), INDEX ib (b), author_id int, FOREIGN KEY (author_id) REFERENCES planReplayer.v(id) ON DELETE CASCADE);")
err = h.HandleDDLEvent(<-h.DDLEventCh())
require.NoError(t, err)
tk.MustExec("create table t(a int, b int, INDEX ia (a), INDEX ib (b), author_id int, FOREIGN KEY (author_id) REFERENCES planReplayer2.t(a) ON DELETE CASCADE);")
err = h.HandleDDLEvent(<-h.DDLEventCh())
require.NoError(t, err)

tk.MustExec("create global binding for select a, b from t where a in (1, 2, 3) using select a, b from t use index (ib) where a in (1, 2, 3)")
rows := tk.MustQuery("plan replayer dump explain select a, b from t where a in (1, 2, 3)")
require.True(t, rows.Next(), "unexpected data")
Expand Down

0 comments on commit df753d1

Please sign in to comment.