Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ddl: prevent dropping in-use resource group #40716

Merged
merged 10 commits into from
Jan 19, 2023
1 change: 1 addition & 0 deletions ddl/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ go_library(
"//parser/opcode",
"//parser/terror",
"//parser/types",
"//privilege",
"//sessionctx",
"//sessionctx/binloginfo",
"//sessionctx/stmtctx",
Expand Down
12 changes: 12 additions & 0 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/parser/terror"
field_types "github.com/pingcap/tidb/parser/types"
"github.com/pingcap/tidb/privilege"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/sessiontxn"
Expand Down Expand Up @@ -7668,6 +7669,17 @@ func (d *ddl) DropResourceGroup(ctx sessionctx.Context, stmt *ast.DropResourceGr
return err
}

// check to see if some user has dependency on the group
checker := privilege.GetPrivilegeManager(ctx)
if checker == nil {
return errors.New("miss privilege checker")
}
user, matched := checker.MatchUserResourceGroupName(groupName.L)
if matched {
err = errors.Errorf("user [%s] depends on the resource group to drop", user)
return err
}

job := &model.Job{
SchemaID: group.ID,
SchemaName: group.Name.L,
Expand Down
4 changes: 4 additions & 0 deletions ddl/resource_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ func TestResourceGroupBasic(t *testing.T) {
tk.MustGetErrCode("create user usr_fail resource group nil_group", mysql.ErrResourceGroupNotExists)
tk.MustExec("create user user2")
tk.MustGetErrCode("alter user user2 resource group nil_group", mysql.ErrResourceGroupNotExists)

tk.MustExec("create resource group do_not_delete_rg rru_per_sec=100 wru_per_sec=200")
tk.MustExec("create user usr3 resource group do_not_delete_rg")
tk.MustContainErrMsg("drop resource group do_not_delete_rg", "user [usr3] depends on the resource group to drop")
}

func testResourceGroupNameFromIS(t *testing.T, ctx sessionctx.Context, name string) *model.ResourceGroupInfo {
Expand Down
2 changes: 1 addition & 1 deletion errno/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{
ErrResourceGroupNotExists: mysql.Message("Unknown resource group '%-.192s'", nil),

ErrColumnInChange: mysql.Message("column %s id %d does not exist, this column may have been updated by other DDL ran in parallel", nil),
ErrResourceGroupSupportDisabled: mysql.Message("Resource group feature is disabled", nil),
ErrResourceGroupSupportDisabled: mysql.Message("Resource control feature is disabled. Run `SET GLOBAL tidb_enable_resource_control='on'` to enable the feature", nil),

// TiKV/PD errors.
ErrPDServerTimeout: mysql.Message("PD server timeout: %s", nil),
Expand Down
2 changes: 1 addition & 1 deletion errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2538,7 +2538,7 @@ Unknown resource group '%-.192s'

["schema:8250"]
error = '''
Resource group feature is disabled
Resource control feature is disabled. Run `SET GLOBAL tidb_enable_resource_control='on'` to enable the feature
'''

["session:8002"]
Expand Down
3 changes: 3 additions & 0 deletions privilege/privilege.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ type Manager interface {
// MatchIdentity matches an identity
MatchIdentity(user, host string, skipNameResolve bool) (string, string, bool)

// MatchUserResourceGroupName matches a user with specified resource group name
MatchUserResourceGroupName(resourceGroupName string) (string, bool)

// DBIsVisible returns true is the database is visible to current user.
DBIsVisible(activeRole []*auth.RoleIdentity, db string) bool

Expand Down
11 changes: 11 additions & 0 deletions privilege/privileges/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,17 @@ func (p *MySQLPrivilege) matchIdentity(user, host string, skipNameResolve bool)
return nil
}

// matchResoureGroup finds an identity to match resource group.
func (p *MySQLPrivilege) matchResoureGroup(resourceGroupName string) *UserRecord {
for i := 0; i < len(p.User); i++ {
record := &p.User[i]
if record.ResourceGroup == resourceGroupName {
return record
}
}
return nil
}

// connectionVerification verifies the username + hostname according to exact
// match from the mysql.user privilege table. call matchIdentity() first if you
// do not have an exact match yet.
Expand Down
10 changes: 10 additions & 0 deletions privilege/privileges/privileges.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ func (p *UserPrivileges) MatchIdentity(user, host string, skipNameResolve bool)
return "", "", false
}

// MatchUserResourceGroupName implements the Manager interface.
func (p *UserPrivileges) MatchUserResourceGroupName(resourceGroupName string) (u string, success bool) {
mysqlPriv := p.Handle.Get()
record := mysqlPriv.matchResoureGroup(resourceGroupName)
if record != nil {
return record.User, true
}
return "", false
}

// GetAuthWithoutVerification implements the Manager interface.
func (p *UserPrivileges) GetAuthWithoutVerification(user, host string) (success bool) {
if SkipWithGrant {
Expand Down