Skip to content

Commit 4fec22b

Browse files
authored
planner: play replay load restore the table with foreign key with right order. (#56457) (#57098)
close #56456
1 parent 6e4f79a commit 4fec22b

File tree

3 files changed

+94
-22
lines changed

3 files changed

+94
-22
lines changed

domain/plan_replayer_dump.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,23 +105,35 @@ func (tne *tableNameExtractor) getTablesAndViews() (map[tableNamePair]struct{},
105105
r[tablePair] = struct{}{}
106106
}
107107
// if the table has a foreign key, we need to add the referenced table to the list
108-
tblInfo, err := tne.is.TableByName(model.NewCIStr(tablePair.DBName), model.NewCIStr(tablePair.TableName))
108+
err := findFK(tne.is, tablePair.DBName, tablePair.TableName, r)
109109
if err != nil {
110110
return nil, err
111111
}
112-
for _, fk := range tblInfo.Meta().ForeignKeys {
113-
key := tableNamePair{
114-
DBName: fk.RefSchema.L,
115-
TableName: fk.RefTable.L,
116-
IsView: false,
117-
}
118-
r[key] = struct{}{}
119-
}
120112
}
121113
return r, nil
122114
}
123115

124-
func (tne *tableNameExtractor) Enter(in ast.Node) (ast.Node, bool) {
116+
func findFK(is infoschema.InfoSchema, dbName, tableName string, tableMap map[tableNamePair]struct{}) error {
117+
tblInfo, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName))
118+
if err != nil {
119+
return err
120+
}
121+
for _, fk := range tblInfo.Meta().ForeignKeys {
122+
key := tableNamePair{
123+
DBName: fk.RefSchema.L,
124+
TableName: fk.RefTable.L,
125+
IsView: false,
126+
}
127+
tableMap[key] = struct{}{}
128+
err := findFK(is, key.DBName, key.TableName, tableMap)
129+
if err != nil {
130+
return err
131+
}
132+
}
133+
return nil
134+
}
135+
136+
func (*tableNameExtractor) Enter(in ast.Node) (ast.Node, bool) {
125137
if _, ok := in.(*ast.TableName); ok {
126138
return in, true
127139
}

executor/plan_replayer.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -478,17 +478,9 @@ func (e *PlanReplayerLoadInfo) Update(data []byte) error {
478478
}
479479

480480
// build schema and table first
481-
for _, zipFile := range z.File {
482-
if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) {
483-
continue
484-
}
485-
path := strings.Split(zipFile.Name, "/")
486-
if len(path) == 2 && strings.Compare(path[0], "schema") == 0 && zipFile.Mode().IsRegular() {
487-
err = createSchemaAndItems(e.Ctx, zipFile)
488-
if err != nil {
489-
return err
490-
}
491-
}
481+
err = e.createTable(z)
482+
if err != nil {
483+
return err
492484
}
493485

494486
// set tiflash replica if exists
@@ -526,3 +518,26 @@ func (e *PlanReplayerLoadInfo) Update(data []byte) error {
526518
}
527519
return nil
528520
}
521+
522+
func (e *PlanReplayerLoadInfo) createTable(z *zip.Reader) error {
523+
origin := e.Ctx.GetSessionVars().ForeignKeyChecks
524+
// We need to disable foreign key check when we create schema and tables.
525+
// because the order of creating schema and tables is not guaranteed.
526+
e.Ctx.GetSessionVars().ForeignKeyChecks = false
527+
defer func() {
528+
e.Ctx.GetSessionVars().ForeignKeyChecks = origin
529+
}()
530+
for _, zipFile := range z.File {
531+
if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) {
532+
continue
533+
}
534+
path := strings.Split(zipFile.Name, "/")
535+
if len(path) == 2 && strings.Compare(path[0], "schema") == 0 && zipFile.Mode().IsRegular() {
536+
err := createSchemaAndItems(e.Ctx, zipFile)
537+
if err != nil {
538+
return err
539+
}
540+
}
541+
}
542+
return nil
543+
}

server/plan_replayer_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ func prepareData4PlanReplayer(t *testing.T, client *testServerClient, dom *domai
201201
tk.MustExec("create database planReplayer")
202202
tk.MustExec("use planReplayer")
203203
tk.MustExec("create table t(a int)")
204+
tk.MustExec("CREATE TABLE authors (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100) NOT NULL,email VARCHAR(100) UNIQUE NOT NULL);")
205+
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);")
204206
err = h.HandleDDLEvent(<-h.DDLEventCh())
205207
require.NoError(t, err)
206208
tk.MustExec("insert into t values(1), (2), (3), (4)")
@@ -306,7 +308,7 @@ func prepareServerAndClientForTest(t *testing.T, store kv.Storage, dom *domain.D
306308
return
307309
}
308310

309-
func TestIssue56458(t *testing.T) {
311+
func TestPlanReplayerWithMultiForeignKey(t *testing.T) {
310312
store := testkit.CreateMockStore(t)
311313
dom, err := session.GetDomain(store)
312314
require.NoError(t, err)
@@ -341,16 +343,59 @@ func TestIssue56458(t *testing.T) {
341343
"global_bindings.sql",
342344
"meta.txt",
343345
"schema/planreplayer.t.schema.txt",
346+
"schema/planreplayer.v.schema.txt",
344347
"schema/planreplayer2.t.schema.txt",
345348
"schema/schema_meta.txt",
346349
"session_bindings.sql",
347350
"sql/sql0.sql",
348351
"sql_meta.toml",
349352
"stats/planreplayer.t.json",
353+
"stats/planreplayer.v.json",
350354
"stats/planreplayer2.t.json",
351355
"statsMem/planreplayer.t.txt",
356+
"statsMem/planreplayer.v.txt",
352357
"statsMem/planreplayer2.t.txt",
353358
"table_tiflash_replica.txt",
354359
"variables.toml",
355360
}, filesInReplayer)
361+
362+
// 3. check plan replayer load
363+
// 3-1. write the plan replayer file from manual command to a file
364+
path := "/tmp/plan_replayer.zip"
365+
fp, err := os.Create(path)
366+
require.NoError(t, err)
367+
require.NotNil(t, fp)
368+
defer func() {
369+
require.NoError(t, fp.Close())
370+
require.NoError(t, os.Remove(path))
371+
}()
372+
373+
_, err = io.Copy(fp, bytes.NewReader(body))
374+
require.NoError(t, err)
375+
require.NoError(t, fp.Sync())
376+
377+
// 3-2. connect to tidb and use PLAN REPLAYER LOAD to load this file
378+
db, err := sql.Open("mysql", client.getDSN(func(config *mysql.Config) {
379+
config.AllowAllFiles = true
380+
}))
381+
require.NoError(t, err, "Error connecting")
382+
defer func() {
383+
err := db.Close()
384+
require.NoError(t, err)
385+
}()
386+
tk := testkit.NewDBTestKit(t, db)
387+
tk.MustExec("use planReplayer")
388+
tk.MustExec("drop table planReplayer.t")
389+
tk.MustExec("drop table planReplayer2.t")
390+
tk.MustExec("drop table planReplayer.v")
391+
tk.MustExec(`plan replayer load "/tmp/plan_replayer.zip"`)
392+
393+
// 3-3. check whether binding takes effect
394+
tk.MustExec(`select a, b from t where a in (1, 2, 3)`)
395+
rows := tk.MustQuery("select @@last_plan_from_binding")
396+
require.True(t, rows.Next(), "unexpected data")
397+
var count int64
398+
err = rows.Scan(&count)
399+
require.NoError(t, err)
400+
require.Equal(t, int64(1), count)
356401
}

0 commit comments

Comments
 (0)