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

*: support ATTRIBUTE and COMMENT in CREATE USER and ALTER USER statements #38201

Merged
merged 39 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1086ffa
parser
CbcWestwolf Sep 27, 2022
d22b595
Fix
CbcWestwolf Sep 27, 2022
3d72855
Add `user_attributes` column to `mysql.user`; add view
CbcWestwolf Sep 27, 2022
b503ee1
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Sep 27, 2022
077f414
Fix UT fail
CbcWestwolf Sep 27, 2022
d1fd53f
Support `create user`; remain a `mysql.user` privilege problem
CbcWestwolf Sep 28, 2022
f3eb749
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Sep 28, 2022
155a3f4
Support `alter user`
CbcWestwolf Sep 28, 2022
9168a27
Merge branch 'master' into add_more_comments
CbcWestwolf Sep 28, 2022
1b56faf
Fix
CbcWestwolf Sep 28, 2022
1b93aa1
Merge branch 'master' into add_more_comments
CbcWestwolf Sep 29, 2022
e9be234
show create user
CbcWestwolf Sep 29, 2022
9925384
Fix
CbcWestwolf Sep 29, 2022
122b5ff
Move user_attributes view from mysql db to infoschema db
CbcWestwolf Oct 6, 2022
356df5b
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Oct 6, 2022
1f5a049
Add test of different user
CbcWestwolf Oct 6, 2022
a9328be
fmt
CbcWestwolf Oct 6, 2022
ce2dcc7
bazel
CbcWestwolf Oct 7, 2022
e39ef88
Merge branch 'master' into add_more_comments
CbcWestwolf Oct 8, 2022
7bd4a8f
Fix
CbcWestwolf Oct 8, 2022
9e09a6e
Merge branch 'add_more_comments' of github.com:CbcWestwolf/tidb into …
CbcWestwolf Oct 8, 2022
eb54607
Fix UT
CbcWestwolf Oct 8, 2022
09c980e
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Oct 10, 2022
d715049
Merge branch 'master' into add_more_comments
CbcWestwolf Oct 10, 2022
23931fd
Merge branch 'master' into add_more_comments
CbcWestwolf Oct 11, 2022
00a33a0
Merge branch 'master' into add_more_comments
CbcWestwolf Oct 13, 2022
7fc99d3
Merge branch 'master' into add_more_comments
CbcWestwolf Oct 17, 2022
93fb320
Merge branch 'master' into add_more_comments
CbcWestwolf Oct 19, 2022
3e10fb9
fmt
CbcWestwolf Oct 19, 2022
5cd4f2c
Update
CbcWestwolf Oct 19, 2022
3e35029
Handle <nil>
CbcWestwolf Oct 19, 2022
da4edef
Remove getNewAttributes
CbcWestwolf Oct 19, 2022
56be030
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Oct 19, 2022
952aa1d
bazel
CbcWestwolf Oct 19, 2022
903bca9
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Oct 19, 2022
1420679
Merge branch 'master' of github.com:pingcap/tidb into add_more_comments
CbcWestwolf Oct 19, 2022
80408dd
remove oldAttributesStr
CbcWestwolf Oct 20, 2022
ef1532b
refactor `executeAlterUser`
CbcWestwolf Oct 20, 2022
d68f4d1
Merge branch 'master' into add_more_comments
ti-chi-bot Oct 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions executor/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ go_test(
"@com_github_tikv_client_go_v2//testutils",
"@com_github_tikv_client_go_v2//tikv",
"@com_github_tikv_client_go_v2//tikvrpc",
"@com_github_tikv_client_go_v2//util",
"@org_golang_google_grpc//:grpc",
"@org_golang_x_exp//slices",
"@org_uber_go_atomic//:atomic",
Expand Down
1 change: 1 addition & 0 deletions executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,7 @@ func (b *executorBuilder) buildMemTable(v *plannercore.PhysicalMemTable) Executo
strings.ToLower(infoschema.TablePlacementPolicies),
strings.ToLower(infoschema.TableTrxSummary),
strings.ToLower(infoschema.TableVariablesInfo),
strings.ToLower(infoschema.TableUserAttributes),
strings.ToLower(infoschema.ClusterTableTrxSummary):
return &MemTableReaderExec{
baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ID()),
Expand Down
31 changes: 31 additions & 0 deletions executor/infoschema_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ func (e *memtableRetriever) retrieve(ctx context.Context, sctx sessionctx.Contex
err = e.setDataForClusterTrxSummary(sctx)
case infoschema.TableVariablesInfo:
err = e.setDataForVariablesInfo(sctx)
case infoschema.TableUserAttributes:
err = e.setDataForUserAttributes(ctx, sctx)
}
if err != nil {
return nil, err
Expand Down Expand Up @@ -435,6 +437,35 @@ func (e *memtableRetriever) setDataForVariablesInfo(ctx sessionctx.Context) erro
return nil
}

func (e *memtableRetriever) setDataForUserAttributes(ctx context.Context, sctx sessionctx.Context) error {
exec, _ := sctx.(sqlexec.RestrictedSQLExecutor)
chunkRows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT user, host, JSON_UNQUOTE(JSON_EXTRACT(user_attributes, '$.metadata')) FROM mysql.user`)
if err != nil {
return err
}
if len(chunkRows) == 0 {
return nil
}
rows := make([][]types.Datum, 0, len(chunkRows))
for _, chunkRow := range chunkRows {
if chunkRow.Len() != 3 {
continue
}
user := chunkRow.GetString(0)
host := chunkRow.GetString(1)
// Compatible with results in MySQL
var attribute any
if attribute = chunkRow.GetString(2); attribute == "" {
attribute = nil
}
row := types.MakeDatums(user, host, attribute)
rows = append(rows, row)
}

e.rows = rows
return nil
}

func (e *memtableRetriever) setDataFromSchemata(ctx sessionctx.Context, schemas []*model.DBInfo) {
checker := privilege.GetPrivilegeManager(ctx)
rows := make([][]types.Datum, 0, len(schemas))
Expand Down
13 changes: 10 additions & 3 deletions executor/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,9 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {

exec := e.ctx.(sqlexec.RestrictedSQLExecutor)

rows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT plugin, Account_locked FROM %n.%n WHERE User=%? AND Host=%?`, mysql.SystemDB, mysql.UserTable, userName, strings.ToLower(hostName))
rows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT plugin, Account_locked, JSON_UNQUOTE(JSON_EXTRACT(user_attributes, '$.metadata'))
FROM %n.%n WHERE User=%? AND Host=%?`,
mysql.SystemDB, mysql.UserTable, userName, strings.ToLower(hostName))
if err != nil {
return errors.Trace(err)
}
Expand All @@ -1550,6 +1552,11 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {
accountLocked = "UNLOCK"
}

userAttributes := rows[0].GetString(2)
if len(userAttributes) > 0 {
userAttributes = " ATTRIBUTE " + userAttributes
}

rows, _, err = exec.ExecRestrictedSQL(ctx, nil, `SELECT Priv FROM %n.%n WHERE User=%? AND Host=%?`, mysql.SystemDB, mysql.GlobalPrivTable, userName, hostName)
if err != nil {
return errors.Trace(err)
Expand All @@ -1573,8 +1580,8 @@ func (e *ShowExec) fetchShowCreateUser(ctx context.Context) error {
}

// FIXME: the returned string is not escaped safely
showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH '%s'%s REQUIRE %s PASSWORD EXPIRE DEFAULT ACCOUNT %s",
e.User.Username, e.User.Hostname, authplugin, authStr, require, accountLocked)
showStr := fmt.Sprintf("CREATE USER '%s'@'%s' IDENTIFIED WITH '%s'%s REQUIRE %s PASSWORD EXPIRE DEFAULT ACCOUNT %s%s",
e.User.Username, e.User.Hostname, authplugin, authStr, require, accountLocked, userAttributes)
e.appendRow([]interface{}{showStr})
return nil
}
Expand Down
6 changes: 6 additions & 0 deletions executor/showtest/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,12 @@ func TestShowCreateUser(t *testing.T) {
tk.MustExec("CREATE USER 'lockness'@'%' IDENTIFIED BY 'monster' ACCOUNT LOCK")
rows = tk.MustQuery("SHOW CREATE USER 'lockness'@'%'")
require.Equal(t, "CREATE USER 'lockness'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*BC05309E7FE12AFD4EBB9FFE7E488A6320F12FF3' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK", rows.Rows()[0][0].(string))

// Test COMMENT and ATTRIBUTE
tk.MustExec("CREATE USER commentUser COMMENT '1234'")
tk.MustQuery("SHOW CREATE USER commentUser").Check(testkit.Rows(`CREATE USER 'commentUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ATTRIBUTE {"comment": "1234"}`))
tk.MustExec(`CREATE USER attributeUser attribute '{"name": "Tom", "age": 19}'`)
tk.MustQuery("SHOW CREATE USER attributeUser").Check(testkit.Rows(`CREATE USER 'attributeUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ATTRIBUTE {"age": 19, "name": "Tom"}`))
}

func TestUnprivilegedShow(t *testing.T) {
Expand Down
47 changes: 33 additions & 14 deletions executor/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -810,27 +810,35 @@ func (e *SimpleExec) executeCreateUser(ctx context.Context, s *ast.CreateUserStm
return err
}

lockAccount := false
if len(s.PasswordOrLockOptions) > 0 {
lockAccount := "N"
if length := len(s.PasswordOrLockOptions); length > 0 {
// If "ACCOUNT LOCK" or "ACCOUNT UNLOCK" appears many times,
// the last declaration takes effect.
for i := len(s.PasswordOrLockOptions) - 1; i >= 0; i-- {
for i := length - 1; i >= 0; i-- {
if s.PasswordOrLockOptions[i].Type == ast.Lock {
lockAccount = true
lockAccount = "Y"
break
} else if s.PasswordOrLockOptions[i].Type == ast.Unlock {
break
}
}
}
if s.IsCreateRole {
lockAccount = "Y"
}

sql := new(strings.Builder)
if s.IsCreateRole || lockAccount {
sqlexec.MustFormatSQL(sql, `INSERT INTO %n.%n (Host, User, authentication_string, plugin, Account_locked) VALUES `, mysql.SystemDB, mysql.UserTable)
} else {
sqlexec.MustFormatSQL(sql, `INSERT INTO %n.%n (Host, User, authentication_string, plugin) VALUES `, mysql.SystemDB, mysql.UserTable)
var userAttributes any = nil
if s.CommentOrAttributeOption != nil {
if s.CommentOrAttributeOption.Type == ast.UserCommentType {
userAttributes = fmt.Sprintf("{\"metadata\": {\"comment\": \"%s\"}}", s.CommentOrAttributeOption.Value)
} else if s.CommentOrAttributeOption.Type == ast.UserAttributeType {
userAttributes = fmt.Sprintf("{\"metadata\": %s}", s.CommentOrAttributeOption.Value)
}
}

sql := new(strings.Builder)
sqlexec.MustFormatSQL(sql, `INSERT INTO %n.%n (Host, User, authentication_string, plugin, user_attributes, Account_locked) VALUES `, mysql.SystemDB, mysql.UserTable)

users := make([]*auth.UserIdentity, 0, len(s.Specs))
for _, spec := range s.Specs {
if len(spec.User.Username) > auth.UserNameMaxLength {
Expand Down Expand Up @@ -875,11 +883,7 @@ func (e *SimpleExec) executeCreateUser(ctx context.Context, s *ast.CreateUserStm
}

hostName := strings.ToLower(spec.User.Hostname)
if s.IsCreateRole || lockAccount {
sqlexec.MustFormatSQL(sql, `(%?, %?, %?, %?, %?)`, hostName, spec.User.Username, pwd, authPlugin, "Y")
} else {
sqlexec.MustFormatSQL(sql, `(%?, %?, %?, %?)`, hostName, spec.User.Username, pwd, authPlugin)
}
sqlexec.MustFormatSQL(sql, `(%?, %?, %?, %?, %?, %?)`, hostName, spec.User.Username, pwd, authPlugin, userAttributes, lockAccount)
users = append(users, spec.User)
}
if len(users) == 0 {
Expand Down Expand Up @@ -1053,6 +1057,21 @@ func (e *SimpleExec) executeAlterUser(ctx context.Context, s *ast.AlterUserStmt)
}
}

if s.CommentOrAttributeOption != nil {
newAttributesStr := ""
if s.CommentOrAttributeOption.Type == ast.UserCommentType {
newAttributesStr = fmt.Sprintf(`{"metadata": {"comment": "%s"}}`, s.CommentOrAttributeOption.Value)
} else {
newAttributesStr = fmt.Sprintf(`{"metadata": %s}`, s.CommentOrAttributeOption.Value)
}
_, _, err := exec.ExecRestrictedSQL(ctx, nil,
`UPDATE %n.%n SET user_attributes=json_merge_patch(user_attributes, %?) WHERE Host=%? and User=%?;`,
mysql.SystemDB, mysql.UserTable, newAttributesStr, spec.User.Hostname, spec.User.Username)
if err != nil {
failedUsers = append(failedUsers, spec.User.String())
}
}

if len(privData) > 0 {
_, _, err := exec.ExecRestrictedSQL(ctx, nil, "INSERT INTO %n.%n (Host, User, Priv) VALUES (%?,%?,%?) ON DUPLICATE KEY UPDATE Priv = values(Priv)", mysql.SystemDB, mysql.GlobalPrivTable, spec.User.Hostname, spec.User.Username, string(hack.String(privData)))
if err != nil {
Expand Down
45 changes: 45 additions & 0 deletions executor/simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
package executor_test

import (
"context"
"fmt"
"strconv"
"testing"

"github.com/pingcap/tidb/config"
"github.com/pingcap/tidb/parser/auth"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/server"
"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/util"
"github.com/stretchr/testify/require"
tikvutil "github.com/tikv/client-go/v2/util"
)

func TestKillStmt(t *testing.T) {
Expand Down Expand Up @@ -76,3 +81,43 @@ func TestKillStmt(t *testing.T) {

// remote kill is tested in `tests/globalkilltest`
}

func TestUserAttributes(t *testing.T) {
store, _ := testkit.CreateMockStoreAndDomain(t)
rootTK := testkit.NewTestKit(t, store)
ctx := context.WithValue(context.Background(), tikvutil.RequestSourceKey, tikvutil.RequestSource{RequestSourceInternal: true})

// https://dev.mysql.com/doc/refman/8.0/en/create-user.html#create-user-comments-attributes
rootTK.MustExec(`CREATE USER testuser COMMENT '1234'`)
rootTK.MustExec(`CREATE USER testuser1 ATTRIBUTE '{"name": "Tom", "age": 19}'`)
_, err := rootTK.Exec(`CREATE USER testuser2 ATTRIBUTE '{"name": "Tom", age: 19}'`)
rootTK.MustExec(`CREATE USER testuser2`)
require.Error(t, err)
rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser'`).Check(testkit.Rows(`{"metadata": {"comment": "1234"}}`))
rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser1'`).Check(testkit.Rows(`{"metadata": {"age": 19, "name": "Tom"}}`))
rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser2'`).Check(testkit.Rows(`<nil>`))
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser'`).Check(testkit.Rows(`{"comment": "1234"}`))
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 19, "name": "Tom"}`))
rootTK.MustQueryWithContext(ctx, `SELECT attribute->>"$.age" AS age, attribute->>"$.name" AS name FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`19 Tom`))
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser2'`).Check(testkit.Rows(`<nil>`))

// https://dev.mysql.com/doc/refman/8.0/en/alter-user.html#alter-user-comments-attributes
rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"age": 20, "sex": "male"}'`)
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom", "sex": "male"}`))
rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"sex": null}'`)
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom"}`))
rootTK.MustExec(`ALTER USER testuser1 COMMENT '5678'`)
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "comment": "5678", "name": "Tom"}`))
rootTK.MustExec(`ALTER USER testuser1 COMMENT ''`)
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "comment": "", "name": "Tom"}`))
rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"comment": null}'`)
rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom"}`))

// Non-root users could access COMMENT or ATTRIBUTE of all users via the view,
// but not via the mysql.user table.
tk := testkit.NewTestKit(t, store)
require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "testuser1"}, nil, nil))
tk.MustQueryWithContext(ctx, `SELECT user, host, attribute FROM information_schema.user_attributes ORDER BY user`).Check(
testkit.Rows("root % <nil>", "testuser % {\"comment\": \"1234\"}", "testuser1 % {\"age\": 20, \"name\": \"Tom\"}", "testuser2 % <nil>"))
tk.MustGetErrCode(`SELECT user, host, user_attributes FROM mysql.user ORDER BY user`, mysql.ErrTableaccessDenied)
}
10 changes: 10 additions & 0 deletions infoschema/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ const (
TableTrxSummary = "TRX_SUMMARY"
// TableVariablesInfo is the string constant of variables_info table.
TableVariablesInfo = "VARIABLES_INFO"
// TableUserAttributes is the string constant of user_attributes view.
TableUserAttributes = "USER_ATTRIBUTES"
)

const (
Expand Down Expand Up @@ -285,6 +287,7 @@ var tableIDMap = map[string]int64{
TableTrxSummary: autoid.InformationSchemaDBID + 80,
ClusterTableTrxSummary: autoid.InformationSchemaDBID + 81,
TableVariablesInfo: autoid.InformationSchemaDBID + 82,
TableUserAttributes: autoid.InformationSchemaDBID + 83,
}

// columnInfo represents the basic column information of all kinds of INFORMATION_SCHEMA tables
Expand Down Expand Up @@ -1536,6 +1539,12 @@ var tableVariablesInfoCols = []columnInfo{
{name: "IS_NOOP", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag},
}

var tableUserAttributesCols = []columnInfo{
{name: "USER", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag},
{name: "HOST", tp: mysql.TypeVarchar, size: 255, flag: mysql.NotNullFlag},
{name: "ATTRIBUTE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength},
}

// GetShardingInfo returns a nil or description string for the sharding information of given TableInfo.
// The returned description string may be:
// - "NOT_SHARDED": for tables that SHARD_ROW_ID_BITS is not specified.
Expand Down Expand Up @@ -1995,6 +2004,7 @@ var tableNameToColumns = map[string][]columnInfo{
TablePlacementPolicies: tablePlacementPoliciesCols,
TableTrxSummary: tableTrxSummaryCols,
TableVariablesInfo: tableVariablesInfoCols,
TableUserAttributes: tableUserAttributesCols,
}

func createInfoSchemaTable(_ autoid.Allocators, meta *model.TableInfo) (table.Table, error) {
Expand Down
63 changes: 49 additions & 14 deletions parser/ast/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,9 @@ const (
PasswordExpireInterval
Lock
Unlock

UserCommentType
UserAttributeType
)

type PasswordOrLockOption struct {
Expand Down Expand Up @@ -1486,17 +1489,34 @@ func (p *PasswordOrLockOption) Restore(ctx *format.RestoreCtx) error {
return nil
}

type CommentOrAttributeOption struct {
Type int
Value string
}

func (c *CommentOrAttributeOption) Restore(ctx *format.RestoreCtx) error {
if c.Type == UserCommentType {
ctx.WriteKeyWord(" COMMENT ")
ctx.WriteString(c.Value)
} else if c.Type == UserAttributeType {
ctx.WriteKeyWord(" ATTRIBUTE ")
ctx.WriteString(c.Value)
}
return nil
}

// CreateUserStmt creates user account.
// See https://dev.mysql.com/doc/refman/5.7/en/create-user.html
// See https://dev.mysql.com/doc/refman/8.0/en/create-user.html
type CreateUserStmt struct {
stmtNode

IsCreateRole bool
IfNotExists bool
Specs []*UserSpec
TLSOptions []*TLSOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
IsCreateRole bool
IfNotExists bool
Specs []*UserSpec
TLSOptions []*TLSOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
CommentOrAttributeOption *CommentOrAttributeOption
}

// Restore implements Node interface.
Expand Down Expand Up @@ -1548,6 +1568,13 @@ func (n *CreateUserStmt) Restore(ctx *format.RestoreCtx) error {
return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.PasswordOrLockOptions[%d]", i)
}
}

if n.CommentOrAttributeOption != nil {
if err := n.CommentOrAttributeOption.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.CommentOrAttributeOption")
}
}

return nil
}

Expand All @@ -1573,16 +1600,17 @@ func (n *CreateUserStmt) SecureText() string {
}

// AlterUserStmt modifies user account.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-user.html
// See https://dev.mysql.com/doc/refman/8.0/en/alter-user.html
type AlterUserStmt struct {
stmtNode

IfExists bool
CurrentAuth *AuthOption
Specs []*UserSpec
TLSOptions []*TLSOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
IfExists bool
CurrentAuth *AuthOption
Specs []*UserSpec
TLSOptions []*TLSOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
CommentOrAttributeOption *CommentOrAttributeOption
}

// Restore implements Node interface.
Expand Down Expand Up @@ -1637,6 +1665,13 @@ func (n *AlterUserStmt) Restore(ctx *format.RestoreCtx) error {
return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.PasswordOrLockOptions[%d]", i)
}
}

if n.CommentOrAttributeOption != nil {
if err := n.CommentOrAttributeOption.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.CommentOrAttributeOption")
}
}

return nil
}

Expand Down
Loading