github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/grant.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package interlock
    15  
    16  import (
    17  	"context"
    18  	"encoding/json"
    19  	"fmt"
    20  	"strings"
    21  
    22  	"github.com/whtcorpsinc/errors"
    23  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    24  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    25  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    26  	"github.com/whtcorpsinc/milevadb/petri"
    27  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    28  	"github.com/whtcorpsinc/milevadb/privilege/privileges"
    29  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    30  	"github.com/whtcorpsinc/milevadb/causet"
    31  	"github.com/whtcorpsinc/milevadb/soliton"
    32  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    33  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    34  	"github.com/whtcorpsinc/milevadb/soliton/sqlexec"
    35  	"go.uber.org/zap"
    36  )
    37  
    38  /***
    39   * Grant Statement
    40   * See https://dev.allegrosql.com/doc/refman/5.7/en/grant.html
    41   ************************************************************************************/
    42  var (
    43  	_ InterlockingDirectorate = (*GrantInterDirc)(nil)
    44  )
    45  
    46  // GrantInterDirc executes GrantStmt.
    47  type GrantInterDirc struct {
    48  	baseInterlockingDirectorate
    49  
    50  	Privs      []*ast.PrivElem
    51  	ObjectType ast.ObjectTypeType
    52  	Level      *ast.GrantLevel
    53  	Users      []*ast.UserSpec
    54  	TLSOptions []*ast.TLSOption
    55  
    56  	is        schemareplicant.SchemaReplicant
    57  	WithGrant bool
    58  	done      bool
    59  }
    60  
    61  // Next implements the InterlockingDirectorate Next interface.
    62  func (e *GrantInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
    63  	if e.done {
    64  		return nil
    65  	}
    66  	e.done = true
    67  
    68  	dbName := e.Level.DBName
    69  	if len(dbName) == 0 {
    70  		dbName = e.ctx.GetStochastikVars().CurrentDB
    71  	}
    72  
    73  	// Make sure the causet exist.
    74  	if e.Level.Level == ast.GrantLevelBlock {
    75  		dbNameStr := perceptron.NewCIStr(dbName)
    76  		schemaReplicant := schemareplicant.GetSchemaReplicant(e.ctx)
    77  		tbl, err := schemaReplicant.BlockByName(dbNameStr, perceptron.NewCIStr(e.Level.BlockName))
    78  		if err != nil {
    79  			return err
    80  		}
    81  		err = schemareplicant.ErrBlockNotExists.GenWithStackByArgs(dbName, e.Level.BlockName)
    82  		// Note the causet name compare is case sensitive here.
    83  		if tbl.Meta().Name.String() != e.Level.BlockName {
    84  			return err
    85  		}
    86  		if len(e.Level.DBName) > 0 {
    87  			// The database name should also match.
    88  			EDB, succ := schemaReplicant.SchemaByName(dbNameStr)
    89  			if !succ || EDB.Name.String() != dbName {
    90  				return err
    91  			}
    92  		}
    93  	}
    94  
    95  	// Commit the old transaction, like DBS.
    96  	if err := e.ctx.NewTxn(ctx); err != nil {
    97  		return err
    98  	}
    99  	defer func() { e.ctx.GetStochastikVars().SetStatusFlag(allegrosql.ServerStatusInTrans, false) }()
   100  
   101  	// Create internal stochastik to start internal transaction.
   102  	isCommit := false
   103  	internalStochastik, err := e.getSysStochastik()
   104  	if err != nil {
   105  		return err
   106  	}
   107  	defer func() {
   108  		if !isCommit {
   109  			_, err := internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), "rollback")
   110  			if err != nil {
   111  				logutil.BgLogger().Error("rollback error occur at grant privilege", zap.Error(err))
   112  			}
   113  		}
   114  		e.releaseSysStochastik(internalStochastik)
   115  	}()
   116  
   117  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), "begin")
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	// Check which user is not exist.
   123  	for _, user := range e.Users {
   124  		exists, err := userExists(e.ctx, user.User.Username, user.User.Hostname)
   125  		if err != nil {
   126  			return err
   127  		}
   128  		if !exists && e.ctx.GetStochastikVars().ALLEGROSQLMode.HasNoAutoCreateUserMode() {
   129  			return ErrCantCreateUserWithGrant
   130  		} else if !exists {
   131  			pwd, ok := user.EncodedPassword()
   132  			if !ok {
   133  				return errors.Trace(ErrPasswordFormat)
   134  			}
   135  			user := fmt.Sprintf(`('%s', '%s', '%s')`, user.User.Hostname, user.User.Username, pwd)
   136  			allegrosql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, authentication_string) VALUES %s;`, allegrosql.SystemDB, allegrosql.UserBlock, user)
   137  			_, err := internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(ctx, allegrosql)
   138  			if err != nil {
   139  				return err
   140  			}
   141  		}
   142  	}
   143  
   144  	// Grant for each user
   145  	for _, user := range e.Users {
   146  		// If there is no privilege entry in corresponding causet, insert a new one.
   147  		// Global scope:		allegrosql.global_priv
   148  		// EDB scope:			allegrosql.EDB
   149  		// Block scope:			allegrosql.Blocks_priv
   150  		// DeferredCauset scope:		allegrosql.DeferredCausets_priv
   151  		if e.TLSOptions != nil {
   152  			err = checkAndInitGlobalPriv(internalStochastik, user.User.Username, user.User.Hostname)
   153  			if err != nil {
   154  				return err
   155  			}
   156  		}
   157  		switch e.Level.Level {
   158  		case ast.GrantLevelDB:
   159  			err := checkAndInitDBPriv(internalStochastik, dbName, e.is, user.User.Username, user.User.Hostname)
   160  			if err != nil {
   161  				return err
   162  			}
   163  		case ast.GrantLevelBlock:
   164  			err := checkAndInitBlockPriv(internalStochastik, dbName, e.Level.BlockName, e.is, user.User.Username, user.User.Hostname)
   165  			if err != nil {
   166  				return err
   167  			}
   168  		}
   169  		privs := e.Privs
   170  		if e.WithGrant {
   171  			privs = append(privs, &ast.PrivElem{Priv: allegrosql.GrantPriv})
   172  		}
   173  
   174  		// Grant global priv to user.
   175  		err = e.grantGlobalPriv(internalStochastik, user)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		// Grant each priv to the user.
   180  		for _, priv := range privs {
   181  			if len(priv.DefCauss) > 0 {
   182  				// Check defCausumn scope privilege entry.
   183  				// TODO: Check validity before insert new entry.
   184  				err := e.checkAndInitDeferredCausetPriv(user.User.Username, user.User.Hostname, priv.DefCauss, internalStochastik)
   185  				if err != nil {
   186  					return err
   187  				}
   188  			}
   189  			err := e.grantLevelPriv(priv, user, internalStochastik)
   190  			if err != nil {
   191  				return err
   192  			}
   193  		}
   194  	}
   195  
   196  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), "commit")
   197  	if err != nil {
   198  		return err
   199  	}
   200  	isCommit = true
   201  	petri.GetPetri(e.ctx).NotifyUFIDelatePrivilege(e.ctx)
   202  	return nil
   203  }
   204  
   205  // checkAndInitGlobalPriv checks if global scope privilege entry exists in allegrosql.global_priv.
   206  // If not exists, insert a new one.
   207  func checkAndInitGlobalPriv(ctx stochastikctx.Context, user string, host string) error {
   208  	ok, err := globalPrivEntryExists(ctx, user, host)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	if ok {
   213  		return nil
   214  	}
   215  	// Entry does not exist for user-host-EDB. Insert a new entry.
   216  	return initGlobalPrivEntry(ctx, user, host)
   217  }
   218  
   219  // checkAndInitDBPriv checks if EDB scope privilege entry exists in allegrosql.EDB.
   220  // If unexists, insert a new one.
   221  func checkAndInitDBPriv(ctx stochastikctx.Context, dbName string, is schemareplicant.SchemaReplicant, user string, host string) error {
   222  	ok, err := dbUserExists(ctx, user, host, dbName)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	if ok {
   227  		return nil
   228  	}
   229  	// Entry does not exist for user-host-EDB. Insert a new entry.
   230  	return initDBPrivEntry(ctx, user, host, dbName)
   231  }
   232  
   233  // checkAndInitBlockPriv checks if causet scope privilege entry exists in allegrosql.Blocks_priv.
   234  // If unexists, insert a new one.
   235  func checkAndInitBlockPriv(ctx stochastikctx.Context, dbName, tblName string, is schemareplicant.SchemaReplicant, user string, host string) error {
   236  	ok, err := blockUserExists(ctx, user, host, dbName, tblName)
   237  	if err != nil {
   238  		return err
   239  	}
   240  	if ok {
   241  		return nil
   242  	}
   243  	// Entry does not exist for user-host-EDB-tbl. Insert a new entry.
   244  	return initBlockPrivEntry(ctx, user, host, dbName, tblName)
   245  }
   246  
   247  // checkAndInitDeferredCausetPriv checks if defCausumn scope privilege entry exists in allegrosql.DeferredCausets_priv.
   248  // If unexists, insert a new one.
   249  func (e *GrantInterDirc) checkAndInitDeferredCausetPriv(user string, host string, defcaus []*ast.DeferredCausetName, internalStochastik stochastikctx.Context) error {
   250  	dbName, tbl, err := getTargetSchemaAndBlock(e.ctx, e.Level.DBName, e.Level.BlockName, e.is)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	for _, c := range defcaus {
   255  		defCaus := causet.FindDefCaus(tbl.DefCauss(), c.Name.L)
   256  		if defCaus == nil {
   257  			return errors.Errorf("Unknown defCausumn: %s", c.Name.O)
   258  		}
   259  		ok, err := defCausumnPrivEntryExists(internalStochastik, user, host, dbName, tbl.Meta().Name.O, defCaus.Name.O)
   260  		if err != nil {
   261  			return err
   262  		}
   263  		if ok {
   264  			continue
   265  		}
   266  		// Entry does not exist for user-host-EDB-tbl-defCaus. Insert a new entry.
   267  		err = initDeferredCausetPrivEntry(internalStochastik, user, host, dbName, tbl.Meta().Name.O, defCaus.Name.O)
   268  		if err != nil {
   269  			return err
   270  		}
   271  	}
   272  	return nil
   273  }
   274  
   275  // initGlobalPrivEntry inserts a new event into allegrosql.EDB with empty privilege.
   276  func initGlobalPrivEntry(ctx stochastikctx.Context, user string, host string) error {
   277  	allegrosql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, PRIV) VALUES ('%s', '%s', '%s')`, allegrosql.SystemDB, allegrosql.GlobalPrivBlock, host, user, "{}")
   278  	_, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   279  	return err
   280  }
   281  
   282  // initDBPrivEntry inserts a new event into allegrosql.EDB with empty privilege.
   283  func initDBPrivEntry(ctx stochastikctx.Context, user string, host string, EDB string) error {
   284  	allegrosql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, EDB) VALUES ('%s', '%s', '%s')`, allegrosql.SystemDB, allegrosql.DBBlock, host, user, EDB)
   285  	_, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   286  	return err
   287  }
   288  
   289  // initBlockPrivEntry inserts a new event into allegrosql.Blocks_priv with empty privilege.
   290  func initBlockPrivEntry(ctx stochastikctx.Context, user string, host string, EDB string, tbl string) error {
   291  	allegrosql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, EDB, Block_name, Block_priv, DeferredCauset_priv) VALUES ('%s', '%s', '%s', '%s', '', '')`, allegrosql.SystemDB, allegrosql.BlockPrivBlock, host, user, EDB, tbl)
   292  	_, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   293  	return err
   294  }
   295  
   296  // initDeferredCausetPrivEntry inserts a new event into allegrosql.DeferredCausets_priv with empty privilege.
   297  func initDeferredCausetPrivEntry(ctx stochastikctx.Context, user string, host string, EDB string, tbl string, defCaus string) error {
   298  	allegrosql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, EDB, Block_name, DeferredCauset_name, DeferredCauset_priv) VALUES ('%s', '%s', '%s', '%s', '%s', '')`, allegrosql.SystemDB, allegrosql.DeferredCausetPrivBlock, host, user, EDB, tbl, defCaus)
   299  	_, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   300  	return err
   301  }
   302  
   303  // grantGlobalPriv grants priv to user in global scope.
   304  func (e *GrantInterDirc) grantGlobalPriv(ctx stochastikctx.Context, user *ast.UserSpec) error {
   305  	if len(e.TLSOptions) == 0 {
   306  		return nil
   307  	}
   308  	priv, err := tlsOption2GlobalPriv(e.TLSOptions)
   309  	if err != nil {
   310  		return errors.Trace(err)
   311  	}
   312  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET PRIV = '%s' WHERE User='%s' AND Host='%s'`, allegrosql.SystemDB, allegrosql.GlobalPrivBlock, priv, user.User.Username, user.User.Hostname)
   313  	_, err = ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   314  	return err
   315  }
   316  
   317  func tlsOption2GlobalPriv(tlsOptions []*ast.TLSOption) (priv []byte, err error) {
   318  	if len(tlsOptions) == 0 {
   319  		priv = []byte("{}")
   320  		return
   321  	}
   322  	dupSet := make(map[int]struct{})
   323  	for _, opt := range tlsOptions {
   324  		if _, dup := dupSet[opt.Type]; dup {
   325  			var typeName string
   326  			switch opt.Type {
   327  			case ast.Cipher:
   328  				typeName = "CIPHER"
   329  			case ast.Issuer:
   330  				typeName = "ISSUER"
   331  			case ast.Subject:
   332  				typeName = "SUBJECT"
   333  			case ast.SAN:
   334  				typeName = "SAN"
   335  			}
   336  			err = errors.Errorf("Duplicate require %s clause", typeName)
   337  			return
   338  		}
   339  		dupSet[opt.Type] = struct{}{}
   340  	}
   341  	gp := privileges.GlobalPrivValue{SSLType: privileges.SslTypeNotSpecified}
   342  	for _, tlsOpt := range tlsOptions {
   343  		switch tlsOpt.Type {
   344  		case ast.TslNone:
   345  			gp.SSLType = privileges.SslTypeNone
   346  		case ast.Ssl:
   347  			gp.SSLType = privileges.SslTypeAny
   348  		case ast.X509:
   349  			gp.SSLType = privileges.SslTypeX509
   350  		case ast.Cipher:
   351  			gp.SSLType = privileges.SslTypeSpecified
   352  			if len(tlsOpt.Value) > 0 {
   353  				if _, ok := soliton.SupportCipher[tlsOpt.Value]; !ok {
   354  					err = errors.Errorf("Unsupported cipher suit: %s", tlsOpt.Value)
   355  					return
   356  				}
   357  				gp.SSLCipher = tlsOpt.Value
   358  			}
   359  		case ast.Issuer:
   360  			err = soliton.CheckSupportX509NameOneline(tlsOpt.Value)
   361  			if err != nil {
   362  				return
   363  			}
   364  			gp.SSLType = privileges.SslTypeSpecified
   365  			gp.X509Issuer = tlsOpt.Value
   366  		case ast.Subject:
   367  			err = soliton.CheckSupportX509NameOneline(tlsOpt.Value)
   368  			if err != nil {
   369  				return
   370  			}
   371  			gp.SSLType = privileges.SslTypeSpecified
   372  			gp.X509Subject = tlsOpt.Value
   373  		case ast.SAN:
   374  			gp.SSLType = privileges.SslTypeSpecified
   375  			_, err = soliton.ParseAndCheckSAN(tlsOpt.Value)
   376  			if err != nil {
   377  				return
   378  			}
   379  			gp.SAN = tlsOpt.Value
   380  		default:
   381  			err = errors.Errorf("Unknown ssl type: %#v", tlsOpt.Type)
   382  			return
   383  		}
   384  	}
   385  	if gp.SSLType == privileges.SslTypeNotSpecified && len(gp.SSLCipher) == 0 &&
   386  		len(gp.X509Issuer) == 0 && len(gp.X509Subject) == 0 && len(gp.SAN) == 0 {
   387  		return
   388  	}
   389  	priv, err = json.Marshal(&gp)
   390  	if err != nil {
   391  		return
   392  	}
   393  	return
   394  }
   395  
   396  // grantLevelPriv grants priv to user in s.Level scope.
   397  func (e *GrantInterDirc) grantLevelPriv(priv *ast.PrivElem, user *ast.UserSpec, internalStochastik stochastikctx.Context) error {
   398  	switch e.Level.Level {
   399  	case ast.GrantLevelGlobal:
   400  		return e.grantGlobalLevel(priv, user, internalStochastik)
   401  	case ast.GrantLevelDB:
   402  		return e.grantDBLevel(priv, user, internalStochastik)
   403  	case ast.GrantLevelBlock:
   404  		if len(priv.DefCauss) == 0 {
   405  			return e.grantBlockLevel(priv, user, internalStochastik)
   406  		}
   407  		return e.grantDeferredCausetLevel(priv, user, internalStochastik)
   408  	default:
   409  		return errors.Errorf("Unknown grant level: %#v", e.Level)
   410  	}
   411  }
   412  
   413  // grantGlobalLevel manipulates allegrosql.user causet.
   414  func (e *GrantInterDirc) grantGlobalLevel(priv *ast.PrivElem, user *ast.UserSpec, internalStochastik stochastikctx.Context) error {
   415  	if priv.Priv == 0 {
   416  		return nil
   417  	}
   418  	asgns, err := composeGlobalPrivUFIDelate(priv.Priv, "Y")
   419  	if err != nil {
   420  		return err
   421  	}
   422  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s'`, allegrosql.SystemDB, allegrosql.UserBlock, asgns, user.User.Username, user.User.Hostname)
   423  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   424  	return err
   425  }
   426  
   427  // grantDBLevel manipulates allegrosql.EDB causet.
   428  func (e *GrantInterDirc) grantDBLevel(priv *ast.PrivElem, user *ast.UserSpec, internalStochastik stochastikctx.Context) error {
   429  	dbName := e.Level.DBName
   430  	if len(dbName) == 0 {
   431  		dbName = e.ctx.GetStochastikVars().CurrentDB
   432  	}
   433  	asgns, err := composeDBPrivUFIDelate(priv.Priv, "Y")
   434  	if err != nil {
   435  		return err
   436  	}
   437  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s' AND EDB='%s';`, allegrosql.SystemDB, allegrosql.DBBlock, asgns, user.User.Username, user.User.Hostname, dbName)
   438  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   439  	return err
   440  }
   441  
   442  // grantBlockLevel manipulates allegrosql.blocks_priv causet.
   443  func (e *GrantInterDirc) grantBlockLevel(priv *ast.PrivElem, user *ast.UserSpec, internalStochastik stochastikctx.Context) error {
   444  	dbName := e.Level.DBName
   445  	if len(dbName) == 0 {
   446  		dbName = e.ctx.GetStochastikVars().CurrentDB
   447  	}
   448  	tblName := e.Level.BlockName
   449  	asgns, err := composeBlockPrivUFIDelateForGrant(internalStochastik, priv.Priv, user.User.Username, user.User.Hostname, dbName, tblName)
   450  	if err != nil {
   451  		return err
   452  	}
   453  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s';`, allegrosql.SystemDB, allegrosql.BlockPrivBlock, asgns, user.User.Username, user.User.Hostname, dbName, tblName)
   454  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   455  	return err
   456  }
   457  
   458  // grantDeferredCausetLevel manipulates allegrosql.blocks_priv causet.
   459  func (e *GrantInterDirc) grantDeferredCausetLevel(priv *ast.PrivElem, user *ast.UserSpec, internalStochastik stochastikctx.Context) error {
   460  	dbName, tbl, err := getTargetSchemaAndBlock(e.ctx, e.Level.DBName, e.Level.BlockName, e.is)
   461  	if err != nil {
   462  		return err
   463  	}
   464  
   465  	for _, c := range priv.DefCauss {
   466  		defCaus := causet.FindDefCaus(tbl.DefCauss(), c.Name.L)
   467  		if defCaus == nil {
   468  			return errors.Errorf("Unknown defCausumn: %s", c)
   469  		}
   470  		asgns, err := composeDeferredCausetPrivUFIDelateForGrant(internalStochastik, priv.Priv, user.User.Username, user.User.Hostname, dbName, tbl.Meta().Name.O, defCaus.Name.O)
   471  		if err != nil {
   472  			return err
   473  		}
   474  		allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s' AND DeferredCauset_name='%s';`, allegrosql.SystemDB, allegrosql.DeferredCausetPrivBlock, asgns, user.User.Username, user.User.Hostname, dbName, tbl.Meta().Name.O, defCaus.Name.O)
   475  		_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   476  		if err != nil {
   477  			return err
   478  		}
   479  	}
   480  	return nil
   481  }
   482  
   483  // composeGlobalPrivUFIDelate composes uFIDelate stmt assignment list string for global scope privilege uFIDelate.
   484  func composeGlobalPrivUFIDelate(priv allegrosql.PrivilegeType, value string) (string, error) {
   485  	if priv == allegrosql.AllPriv {
   486  		strs := make([]string, 0, len(allegrosql.Priv2UserDefCaus))
   487  		for _, v := range allegrosql.AllGlobalPrivs {
   488  			strs = append(strs, fmt.Sprintf(`%s='%s'`, allegrosql.Priv2UserDefCaus[v], value))
   489  		}
   490  		return strings.Join(strs, ", "), nil
   491  	}
   492  	defCaus, ok := allegrosql.Priv2UserDefCaus[priv]
   493  	if !ok {
   494  		return "", errors.Errorf("Unknown priv: %v", priv)
   495  	}
   496  	return fmt.Sprintf(`%s='%s'`, defCaus, value), nil
   497  }
   498  
   499  // composeDBPrivUFIDelate composes uFIDelate stmt assignment list for EDB scope privilege uFIDelate.
   500  func composeDBPrivUFIDelate(priv allegrosql.PrivilegeType, value string) (string, error) {
   501  	if priv == allegrosql.AllPriv {
   502  		strs := make([]string, 0, len(allegrosql.AllDBPrivs))
   503  		for _, p := range allegrosql.AllDBPrivs {
   504  			v, ok := allegrosql.Priv2UserDefCaus[p]
   505  			if !ok {
   506  				return "", errors.Errorf("Unknown EDB privilege %v", priv)
   507  			}
   508  			strs = append(strs, fmt.Sprintf(`%s='%s'`, v, value))
   509  		}
   510  		return strings.Join(strs, ", "), nil
   511  	}
   512  	defCaus, ok := allegrosql.Priv2UserDefCaus[priv]
   513  	if !ok {
   514  		return "", errors.Errorf("Unknown priv: %v", priv)
   515  	}
   516  	return fmt.Sprintf(`%s='%s'`, defCaus, value), nil
   517  }
   518  
   519  // composeBlockPrivUFIDelateForGrant composes uFIDelate stmt assignment list for causet scope privilege uFIDelate.
   520  func composeBlockPrivUFIDelateForGrant(ctx stochastikctx.Context, priv allegrosql.PrivilegeType, name string, host string, EDB string, tbl string) (string, error) {
   521  	var newBlockPriv, newDeferredCausetPriv string
   522  	if priv == allegrosql.AllPriv {
   523  		for _, p := range allegrosql.AllBlockPrivs {
   524  			v, ok := allegrosql.Priv2SetStr[p]
   525  			if !ok {
   526  				return "", errors.Errorf("Unknown causet privilege %v", p)
   527  			}
   528  			newBlockPriv = addToSet(newBlockPriv, v)
   529  		}
   530  		for _, p := range allegrosql.AllDeferredCausetPrivs {
   531  			v, ok := allegrosql.Priv2SetStr[p]
   532  			if !ok {
   533  				return "", errors.Errorf("Unknown defCausumn privilege %v", p)
   534  			}
   535  			newDeferredCausetPriv = addToSet(newDeferredCausetPriv, v)
   536  		}
   537  	} else {
   538  		currBlockPriv, currDeferredCausetPriv, err := getBlockPriv(ctx, name, host, EDB, tbl)
   539  		if err != nil {
   540  			return "", err
   541  		}
   542  		p, ok := allegrosql.Priv2SetStr[priv]
   543  		if !ok {
   544  			return "", errors.Errorf("Unknown priv: %v", priv)
   545  		}
   546  		newBlockPriv = addToSet(currBlockPriv, p)
   547  
   548  		for _, cp := range allegrosql.AllDeferredCausetPrivs {
   549  			if priv == cp {
   550  				newDeferredCausetPriv = addToSet(currDeferredCausetPriv, p)
   551  				break
   552  			}
   553  		}
   554  	}
   555  	return fmt.Sprintf(`Block_priv='%s', DeferredCauset_priv='%s', Grantor='%s'`, newBlockPriv, newDeferredCausetPriv, ctx.GetStochastikVars().User), nil
   556  }
   557  
   558  func composeBlockPrivUFIDelateForRevoke(ctx stochastikctx.Context, priv allegrosql.PrivilegeType, name string, host string, EDB string, tbl string) (string, error) {
   559  	var newBlockPriv, newDeferredCausetPriv string
   560  	if priv == allegrosql.AllPriv {
   561  		newBlockPriv = ""
   562  		newDeferredCausetPriv = ""
   563  	} else {
   564  		currBlockPriv, currDeferredCausetPriv, err := getBlockPriv(ctx, name, host, EDB, tbl)
   565  		if err != nil {
   566  			return "", err
   567  		}
   568  		p, ok := allegrosql.Priv2SetStr[priv]
   569  		if !ok {
   570  			return "", errors.Errorf("Unknown priv: %v", priv)
   571  		}
   572  		newBlockPriv = deleteFromSet(currBlockPriv, p)
   573  
   574  		for _, cp := range allegrosql.AllDeferredCausetPrivs {
   575  			if priv == cp {
   576  				newDeferredCausetPriv = deleteFromSet(currDeferredCausetPriv, p)
   577  				break
   578  			}
   579  		}
   580  	}
   581  	return fmt.Sprintf(`Block_priv='%s', DeferredCauset_priv='%s', Grantor='%s'`, newBlockPriv, newDeferredCausetPriv, ctx.GetStochastikVars().User), nil
   582  }
   583  
   584  // addToSet add a value to the set, e.g:
   585  // addToSet("Select,Insert", "UFIDelate") returns "Select,Insert,UFIDelate".
   586  func addToSet(set string, value string) string {
   587  	if set == "" {
   588  		return value
   589  	}
   590  	return fmt.Sprintf("%s,%s", set, value)
   591  }
   592  
   593  // deleteFromSet delete the value from the set, e.g:
   594  // deleteFromSet("Select,Insert,UFIDelate", "UFIDelate") returns "Select,Insert".
   595  func deleteFromSet(set string, value string) string {
   596  	sets := strings.Split(set, ",")
   597  	res := make([]string, 0, len(sets))
   598  	for _, v := range sets {
   599  		if v != value {
   600  			res = append(res, v)
   601  		}
   602  	}
   603  	return strings.Join(res, ",")
   604  }
   605  
   606  // composeDeferredCausetPrivUFIDelateForGrant composes uFIDelate stmt assignment list for defCausumn scope privilege uFIDelate.
   607  func composeDeferredCausetPrivUFIDelateForGrant(ctx stochastikctx.Context, priv allegrosql.PrivilegeType, name string, host string, EDB string, tbl string, defCaus string) (string, error) {
   608  	newDeferredCausetPriv := ""
   609  	if priv == allegrosql.AllPriv {
   610  		for _, p := range allegrosql.AllDeferredCausetPrivs {
   611  			v, ok := allegrosql.Priv2SetStr[p]
   612  			if !ok {
   613  				return "", errors.Errorf("Unknown defCausumn privilege %v", p)
   614  			}
   615  			newDeferredCausetPriv = addToSet(newDeferredCausetPriv, v)
   616  		}
   617  	} else {
   618  		currDeferredCausetPriv, err := getDeferredCausetPriv(ctx, name, host, EDB, tbl, defCaus)
   619  		if err != nil {
   620  			return "", err
   621  		}
   622  		p, ok := allegrosql.Priv2SetStr[priv]
   623  		if !ok {
   624  			return "", errors.Errorf("Unknown priv: %v", priv)
   625  		}
   626  		newDeferredCausetPriv = addToSet(currDeferredCausetPriv, p)
   627  	}
   628  	return fmt.Sprintf(`DeferredCauset_priv='%s'`, newDeferredCausetPriv), nil
   629  }
   630  
   631  func composeDeferredCausetPrivUFIDelateForRevoke(ctx stochastikctx.Context, priv allegrosql.PrivilegeType, name string, host string, EDB string, tbl string, defCaus string) (string, error) {
   632  	newDeferredCausetPriv := ""
   633  	if priv == allegrosql.AllPriv {
   634  		newDeferredCausetPriv = ""
   635  	} else {
   636  		currDeferredCausetPriv, err := getDeferredCausetPriv(ctx, name, host, EDB, tbl, defCaus)
   637  		if err != nil {
   638  			return "", err
   639  		}
   640  		p, ok := allegrosql.Priv2SetStr[priv]
   641  		if !ok {
   642  			return "", errors.Errorf("Unknown priv: %v", priv)
   643  		}
   644  		newDeferredCausetPriv = deleteFromSet(currDeferredCausetPriv, p)
   645  	}
   646  	return fmt.Sprintf(`DeferredCauset_priv='%s'`, newDeferredCausetPriv), nil
   647  }
   648  
   649  // recordExists is a helper function to check if the allegrosql returns any event.
   650  func recordExists(ctx stochastikctx.Context, allegrosql string) (bool, error) {
   651  	recordSets, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   652  	if err != nil {
   653  		return false, err
   654  	}
   655  	rows, _, err := getEventsAndFields(ctx, recordSets)
   656  	if err != nil {
   657  		return false, err
   658  	}
   659  	return len(rows) > 0, nil
   660  }
   661  
   662  // globalPrivEntryExists checks if there is an entry with key user-host in allegrosql.global_priv.
   663  func globalPrivEntryExists(ctx stochastikctx.Context, name string, host string) (bool, error) {
   664  	allegrosql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User='%s' AND Host='%s';`, allegrosql.SystemDB, allegrosql.GlobalPrivBlock, name, host)
   665  	return recordExists(ctx, allegrosql)
   666  }
   667  
   668  // dbUserExists checks if there is an entry with key user-host-EDB in allegrosql.EDB.
   669  func dbUserExists(ctx stochastikctx.Context, name string, host string, EDB string) (bool, error) {
   670  	allegrosql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User='%s' AND Host='%s' AND EDB='%s';`, allegrosql.SystemDB, allegrosql.DBBlock, name, host, EDB)
   671  	return recordExists(ctx, allegrosql)
   672  }
   673  
   674  // blockUserExists checks if there is an entry with key user-host-EDB-tbl in allegrosql.Blocks_priv.
   675  func blockUserExists(ctx stochastikctx.Context, name string, host string, EDB string, tbl string) (bool, error) {
   676  	allegrosql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s';`, allegrosql.SystemDB, allegrosql.BlockPrivBlock, name, host, EDB, tbl)
   677  	return recordExists(ctx, allegrosql)
   678  }
   679  
   680  // defCausumnPrivEntryExists checks if there is an entry with key user-host-EDB-tbl-defCaus in allegrosql.DeferredCausets_priv.
   681  func defCausumnPrivEntryExists(ctx stochastikctx.Context, name string, host string, EDB string, tbl string, defCaus string) (bool, error) {
   682  	allegrosql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s' AND DeferredCauset_name='%s';`, allegrosql.SystemDB, allegrosql.DeferredCausetPrivBlock, name, host, EDB, tbl, defCaus)
   683  	return recordExists(ctx, allegrosql)
   684  }
   685  
   686  // getBlockPriv gets current causet scope privilege set from allegrosql.Blocks_priv.
   687  // Return Block_priv and DeferredCauset_priv.
   688  func getBlockPriv(ctx stochastikctx.Context, name string, host string, EDB string, tbl string) (string, string, error) {
   689  	allegrosql := fmt.Sprintf(`SELECT Block_priv, DeferredCauset_priv FROM %s.%s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s';`, allegrosql.SystemDB, allegrosql.BlockPrivBlock, name, host, EDB, tbl)
   690  	rs, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   691  	if err != nil {
   692  		return "", "", err
   693  	}
   694  	if len(rs) < 1 {
   695  		return "", "", errors.Errorf("get causet privilege fail for %s %s %s %s", name, host, EDB, tbl)
   696  	}
   697  	var tPriv, cPriv string
   698  	rows, fields, err := getEventsAndFields(ctx, rs)
   699  	if err != nil {
   700  		return "", "", err
   701  	}
   702  	if len(rows) < 1 {
   703  		return "", "", errors.Errorf("get causet privilege fail for %s %s %s %s", name, host, EDB, tbl)
   704  	}
   705  	event := rows[0]
   706  	if fields[0].DeferredCauset.Tp == allegrosql.TypeSet {
   707  		blockPriv := event.GetSet(0)
   708  		tPriv = blockPriv.Name
   709  	}
   710  	if fields[1].DeferredCauset.Tp == allegrosql.TypeSet {
   711  		defCausumnPriv := event.GetSet(1)
   712  		cPriv = defCausumnPriv.Name
   713  	}
   714  	return tPriv, cPriv, nil
   715  }
   716  
   717  // getDeferredCausetPriv gets current defCausumn scope privilege set from allegrosql.DeferredCausets_priv.
   718  // Return DeferredCauset_priv.
   719  func getDeferredCausetPriv(ctx stochastikctx.Context, name string, host string, EDB string, tbl string, defCaus string) (string, error) {
   720  	allegrosql := fmt.Sprintf(`SELECT DeferredCauset_priv FROM %s.%s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s' AND DeferredCauset_name='%s';`, allegrosql.SystemDB, allegrosql.DeferredCausetPrivBlock, name, host, EDB, tbl, defCaus)
   721  	rs, err := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   722  	if err != nil {
   723  		return "", err
   724  	}
   725  	if len(rs) < 1 {
   726  		return "", errors.Errorf("get defCausumn privilege fail for %s %s %s %s", name, host, EDB, tbl)
   727  	}
   728  	rows, fields, err := getEventsAndFields(ctx, rs)
   729  	if err != nil {
   730  		return "", err
   731  	}
   732  	if len(rows) < 1 {
   733  		return "", errors.Errorf("get defCausumn privilege fail for %s %s %s %s %s", name, host, EDB, tbl, defCaus)
   734  	}
   735  	cPriv := ""
   736  	if fields[0].DeferredCauset.Tp == allegrosql.TypeSet {
   737  		setVal := rows[0].GetSet(0)
   738  		cPriv = setVal.Name
   739  	}
   740  	return cPriv, nil
   741  }
   742  
   743  // getTargetSchemaAndBlock finds the schemaReplicant and causet by dbName and blockName.
   744  func getTargetSchemaAndBlock(ctx stochastikctx.Context, dbName, blockName string, is schemareplicant.SchemaReplicant) (string, causet.Block, error) {
   745  	if len(dbName) == 0 {
   746  		dbName = ctx.GetStochastikVars().CurrentDB
   747  		if len(dbName) == 0 {
   748  			return "", nil, errors.New("miss EDB name for grant privilege")
   749  		}
   750  	}
   751  	name := perceptron.NewCIStr(blockName)
   752  	tbl, err := is.BlockByName(perceptron.NewCIStr(dbName), name)
   753  	if err != nil {
   754  		return "", nil, err
   755  	}
   756  	return dbName, tbl, nil
   757  }
   758  
   759  // getEventsAndFields is used to extract rows from record sets.
   760  func getEventsAndFields(ctx stochastikctx.Context, recordSets []sqlexec.RecordSet) ([]chunk.Event, []*ast.ResultField, error) {
   761  	var (
   762  		rows   []chunk.Event
   763  		fields []*ast.ResultField
   764  	)
   765  
   766  	for i, rs := range recordSets {
   767  		tmp, err := getEventFromRecordSet(context.Background(), ctx, rs)
   768  		if err != nil {
   769  			return nil, nil, err
   770  		}
   771  		if err = rs.Close(); err != nil {
   772  			return nil, nil, err
   773  		}
   774  
   775  		if i == 0 {
   776  			rows = tmp
   777  			fields = rs.Fields()
   778  		}
   779  	}
   780  	return rows, fields, nil
   781  }
   782  
   783  func getEventFromRecordSet(ctx context.Context, se stochastikctx.Context, rs sqlexec.RecordSet) ([]chunk.Event, error) {
   784  	var rows []chunk.Event
   785  	req := rs.NewChunk()
   786  	for {
   787  		err := rs.Next(ctx, req)
   788  		if err != nil || req.NumEvents() == 0 {
   789  			return rows, err
   790  		}
   791  		iter := chunk.NewIterator4Chunk(req)
   792  		for r := iter.Begin(); r != iter.End(); r = iter.Next() {
   793  			rows = append(rows, r)
   794  		}
   795  		req = chunk.Renew(req, se.GetStochastikVars().MaxChunkSize)
   796  	}
   797  }