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
…ht order. (#56457) (#57098)

close #56456
  • Loading branch information
ti-chi-bot authored Nov 4, 2024
1 parent 6e4f79a commit 4fec22b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 22 deletions.
32 changes: 22 additions & 10 deletions domain/plan_replayer_dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,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 executor/plan_replayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,17 +478,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 @@ -526,3 +518,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
}
47 changes: 46 additions & 1 deletion server/plan_replayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ func prepareData4PlanReplayer(t *testing.T, client *testServerClient, dom *domai
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 @@ -306,7 +308,7 @@ func prepareServerAndClientForTest(t *testing.T, store kv.Storage, dom *domain.D
return
}

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 @@ -341,16 +343,59 @@ func TestIssue56458(t *testing.T) {
"global_bindings.sql",
"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)
}

0 comments on commit 4fec22b

Please sign in to comment.