github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/revoke.go (about)

     1  // Copyright 2021 Dolthub, 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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package plan
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql/mysql_db"
    22  	"github.com/dolthub/go-mysql-server/sql/types"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  )
    26  
    27  // Revoke represents the statement REVOKE [privilege...] ON [item] FROM [user...].
    28  type Revoke struct {
    29  	Privileges     []Privilege
    30  	ObjectType     ObjectType
    31  	PrivilegeLevel PrivilegeLevel
    32  	Users          []UserName
    33  	MySQLDb        sql.Database
    34  }
    35  
    36  var _ sql.Node = (*Revoke)(nil)
    37  var _ sql.Databaser = (*Revoke)(nil)
    38  var _ sql.CollationCoercible = (*Revoke)(nil)
    39  
    40  // Schema implements the interface sql.Node.
    41  func (n *Revoke) Schema() sql.Schema {
    42  	return types.OkResultSchema
    43  }
    44  
    45  func (n *Revoke) IsReadOnly() bool {
    46  	return false
    47  }
    48  
    49  // String implements the interface sql.Node.
    50  func (n *Revoke) String() string {
    51  	users := make([]string, len(n.Users))
    52  	for i, user := range n.Users {
    53  		users[i] = user.String("")
    54  	}
    55  	return fmt.Sprintf("Revoke(On: %s, From: %s)", n.PrivilegeLevel.String(), strings.Join(users, ", "))
    56  }
    57  
    58  // Database implements the interface sql.Databaser.
    59  func (n *Revoke) Database() sql.Database {
    60  	return n.MySQLDb
    61  }
    62  
    63  // WithDatabase implements the interface sql.Databaser.
    64  func (n *Revoke) WithDatabase(db sql.Database) (sql.Node, error) {
    65  	nn := *n
    66  	nn.MySQLDb = db
    67  	return &nn, nil
    68  }
    69  
    70  // Resolved implements the interface sql.Node.
    71  func (n *Revoke) Resolved() bool {
    72  	_, ok := n.MySQLDb.(sql.UnresolvedDatabase)
    73  	return !ok
    74  }
    75  
    76  // Children implements the interface sql.Node.
    77  func (n *Revoke) Children() []sql.Node {
    78  	return nil
    79  }
    80  
    81  // WithChildren implements the interface sql.Node.
    82  func (n *Revoke) WithChildren(children ...sql.Node) (sql.Node, error) {
    83  	if len(children) != 0 {
    84  		return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0)
    85  	}
    86  	return n, nil
    87  }
    88  
    89  // CheckPrivileges implements the interface sql.Node.
    90  func (n *Revoke) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
    91  	subject := sql.PrivilegeCheckSubject{Database: "mysql"}
    92  	if opChecker.UserHasPrivileges(ctx,
    93  		sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Update)) {
    94  		return true
    95  	}
    96  	if n.PrivilegeLevel.Database == "*" && n.PrivilegeLevel.TableRoutine == "*" {
    97  		if n.Privileges[0].Type == PrivilegeType_All {
    98  			return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{},
    99  				sql.PrivilegeType_Select,
   100  				sql.PrivilegeType_Insert,
   101  				sql.PrivilegeType_Update,
   102  				sql.PrivilegeType_Delete,
   103  				sql.PrivilegeType_Create,
   104  				sql.PrivilegeType_Drop,
   105  				sql.PrivilegeType_Reload,
   106  				sql.PrivilegeType_Shutdown,
   107  				sql.PrivilegeType_Process,
   108  				sql.PrivilegeType_File,
   109  				sql.PrivilegeType_References,
   110  				sql.PrivilegeType_Index,
   111  				sql.PrivilegeType_Alter,
   112  				sql.PrivilegeType_ShowDB,
   113  				sql.PrivilegeType_Super,
   114  				sql.PrivilegeType_CreateTempTable,
   115  				sql.PrivilegeType_LockTables,
   116  				sql.PrivilegeType_Execute,
   117  				sql.PrivilegeType_ReplicationSlave,
   118  				sql.PrivilegeType_ReplicationClient,
   119  				sql.PrivilegeType_CreateView,
   120  				sql.PrivilegeType_ShowView,
   121  				sql.PrivilegeType_CreateRoutine,
   122  				sql.PrivilegeType_AlterRoutine,
   123  				sql.PrivilegeType_CreateUser,
   124  				sql.PrivilegeType_Event,
   125  				sql.PrivilegeType_Trigger,
   126  				sql.PrivilegeType_CreateTablespace,
   127  				sql.PrivilegeType_CreateRole,
   128  				sql.PrivilegeType_DropRole,
   129  				sql.PrivilegeType_GrantOption,
   130  			))
   131  		}
   132  		return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{},
   133  			convertToSqlPrivilegeType(true, n.Privileges...)...))
   134  	} else if n.PrivilegeLevel.Database != "*" && n.PrivilegeLevel.TableRoutine == "*" {
   135  		database := n.PrivilegeLevel.Database
   136  		if database == "" {
   137  			database = ctx.GetCurrentDatabase()
   138  		}
   139  		subject = sql.PrivilegeCheckSubject{Database: database}
   140  
   141  		if n.Privileges[0].Type == PrivilegeType_All {
   142  			return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject,
   143  				sql.PrivilegeType_Alter,
   144  				sql.PrivilegeType_AlterRoutine,
   145  				sql.PrivilegeType_Create,
   146  				sql.PrivilegeType_CreateRoutine,
   147  				sql.PrivilegeType_CreateTempTable,
   148  				sql.PrivilegeType_CreateView,
   149  				sql.PrivilegeType_Delete,
   150  				sql.PrivilegeType_Drop,
   151  				sql.PrivilegeType_Event,
   152  				sql.PrivilegeType_Execute,
   153  				sql.PrivilegeType_Index,
   154  				sql.PrivilegeType_Insert,
   155  				sql.PrivilegeType_LockTables,
   156  				sql.PrivilegeType_References,
   157  				sql.PrivilegeType_Select,
   158  				sql.PrivilegeType_ShowView,
   159  				sql.PrivilegeType_Trigger,
   160  				sql.PrivilegeType_Update,
   161  				sql.PrivilegeType_GrantOption,
   162  			))
   163  		}
   164  		return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject,
   165  			convertToSqlPrivilegeType(true, n.Privileges...)...))
   166  	} else {
   167  		//TODO: add column checks
   168  		subject = sql.PrivilegeCheckSubject{
   169  			Database: n.PrivilegeLevel.Database,
   170  			Table:    n.PrivilegeLevel.TableRoutine,
   171  		}
   172  
   173  		if n.Privileges[0].Type == PrivilegeType_All {
   174  			return opChecker.UserHasPrivileges(ctx,
   175  				sql.NewPrivilegedOperation(subject,
   176  					sql.PrivilegeType_Alter,
   177  					sql.PrivilegeType_Create,
   178  					sql.PrivilegeType_CreateView,
   179  					sql.PrivilegeType_Delete,
   180  					sql.PrivilegeType_Drop,
   181  					sql.PrivilegeType_Index,
   182  					sql.PrivilegeType_Insert,
   183  					sql.PrivilegeType_References,
   184  					sql.PrivilegeType_Select,
   185  					sql.PrivilegeType_ShowView,
   186  					sql.PrivilegeType_Trigger,
   187  					sql.PrivilegeType_Update,
   188  					sql.PrivilegeType_GrantOption,
   189  				))
   190  		}
   191  		return opChecker.UserHasPrivileges(ctx,
   192  			sql.NewPrivilegedOperation(subject,
   193  				convertToSqlPrivilegeType(true, n.Privileges...)...))
   194  	}
   195  }
   196  
   197  // CollationCoercibility implements the interface sql.CollationCoercible.
   198  func (*Revoke) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   199  	return sql.Collation_binary, 7
   200  }
   201  
   202  // HandleGlobalPrivileges handles removing global privileges from a user.
   203  func (n *Revoke) HandleGlobalPrivileges(user *mysql_db.User) error {
   204  	for i, priv := range n.Privileges {
   205  		if len(priv.Columns) > 0 {
   206  			return sql.ErrGrantRevokeIllegalPrivilege.New()
   207  		}
   208  		switch priv.Type {
   209  		case PrivilegeType_All:
   210  			// If ALL is present, then no other privileges may be provided.
   211  			// This should be enforced by the parser, so this is a backup check just in case
   212  			if i == 0 && len(n.Privileges) == 1 {
   213  				user.PrivilegeSet.ClearGlobal()
   214  			} else {
   215  				return sql.ErrGrantRevokeIllegalPrivilege.New()
   216  			}
   217  		case PrivilegeType_Alter:
   218  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Alter)
   219  		case PrivilegeType_AlterRoutine:
   220  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_AlterRoutine)
   221  		case PrivilegeType_Create:
   222  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Create)
   223  		case PrivilegeType_CreateRole:
   224  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_CreateRole)
   225  		case PrivilegeType_CreateRoutine:
   226  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_CreateRoutine)
   227  		case PrivilegeType_CreateTablespace:
   228  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_CreateTablespace)
   229  		case PrivilegeType_CreateTemporaryTables:
   230  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_CreateTempTable)
   231  		case PrivilegeType_CreateUser:
   232  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_CreateUser)
   233  		case PrivilegeType_CreateView:
   234  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_CreateView)
   235  		case PrivilegeType_Delete:
   236  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Delete)
   237  		case PrivilegeType_Drop:
   238  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Drop)
   239  		case PrivilegeType_DropRole:
   240  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_DropRole)
   241  		case PrivilegeType_Event:
   242  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Event)
   243  		case PrivilegeType_Execute:
   244  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Execute)
   245  		case PrivilegeType_File:
   246  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_File)
   247  		case PrivilegeType_GrantOption:
   248  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_GrantOption)
   249  		case PrivilegeType_Index:
   250  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Index)
   251  		case PrivilegeType_Insert:
   252  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Insert)
   253  		case PrivilegeType_LockTables:
   254  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_LockTables)
   255  		case PrivilegeType_Process:
   256  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Process)
   257  		case PrivilegeType_References:
   258  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_References)
   259  		case PrivilegeType_Reload:
   260  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Reload)
   261  		case PrivilegeType_ReplicationClient:
   262  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_ReplicationClient)
   263  		case PrivilegeType_ReplicationSlave:
   264  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_ReplicationSlave)
   265  		case PrivilegeType_Select:
   266  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Select)
   267  		case PrivilegeType_ShowDatabases:
   268  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_ShowDB)
   269  		case PrivilegeType_ShowView:
   270  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_ShowView)
   271  		case PrivilegeType_Shutdown:
   272  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Shutdown)
   273  		case PrivilegeType_Super:
   274  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Super)
   275  		case PrivilegeType_Trigger:
   276  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Trigger)
   277  		case PrivilegeType_Update:
   278  			user.PrivilegeSet.RemoveGlobalStatic(sql.PrivilegeType_Update)
   279  		case PrivilegeType_Usage:
   280  			// Usage is equal to no privilege
   281  		case PrivilegeType_Dynamic:
   282  			if !priv.IsValidDynamic() {
   283  				return fmt.Errorf(`REVOKE does not yet support the dynamic privilege: "%s"`, priv.Dynamic)
   284  			}
   285  			user.PrivilegeSet.RemoveGlobalDynamic(priv.Dynamic)
   286  		default:
   287  			return sql.ErrGrantRevokeIllegalPrivilege.New()
   288  		}
   289  	}
   290  	return nil
   291  }
   292  
   293  // HandleDatabasePrivileges  handles removing database privileges from a user.
   294  func (n *Revoke) HandleDatabasePrivileges(user *mysql_db.User, dbName string) error {
   295  	for i, priv := range n.Privileges {
   296  		if len(priv.Columns) > 0 {
   297  			return sql.ErrGrantRevokeIllegalPrivilege.New()
   298  		}
   299  		switch priv.Type {
   300  		case PrivilegeType_All:
   301  			// If ALL is present, then no other privileges may be provided.
   302  			// This should be enforced by the parser, so this is a backup check just in case
   303  			if i == 0 && len(n.Privileges) == 1 {
   304  				user.PrivilegeSet.ClearDatabase(dbName)
   305  			} else {
   306  				return sql.ErrGrantRevokeIllegalPrivilege.New()
   307  			}
   308  		case PrivilegeType_Alter:
   309  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Alter)
   310  		case PrivilegeType_AlterRoutine:
   311  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_AlterRoutine)
   312  		case PrivilegeType_Create:
   313  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Create)
   314  		case PrivilegeType_CreateRoutine:
   315  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_CreateRoutine)
   316  		case PrivilegeType_CreateTemporaryTables:
   317  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_CreateTempTable)
   318  		case PrivilegeType_CreateView:
   319  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_CreateView)
   320  		case PrivilegeType_Delete:
   321  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Delete)
   322  		case PrivilegeType_Drop:
   323  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Drop)
   324  		case PrivilegeType_Event:
   325  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Event)
   326  		case PrivilegeType_Execute:
   327  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Execute)
   328  		case PrivilegeType_GrantOption:
   329  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_GrantOption)
   330  		case PrivilegeType_Index:
   331  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Index)
   332  		case PrivilegeType_Insert:
   333  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Insert)
   334  		case PrivilegeType_LockTables:
   335  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_LockTables)
   336  		case PrivilegeType_References:
   337  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_References)
   338  		case PrivilegeType_Select:
   339  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Select)
   340  		case PrivilegeType_ShowView:
   341  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_ShowView)
   342  		case PrivilegeType_Trigger:
   343  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Trigger)
   344  		case PrivilegeType_Update:
   345  			user.PrivilegeSet.RemoveDatabase(dbName, sql.PrivilegeType_Update)
   346  		case PrivilegeType_Usage:
   347  			// Usage is equal to no privilege
   348  		case PrivilegeType_Dynamic:
   349  			return sql.ErrGrantRevokeIllegalPrivilegeWithMessage.New(
   350  				"dynamic privileges may only operate at a global scope")
   351  		default:
   352  			return sql.ErrGrantRevokeIllegalPrivilege.New()
   353  		}
   354  	}
   355  	return nil
   356  }
   357  
   358  // HandleTablePrivileges  handles removing table privileges from a user.
   359  func (n *Revoke) HandleTablePrivileges(user *mysql_db.User, dbName string, tblName string) error {
   360  	for i, priv := range n.Privileges {
   361  		if len(priv.Columns) > 0 {
   362  			return fmt.Errorf("GRANT has not yet implemented column privileges")
   363  		}
   364  		switch priv.Type {
   365  		case PrivilegeType_All:
   366  			// If ALL is present, then no other privileges may be provided.
   367  			// This should be enforced by the parser, so this is a backup check just in case
   368  			if i == 0 && len(n.Privileges) == 1 {
   369  				user.PrivilegeSet.ClearTable(dbName, tblName)
   370  			} else {
   371  				return sql.ErrGrantRevokeIllegalPrivilege.New()
   372  			}
   373  		case PrivilegeType_Alter:
   374  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Alter)
   375  		case PrivilegeType_Create:
   376  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Create)
   377  		case PrivilegeType_CreateView:
   378  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_CreateView)
   379  		case PrivilegeType_Delete:
   380  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Delete)
   381  		case PrivilegeType_Drop:
   382  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Drop)
   383  		case PrivilegeType_GrantOption:
   384  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_GrantOption)
   385  		case PrivilegeType_Index:
   386  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Index)
   387  		case PrivilegeType_Insert:
   388  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Insert)
   389  		case PrivilegeType_References:
   390  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_References)
   391  		case PrivilegeType_Select:
   392  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Select)
   393  		case PrivilegeType_ShowView:
   394  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_ShowView)
   395  		case PrivilegeType_Trigger:
   396  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Trigger)
   397  		case PrivilegeType_Update:
   398  			user.PrivilegeSet.RemoveTable(dbName, tblName, sql.PrivilegeType_Update)
   399  		case PrivilegeType_Usage:
   400  			// Usage is equal to no privilege
   401  		case PrivilegeType_Dynamic:
   402  			return sql.ErrGrantRevokeIllegalPrivilegeWithMessage.New(
   403  				"dynamic privileges may only operate at a global scope")
   404  		default:
   405  			return sql.ErrGrantRevokeIllegalPrivilege.New()
   406  		}
   407  	}
   408  	return nil
   409  }
   410  
   411  func (n *Revoke) HandleRoutinePrivileges(user *mysql_db.User, dbName string, routineName string, isProcedureType bool) error {
   412  	for _, priv := range n.Privileges {
   413  		switch priv.Type {
   414  		case PrivilegeType_AlterRoutine:
   415  			user.PrivilegeSet.RemoveRoutine(dbName, routineName, isProcedureType, sql.PrivilegeType_AlterRoutine)
   416  		case PrivilegeType_Execute:
   417  			user.PrivilegeSet.RemoveRoutine(dbName, routineName, isProcedureType, sql.PrivilegeType_Execute)
   418  		case PrivilegeType_GrantOption:
   419  			user.PrivilegeSet.RemoveRoutine(dbName, routineName, isProcedureType, sql.PrivilegeType_GrantOption)
   420  		default:
   421  			return sql.ErrGrantRevokeIllegalPrivilege.New()
   422  		}
   423  	}
   424  	return nil
   425  }
   426  
   427  // RevokeAll represents the statement REVOKE ALL PRIVILEGES.
   428  type RevokeAll struct {
   429  	Users []UserName
   430  }
   431  
   432  var _ sql.Node = (*RevokeAll)(nil)
   433  var _ sql.CollationCoercible = (*RevokeAll)(nil)
   434  
   435  // NewRevokeAll returns a new RevokeAll node.
   436  func NewRevokeAll(users []UserName) *RevokeAll {
   437  	return &RevokeAll{
   438  		Users: users,
   439  	}
   440  }
   441  
   442  // Schema implements the interface sql.Node.
   443  func (n *RevokeAll) Schema() sql.Schema {
   444  	return types.OkResultSchema
   445  }
   446  
   447  func (n *RevokeAll) IsReadOnly() bool {
   448  	return false
   449  }
   450  
   451  // String implements the interface sql.Node.
   452  func (n *RevokeAll) String() string {
   453  	users := make([]string, len(n.Users))
   454  	for i, user := range n.Users {
   455  		users[i] = user.String("")
   456  	}
   457  	return fmt.Sprintf("RevokeAll(From: %s)", strings.Join(users, ", "))
   458  }
   459  
   460  // Resolved implements the interface sql.Node.
   461  func (n *RevokeAll) Resolved() bool {
   462  	return true
   463  }
   464  
   465  // Children implements the interface sql.Node.
   466  func (n *RevokeAll) Children() []sql.Node {
   467  	return nil
   468  }
   469  
   470  // WithChildren implements the interface sql.Node.
   471  func (n *RevokeAll) WithChildren(children ...sql.Node) (sql.Node, error) {
   472  	if len(children) != 0 {
   473  		return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0)
   474  	}
   475  	return n, nil
   476  }
   477  
   478  // CheckPrivileges implements the interface sql.Node.
   479  func (n *RevokeAll) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   480  	createUser := sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_CreateUser)
   481  	superUser := sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_Super)
   482  
   483  	subject := sql.PrivilegeCheckSubject{Database: "mysql"}
   484  	mysqlUpdate := sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Update)
   485  
   486  	return opChecker.UserHasPrivileges(ctx, createUser) ||
   487  		opChecker.UserHasPrivileges(ctx, superUser) ||
   488  		opChecker.UserHasPrivileges(ctx, mysqlUpdate)
   489  }
   490  
   491  // CollationCoercibility implements the interface sql.CollationCoercible.
   492  func (*RevokeAll) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   493  	return sql.Collation_binary, 7
   494  }
   495  
   496  // RevokeRole represents the statement REVOKE [role...] FROM [user...].
   497  type RevokeRole struct {
   498  	Roles       []UserName
   499  	TargetUsers []UserName
   500  	MySQLDb     sql.Database
   501  }
   502  
   503  var _ sql.Node = (*RevokeRole)(nil)
   504  var _ sql.Databaser = (*RevokeRole)(nil)
   505  var _ sql.CollationCoercible = (*RevokeRole)(nil)
   506  
   507  // NewRevokeRole returns a new RevokeRole node.
   508  func NewRevokeRole(roles []UserName, users []UserName) *RevokeRole {
   509  	return &RevokeRole{
   510  		Roles:       roles,
   511  		TargetUsers: users,
   512  		MySQLDb:     sql.UnresolvedDatabase("mysql"),
   513  	}
   514  }
   515  
   516  // Schema implements the interface sql.Node.
   517  func (n *RevokeRole) Schema() sql.Schema {
   518  	return types.OkResultSchema
   519  }
   520  
   521  // String implements the interface sql.Node.
   522  func (n *RevokeRole) String() string {
   523  	roles := make([]string, len(n.Roles))
   524  	for i, role := range n.Roles {
   525  		roles[i] = role.String("")
   526  	}
   527  	users := make([]string, len(n.TargetUsers))
   528  	for i, user := range n.TargetUsers {
   529  		users[i] = user.String("")
   530  	}
   531  	return fmt.Sprintf("RevokeRole(Roles: %s, From: %s)", strings.Join(roles, ", "), strings.Join(users, ", "))
   532  }
   533  
   534  // Database implements the interface sql.Databaser.
   535  func (n *RevokeRole) Database() sql.Database {
   536  	return n.MySQLDb
   537  }
   538  
   539  // WithDatabase implements the interface sql.Databaser.
   540  func (n *RevokeRole) WithDatabase(db sql.Database) (sql.Node, error) {
   541  	nn := *n
   542  	nn.MySQLDb = db
   543  	return &nn, nil
   544  }
   545  
   546  // Resolved implements the interface sql.Node.
   547  func (n *RevokeRole) Resolved() bool {
   548  	_, ok := n.MySQLDb.(sql.UnresolvedDatabase)
   549  	return !ok
   550  }
   551  
   552  func (n *RevokeRole) IsReadOnly() bool {
   553  	return false
   554  }
   555  
   556  // Children implements the interface sql.Node.
   557  func (n *RevokeRole) Children() []sql.Node {
   558  	return nil
   559  }
   560  
   561  // WithChildren implements the interface sql.Node.
   562  func (n *RevokeRole) WithChildren(children ...sql.Node) (sql.Node, error) {
   563  	if len(children) != 0 {
   564  		return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0)
   565  	}
   566  	return n, nil
   567  }
   568  
   569  // CheckPrivileges implements the interface sql.Node.
   570  func (n *RevokeRole) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   571  	if opChecker.UserHasPrivileges(ctx,
   572  		sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_Super)) {
   573  		return true
   574  	}
   575  	//TODO: only active roles may be revoked if the SUPER privilege is not held
   576  	mysqlDb := n.MySQLDb.(*mysql_db.MySQLDb)
   577  	client := ctx.Session.Client()
   578  
   579  	reader := mysqlDb.Reader()
   580  	defer reader.Close()
   581  
   582  	user := mysqlDb.GetUser(reader, client.User, client.Address, false)
   583  	if user == nil {
   584  		return false
   585  	}
   586  	roleEdges := reader.GetToUserRoleEdges(mysql_db.RoleEdgesToKey{
   587  		ToHost: user.Host,
   588  		ToUser: user.User,
   589  	})
   590  ROLES:
   591  	for _, roleName := range n.Roles {
   592  		role := mysqlDb.GetUser(reader, roleName.Name, roleName.Host, true)
   593  		if role == nil {
   594  			return false
   595  		}
   596  		for _, roleEdge := range roleEdges {
   597  			if roleEdge.FromUser == role.User && roleEdge.FromHost == role.Host && roleEdge.WithAdminOption {
   598  				continue ROLES
   599  			}
   600  		}
   601  		return false
   602  	}
   603  	return true
   604  }
   605  
   606  // CollationCoercibility implements the interface sql.CollationCoercible.
   607  func (*RevokeRole) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   608  	return sql.Collation_binary, 7
   609  }
   610  
   611  // RevokeProxy represents the statement REVOKE PROXY.
   612  type RevokeProxy struct {
   613  	On   UserName
   614  	From []UserName
   615  }
   616  
   617  var _ sql.Node = (*RevokeProxy)(nil)
   618  var _ sql.CollationCoercible = (*RevokeProxy)(nil)
   619  
   620  // NewRevokeProxy returns a new RevokeProxy node.
   621  func NewRevokeProxy(on UserName, from []UserName) *RevokeProxy {
   622  	return &RevokeProxy{
   623  		On:   on,
   624  		From: from,
   625  	}
   626  }
   627  
   628  // Schema implements the interface sql.Node.
   629  func (n *RevokeProxy) Schema() sql.Schema {
   630  	return types.OkResultSchema
   631  }
   632  
   633  // String implements the interface sql.Node.
   634  func (n *RevokeProxy) String() string {
   635  	users := make([]string, len(n.From))
   636  	for i, user := range n.From {
   637  		users[i] = user.String("")
   638  	}
   639  	return fmt.Sprintf("RevokeProxy(On: %s, From: %s)", n.On.String(""), strings.Join(users, ", "))
   640  }
   641  
   642  // Resolved implements the interface sql.Node.
   643  func (n *RevokeProxy) Resolved() bool {
   644  	return true
   645  }
   646  
   647  func (n *RevokeProxy) IsReadOnly() bool {
   648  	return false
   649  }
   650  
   651  // Children implements the interface sql.Node.
   652  func (n *RevokeProxy) Children() []sql.Node {
   653  	return nil
   654  }
   655  
   656  // WithChildren implements the interface sql.Node.
   657  func (n *RevokeProxy) WithChildren(children ...sql.Node) (sql.Node, error) {
   658  	if len(children) != 0 {
   659  		return nil, sql.ErrInvalidChildrenNumber.New(n, len(children), 0)
   660  	}
   661  	return n, nil
   662  }
   663  
   664  // CheckPrivileges implements the interface sql.Node.
   665  func (n *RevokeProxy) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   666  	//TODO: add this when proxy support is added
   667  	return true
   668  }
   669  
   670  // CollationCoercibility implements the interface sql.CollationCoercible.
   671  func (*RevokeProxy) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   672  	return sql.Collation_binary, 7
   673  }
   674  
   675  // RowIter implements the interface sql.Node.
   676  func (n *RevokeProxy) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) {
   677  	return nil, fmt.Errorf("not yet implemented")
   678  }