diff --git a/pkg/frontend/pitr.go b/pkg/frontend/pitr.go index 83a7365bf4444..c74ce57bef365 100644 --- a/pkg/frontend/pitr.go +++ b/pkg/frontend/pitr.go @@ -1687,7 +1687,15 @@ func restoreViewsWithPitr( viewMap map[string]*tableInfo, accountName string, curAccount uint32) error { - snapshot := &pbplan.Snapshot{ + getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] start to restore views", pitrName)) + var ( + err error + stmts []tree.Statement + sortedViews []string + snapshot *pbplan.Snapshot + oldSnapshot *pbplan.Snapshot + ) + snapshot = &pbplan.Snapshot{ TS: ×tamp.Timestamp{PhysicalTime: ts}, Tenant: &pbplan.SnapshotTenant{ TenantName: accountName, @@ -1696,23 +1704,29 @@ func restoreViewsWithPitr( } compCtx := ses.GetTxnCompileCtx() - oldSnapshot := compCtx.GetSnapshot() + oldSnapshot = compCtx.GetSnapshot() compCtx.SetSnapshot(snapshot) defer func() { compCtx.SetSnapshot(oldSnapshot) }() g := toposort{next: make(map[string][]string)} - for key, view := range viewMap { - stmts, err := parsers.Parse(ctx, dialect.MYSQL, view.createSql, 1) + for key, viewEntry := range viewMap { + getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] start to restore view: %v", pitrName, viewEntry.tblName)) + stmts, err = parsers.Parse(ctx, dialect.MYSQL, viewEntry.createSql, 1) if err != nil { return err } - compCtx.SetDatabase(view.dbName) + compCtx.SetDatabase(viewEntry.dbName) // build create sql to find dependent views - if _, err = plan.BuildPlan(compCtx, stmts[0], false); err != nil { - return err + _, err = plan.BuildPlan(compCtx, stmts[0], false) + if err != nil { + stmts, _ = parsers.Parse(ctx, dialect.MYSQL, viewEntry.createSql, 0) + _, err = plan.BuildPlan(compCtx, stmts[0], false) + if err != nil { + return err + } } g.addVertex(key) @@ -1722,7 +1736,7 @@ func restoreViewsWithPitr( } // topsort - sortedViews, err := g.sort() + sortedViews, err = g.sort() if err != nil { return err } diff --git a/pkg/frontend/pitr_test.go b/pkg/frontend/pitr_test.go index 6eedea65a1566..22bee88ffcda1 100644 --- a/pkg/frontend/pitr_test.go +++ b/pkg/frontend/pitr_test.go @@ -2902,3 +2902,138 @@ func TestCheckDbIsSubDb(t *testing.T) { }) } } + +func Test_restoreViews(t *testing.T) { + convey.Convey("restoreViews", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses := newTestSession(t, ctrl) + defer ses.Close() + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.SV.SetDefaultValues() + pu.SV.KillRountinesInterval = 0 + setPu("", pu) + ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) + rm, _ := NewRoutineManager(ctx, "") + ses.rm = rm + + tenant := &TenantInfo{ + Tenant: sysAccountName, + User: rootName, + DefaultRole: moAdminRoleName, + TenantID: sysAccountID, + UserID: rootID, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + + ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) + + //no result set + bh.sql2result["begin;"] = nil + bh.sql2result["commit;"] = nil + bh.sql2result["rollback;"] = nil + + viewMap := map[string]*tableInfo{} + err := restoreViews(ctx, ses, bh, "sp01", viewMap, 0) + assert.Error(t, err) + + sql := "select * from mo_catalog.mo_snapshots where sname = 'sp01'" + // string/ string/ int64/ string/ string/ string/ string/ uint64 + mrs := newMrsForPitrRecord([][]interface{}{{"1", "sp01", int64(0), "ACCOUNT", "sys", "", "", uint64(1)}}) + bh.sql2result[sql] = mrs + + sql = "select account_id, account_name, status, version, suspended_time from mo_catalog.mo_account where 1=1 and account_name = 'sys'" + mrs = newMrsForPitrRecord([][]interface{}{{uint64(0), "sys", "open", uint64(1), ""}}) + bh.sql2result[sql] = mrs + + err = restoreViews(ctx, ses, bh, "sp01", viewMap, 0) + assert.NoError(t, err) + + viewMap = map[string]*tableInfo{ + "view01": { + dbName: "db01", + tblName: "tbl01", + typ: "VIEW", + createSql: "create view view01", + }, + } + err = restoreViews(ctx, ses, bh, "sp01", viewMap, 0) + assert.Error(t, err) + }) +} + +func Test_restoreViewsWithPitr(t *testing.T) { + convey.Convey("restoreViewsWithPitr", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses := newTestSession(t, ctrl) + defer ses.Close() + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.SV.SetDefaultValues() + pu.SV.KillRountinesInterval = 0 + setPu("", pu) + ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) + rm, _ := NewRoutineManager(ctx, "") + ses.rm = rm + + tenant := &TenantInfo{ + Tenant: sysAccountName, + User: rootName, + DefaultRole: moAdminRoleName, + TenantID: sysAccountID, + UserID: rootID, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + + ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) + + //no result set + bh.sql2result["begin;"] = nil + bh.sql2result["commit;"] = nil + bh.sql2result["rollback;"] = nil + + viewMap := map[string]*tableInfo{} + err := restoreViewsWithPitr(ctx, ses, bh, "sp01", 0, viewMap, "sys", 0) + assert.NoError(t, err) + + viewMap = map[string]*tableInfo{ + "view01": { + dbName: "db01", + tblName: "tbl01", + typ: "VIEW", + createSql: "create view view01", + }, + } + err = restoreViewsWithPitr(ctx, ses, bh, "sp01", 0, viewMap, "sys", 0) + assert.Error(t, err) + + viewMap = map[string]*tableInfo{ + "view01": { + dbName: "db01", + tblName: "tbl01", + typ: "VIEW", + createSql: "create database db02", + }, + } + err = restoreViewsWithPitr(ctx, ses, bh, "sp01", 0, viewMap, "sys", 0) + assert.NoError(t, err) + }) +} diff --git a/pkg/frontend/snapshot.go b/pkg/frontend/snapshot.go index ac39c8f399d9a..9eb1c794ca79f 100644 --- a/pkg/frontend/snapshot.go +++ b/pkg/frontend/snapshot.go @@ -985,29 +985,44 @@ func restoreViews( snapshotName string, viewMap map[string]*tableInfo, toAccountId uint32) error { - snapshot, err := getSnapshotPlanWithSharedBh(ctx, bh, snapshotName) + getLogger(ses.GetService()).Info("start to restore views") + var ( + err error + snapshot *plan.Snapshot + stmts []tree.Statement + sortedViews []string + oldSnapshot *plan.Snapshot + ) + snapshot, err = getSnapshotPlanWithSharedBh(ctx, bh, snapshotName) if err != nil { return err } compCtx := ses.GetTxnCompileCtx() - oldSnapshot := compCtx.GetSnapshot() + oldSnapshot = compCtx.GetSnapshot() compCtx.SetSnapshot(snapshot) defer func() { compCtx.SetSnapshot(oldSnapshot) }() g := toposort{next: make(map[string][]string)} - for key, view := range viewMap { - stmts, err := parsers.Parse(ctx, dialect.MYSQL, view.createSql, 1) + for key, viewEntry := range viewMap { + getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] start to restore view: %v", snapshotName, viewEntry.tblName)) + stmts, err = parsers.Parse(ctx, dialect.MYSQL, viewEntry.createSql, 1) if err != nil { return err } - compCtx.SetDatabase(view.dbName) + compCtx.SetDatabase(viewEntry.dbName) // build create sql to find dependent views - if _, err = plan.BuildPlan(compCtx, stmts[0], false); err != nil { - return err + _, err = plan.BuildPlan(compCtx, stmts[0], false) + if err != nil { + getLogger(ses.GetService()).Info(fmt.Sprintf("try to build view %v failed, try to build it again", viewEntry.tblName)) + stmts, _ = parsers.Parse(ctx, dialect.MYSQL, viewEntry.createSql, 0) + _, err = plan.BuildPlan(compCtx, stmts[0], false) + if err != nil { + return err + } } g.addVertex(key) @@ -1017,7 +1032,7 @@ func restoreViews( } // toposort - sortedViews, err := g.sort() + sortedViews, err = g.sort() if err != nil { return err } diff --git a/test/distributed/cases/snapshot/snapshot_restore_lower_case_2.result b/test/distributed/cases/snapshot/snapshot_restore_lower_case_2.result new file mode 100644 index 0000000000000..cf1368989d030 --- /dev/null +++ b/test/distributed/cases/snapshot/snapshot_restore_lower_case_2.result @@ -0,0 +1,153 @@ +drop account if exists a1; +create account a1 ADMIN_NAME 'admin1' IDENTIFIED BY 'test123'; +select @@lower_case_table_names; +@@lower_case_table_names +1 +set global lower_case_table_names = 0; +select @@lower_case_table_names; +@@lower_case_table_names +0 +drop database if exists test02; +create database test02; +use test02; +drop table if exists Departments; +drop table if exists Employees; +create table Departments ( +DepartmentID INT PRIMARY KEY, +DepartmentName VARCHAR(255) NOT NULL +); +create table Employees ( +EmployeeID INT PRIMARY KEY, +FirstName VARCHAR(255) NOT NULL, +LastName VARCHAR(255) NOT NULL, +DepartmentID INT, +foreign key (DepartmentID) REFERENCES Departments(DepartmentID) +); +insert into Departments (DepartmentID, DepartmentName) values +(1, 'Human Resources'), +(2, 'Engineering'), +(3, 'Marketing'), +(4, 'Sales'), +(5, 'Finance'); +insert into Employees (EmployeeID, FirstName, LastName, DepartmentID) values +(101, 'John', 'Doe', 1), +(102, 'Jane', 'Smith', 2), +(103, 'Alice', 'Johnson', 3), +(104, 'Mark', 'Patterson', 4), +(105, 'David', 'Finley', 5); +drop view if exists EmployeeDepartmentView; +create view EmployeeDepartmentView as +select +e.FirstName, +e.LastName, +d.DepartmentName +from +Employees e +inner join +Departments d ON e.DepartmentID = d.DepartmentID; +select * from EmployeeDepartmentView; +firstname lastname departmentname +John Doe Human Resources +Jane Smith Engineering +Alice Johnson Marketing +Mark Patterson Sales +David Finley Finance +drop snapshot if exists sp02_restore_lower; +create snapshot sp02_restore_lower for account a1; +select @@lower_case_table_names; +@@lower_case_table_names +0 +drop database test02; +restore account a1 from snapshot sp02_restore_lower; +use test02; +show tables; +Tables_in_test02 +Departments +EmployeeDepartmentView +Employees +drop database if exists test02; +restore account a1 database test02 from snapshot sp02_restore_lower; +use test02; +show tables; +Tables_in_test02 +Departments +EmployeeDepartmentView +Employees +drop account if exists a1; +drop account if exists a1; +create account a1 ADMIN_NAME 'admin1' IDENTIFIED BY 'test123'; +select @@lower_case_table_names; +@@lower_case_table_names +1 +set global lower_case_table_names = 0; +select @@lower_case_table_names; +@@lower_case_table_names +0 +select @@lower_case_table_names; +@@lower_case_table_names +0 +drop database if exists test02; +create database test02; +use test02; +drop table if exists Departments; +drop table if exists Employees; +create table Departments ( +DepartmentID INT PRIMARY KEY, +DepartmentName VARCHAR(255) NOT NULL +); +create table Employees ( +EmployeeID INT PRIMARY KEY, +FirstName VARCHAR(255) NOT NULL, +LastName VARCHAR(255) NOT NULL, +DepartmentID INT, +foreign key (DepartmentID) REFERENCES Departments(DepartmentID) +); +insert into Departments (DepartmentID, DepartmentName) values +(1, 'Human Resources'), +(2, 'Engineering'), +(3, 'Marketing'), +(4, 'Sales'), +(5, 'Finance'); +insert into Employees (EmployeeID, FirstName, LastName, DepartmentID) values +(101, 'John', 'Doe', 1), +(102, 'Jane', 'Smith', 2), +(103, 'Alice', 'Johnson', 3), +(104, 'Mark', 'Patterson', 4), +(105, 'David', 'Finley', 5); +drop view if exists EmployeeDepartmentView; +create view EmployeeDepartmentView as +select +e.FirstName, +e.LastName, +d.DepartmentName +from +Employees e +inner join +Departments d ON e.DepartmentID = d.DepartmentID; +select * from EmployeeDepartmentView; +firstname lastname departmentname +John Doe Human Resources +Jane Smith Engineering +Alice Johnson Marketing +Mark Patterson Sales +David Finley Finance +show tables; +Tables_in_test02 +Departments +EmployeeDepartmentView +Employees +drop snapshot if exists sp02_restore_lower; +create snapshot sp02_restore_lower for account a1; +select @@lower_case_table_names; +@@lower_case_table_names +0 +drop database test02; +restore account a1 from snapshot sp02_restore_lower; +use test02; +show tables; +Tables_in_test02 +Departments +EmployeeDepartmentView +Employees +drop account if exists a1; +drop snapshot if exists sp02_restore_lower; diff --git a/test/distributed/cases/snapshot/snapshot_restore_lower_case_2.sql b/test/distributed/cases/snapshot/snapshot_restore_lower_case_2.sql new file mode 100644 index 0000000000000..4714147fabbf9 --- /dev/null +++ b/test/distributed/cases/snapshot/snapshot_restore_lower_case_2.sql @@ -0,0 +1,153 @@ +drop account if exists a1; +create account a1 ADMIN_NAME 'admin1' IDENTIFIED BY 'test123'; + +-- @session:id=1&user=a1:admin1&password=test123 +-- default value is 1 +select @@lower_case_table_names; +set global lower_case_table_names = 0; +-- @session + +-- @session:id=2&user=a1:admin1&password=test123 +-- it's 0 now +select @@lower_case_table_names; +drop database if exists test02; +create database test02; +use test02; +drop table if exists Departments; +drop table if exists Employees; +create table Departments ( + DepartmentID INT PRIMARY KEY, + DepartmentName VARCHAR(255) NOT NULL +); + +create table Employees ( +EmployeeID INT PRIMARY KEY, +FirstName VARCHAR(255) NOT NULL, +LastName VARCHAR(255) NOT NULL, +DepartmentID INT, +foreign key (DepartmentID) REFERENCES Departments(DepartmentID) +); + +insert into Departments (DepartmentID, DepartmentName) values +(1, 'Human Resources'), +(2, 'Engineering'), +(3, 'Marketing'), +(4, 'Sales'), +(5, 'Finance'); + +insert into Employees (EmployeeID, FirstName, LastName, DepartmentID) values +(101, 'John', 'Doe', 1), +(102, 'Jane', 'Smith', 2), +(103, 'Alice', 'Johnson', 3), +(104, 'Mark', 'Patterson', 4), +(105, 'David', 'Finley', 5); + +drop view if exists EmployeeDepartmentView; +create view EmployeeDepartmentView as +select + e.FirstName, + e.LastName, + d.DepartmentName +from + Employees e + inner join + Departments d ON e.DepartmentID = d.DepartmentID; +select * from EmployeeDepartmentView; + +drop snapshot if exists sp02_restore_lower; +create snapshot sp02_restore_lower for account a1; + +select @@lower_case_table_names; +drop database test02; + +restore account a1 from snapshot sp02_restore_lower; + +use test02; +show tables; + +drop database if exists test02; +restore account a1 database test02 from snapshot sp02_restore_lower; + +use test02; +show tables; +-- @session + +drop account if exists a1; + +drop account if exists a1; +create account a1 ADMIN_NAME 'admin1' IDENTIFIED BY 'test123'; + +-- @session:id=3&user=a1:admin1&password=test123 +-- default value is 1 +select @@lower_case_table_names; +set global lower_case_table_names = 0; +-- @session + +-- @session:id=4&user=a1:admin1&password=test123 +-- it's 0 now +select @@lower_case_table_names; +select @@lower_case_table_names; +drop database if exists test02; +create database test02; +use test02; +drop table if exists Departments; +drop table if exists Employees; +create table Departments ( + DepartmentID INT PRIMARY KEY, + DepartmentName VARCHAR(255) NOT NULL +); + +create table Employees ( +EmployeeID INT PRIMARY KEY, +FirstName VARCHAR(255) NOT NULL, +LastName VARCHAR(255) NOT NULL, +DepartmentID INT, +foreign key (DepartmentID) REFERENCES Departments(DepartmentID) +); + +insert into Departments (DepartmentID, DepartmentName) values +(1, 'Human Resources'), +(2, 'Engineering'), +(3, 'Marketing'), +(4, 'Sales'), +(5, 'Finance'); + +insert into Employees (EmployeeID, FirstName, LastName, DepartmentID) values +(101, 'John', 'Doe', 1), +(102, 'Jane', 'Smith', 2), +(103, 'Alice', 'Johnson', 3), +(104, 'Mark', 'Patterson', 4), +(105, 'David', 'Finley', 5); + +drop view if exists EmployeeDepartmentView; +create view EmployeeDepartmentView as +select + e.FirstName, + e.LastName, + d.DepartmentName +from + Employees e + inner join + Departments d ON e.DepartmentID = d.DepartmentID; +select * from EmployeeDepartmentView; + +show tables; +-- @session + +drop snapshot if exists sp02_restore_lower; +create snapshot sp02_restore_lower for account a1; + +-- @session:id=4&user=a1:admin1&password=test123 +select @@lower_case_table_names; +drop database test02; +-- @session + +restore account a1 from snapshot sp02_restore_lower; + +-- @session:id=4&user=a1:admin1&password=test123 +use test02; +show tables; +-- @session + +drop account if exists a1; +drop snapshot if exists sp02_restore_lower;