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 }