From 9148ff9f44555d681a401b3f33681f7dc2414b6b Mon Sep 17 00:00:00 2001 From: djshow832 Date: Mon, 17 May 2021 22:31:39 +0800 Subject: [PATCH] ddl, transaction: DDL on temporary tables won't affect transactions (#24534) --- domain/schema_validator.go | 5 +++-- session/session.go | 6 ++++++ session/session_test.go | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/domain/schema_validator.go b/domain/schema_validator.go index b983eff1d6203..a8baa49db93b9 100644 --- a/domain/schema_validator.go +++ b/domain/schema_validator.go @@ -234,8 +234,9 @@ func (s *schemaValidator) Check(txnTS uint64, schemaVer int64, relatedPhysicalTa // Schema changed, result decided by whether related tables change. if schemaVer < s.latestSchemaVer { - // The DDL relatedPhysicalTableIDs is empty. - if len(relatedPhysicalTableIDs) == 0 { + // When a transaction executes a DDL, relatedPhysicalTableIDs is nil. + // When a transaction only contains DML on temporary tables, relatedPhysicalTableIDs is []. + if relatedPhysicalTableIDs == nil { logutil.BgLogger().Info("the related physical table ID is empty", zap.Int64("schemaVer", schemaVer), zap.Int64("latestSchemaVer", s.latestSchemaVer)) return nil, ResultFail diff --git a/session/session.go b/session/session.go index e13be2045e941..78a60a6ebaecf 100644 --- a/session/session.go +++ b/session/session.go @@ -505,8 +505,14 @@ func (s *session) doCommit(ctx context.Context) error { // Get the related table or partition IDs. relatedPhysicalTables := s.GetSessionVars().TxnCtx.TableDeltaMap + // Get accessed global temporary tables in the transaction. + temporaryTables := s.GetSessionVars().TxnCtx.GlobalTemporaryTables physicalTableIDs := make([]int64, 0, len(relatedPhysicalTables)) for id := range relatedPhysicalTables { + // Schema change on global temporary tables doesn't affect transactions. + if _, ok := temporaryTables[id]; ok { + continue + } physicalTableIDs = append(physicalTableIDs, id) } // Set this option for 2 phase commit to validate schema lease. diff --git a/session/session_test.go b/session/session_test.go index 9845e757470f0..f7267e3a13259 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -2131,6 +2131,45 @@ func (s *testSchemaSerialSuite) TestSchemaCheckerSQL(c *C) { c.Assert(err, NotNil) } +func (s *testSchemaSerialSuite) TestSchemaCheckerTempTable(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + tk1 := testkit.NewTestKitWithInit(c, s.store) + + // create table + tk.MustExec(`drop table if exists normal_table`) + tk.MustExec(`create table normal_table (id int, c int);`) + defer tk.MustExec(`drop table if exists normal_table`) + tk.MustExec(`drop table if exists temp_table`) + tk.MustExec(`create global temporary table temp_table (id int, c int) on commit delete rows;`) + defer tk.MustExec(`drop table if exists temp_table`) + + // The schema version is out of date in the first transaction, and the SQL can't be retried. + atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 1) + defer func() { + atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 0) + }() + + // It's fine to change the schema of temporary tables. + tk.MustExec(`begin;`) + tk1.MustExec(`alter table temp_table modify column c bigint;`) + tk.MustExec(`insert into temp_table values(3, 3);`) + tk.MustExec(`commit;`) + + // Truncate will modify table ID. + tk.MustExec(`begin;`) + tk1.MustExec(`truncate table temp_table;`) + tk.MustExec(`insert into temp_table values(3, 3);`) + tk.MustExec(`commit;`) + + // It reports error when also changing the schema of a normal table. + tk.MustExec(`begin;`) + tk1.MustExec(`alter table normal_table modify column c bigint;`) + tk.MustExec(`insert into temp_table values(3, 3);`) + tk.MustExec(`insert into normal_table values(3, 3);`) + _, err := tk.Exec(`commit;`) + c.Assert(terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), IsTrue, Commentf("err %v", err)) +} + func (s *testSchemaSuite) TestPrepareStmtCommitWhenSchemaChanged(c *C) { tk := testkit.NewTestKitWithInit(c, s.store) tk1 := testkit.NewTestKitWithInit(c, s.store)