diff --git a/ddl/BUILD.bazel b/ddl/BUILD.bazel index 080f2ccbb1584..88d045c5fde9b 100644 --- a/ddl/BUILD.bazel +++ b/ddl/BUILD.bazel @@ -120,6 +120,7 @@ go_library( "//util/ranger", "//util/resourcegrouptag", "//util/rowDecoder", + "//util/serverless", "//util/set", "//util/slice", "//util/sqlexec", diff --git a/ddl/ttl.go b/ddl/ttl.go index 58011ee9e79f9..b8c7c8fac0920 100644 --- a/ddl/ttl.go +++ b/ddl/ttl.go @@ -30,6 +30,7 @@ import ( "github.com/pingcap/tidb/sessiontxn" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/util/serverless" ) func onTTLInfoRemove(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { @@ -97,6 +98,10 @@ func onTTLInfoChange(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err er } func checkTTLInfoValid(ctx sessionctx.Context, schema model.CIStr, tblInfo *model.TableInfo) error { + if err := serverless.VerifyTTLInfo(schema, tblInfo); err != nil { + return err + } + if err := checkTTLIntervalExpr(ctx, tblInfo.TTLInfo); err != nil { return err } diff --git a/errno/errcode.go b/errno/errcode.go index 0501bc0020e07..6acd24065bb96 100644 --- a/errno/errcode.go +++ b/errno/errcode.go @@ -1112,4 +1112,7 @@ const ( ErrTiKVMaxTimestampNotSynced = 9011 ErrTiFlashServerTimeout = 9012 ErrTiFlashServerBusy = 9013 + + // Serverless tier errors. + ErrNotSupportedOnServerless = 20001 ) diff --git a/errno/errname.go b/errno/errname.go index 39deb486ff2fb..6a9b947f97454 100644 --- a/errno/errname.go +++ b/errno/errname.go @@ -1119,4 +1119,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{ ErrPrometheusAddrIsNotSet: mysql.Message("Prometheus address is not set in PD and etcd", nil), ErrTiKVStaleCommand: mysql.Message("TiKV server reports stale command", nil), ErrTiKVMaxTimestampNotSynced: mysql.Message("TiKV max timestamp is not synced", nil), + + // Serverless Tier errors. + ErrNotSupportedOnServerless: mysql.Message("'%s' is not supported on the Serverless Tier.", nil), } diff --git a/errors.toml b/errors.toml index 4ab8a0d625bd2..31b45909cee74 100644 --- a/errors.toml +++ b/errors.toml @@ -1071,6 +1071,11 @@ error = ''' %s is not supported. Reason: %s. Try %s. ''' +["ddl:20001"] +error = ''' +'%s' is not supported on the Serverless Tier. +''' + ["ddl:3102"] error = ''' Expression of generated column '%s' contains a disallowed function. diff --git a/planner/core/BUILD.bazel b/planner/core/BUILD.bazel index 650ee4a7c6552..ff593eb6766d0 100644 --- a/planner/core/BUILD.bazel +++ b/planner/core/BUILD.bazel @@ -137,6 +137,7 @@ go_library( "//util/ranger", "//util/rowcodec", "//util/sem", + "//util/serverless", "//util/set", "//util/size", "//util/sqlexec", diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index ce1d889b06e58..814595bb48791 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -63,6 +63,7 @@ import ( utilparser "github.com/pingcap/tidb/util/parser" "github.com/pingcap/tidb/util/ranger" "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/util/serverless" "github.com/pingcap/tidb/util/set" "github.com/pingcap/tidb/util/sqlexec" "github.com/pingcap/tidb/util/stmtsummary" @@ -777,6 +778,10 @@ func (b *PlanBuilder) ResetForReuse() *PlanBuilder { // Build builds the ast node to a Plan. func (b *PlanBuilder) Build(ctx context.Context, node ast.Node) (Plan, error) { b.optFlag |= flagPrunColumns + + if err := serverless.VerifyStatement(node); err != nil { + return nil, err + } switch x := node.(type) { case *ast.AdminStmt: return b.buildAdmin(ctx, x) diff --git a/util/dbterror/ddl_terror.go b/util/dbterror/ddl_terror.go index ddacf77c025ef..a914dfce6f2c3 100644 --- a/util/dbterror/ddl_terror.go +++ b/util/dbterror/ddl_terror.go @@ -436,4 +436,7 @@ var ( // ErrNotSupportedYet returns when tidb does not support this feature. ErrNotSupportedYet = ClassDDL.NewStd(mysql.ErrNotSupportedYet) + + // ErrNotSupportedOnServerless returns when user requested a serverless unsupported feature. + ErrNotSupportedOnServerless = ClassDDL.NewStd(mysql.ErrNotSupportedOnServerless) ) diff --git a/util/sem/sem.go b/util/sem/sem.go index 8a6193cf7977e..44751876b864b 100644 --- a/util/sem/sem.go +++ b/util/sem/sem.go @@ -63,14 +63,21 @@ const ( restrictedPriv = "RESTRICTED_" tidbAuditRetractLog = "tidb_audit_redact_log" // sysvar installed by a plugin - placementAdmin = "PLACEMENT_ADMIN" + placementAdmin = "PLACEMENT_ADMIN" + backupAdmin = "BACKUP_ADMIN" + restoreAdmin = "RESTORE_ADMIN" + resourceGroupAdmin = "RESOURCE_GROUP_ADMIN" // Additional tables for serverless tier. - clusterInfo = "cluster_info" - tikvRegionStatus = "tikv_region_status" - tikvStoreStatus = "tikv_store_status" - tiflashSegments = "tiflash_segments" - tiflashTables = "tiflash_tables" + clusterInfo = "cluster_info" + tikvRegionStatus = "tikv_region_status" + tikvStoreStatus = "tikv_store_status" + tiflashSegments = "tiflash_segments" + tiflashTables = "tiflash_tables" + resourceGroups = "resource_groups" + tidbHotRegionsHistory = "tidb_hot_regions_history" + tidbServersInfo = "tidb_servers_info" + // Serverless tier slow query related tables. slowQuery = "slow_query" clusterSlowQuery = "cluster_slow_query" @@ -132,7 +139,8 @@ func IsInvisibleTable(dbLowerName, tblLowerName string) bool { inspectionRules, inspectionSummary, metricsSummary, metricsSummaryByLabel, metricsTables, tidbHotRegions, clusterInfo, tikvRegionStatus, tikvStoreStatus, tiflashSegments, tiflashTables, clusterSlowQuery, slowQuery, statementsSummary, statementsSummaryEvicted, statementsSummaryHistory, clusterStatementsSummary, - clusterStatementsSummaryEvicted, clusterStatementsSummaryHistory: + clusterStatementsSummaryEvicted, clusterStatementsSummaryHistory, resourceGroups, tidbHotRegionsHistory, + tidbServersInfo: return true } case performanceSchema: @@ -191,7 +199,8 @@ func IsInvisibleSysVar(varNameInLower string) bool { variable.TiDBStmtSummaryFileMaxBackups, variable.TiDBStmtSummaryFilename, tidbAuditRetractLog, - variable.TiDBEnableAsyncCommit: + variable.TiDBEnableAsyncCommit, + variable.DataDir: return true } return false @@ -200,7 +209,12 @@ func IsInvisibleSysVar(varNameInLower string) bool { // IsRestrictedPrivilege returns true if the privilege shuld not be satisfied by SUPER // As most dynamic privileges are. func IsRestrictedPrivilege(privNameInUpper string) bool { - if privNameInUpper == placementAdmin { + switch privNameInUpper { + case + placementAdmin, + backupAdmin, + restoreAdmin, + resourceGroupAdmin: return true } if len(privNameInUpper) < 12 { diff --git a/util/serverless/BUILD.bazel b/util/serverless/BUILD.bazel new file mode 100644 index 0000000000000..ff7801db4d30f --- /dev/null +++ b/util/serverless/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "serverless", + srcs = ["feature_control.go"], + importpath = "github.com/pingcap/tidb/util/serverless", + visibility = ["//visibility:public"], + deps = [ + "//parser/ast", + "//parser/model", + "//util/dbterror", + ], +) diff --git a/util/serverless/feature_control.go b/util/serverless/feature_control.go new file mode 100644 index 0000000000000..739b53ff16110 --- /dev/null +++ b/util/serverless/feature_control.go @@ -0,0 +1,332 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package serverless + +import ( + "fmt" + + "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/util/dbterror" +) + +// VerifyStatement checks if the statement node is supported on serverless tier, +// returns error if it's not. +func VerifyStatement(stmt ast.Node) error { + switch x := stmt.(type) { + case *ast.DeallocateStmt, + *ast.DeleteStmt, + *ast.ExecuteStmt, + *ast.ExplainStmt, + *ast.ExplainForStmt, + *ast.TraceStmt, + *ast.InsertStmt, + *ast.LoadDataStmt, + *ast.LockStatsStmt, + *ast.UnlockStatsStmt, + *ast.IndexAdviseStmt, + *ast.PlanReplayerStmt, + *ast.PrepareStmt, + *ast.SelectStmt, + *ast.SetOprStmt, + *ast.UpdateStmt, + *ast.DoStmt, + *ast.SetStmt, + *ast.AnalyzeTableStmt, + *ast.CreateBindingStmt, + *ast.DropBindingStmt, + *ast.SetBindingStmt, + *ast.CompactTableStmt: + return nil + case *ast.AdminStmt: + return verifyAdmin(x) + case *ast.LoadStatsStmt: + return verifyLoadStats(x) + case *ast.ShowStmt: + return verifyShow(x) + case *ast.SetConfigStmt: + return verifySetConfig(x) + case *ast.BinlogStmt, *ast.FlushStmt, *ast.UseStmt, *ast.BRIEStmt, + *ast.BeginStmt, *ast.CommitStmt, *ast.SavepointStmt, *ast.ReleaseSavepointStmt, *ast.RollbackStmt, *ast.CreateUserStmt, *ast.SetPwdStmt, *ast.AlterInstanceStmt, + *ast.GrantStmt, *ast.DropUserStmt, *ast.AlterUserStmt, *ast.RevokeStmt, *ast.KillStmt, *ast.DropStatsStmt, + *ast.GrantRoleStmt, *ast.RevokeRoleStmt, *ast.SetRoleStmt, *ast.SetDefaultRoleStmt, *ast.ShutdownStmt, + *ast.RenameUserStmt, *ast.NonTransactionalDMLStmt, *ast.SetSessionStatesStmt: + return verifySimple(x) + case ast.DDLNode: + return verifyDDL(x) + case *ast.ChangeStmt: + return verifyChange(x) + case *ast.SplitRegionStmt: + return verifySplitRegion(x) + } + + return nil +} +func verifyDDL(stmt ast.DDLNode) error { + switch s := stmt.(type) { + case + *ast.CreateDatabaseStmt, + *ast.AlterDatabaseStmt, + *ast.DropDatabaseStmt, + *ast.DropTableStmt, + *ast.DropSequenceStmt, + *ast.RenameTableStmt, + *ast.CreateViewStmt, + *ast.CreateSequenceStmt, + *ast.CreateIndexStmt, + *ast.DropIndexStmt, + *ast.LockTablesStmt, + *ast.UnlockTablesStmt, + *ast.CleanupTableLockStmt, + *ast.RepairTableStmt, + *ast.TruncateTableStmt, + *ast.AlterSequenceStmt: + return nil + case *ast.CreateTableStmt: + for _, option := range s.Options { + if option.Tp == ast.TableOptionTTL || + option.Tp == ast.TableOptionTTLEnable || + option.Tp == ast.TableOptionTTLJobInterval { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("TTL") + } + } + return nil + case *ast.AlterTableStmt: + for _, spec := range s.Specs { + if spec.Tp == ast.AlterTableRemoveTTL { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("TTL") + } + } + return nil + case *ast.RecoverTableStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("RECOVER TABLE") + case *ast.FlashBackDatabaseStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("FLASHBACK DATABASE") + case *ast.DropPlacementPolicyStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("DROP PLACEMENT POLICY") + case *ast.DropResourceGroupStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("DROP RESOURCE GROUP") + case *ast.CreatePlacementPolicyStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("CREATE PLACEMENT POLICY") + case *ast.CreateResourceGroupStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("CREATE RESOURCE GROUP") + case *ast.FlashBackToTimestampStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("FLASHBACK CLUSTER") + case *ast.FlashBackTableStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("FLASHBACK TABLE") + case *ast.AlterPlacementPolicyStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ALTER PLACEMENT POLICY") + case *ast.AlterResourceGroupStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ALTER RESOURCE GROUP") + + } + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause(fmt.Sprintf("Unsupported DDL %T", stmt)) +} + +func verifySimple(stmt ast.Node) error { + switch s := stmt.(type) { + case + *ast.GrantRoleStmt, + *ast.FlushStmt, + *ast.BeginStmt, + *ast.CommitStmt, + *ast.SavepointStmt, + *ast.ReleaseSavepointStmt, + *ast.RollbackStmt, + *ast.CreateUserStmt, + *ast.AlterUserStmt, + *ast.DropUserStmt, + *ast.RenameUserStmt, + *ast.SetPwdStmt, + *ast.SetSessionStatesStmt, + *ast.KillStmt, + *ast.BinlogStmt, + *ast.DropStatsStmt, + *ast.SetRoleStmt, + *ast.RevokeRoleStmt, + *ast.SetDefaultRoleStmt, + *ast.AdminStmt, + *ast.GrantStmt, + *ast.RevokeStmt, + *ast.NonTransactionalDMLStmt: + return nil + case *ast.UseStmt: + dbname := model.NewCIStr(s.DBName) + if dbname.L == "metrics_schema" { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("METRICS_SCHEMA") + } + return nil + case *ast.AlterInstanceStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ALTER INSTANCE") + case *ast.ShutdownStmt: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHUTDOWN") + case *ast.BRIEStmt: + return verifyBRIE(s) + } + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause(fmt.Sprintf("Unsupported Executor %T", stmt)) +} + +func verifyBRIE(stmt *ast.BRIEStmt) error { + switch stmt.Kind { + case ast.BRIEKindBackup: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("BACKUP") + case ast.BRIEKindRestore: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("RESTORE") + } + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("BRIE") +} + +func verifyShow(stmt *ast.ShowStmt) error { + switch stmt.Tp { + case + ast.ShowNone, + ast.ShowEngines, + ast.ShowDatabases, + ast.ShowTables, + ast.ShowTableStatus, + ast.ShowColumns, + ast.ShowWarnings, + ast.ShowCharset, + ast.ShowVariables, + ast.ShowStatus, + ast.ShowCollation, + ast.ShowCreateTable, + ast.ShowCreateView, + ast.ShowCreateUser, + ast.ShowCreateSequence, + ast.ShowGrants, + ast.ShowTriggers, + ast.ShowProcedureStatus, + ast.ShowIndex, + ast.ShowProcessList, + ast.ShowCreateDatabase, + ast.ShowEvents, + ast.ShowStatsExtended, + ast.ShowStatsMeta, + ast.ShowStatsHistograms, + ast.ShowStatsTopN, + ast.ShowStatsBuckets, + ast.ShowStatsHealthy, + ast.ShowStatsLocked, + ast.ShowHistogramsInFlight, + ast.ShowColumnStatsUsage, + ast.ShowProfile, + ast.ShowProfiles, + ast.ShowMasterStatus, + ast.ShowPrivileges, + ast.ShowErrors, + ast.ShowBindings, + ast.ShowBindingCacheStatus, + ast.ShowOpenTables, + ast.ShowAnalyzeStatus, + ast.ShowRegions, + ast.ShowBuiltins, + ast.ShowTableNextRowId, + ast.ShowImports, + ast.ShowCreateImport, + ast.ShowSessionStates, + // "SHOW CONFIG" command is necessary for lightning to function, + // therefore, it's access is restricted via privileges. + ast.ShowConfig: + return nil + case ast.ShowCreateResourceGroup: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW CREATE RESOURCE GROUP") + case ast.ShowCreatePlacementPolicy: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW CREATE PLACEMENT POLICY") + case ast.ShowPlugins: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PLUGINS") + case ast.ShowDrainerStatus: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW DRAINER STATUS") + case ast.ShowPumpStatus: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PUMP STATUS") + case ast.ShowBackups: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW BACKUPS") + case ast.ShowRestores: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW RESTORES") + case ast.ShowPlacement: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PLACEMENT POLICY") + case ast.ShowPlacementForDatabase: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PLACEMENT FOR DATABASE") + case ast.ShowPlacementForTable: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PLACEMENT FOR TABLE") + case ast.ShowPlacementForPartition: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PLACEMENT FOR PARTITION") + case ast.ShowPlacementLabels: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SHOW PLACEMENT LABELS") + + } + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("Unsupported SHOW type") +} + +func verifyChange(stmt *ast.ChangeStmt) error { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("CHANGE " + stmt.NodeType) +} + +func verifyLoadStats(stmt *ast.LoadStatsStmt) error { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("LOAD STATS") +} + +func verifySplitRegion(stmt *ast.SplitRegionStmt) error { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SPLIT REGION") +} + +func verifyAdmin(stmt *ast.AdminStmt) error { + switch stmt.Tp { + case + ast.AdminShowDDL, + ast.AdminCheckTable, + ast.AdminShowDDLJobs, + ast.AdminCancelDDLJobs, + ast.AdminCheckIndex, + ast.AdminRecoverIndex, + ast.AdminCleanupIndex, + ast.AdminCheckIndexRange, + ast.AdminShowDDLJobQueries, + ast.AdminShowDDLJobQueriesWithRange, + ast.AdminChecksumTable, + ast.AdminShowNextRowID, + ast.AdminReloadExprPushdownBlacklist, + ast.AdminReloadOptRuleBlacklist, + ast.AdminFlushBindings, + ast.AdminCaptureBindings, + ast.AdminEvolveBindings, + ast.AdminReloadBindings, + ast.AdminReloadStatistics, + ast.AdminFlushPlanCache: + return nil + case + ast.AdminPluginDisable: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ADMIN PLUGIN DISABLE") + case ast.AdminPluginEnable: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ADMIN PLUGIN ENABLE") + case ast.AdminShowSlow: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ADMIN SHOW SLOW") + case ast.AdminShowTelemetry: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ADMIN SHOW TELEMETRY") + case ast.AdminResetTelemetryID: + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("ADMIN RESET TELEMETRY ID") + } + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause(fmt.Sprintf("Unsupported statement: %T", stmt)) +} + +func verifySetConfig(stmt *ast.SetConfigStmt) error { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("SET CONFIG") +} + +// VerifyTTLInfo checks if ttl info is valid on serverless tier, +// Unlike other checks, this check is inserted when CREATE TABLE statement execute. +func VerifyTTLInfo(schema model.CIStr, tblInfo *model.TableInfo) error { + return dbterror.ErrNotSupportedOnServerless.GenWithStackByCause("TTL") +}