vitess.io/vitess@v0.16.2/go/vt/vtgate/vcursor_impl.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vtgate
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sort"
    23  	"strings"
    24  	"sync/atomic"
    25  
    26  	"vitess.io/vitess/go/vt/vtgate/logstats"
    27  
    28  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    29  
    30  	"github.com/google/uuid"
    31  
    32  	"vitess.io/vitess/go/mysql"
    33  	"vitess.io/vitess/go/mysql/collations"
    34  	"vitess.io/vitess/go/sqltypes"
    35  	"vitess.io/vitess/go/vt/callerid"
    36  	"vitess.io/vitess/go/vt/discovery"
    37  	"vitess.io/vitess/go/vt/key"
    38  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    39  	querypb "vitess.io/vitess/go/vt/proto/query"
    40  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    41  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    42  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    43  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    44  	"vitess.io/vitess/go/vt/sqlparser"
    45  	"vitess.io/vitess/go/vt/srvtopo"
    46  	"vitess.io/vitess/go/vt/topo"
    47  	topoprotopb "vitess.io/vitess/go/vt/topo/topoproto"
    48  	"vitess.io/vitess/go/vt/topotools"
    49  	"vitess.io/vitess/go/vt/vterrors"
    50  	"vitess.io/vitess/go/vt/vtgate/buffer"
    51  	"vitess.io/vitess/go/vt/vtgate/engine"
    52  	"vitess.io/vitess/go/vt/vtgate/semantics"
    53  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    54  	"vitess.io/vitess/go/vt/vtgate/vschemaacl"
    55  )
    56  
    57  var _ engine.VCursor = (*vcursorImpl)(nil)
    58  var _ plancontext.VSchema = (*vcursorImpl)(nil)
    59  var _ iExecute = (*Executor)(nil)
    60  var _ vindexes.VCursor = (*vcursorImpl)(nil)
    61  
    62  // vcursor_impl needs these facilities to be able to be able to execute queries for vindexes
    63  type iExecute interface {
    64  	Execute(ctx context.Context, method string, session *SafeSession, s string, vars map[string]*querypb.BindVariable) (*sqltypes.Result, error)
    65  	ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool) (qr *sqltypes.Result, errs []error)
    66  	StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error) []error
    67  	ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error)
    68  	Commit(ctx context.Context, safeSession *SafeSession) error
    69  	ExecuteMessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, name string, callback func(*sqltypes.Result) error) error
    70  	ExecuteVStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error
    71  	ReleaseLock(ctx context.Context, session *SafeSession) error
    72  
    73  	showVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error)
    74  	showShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error)
    75  	showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error)
    76  	showVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error)
    77  	setVitessMetadata(ctx context.Context, name, value string) error
    78  
    79  	// TODO: remove when resolver is gone
    80  	ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error)
    81  	VSchema() *vindexes.VSchema
    82  }
    83  
    84  // VSchemaOperator is an interface to Vschema Operations
    85  type VSchemaOperator interface {
    86  	GetCurrentSrvVschema() *vschemapb.SrvVSchema
    87  	UpdateVSchema(ctx context.Context, ksName string, vschema *vschemapb.SrvVSchema) error
    88  }
    89  
    90  // vcursorImpl implements the VCursor functionality used by dependent
    91  // packages to call back into VTGate.
    92  type vcursorImpl struct {
    93  	safeSession    *SafeSession
    94  	keyspace       string
    95  	tabletType     topodatapb.TabletType
    96  	destination    key.Destination
    97  	marginComments sqlparser.MarginComments
    98  	executor       iExecute
    99  	resolver       *srvtopo.Resolver
   100  	topoServer     *topo.Server
   101  	logStats       *logstats.LogStats
   102  	collation      collations.ID
   103  
   104  	ignoreMaxMemoryRows bool
   105  	vschema             *vindexes.VSchema
   106  	vm                  VSchemaOperator
   107  	semTable            *semantics.SemTable
   108  	warnShardedOnly     bool // when using sharded only features, a warning will be warnings field
   109  
   110  	warnings []*querypb.QueryWarning // any warnings that are accumulated during the planning phase are stored here
   111  	pv       plancontext.PlannerVersion
   112  }
   113  
   114  // newVcursorImpl creates a vcursorImpl. Before creating this object, you have to separate out any marginComments that came with
   115  // the query and supply it here. Trailing comments are typically sent by the application for various reasons,
   116  // including as identifying markers. So, they have to be added back to all queries that are executed
   117  // on behalf of the original query.
   118  func newVCursorImpl(
   119  	safeSession *SafeSession,
   120  	marginComments sqlparser.MarginComments,
   121  	executor *Executor,
   122  	logStats *logstats.LogStats,
   123  	vm VSchemaOperator,
   124  	vschema *vindexes.VSchema,
   125  	resolver *srvtopo.Resolver,
   126  	serv srvtopo.Server,
   127  	warnShardedOnly bool,
   128  	pv plancontext.PlannerVersion,
   129  ) (*vcursorImpl, error) {
   130  	keyspace, tabletType, destination, err := parseDestinationTarget(safeSession.TargetString, vschema)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	var ts *topo.Server
   136  	// We don't have access to the underlying TopoServer if this vtgate is
   137  	// filtering keyspaces because we don't have an accurate view of the topo.
   138  	if serv != nil && !discovery.FilteringKeyspaces() {
   139  		ts, err = serv.GetTopoServer()
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  	}
   144  
   145  	// we only support collations for the new TabletGateway implementation
   146  	var connCollation collations.ID
   147  	if executor != nil {
   148  		if gw, isTabletGw := executor.resolver.resolver.GetGateway().(*TabletGateway); isTabletGw {
   149  			connCollation = gw.DefaultConnCollation()
   150  		}
   151  	}
   152  	if connCollation == collations.Unknown {
   153  		connCollation = collations.Default()
   154  	}
   155  
   156  	return &vcursorImpl{
   157  		safeSession:     safeSession,
   158  		keyspace:        keyspace,
   159  		tabletType:      tabletType,
   160  		destination:     destination,
   161  		marginComments:  marginComments,
   162  		executor:        executor,
   163  		logStats:        logStats,
   164  		collation:       connCollation,
   165  		resolver:        resolver,
   166  		vschema:         vschema,
   167  		vm:              vm,
   168  		topoServer:      ts,
   169  		warnShardedOnly: warnShardedOnly,
   170  		pv:              pv,
   171  	}, nil
   172  }
   173  
   174  // HasSystemVariables returns whether the session has set system variables or not
   175  func (vc *vcursorImpl) HasSystemVariables() bool {
   176  	return vc.safeSession.HasSystemVariables()
   177  }
   178  
   179  // GetSystemVariables takes a visitor function that will save each system variables of the session
   180  func (vc *vcursorImpl) GetSystemVariables(f func(k string, v string)) {
   181  	vc.safeSession.GetSystemVariables(f)
   182  }
   183  
   184  // ConnCollation returns the collation of this session
   185  func (vc *vcursorImpl) ConnCollation() collations.ID {
   186  	return vc.collation
   187  }
   188  
   189  // MaxMemoryRows returns the maxMemoryRows flag value.
   190  func (vc *vcursorImpl) MaxMemoryRows() int {
   191  	return maxMemoryRows
   192  }
   193  
   194  // ExceedsMaxMemoryRows returns a boolean indicating whether the maxMemoryRows value has been exceeded.
   195  // Returns false if the max memory rows override directive is set to true.
   196  func (vc *vcursorImpl) ExceedsMaxMemoryRows(numRows int) bool {
   197  	return !vc.ignoreMaxMemoryRows && numRows > maxMemoryRows
   198  }
   199  
   200  // SetIgnoreMaxMemoryRows sets the ignoreMaxMemoryRows value.
   201  func (vc *vcursorImpl) SetIgnoreMaxMemoryRows(ignoreMaxMemoryRows bool) {
   202  	vc.ignoreMaxMemoryRows = ignoreMaxMemoryRows
   203  }
   204  
   205  // RecordWarning stores the given warning in the current session
   206  func (vc *vcursorImpl) RecordWarning(warning *querypb.QueryWarning) {
   207  	vc.safeSession.RecordWarning(warning)
   208  }
   209  
   210  // IsShardRoutingEnabled implements the VCursor interface.
   211  func (vc *vcursorImpl) IsShardRoutingEnabled() bool {
   212  	return enableShardRouting
   213  }
   214  
   215  // FindTable finds the specified table. If the keyspace what specified in the input, it gets used as qualifier.
   216  // Otherwise, the keyspace from the request is used, if one was provided.
   217  func (vc *vcursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) {
   218  	destKeyspace, destTabletType, dest, err := vc.executor.ParseDestinationTarget(name.Qualifier.String())
   219  	if err != nil {
   220  		return nil, "", destTabletType, nil, err
   221  	}
   222  	if destKeyspace == "" {
   223  		destKeyspace = vc.keyspace
   224  	}
   225  	table, err := vc.vschema.FindTable(destKeyspace, name.Name.String())
   226  	if err != nil {
   227  		return nil, "", destTabletType, nil, err
   228  	}
   229  	return table, destKeyspace, destTabletType, dest, err
   230  }
   231  
   232  func (vc *vcursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatement {
   233  	ks, _, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String())
   234  	if err != nil {
   235  		return nil
   236  	}
   237  	if ks == "" {
   238  		ks = vc.keyspace
   239  	}
   240  	return vc.vschema.FindView(ks, name.Name.String())
   241  }
   242  
   243  func (vc *vcursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Table, error) {
   244  	destKeyspace, destTabletType, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String())
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	if destKeyspace == "" {
   249  		destKeyspace = vc.keyspace
   250  	}
   251  
   252  	table, err := vc.vschema.FindRoutedTable(destKeyspace, name.Name.String(), destTabletType)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	return table, nil
   258  }
   259  
   260  // FindTableOrVindex finds the specified table or vindex.
   261  func (vc *vcursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) {
   262  	destKeyspace, destTabletType, dest, err := vc.executor.ParseDestinationTarget(name.Qualifier.String())
   263  	if err != nil {
   264  		return nil, nil, "", destTabletType, nil, err
   265  	}
   266  	if destKeyspace == "" {
   267  		destKeyspace = vc.getActualKeyspace()
   268  	}
   269  	table, vindex, err := vc.vschema.FindTableOrVindex(destKeyspace, name.Name.String(), vc.tabletType)
   270  	if err != nil {
   271  		return nil, nil, "", destTabletType, nil, err
   272  	}
   273  	return table, vindex, destKeyspace, destTabletType, dest, nil
   274  }
   275  
   276  func (vc *vcursorImpl) getActualKeyspace() string {
   277  	if !sqlparser.SystemSchema(vc.keyspace) {
   278  		return vc.keyspace
   279  	}
   280  	ks, err := vc.AnyKeyspace()
   281  	if err != nil {
   282  		return ""
   283  	}
   284  	return ks.Name
   285  }
   286  
   287  // DefaultKeyspace returns the default keyspace of the current request
   288  // if there is one. If the keyspace specified in the target cannot be
   289  // identified, it returns an error.
   290  func (vc *vcursorImpl) DefaultKeyspace() (*vindexes.Keyspace, error) {
   291  	if ignoreKeyspace(vc.keyspace) {
   292  		return nil, errNoKeyspace
   293  	}
   294  	ks, ok := vc.vschema.Keyspaces[vc.keyspace]
   295  	if !ok {
   296  		return nil, vterrors.VT05003(vc.keyspace)
   297  	}
   298  	return ks.Keyspace, nil
   299  }
   300  
   301  var errNoDbAvailable = vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.NoDB, "no database available")
   302  
   303  func (vc *vcursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) {
   304  	keyspace, err := vc.DefaultKeyspace()
   305  	if err == nil {
   306  		return keyspace, nil
   307  	}
   308  	if err != errNoKeyspace {
   309  		return nil, err
   310  	}
   311  
   312  	if len(vc.vschema.Keyspaces) == 0 {
   313  		return nil, errNoDbAvailable
   314  	}
   315  
   316  	var keyspaces = make([]*vindexes.Keyspace, 0, len(vc.vschema.Keyspaces))
   317  	for _, ks := range vc.vschema.Keyspaces {
   318  		keyspaces = append(keyspaces, ks.Keyspace)
   319  	}
   320  	sort.Slice(keyspaces, func(i, j int) bool {
   321  		return keyspaces[i].Name < keyspaces[j].Name
   322  	})
   323  
   324  	// Look for any sharded keyspace if present, otherwise take the first keyspace,
   325  	// sorted alphabetically
   326  	for _, ks := range keyspaces {
   327  		if ks.Sharded {
   328  			return ks, nil
   329  		}
   330  	}
   331  	return keyspaces[0], nil
   332  }
   333  
   334  func (vc *vcursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) {
   335  	if len(vc.vschema.Keyspaces) == 0 {
   336  		return nil, errNoDbAvailable
   337  	}
   338  	kss := vc.vschema.Keyspaces
   339  	keys := make([]string, 0, len(kss))
   340  	for ks := range kss {
   341  		keys = append(keys, ks)
   342  	}
   343  	sort.Strings(keys)
   344  
   345  	return kss[keys[0]].Keyspace, nil
   346  }
   347  
   348  // SysVarSetEnabled implements the ContextVSchema interface
   349  func (vc *vcursorImpl) SysVarSetEnabled() bool {
   350  	return vc.GetSessionEnableSystemSettings()
   351  }
   352  
   353  // KeyspaceExists provides whether the keyspace exists or not.
   354  func (vc *vcursorImpl) KeyspaceExists(ks string) bool {
   355  	return vc.vschema.Keyspaces[ks] != nil
   356  }
   357  
   358  // AllKeyspace implements the ContextVSchema interface
   359  func (vc *vcursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) {
   360  	if len(vc.vschema.Keyspaces) == 0 {
   361  		return nil, errNoDbAvailable
   362  	}
   363  	var kss []*vindexes.Keyspace
   364  	for _, ks := range vc.vschema.Keyspaces {
   365  		kss = append(kss, ks.Keyspace)
   366  	}
   367  	return kss, nil
   368  }
   369  
   370  // FindKeyspace implements the VSchema interface
   371  func (vc *vcursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) {
   372  	if len(vc.vschema.Keyspaces) == 0 {
   373  		return nil, errNoDbAvailable
   374  	}
   375  	for _, ks := range vc.vschema.Keyspaces {
   376  		if ks.Keyspace.Name == keyspace {
   377  			return ks.Keyspace, nil
   378  		}
   379  	}
   380  	return nil, nil
   381  }
   382  
   383  // Planner implements the ContextVSchema interface
   384  func (vc *vcursorImpl) Planner() plancontext.PlannerVersion {
   385  	if vc.safeSession.Options != nil &&
   386  		vc.safeSession.Options.PlannerVersion != querypb.ExecuteOptions_DEFAULT_PLANNER {
   387  		return vc.safeSession.Options.PlannerVersion
   388  	}
   389  	return vc.pv
   390  }
   391  
   392  // GetSemTable implements the ContextVSchema interface
   393  func (vc *vcursorImpl) GetSemTable() *semantics.SemTable {
   394  	return vc.semTable
   395  }
   396  
   397  // TargetString returns the current TargetString of the session.
   398  func (vc *vcursorImpl) TargetString() string {
   399  	return vc.safeSession.TargetString
   400  }
   401  
   402  // MaxBufferingRetries is to represent max retries on buffering.
   403  const MaxBufferingRetries = 3
   404  
   405  func (vc *vcursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
   406  	for try := 0; try < MaxBufferingRetries; try++ {
   407  		res, err := primitive.TryExecute(ctx, vc, bindVars, wantfields)
   408  		if err != nil && vterrors.RootCause(err) == buffer.ShardMissingError {
   409  			continue
   410  		}
   411  		return res, err
   412  	}
   413  	return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available")
   414  }
   415  
   416  func (vc *vcursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
   417  	// clone the vcursorImpl with a new session.
   418  	newVC := vc.cloneWithAutocommitSession()
   419  	for try := 0; try < MaxBufferingRetries; try++ {
   420  		res, err := primitive.TryExecute(ctx, newVC, bindVars, wantfields)
   421  		if err != nil && vterrors.RootCause(err) == buffer.ShardMissingError {
   422  			continue
   423  		}
   424  		return res, err
   425  	}
   426  	return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available")
   427  }
   428  
   429  func (vc *vcursorImpl) StreamExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
   430  	for try := 0; try < MaxBufferingRetries; try++ {
   431  		err := primitive.TryStreamExecute(ctx, vc, bindVars, wantfields, callback)
   432  		if err != nil && vterrors.RootCause(err) == buffer.ShardMissingError {
   433  			continue
   434  		}
   435  		return err
   436  	}
   437  	return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available")
   438  }
   439  
   440  func (vc *vcursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error {
   441  	// clone the vcursorImpl with a new session.
   442  	newVC := vc.cloneWithAutocommitSession()
   443  	for try := 0; try < MaxBufferingRetries; try++ {
   444  		err := primitive.TryStreamExecute(ctx, newVC, bindVars, wantfields, callback)
   445  		if err != nil && vterrors.RootCause(err) == buffer.ShardMissingError {
   446  			continue
   447  		}
   448  		return err
   449  	}
   450  	return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available")
   451  }
   452  
   453  // Execute is part of the engine.VCursor interface.
   454  func (vc *vcursorImpl) Execute(ctx context.Context, method string, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) {
   455  	session := vc.safeSession
   456  	if co == vtgatepb.CommitOrder_AUTOCOMMIT {
   457  		// For autocommit, we have to create an independent session.
   458  		session = NewAutocommitSession(vc.safeSession.Session)
   459  		session.logging = vc.safeSession.logging
   460  		rollbackOnError = false
   461  	} else {
   462  		session.SetCommitOrder(co)
   463  		defer session.SetCommitOrder(vtgatepb.CommitOrder_NORMAL)
   464  	}
   465  
   466  	err := vc.markSavepoint(ctx, rollbackOnError, map[string]*querypb.BindVariable{})
   467  	if err != nil {
   468  		return nil, err
   469  	}
   470  
   471  	qr, err := vc.executor.Execute(ctx, method, session, vc.marginComments.Leading+query+vc.marginComments.Trailing, bindVars)
   472  	vc.setRollbackOnPartialExecIfRequired(err != nil, rollbackOnError)
   473  
   474  	return qr, err
   475  }
   476  
   477  // markSavepoint opens an internal savepoint before executing the original query.
   478  // This happens only when rollback is allowed and no other savepoint was executed
   479  // and the query is executed in an explicit transaction (i.e. started by the client).
   480  func (vc *vcursorImpl) markSavepoint(ctx context.Context, needsRollbackOnParialExec bool, bindVars map[string]*querypb.BindVariable) error {
   481  	if !needsRollbackOnParialExec || !vc.safeSession.CanAddSavepoint() {
   482  		return nil
   483  	}
   484  	uID := fmt.Sprintf("_vt%s", strings.ReplaceAll(uuid.NewString(), "-", "_"))
   485  	spQuery := fmt.Sprintf("%ssavepoint %s%s", vc.marginComments.Leading, uID, vc.marginComments.Trailing)
   486  	_, err := vc.executor.Execute(ctx, "MarkSavepoint", vc.safeSession, spQuery, bindVars)
   487  	if err != nil {
   488  		return err
   489  	}
   490  	vc.safeSession.SetSavepoint(uID)
   491  	return nil
   492  }
   493  
   494  const txRollback = "Rollback Transaction"
   495  
   496  // ExecuteMultiShard is part of the engine.VCursor interface.
   497  func (vc *vcursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) {
   498  	noOfShards := len(rss)
   499  	atomic.AddUint64(&vc.logStats.ShardQueries, uint64(noOfShards))
   500  	err := vc.markSavepoint(ctx, rollbackOnError && (noOfShards > 1), map[string]*querypb.BindVariable{})
   501  	if err != nil {
   502  		return nil, []error{err}
   503  	}
   504  
   505  	qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, commentedShardQueries(queries, vc.marginComments), vc.safeSession, canAutocommit, vc.ignoreMaxMemoryRows)
   506  	vc.setRollbackOnPartialExecIfRequired(len(errs) != len(rss), rollbackOnError)
   507  
   508  	return qr, errs
   509  }
   510  
   511  // StreamExecuteMulti is the streaming version of ExecuteMultiShard.
   512  func (vc *vcursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error {
   513  	noOfShards := len(rss)
   514  	atomic.AddUint64(&vc.logStats.ShardQueries, uint64(noOfShards))
   515  	err := vc.markSavepoint(ctx, rollbackOnError && (noOfShards > 1), map[string]*querypb.BindVariable{})
   516  	if err != nil {
   517  		return []error{err}
   518  	}
   519  
   520  	errs := vc.executor.StreamExecuteMulti(ctx, primitive, vc.marginComments.Leading+query+vc.marginComments.Trailing, rss, bindVars, vc.safeSession, autocommit, callback)
   521  	vc.setRollbackOnPartialExecIfRequired(len(errs) != len(rss), rollbackOnError)
   522  
   523  	return errs
   524  }
   525  
   526  // ExecuteLock is for executing advisory lock statements.
   527  func (vc *vcursorImpl) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) {
   528  	query.Sql = vc.marginComments.Leading + query.Sql + vc.marginComments.Trailing
   529  	return vc.executor.ExecuteLock(ctx, rs, query, vc.safeSession, lockFuncType)
   530  }
   531  
   532  // ExecuteStandalone is part of the engine.VCursor interface.
   533  func (vc *vcursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.Primitive, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) {
   534  	rss := []*srvtopo.ResolvedShard{rs}
   535  	bqs := []*querypb.BoundQuery{
   536  		{
   537  			Sql:           vc.marginComments.Leading + query + vc.marginComments.Trailing,
   538  			BindVariables: bindVars,
   539  		},
   540  	}
   541  	// The autocommit flag is always set to false because we currently don't
   542  	// execute DMLs through ExecuteStandalone.
   543  	qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, bqs, NewAutocommitSession(vc.safeSession.Session), false /* autocommit */, vc.ignoreMaxMemoryRows)
   544  	return qr, vterrors.Aggregate(errs)
   545  }
   546  
   547  // ExecuteKeyspaceID is part of the engine.VCursor interface.
   548  func (vc *vcursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) {
   549  	atomic.AddUint64(&vc.logStats.ShardQueries, 1)
   550  	rss, _, err := vc.ResolveDestinations(ctx, keyspace, nil, []key.Destination{key.DestinationKeyspaceID(ksid)})
   551  	if err != nil {
   552  		return nil, err
   553  	}
   554  	queries := []*querypb.BoundQuery{{
   555  		Sql:           query,
   556  		BindVariables: bindVars,
   557  	}}
   558  
   559  	// This applies only when VTGate works in SINGLE transaction_mode.
   560  	// This function is only called from consistent_lookup vindex when the lookup row getting inserting finds a duplicate.
   561  	// In such scenario, original row needs to be locked to check if it already exists or no other transaction is working on it or does not write to it.
   562  	// This creates a transaction but that transaction is for locking purpose only and should not cause multi-db transaction error.
   563  	// This fields helps in to ignore multi-db transaction error when it states `queryFromVindex`.
   564  	if !rollbackOnError {
   565  		vc.safeSession.queryFromVindex = true
   566  		defer func() {
   567  			vc.safeSession.queryFromVindex = false
   568  		}()
   569  	}
   570  	qr, errs := vc.ExecuteMultiShard(ctx, nil, rss, queries, rollbackOnError, autocommit)
   571  	return qr, vterrors.Aggregate(errs)
   572  }
   573  
   574  func (vc *vcursorImpl) InTransactionAndIsDML() bool {
   575  	if !vc.safeSession.InTransaction() {
   576  		return false
   577  	}
   578  	switch vc.logStats.StmtType {
   579  	case "INSERT", "REPLACE", "UPDATE", "DELETE":
   580  		return true
   581  	}
   582  	return false
   583  }
   584  
   585  func (vc *vcursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder {
   586  	switch vc.logStats.StmtType {
   587  	case "DELETE", "UPDATE":
   588  		return vtgatepb.CommitOrder_POST
   589  	}
   590  	return vtgatepb.CommitOrder_PRE
   591  }
   592  
   593  // AutocommitApproval is part of the engine.VCursor interface.
   594  func (vc *vcursorImpl) AutocommitApproval() bool {
   595  	return vc.safeSession.AutocommitApproval()
   596  }
   597  
   598  // setRollbackOnPartialExecIfRequired sets the value on SafeSession.rollbackOnPartialExec
   599  // when the query gets successfully executed on at least one shard,
   600  // there does not exist any old savepoint for which rollback is already set
   601  // and rollback on error is allowed.
   602  func (vc *vcursorImpl) setRollbackOnPartialExecIfRequired(atleastOneSuccess bool, rollbackOnError bool) {
   603  	if atleastOneSuccess && rollbackOnError && !vc.safeSession.IsRollbackSet() {
   604  		vc.safeSession.SetRollbackCommand()
   605  	}
   606  }
   607  
   608  // fixupPartiallyMovedShards checks if any of the shards in the route has a ShardRoutingRule (true when a keyspace
   609  // is in the middle of being moved to another keyspace using MoveTables moving a subset of shards at a time
   610  func (vc *vcursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ([]*srvtopo.ResolvedShard, error) {
   611  	if vc.vschema.ShardRoutingRules == nil {
   612  		return rss, nil
   613  	}
   614  	for ind, rs := range rss {
   615  		targetKeyspace, err := vc.FindRoutedShard(rs.Target.Keyspace, rs.Target.Shard)
   616  		if err != nil {
   617  			return nil, err
   618  		}
   619  		if targetKeyspace == rs.Target.Keyspace {
   620  			continue
   621  		}
   622  		rss[ind] = rs.WithKeyspace(targetKeyspace)
   623  	}
   624  	return rss, nil
   625  }
   626  
   627  func (vc *vcursorImpl) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) {
   628  	rss, values, err := vc.resolver.ResolveDestinations(ctx, keyspace, vc.tabletType, ids, destinations)
   629  	if err != nil {
   630  		return nil, nil, err
   631  	}
   632  	if enableShardRouting {
   633  		rss, err = vc.fixupPartiallyMovedShards(rss)
   634  		if err != nil {
   635  			return nil, nil, err
   636  		}
   637  	}
   638  	return rss, values, err
   639  }
   640  
   641  func (vc *vcursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) {
   642  	rss, values, err := vc.resolver.ResolveDestinationsMultiCol(ctx, keyspace, vc.tabletType, ids, destinations)
   643  	if err != nil {
   644  		return nil, nil, err
   645  	}
   646  	if enableShardRouting {
   647  		rss, err = vc.fixupPartiallyMovedShards(rss)
   648  		if err != nil {
   649  			return nil, nil, err
   650  		}
   651  	}
   652  	return rss, values, err
   653  }
   654  
   655  func (vc *vcursorImpl) Session() engine.SessionActions {
   656  	return vc
   657  }
   658  
   659  func (vc *vcursorImpl) SetTarget(target string) error {
   660  	keyspace, tabletType, _, err := topoprotopb.ParseDestination(target, defaultTabletType)
   661  	if err != nil {
   662  		return err
   663  	}
   664  	if _, ok := vc.vschema.Keyspaces[keyspace]; !ignoreKeyspace(keyspace) && !ok {
   665  		return vterrors.VT05003(keyspace)
   666  	}
   667  
   668  	if vc.safeSession.InTransaction() && tabletType != topodatapb.TabletType_PRIMARY {
   669  		return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.LockOrActiveTransaction, "can't execute the given command because you have an active transaction")
   670  	}
   671  	vc.safeSession.SetTargetString(target)
   672  	return nil
   673  }
   674  
   675  func ignoreKeyspace(keyspace string) bool {
   676  	return keyspace == "" || sqlparser.SystemSchema(keyspace)
   677  }
   678  
   679  func (vc *vcursorImpl) SetUDV(key string, value any) error {
   680  	bindValue, err := sqltypes.BuildBindVariable(value)
   681  	if err != nil {
   682  		return err
   683  	}
   684  	vc.safeSession.SetUserDefinedVariable(key, bindValue)
   685  	return nil
   686  }
   687  
   688  func (vc *vcursorImpl) SetSysVar(name string, expr string) {
   689  	vc.safeSession.SetSystemVariable(name, expr)
   690  }
   691  
   692  // NeedsReservedConn implements the SessionActions interface
   693  func (vc *vcursorImpl) NeedsReservedConn() {
   694  	vc.safeSession.SetReservedConn(true)
   695  }
   696  
   697  func (vc *vcursorImpl) InReservedConn() bool {
   698  	return vc.safeSession.InReservedConn()
   699  }
   700  
   701  func (vc *vcursorImpl) ShardSession() []*srvtopo.ResolvedShard {
   702  	ss := vc.safeSession.GetShardSessions()
   703  	if len(ss) == 0 {
   704  		return nil
   705  	}
   706  	rss := make([]*srvtopo.ResolvedShard, len(ss))
   707  	for i, shardSession := range ss {
   708  		rss[i] = &srvtopo.ResolvedShard{
   709  			Target:  shardSession.Target,
   710  			Gateway: vc.resolver.GetGateway(),
   711  		}
   712  	}
   713  	return rss
   714  }
   715  
   716  // Destination implements the ContextVSchema interface
   717  func (vc *vcursorImpl) Destination() key.Destination {
   718  	return vc.destination
   719  }
   720  
   721  // TabletType implements the ContextVSchema interface
   722  func (vc *vcursorImpl) TabletType() topodatapb.TabletType {
   723  	return vc.tabletType
   724  }
   725  
   726  func commentedShardQueries(shardQueries []*querypb.BoundQuery, marginComments sqlparser.MarginComments) []*querypb.BoundQuery {
   727  	if marginComments.Leading == "" && marginComments.Trailing == "" {
   728  		return shardQueries
   729  	}
   730  	newQueries := make([]*querypb.BoundQuery, len(shardQueries))
   731  	for i, v := range shardQueries {
   732  		newQueries[i] = &querypb.BoundQuery{
   733  			Sql:           marginComments.Leading + v.Sql + marginComments.Trailing,
   734  			BindVariables: v.BindVariables,
   735  		}
   736  	}
   737  	return newQueries
   738  }
   739  
   740  // TargetDestination implements the ContextVSchema interface
   741  func (vc *vcursorImpl) TargetDestination(qualifier string) (key.Destination, *vindexes.Keyspace, topodatapb.TabletType, error) {
   742  	keyspaceName := vc.keyspace
   743  	if vc.destination == nil && qualifier != "" {
   744  		keyspaceName = qualifier
   745  	}
   746  	if keyspaceName == "" {
   747  		return nil, nil, 0, errNoKeyspace
   748  	}
   749  	keyspace := vc.vschema.Keyspaces[keyspaceName]
   750  	if keyspace == nil {
   751  		return nil, nil, 0, vterrors.VT05003(keyspaceName)
   752  	}
   753  	return vc.destination, keyspace.Keyspace, vc.tabletType, nil
   754  }
   755  
   756  // SetAutocommit implements the SessionActions interface
   757  func (vc *vcursorImpl) SetAutocommit(ctx context.Context, autocommit bool) error {
   758  	if autocommit && vc.safeSession.InTransaction() {
   759  		if err := vc.executor.Commit(ctx, vc.safeSession); err != nil {
   760  			return err
   761  		}
   762  	}
   763  	vc.safeSession.Autocommit = autocommit
   764  	return nil
   765  }
   766  
   767  // SetQueryTimeout implements the SessionActions interface
   768  func (vc *vcursorImpl) SetQueryTimeout(maxExecutionTime int64) {
   769  	vc.safeSession.QueryTimeout = maxExecutionTime
   770  }
   771  
   772  // GetQueryTimeout implements the SessionActions interface
   773  // The priority of adding query timeouts -
   774  // 1. Query timeout comment directive.
   775  // 2. If the comment directive is unspecified, then we use the session setting.
   776  // 3. If the comment directive and session settings is unspecified, then we use the global default specified by a flag.
   777  func (vc *vcursorImpl) GetQueryTimeout(queryTimeoutFromComments int) int {
   778  	if queryTimeoutFromComments != 0 {
   779  		return queryTimeoutFromComments
   780  	}
   781  	sessionQueryTimeout := int(vc.safeSession.GetQueryTimeout())
   782  	if sessionQueryTimeout != 0 {
   783  		return sessionQueryTimeout
   784  	}
   785  	return queryTimeout
   786  }
   787  
   788  // SetClientFoundRows implements the SessionActions interface
   789  func (vc *vcursorImpl) SetClientFoundRows(_ context.Context, clientFoundRows bool) error {
   790  	vc.safeSession.GetOrCreateOptions().ClientFoundRows = clientFoundRows
   791  	return nil
   792  }
   793  
   794  // SetSkipQueryPlanCache implements the SessionActions interface
   795  func (vc *vcursorImpl) SetSkipQueryPlanCache(_ context.Context, skipQueryPlanCache bool) error {
   796  	vc.safeSession.GetOrCreateOptions().SkipQueryPlanCache = skipQueryPlanCache
   797  	return nil
   798  }
   799  
   800  // SetSQLSelectLimit implements the SessionActions interface
   801  func (vc *vcursorImpl) SetSQLSelectLimit(limit int64) error {
   802  	vc.safeSession.GetOrCreateOptions().SqlSelectLimit = limit
   803  	return nil
   804  }
   805  
   806  // SetTransactionMode implements the SessionActions interface
   807  func (vc *vcursorImpl) SetTransactionMode(mode vtgatepb.TransactionMode) {
   808  	vc.safeSession.TransactionMode = mode
   809  }
   810  
   811  // SetWorkload implements the SessionActions interface
   812  func (vc *vcursorImpl) SetWorkload(workload querypb.ExecuteOptions_Workload) {
   813  	vc.safeSession.GetOrCreateOptions().Workload = workload
   814  }
   815  
   816  // SetPlannerVersion implements the SessionActions interface
   817  func (vc *vcursorImpl) SetPlannerVersion(v plancontext.PlannerVersion) {
   818  	vc.safeSession.GetOrCreateOptions().PlannerVersion = v
   819  }
   820  
   821  // SetConsolidator implements the SessionActions interface
   822  func (vc *vcursorImpl) SetConsolidator(consolidator querypb.ExecuteOptions_Consolidator) {
   823  	// Avoid creating session Options when they do not yet exist and the
   824  	// consolidator is unspecified.
   825  	if consolidator == querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED && vc.safeSession.GetOptions() == nil {
   826  		return
   827  	}
   828  	vc.safeSession.GetOrCreateOptions().Consolidator = consolidator
   829  }
   830  
   831  // SetFoundRows implements the SessionActions interface
   832  func (vc *vcursorImpl) SetFoundRows(foundRows uint64) {
   833  	vc.safeSession.FoundRows = foundRows
   834  	vc.safeSession.foundRowsHandled = true
   835  }
   836  
   837  // SetDDLStrategy implements the SessionActions interface
   838  func (vc *vcursorImpl) SetDDLStrategy(strategy string) {
   839  	vc.safeSession.SetDDLStrategy(strategy)
   840  }
   841  
   842  // GetDDLStrategy implements the SessionActions interface
   843  func (vc *vcursorImpl) GetDDLStrategy() string {
   844  	return vc.safeSession.GetDDLStrategy()
   845  }
   846  
   847  // GetSessionUUID implements the SessionActions interface
   848  func (vc *vcursorImpl) GetSessionUUID() string {
   849  	return vc.safeSession.GetSessionUUID()
   850  }
   851  
   852  // SetSessionEnableSystemSettings implements the SessionActions interface
   853  func (vc *vcursorImpl) SetSessionEnableSystemSettings(_ context.Context, allow bool) error {
   854  	vc.safeSession.SetSessionEnableSystemSettings(allow)
   855  	return nil
   856  }
   857  
   858  // GetSessionEnableSystemSettings implements the SessionActions interface
   859  func (vc *vcursorImpl) GetSessionEnableSystemSettings() bool {
   860  	return vc.safeSession.GetSessionEnableSystemSettings()
   861  }
   862  
   863  // SetReadAfterWriteGTID implements the SessionActions interface
   864  func (vc *vcursorImpl) SetReadAfterWriteGTID(vtgtid string) {
   865  	vc.safeSession.SetReadAfterWriteGTID(vtgtid)
   866  }
   867  
   868  // SetReadAfterWriteTimeout implements the SessionActions interface
   869  func (vc *vcursorImpl) SetReadAfterWriteTimeout(timeout float64) {
   870  	vc.safeSession.SetReadAfterWriteTimeout(timeout)
   871  }
   872  
   873  // SetSessionTrackGTIDs implements the SessionActions interface
   874  func (vc *vcursorImpl) SetSessionTrackGTIDs(enable bool) {
   875  	vc.safeSession.SetSessionTrackGtids(enable)
   876  }
   877  
   878  // HasCreatedTempTable implements the SessionActions interface
   879  func (vc *vcursorImpl) HasCreatedTempTable() {
   880  	vc.safeSession.GetOrCreateOptions().HasCreatedTempTables = true
   881  }
   882  
   883  // GetWarnings implements the SessionActions interface
   884  func (vc *vcursorImpl) GetWarnings() []*querypb.QueryWarning {
   885  	return vc.safeSession.GetWarnings()
   886  }
   887  
   888  // AnyAdvisoryLockTaken implements the SessionActions interface
   889  func (vc *vcursorImpl) AnyAdvisoryLockTaken() bool {
   890  	return vc.safeSession.HasAdvisoryLock()
   891  }
   892  
   893  // AddAdvisoryLock implements the SessionActions interface
   894  func (vc *vcursorImpl) AddAdvisoryLock(name string) {
   895  	vc.safeSession.AddAdvisoryLock(name)
   896  }
   897  
   898  // RemoveAdvisoryLock implements the SessionActions interface
   899  func (vc *vcursorImpl) RemoveAdvisoryLock(name string) {
   900  	vc.safeSession.RemoveAdvisoryLock(name)
   901  }
   902  
   903  func (vc *vcursorImpl) SetCommitOrder(co vtgatepb.CommitOrder) {
   904  	vc.safeSession.SetCommitOrder(co)
   905  }
   906  
   907  func (vc *vcursorImpl) InTransaction() bool {
   908  	return vc.safeSession.InTransaction()
   909  }
   910  
   911  // GetDBDDLPluginName implements the VCursor interface
   912  func (vc *vcursorImpl) GetDBDDLPluginName() string {
   913  	return dbDDLPlugin
   914  }
   915  
   916  // KeyspaceAvailable implements the VCursor interface
   917  func (vc *vcursorImpl) KeyspaceAvailable(ks string) bool {
   918  	_, exists := vc.executor.VSchema().Keyspaces[ks]
   919  	return exists
   920  }
   921  
   922  // ErrorIfShardedF implements the VCursor interface
   923  func (vc *vcursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat string, params ...any) error {
   924  	if ks.Sharded {
   925  		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, errFormat, params...)
   926  	}
   927  	vc.WarnUnshardedOnly("'%s' not supported in sharded mode", warn)
   928  
   929  	return nil
   930  }
   931  
   932  // WarnUnshardedOnly implements the VCursor interface
   933  func (vc *vcursorImpl) WarnUnshardedOnly(format string, params ...any) {
   934  	if vc.warnShardedOnly {
   935  		vc.warnings = append(vc.warnings, &querypb.QueryWarning{
   936  			Code:    mysql.ERNotSupportedYet,
   937  			Message: fmt.Sprintf(format, params...),
   938  		})
   939  	}
   940  }
   941  
   942  // PlannerWarning implements the VCursor interface
   943  func (vc *vcursorImpl) PlannerWarning(message string) {
   944  	if message == "" {
   945  		return
   946  	}
   947  	vc.warnings = append(vc.warnings, &querypb.QueryWarning{
   948  		Code:    mysql.ERNotSupportedYet,
   949  		Message: message,
   950  	})
   951  }
   952  
   953  // ForeignKeyMode implements the VCursor interface
   954  func (vc *vcursorImpl) ForeignKeyMode() string {
   955  	return strings.ToLower(foreignKeyMode)
   956  }
   957  
   958  // ParseDestinationTarget parses destination target string and sets default keyspace if possible.
   959  func parseDestinationTarget(targetString string, vschema *vindexes.VSchema) (string, topodatapb.TabletType, key.Destination, error) {
   960  	destKeyspace, destTabletType, dest, err := topoprotopb.ParseDestination(targetString, defaultTabletType)
   961  	// Set default keyspace
   962  	if destKeyspace == "" && len(vschema.Keyspaces) == 1 {
   963  		for k := range vschema.Keyspaces {
   964  			destKeyspace = k
   965  		}
   966  	}
   967  	return destKeyspace, destTabletType, dest, err
   968  }
   969  
   970  func (vc *vcursorImpl) planPrefixKey(ctx context.Context) string {
   971  	if vc.destination != nil {
   972  		switch vc.destination.(type) {
   973  		case key.DestinationKeyspaceID, key.DestinationKeyspaceIDs:
   974  			resolved, _, err := vc.ResolveDestinations(ctx, vc.keyspace, nil, []key.Destination{vc.destination})
   975  			if err == nil && len(resolved) > 0 {
   976  				shards := make([]string, len(resolved))
   977  				for i := 0; i < len(shards); i++ {
   978  					shards[i] = resolved[i].Target.GetShard()
   979  				}
   980  				sort.Strings(shards)
   981  				return fmt.Sprintf("%s%sKsIDsResolved(%s)", vc.keyspace, vindexes.TabletTypeSuffix[vc.tabletType], strings.Join(shards, ","))
   982  			}
   983  		default:
   984  			// use destination string (out of the switch)
   985  		}
   986  		return fmt.Sprintf("%s%s%s", vc.keyspace, vindexes.TabletTypeSuffix[vc.tabletType], vc.destination.String())
   987  	}
   988  	return fmt.Sprintf("%s%s", vc.keyspace, vindexes.TabletTypeSuffix[vc.tabletType])
   989  }
   990  
   991  func (vc *vcursorImpl) GetKeyspace() string {
   992  	return vc.keyspace
   993  }
   994  
   995  func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vschemaDDL *sqlparser.AlterVschema) error {
   996  	srvVschema := vc.vm.GetCurrentSrvVschema()
   997  	if srvVschema == nil {
   998  		return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema not loaded")
   999  	}
  1000  
  1001  	user := callerid.ImmediateCallerIDFromContext(ctx)
  1002  	allowed := vschemaacl.Authorized(user)
  1003  	if !allowed {
  1004  		return vterrors.NewErrorf(vtrpcpb.Code_PERMISSION_DENIED, vterrors.AccessDeniedError, "User '%s' is not authorized to perform vschema operations", user.GetUsername())
  1005  
  1006  	}
  1007  
  1008  	// Resolve the keyspace either from the table qualifier or the target keyspace
  1009  	var ksName string
  1010  	if !vschemaDDL.Table.IsEmpty() {
  1011  		ksName = vschemaDDL.Table.Qualifier.String()
  1012  	}
  1013  	if ksName == "" {
  1014  		ksName = keyspace
  1015  	}
  1016  	if ksName == "" {
  1017  		return errNoKeyspace
  1018  	}
  1019  
  1020  	ks := srvVschema.Keyspaces[ksName]
  1021  	ks, err := topotools.ApplyVSchemaDDL(ksName, ks, vschemaDDL)
  1022  
  1023  	if err != nil {
  1024  		return err
  1025  	}
  1026  
  1027  	srvVschema.Keyspaces[ksName] = ks
  1028  
  1029  	return vc.vm.UpdateVSchema(ctx, ksName, srvVschema)
  1030  
  1031  }
  1032  
  1033  func (vc *vcursorImpl) MessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, tableName string, callback func(*sqltypes.Result) error) error {
  1034  	atomic.AddUint64(&vc.logStats.ShardQueries, uint64(len(rss)))
  1035  	return vc.executor.ExecuteMessageStream(ctx, rss, tableName, callback)
  1036  }
  1037  
  1038  func (vc *vcursorImpl) VStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error {
  1039  	return vc.executor.ExecuteVStream(ctx, rss, filter, gtid, callback)
  1040  }
  1041  
  1042  func (vc *vcursorImpl) ShowExec(ctx context.Context, command sqlparser.ShowCommandType, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) {
  1043  	switch command {
  1044  	case sqlparser.VitessReplicationStatus:
  1045  		return vc.executor.showVitessReplicationStatus(ctx, filter)
  1046  	case sqlparser.VitessShards:
  1047  		return vc.executor.showShards(ctx, filter, vc.tabletType)
  1048  	case sqlparser.VitessTablets:
  1049  		return vc.executor.showTablets(filter)
  1050  	case sqlparser.VitessVariables:
  1051  		return vc.executor.showVitessMetadata(ctx, filter)
  1052  	default:
  1053  		return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "bug: unexpected show command: %v", command)
  1054  	}
  1055  }
  1056  
  1057  func (vc *vcursorImpl) GetVSchema() *vindexes.VSchema {
  1058  	return vc.vschema
  1059  }
  1060  
  1061  func (vc *vcursorImpl) GetSrvVschema() *vschemapb.SrvVSchema {
  1062  	return vc.vm.GetCurrentSrvVschema()
  1063  }
  1064  
  1065  func (vc *vcursorImpl) SetExec(ctx context.Context, name string, value string) error {
  1066  	return vc.executor.setVitessMetadata(ctx, name, value)
  1067  }
  1068  
  1069  func (vc *vcursorImpl) CanUseSetVar() bool {
  1070  	return sqlparser.IsMySQL80AndAbove() && setVarEnabled
  1071  }
  1072  
  1073  func (vc *vcursorImpl) ReleaseLock(ctx context.Context) error {
  1074  	return vc.executor.ReleaseLock(ctx, vc.safeSession)
  1075  }
  1076  
  1077  func (vc *vcursorImpl) cloneWithAutocommitSession() *vcursorImpl {
  1078  	safeSession := NewAutocommitSession(vc.safeSession.Session)
  1079  	safeSession.logging = vc.safeSession.logging
  1080  	return &vcursorImpl{
  1081  		safeSession:     safeSession,
  1082  		keyspace:        vc.keyspace,
  1083  		tabletType:      vc.tabletType,
  1084  		destination:     vc.destination,
  1085  		marginComments:  vc.marginComments,
  1086  		executor:        vc.executor,
  1087  		logStats:        vc.logStats,
  1088  		collation:       vc.collation,
  1089  		resolver:        vc.resolver,
  1090  		vschema:         vc.vschema,
  1091  		vm:              vc.vm,
  1092  		topoServer:      vc.topoServer,
  1093  		warnShardedOnly: vc.warnShardedOnly,
  1094  		pv:              vc.pv,
  1095  	}
  1096  }
  1097  
  1098  func (vc *vcursorImpl) VExplainLogging() {
  1099  	vc.safeSession.EnableLogging()
  1100  }
  1101  
  1102  func (vc *vcursorImpl) GetVExplainLogs() []engine.ExecuteEntry {
  1103  	return vc.safeSession.logging.GetLogs()
  1104  }
  1105  func (vc *vcursorImpl) FindRoutedShard(keyspace, shard string) (keyspaceName string, err error) {
  1106  	return vc.vschema.FindRoutedShard(keyspace, shard)
  1107  }
  1108  
  1109  func (vc *vcursorImpl) IsViewsEnabled() bool {
  1110  	return enableViews
  1111  }