diff --git a/errno/errcode.go b/errno/errcode.go index d6b7c912a15d9..319ddd092bd82 100644 --- a/errno/errcode.go +++ b/errno/errcode.go @@ -923,6 +923,7 @@ const ( ErrTxnTooLarge = 8004 ErrWriteConflictInTiDB = 8005 ErrOptOnTemporaryTable = 8006 + ErrDropTableOnTemporaryTable = 8007 ErrUnsupportedReloadPlugin = 8018 ErrUnsupportedReloadPluginVar = 8019 ErrTableLocked = 8020 diff --git a/errno/errname.go b/errno/errname.go index 6ab983acc3f29..1fdb6b82b560b 100644 --- a/errno/errname.go +++ b/errno/errname.go @@ -925,6 +925,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{ ErrForUpdateCantRetry: mysql.Message("[%d] can not retry select for update statement", nil), ErrAdminCheckTable: mysql.Message("TiDB admin check table failed.", nil), ErrOptOnTemporaryTable: mysql.Message("`%s` is unsupported on temporary tables.", nil), + ErrDropTableOnTemporaryTable: mysql.Message("`drop global temporary table` can only drop global temporary table", nil), ErrTxnTooLarge: mysql.Message("Transaction is too large, size: %d", nil), ErrWriteConflictInTiDB: mysql.Message("Write conflict, txnStartTS %d is stale", nil), ErrInvalidPluginID: mysql.Message("Wrong plugin id: %s, valid plugin id is [name]-[version], both name and version should not contain '-'", nil), diff --git a/errors.toml b/errors.toml index 165226659c51e..f7508f8dba983 100644 --- a/errors.toml +++ b/errors.toml @@ -1161,6 +1161,11 @@ error = ''' `%s` is unsupported on temporary tables. ''' +["planner:8007"] +error = ''' +`drop global temporary table` can only drop global temporary table +''' + ["planner:8108"] error = ''' Unsupported type %T diff --git a/planner/core/errors.go b/planner/core/errors.go index c1166f6f288b3..e501af7676c5f 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -94,11 +94,12 @@ var ( ErrCTERecursiveForbiddenJoinOrder = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveForbiddenJoinOrder) ErrInvalidRequiresSingleReference = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidRequiresSingleReference) // Since we cannot know if user logged in with a password, use message of ErrAccessDeniedNoPassword instead - ErrAccessDenied = dbterror.ClassOptimizer.NewStdErr(mysql.ErrAccessDenied, mysql.MySQLErrName[mysql.ErrAccessDeniedNoPassword]) - ErrBadNull = dbterror.ClassOptimizer.NewStd(mysql.ErrBadNull) - ErrNotSupportedWithSem = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedWithSem) - ErrAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrAsOf) - ErrOptOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnTemporaryTable) + ErrAccessDenied = dbterror.ClassOptimizer.NewStdErr(mysql.ErrAccessDenied, mysql.MySQLErrName[mysql.ErrAccessDeniedNoPassword]) + ErrBadNull = dbterror.ClassOptimizer.NewStd(mysql.ErrBadNull) + ErrNotSupportedWithSem = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedWithSem) + ErrAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrAsOf) + ErrOptOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnTemporaryTable) + ErrDropTableOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrDropTableOnTemporaryTable) // ErrPartitionNoTemporary returns when partition at temporary mode ErrPartitionNoTemporary = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionNoTemporary) ) diff --git a/planner/core/preprocess.go b/planner/core/preprocess.go index 26ebfd5c3e125..c78936c485ec7 100644 --- a/planner/core/preprocess.go +++ b/planner/core/preprocess.go @@ -55,7 +55,7 @@ func InTxnRetry(p *preprocessor) { p.flag |= inTxnRetry } -// WithPreprocessorReturn returns a PreprocessOpt to initialize the PreprocesorReturn. +// WithPreprocessorReturn returns a PreprocessOpt to initialize the PreprocessorReturn. func WithPreprocessorReturn(ret *PreprocessorReturn) PreprocessOpt { return func(p *preprocessor) { p.PreprocessorReturn = ret @@ -93,7 +93,7 @@ func TryAddExtraLimit(ctx sessionctx.Context, node ast.StmtNode) ast.StmtNode { } // Preprocess resolves table names of the node, and checks some statements validation. -// prepreocssReturn used to extract the infoschema for the tableName and the timestamp from the asof clause. +// preprocessReturn used to extract the infoschema for the tableName and the timestamp from the asof clause. func Preprocess(ctx sessionctx.Context, node ast.Node, preprocessOpt ...PreprocessOpt) error { v := preprocessor{ctx: ctx, tableAliasInJoin: make([]map[string]interface{}, 0), withName: make(map[string]interface{})} for _, optFn := range preprocessOpt { @@ -842,6 +842,29 @@ func (p *preprocessor) checkDropTableGrammar(stmt *ast.DropTableStmt) { p.err = expression.ErrFunctionsNoopImpl.GenWithStackByArgs("DROP TEMPORARY TABLE") return } + if stmt.TemporaryKeyword == ast.TemporaryNone { + return + } + currentDB := model.NewCIStr(p.ctx.GetSessionVars().CurrentDB) + for _, t := range stmt.Tables { + if isIncorrectName(t.Name.String()) { + p.err = ddl.ErrWrongTableName.GenWithStackByArgs(t.Name.String()) + return + } + if t.Schema.String() != "" { + currentDB = t.Schema + } + tableInfo, err := p.ensureInfoSchema().TableByName(currentDB, t.Name) + if err != nil { + p.err = err + return + } + if tableInfo.Meta().TempTableType != model.TempTableGlobal { + p.err = ErrDropTableOnTemporaryTable + return + } + } + } func (p *preprocessor) checkDropTableNames(tables []*ast.TableName) { diff --git a/planner/core/preprocess_test.go b/planner/core/preprocess_test.go index d9f053f509e92..e32148c572728 100644 --- a/planner/core/preprocess_test.go +++ b/planner/core/preprocess_test.go @@ -337,3 +337,29 @@ func (s *testValidatorSuite) TestForeignKey(c *C) { s.runSQL(c, "ALTER TABLE test.t1 ADD CONSTRAINT fk FOREIGN KEY (c) REFERENCES test2.t (e)", false, nil) } + +func (s *testValidatorSuite) TestDropGlobalTempTable(c *C) { + defer testleak.AfterTest(c)() + defer func() { + s.dom.Close() + s.store.Close() + }() + + ctx := context.Background() + execSQLList := []string{ + "use test", + "set tidb_enable_global_temporary_table=true", + "create table tb(id int);", + "create global temporary table temp(id int) on commit delete rows;", + "create global temporary table temp1(id int) on commit delete rows;", + } + for _, execSQL := range execSQLList { + _, err := s.se.Execute(ctx, execSQL) + c.Assert(err, IsNil) + } + s.is = s.dom.InfoSchema() + s.runSQL(c, "drop global temporary table tb;", false, core.ErrDropTableOnTemporaryTable) + s.runSQL(c, "drop global temporary table temp", false, nil) + s.runSQL(c, "drop global temporary table test.tb;", false, core.ErrDropTableOnTemporaryTable) + s.runSQL(c, "drop global temporary table test.temp1", false, nil) +}