vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/query_executor.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 tabletserver 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "strings" 24 "sync" 25 "time" 26 27 "google.golang.org/protobuf/proto" 28 29 "vitess.io/vitess/go/mysql" 30 "vitess.io/vitess/go/mysql/collations" 31 "vitess.io/vitess/go/pools" 32 "vitess.io/vitess/go/sqltypes" 33 "vitess.io/vitess/go/trace" 34 "vitess.io/vitess/go/vt/callerid" 35 "vitess.io/vitess/go/vt/callinfo" 36 "vitess.io/vitess/go/vt/log" 37 "vitess.io/vitess/go/vt/schema" 38 "vitess.io/vitess/go/vt/sqlparser" 39 "vitess.io/vitess/go/vt/tableacl" 40 "vitess.io/vitess/go/vt/vterrors" 41 "vitess.io/vitess/go/vt/vtgate/evalengine" 42 "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" 43 p "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" 44 "vitess.io/vitess/go/vt/vttablet/tabletserver/rules" 45 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 46 47 querypb "vitess.io/vitess/go/vt/proto/query" 48 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 49 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 50 ) 51 52 // QueryExecutor is used for executing a query request. 53 type QueryExecutor struct { 54 query string 55 marginComments sqlparser.MarginComments 56 bindVars map[string]*querypb.BindVariable 57 connID int64 58 options *querypb.ExecuteOptions 59 plan *TabletPlan 60 ctx context.Context 61 logStats *tabletenv.LogStats 62 tsv *TabletServer 63 tabletType topodatapb.TabletType 64 setting *pools.Setting 65 } 66 67 const ( 68 streamRowsSize = 256 69 maxQueryBufferDuration = 10 * time.Second 70 ) 71 72 var ( 73 streamResultPool = sync.Pool{New: func() any { 74 return &sqltypes.Result{ 75 Rows: make([][]sqltypes.Value, 0, streamRowsSize), 76 } 77 }} 78 sequenceFields = []*querypb.Field{ 79 { 80 Name: "nextval", 81 Type: sqltypes.Int64, 82 }, 83 } 84 ) 85 86 func returnStreamResult(result *sqltypes.Result) error { 87 // only return large results slices to the pool 88 if cap(result.Rows) >= streamRowsSize { 89 rows := result.Rows[:0] 90 *result = sqltypes.Result{} 91 result.Rows = rows 92 streamResultPool.Put(result) 93 } 94 return nil 95 } 96 97 func allocStreamResult() *sqltypes.Result { 98 return streamResultPool.Get().(*sqltypes.Result) 99 } 100 101 func (qre *QueryExecutor) shouldConsolidate() bool { 102 co := qre.options.GetConsolidator() 103 switch co { 104 case querypb.ExecuteOptions_CONSOLIDATOR_DISABLED: 105 return false 106 case querypb.ExecuteOptions_CONSOLIDATOR_ENABLED: 107 return true 108 case querypb.ExecuteOptions_CONSOLIDATOR_ENABLED_REPLICAS: 109 return qre.tabletType != topodatapb.TabletType_PRIMARY 110 default: 111 cm := qre.tsv.qe.consolidatorMode.Get() 112 return cm == tabletenv.Enable || (cm == tabletenv.NotOnPrimary && qre.tabletType != topodatapb.TabletType_PRIMARY) 113 } 114 } 115 116 // Execute performs a non-streaming query execution. 117 func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { 118 planName := qre.plan.PlanID.String() 119 qre.logStats.PlanType = planName 120 defer func(start time.Time) { 121 duration := time.Since(start) 122 qre.tsv.stats.QueryTimings.Add(planName, duration) 123 qre.recordUserQuery("Execute", int64(duration)) 124 125 mysqlTime := qre.logStats.MysqlResponseTime 126 tableName := qre.plan.TableName().String() 127 if tableName == "" { 128 tableName = "Join" 129 } 130 131 if reply == nil { 132 qre.tsv.qe.AddStats(qre.plan.PlanID, tableName, 1, duration, mysqlTime, 0, 0, 1) 133 qre.plan.AddStats(1, duration, mysqlTime, 0, 0, 1) 134 return 135 } 136 qre.tsv.qe.AddStats(qre.plan.PlanID, tableName, 1, duration, mysqlTime, int64(reply.RowsAffected), int64(len(reply.Rows)), 0) 137 qre.plan.AddStats(1, duration, mysqlTime, reply.RowsAffected, uint64(len(reply.Rows)), 0) 138 qre.logStats.RowsAffected = int(reply.RowsAffected) 139 qre.logStats.Rows = reply.Rows 140 qre.tsv.Stats().ResultHistogram.Add(int64(len(reply.Rows))) 141 }(time.Now()) 142 143 if err = qre.checkPermissions(); err != nil { 144 return nil, err 145 } 146 147 if qre.plan.PlanID == p.PlanNextval { 148 return qre.execNextval() 149 } 150 151 if qre.connID != 0 { 152 var conn *StatefulConnection 153 // Need upfront connection for DMLs and transactions 154 conn, err = qre.tsv.te.txPool.GetAndLock(qre.connID, "for query") 155 if err != nil { 156 return nil, err 157 } 158 defer conn.Unlock() 159 if qre.setting != nil { 160 if err = conn.ApplySetting(qre.ctx, qre.setting); err != nil { 161 return nil, vterrors.Wrap(err, "failed to execute system setting on the connection") 162 } 163 } 164 return qre.txConnExec(conn) 165 } 166 167 switch qre.plan.PlanID { 168 case p.PlanSelect, p.PlanSelectImpossible, p.PlanShow: 169 maxrows := qre.getSelectLimit() 170 qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) 171 if qre.bindVars[sqltypes.BvReplaceSchemaName] != nil { 172 qre.bindVars[sqltypes.BvSchemaName] = sqltypes.StringBindVariable(qre.tsv.config.DB.DBName) 173 } 174 qr, err := qre.execSelect() 175 if err != nil { 176 return nil, err 177 } 178 if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { 179 return nil, err 180 } 181 return qr, nil 182 case p.PlanOtherRead, p.PlanOtherAdmin, p.PlanFlush, p.PlanSavepoint, p.PlanRelease, p.PlanSRollback: 183 return qre.execOther() 184 case p.PlanInsert, p.PlanUpdate, p.PlanDelete, p.PlanInsertMessage, p.PlanDDL, p.PlanLoad: 185 return qre.execAutocommit(qre.txConnExec) 186 case p.PlanViewDDL: 187 switch qre.plan.FullStmt.(type) { 188 case *sqlparser.DropView: 189 return qre.execAutocommit(qre.execDropViewDDL) 190 default: 191 return qre.execAsTransaction(qre.execViewDDL) 192 } 193 case p.PlanUpdateLimit, p.PlanDeleteLimit: 194 return qre.execAsTransaction(qre.txConnExec) 195 case p.PlanCallProc: 196 return qre.execCallProc() 197 case p.PlanAlterMigration: 198 return qre.execAlterMigration() 199 case p.PlanRevertMigration: 200 return qre.execRevertMigration() 201 case p.PlanShowMigrationLogs: 202 return qre.execShowMigrationLogs() 203 case p.PlanShowThrottledApps: 204 return qre.execShowThrottledApps() 205 case p.PlanShowThrottlerStatus: 206 return qre.execShowThrottlerStatus() 207 case p.PlanSet: 208 if qre.setting == nil { 209 return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "[BUG] %s not allowed without setting connection", qre.query) 210 } 211 // The execution is not required as this setting will be applied when any other query type is executed. 212 return &sqltypes.Result{}, nil 213 } 214 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %s unexpected plan type", qre.plan.PlanID.String()) 215 } 216 217 func (qre *QueryExecutor) execAutocommit(f func(conn *StatefulConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { 218 if qre.options == nil { 219 qre.options = &querypb.ExecuteOptions{} 220 } else { 221 qre.options = proto.Clone(qre.options).(*querypb.ExecuteOptions) 222 } 223 qre.options.TransactionIsolation = querypb.ExecuteOptions_AUTOCOMMIT 224 225 conn, _, _, err := qre.tsv.te.txPool.Begin(qre.ctx, qre.options, false, 0, nil, qre.setting) 226 227 if err != nil { 228 return nil, err 229 } 230 defer qre.tsv.te.txPool.RollbackAndRelease(qre.ctx, conn) 231 232 return f(conn) 233 } 234 235 func (qre *QueryExecutor) execAsTransaction(f func(conn *StatefulConnection) (*sqltypes.Result, error)) (*sqltypes.Result, error) { 236 conn, beginSQL, _, err := qre.tsv.te.txPool.Begin(qre.ctx, qre.options, false, 0, nil, qre.setting) 237 if err != nil { 238 return nil, err 239 } 240 defer qre.tsv.te.txPool.RollbackAndRelease(qre.ctx, conn) 241 qre.logStats.AddRewrittenSQL(beginSQL, time.Now()) 242 243 result, err := f(conn) 244 if err != nil { 245 // dbConn is nil, it means the transaction was aborted. 246 // If so, we should not relog the rollback. 247 // TODO(sougou): these txPool functions should take the logstats 248 // and log any statements they issue. This needs to be done as 249 // a separate refactor because it impacts lot of code. 250 if conn.IsInTransaction() { 251 defer qre.logStats.AddRewrittenSQL("rollback", time.Now()) 252 } 253 return nil, err 254 } 255 256 defer qre.logStats.AddRewrittenSQL("commit", time.Now()) 257 if _, err := qre.tsv.te.txPool.Commit(qre.ctx, conn); err != nil { 258 return nil, err 259 } 260 return result, nil 261 } 262 263 func (qre *QueryExecutor) txConnExec(conn *StatefulConnection) (*sqltypes.Result, error) { 264 switch qre.plan.PlanID { 265 case p.PlanInsert, p.PlanUpdate, p.PlanDelete, p.PlanSet: 266 return qre.txFetch(conn, true) 267 case p.PlanInsertMessage: 268 qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) 269 return qre.txFetch(conn, true) 270 case p.PlanUpdateLimit, p.PlanDeleteLimit: 271 return qre.execDMLLimit(conn) 272 case p.PlanOtherRead, p.PlanOtherAdmin, p.PlanFlush: 273 return qre.execStatefulConn(conn, qre.query, true) 274 case p.PlanSavepoint, p.PlanRelease, p.PlanSRollback: 275 return qre.execStatefulConn(conn, qre.query, true) 276 case p.PlanSelect, p.PlanSelectImpossible, p.PlanShow, p.PlanSelectLockFunc: 277 maxrows := qre.getSelectLimit() 278 qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) 279 if qre.bindVars[sqltypes.BvReplaceSchemaName] != nil { 280 qre.bindVars[sqltypes.BvSchemaName] = sqltypes.StringBindVariable(qre.tsv.config.DB.DBName) 281 } 282 qr, err := qre.txFetch(conn, false) 283 if err != nil { 284 return nil, err 285 } 286 if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { 287 return nil, err 288 } 289 return qr, nil 290 case p.PlanDDL: 291 return qre.execDDL(conn) 292 case p.PlanLoad: 293 return qre.execLoad(conn) 294 case p.PlanCallProc: 295 return qre.execProc(conn) 296 } 297 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %s unexpected plan type", qre.plan.PlanID.String()) 298 } 299 300 func (qre *QueryExecutor) execViewDDL(conn *StatefulConnection) (*sqltypes.Result, error) { 301 var err error 302 switch stmt := qre.plan.FullStmt.(type) { 303 case *sqlparser.CreateView: 304 _, err = qre.execCreateViewDDL(conn, stmt) 305 case *sqlparser.AlterView: 306 _, err = qre.execAlterViewDDL(conn, stmt) 307 default: 308 err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: unexpected view DDL type: %T", qre.plan.FullStmt) 309 } 310 if err != nil { 311 return nil, err 312 } 313 // We need to use a different connection for executing the DDL on MySQL 314 // because the previous DMLs are running in a transaction and we don't want to autocommit 315 // those changes. 316 ddlConn, err := qre.getConn() 317 if err != nil { 318 return nil, err 319 } 320 defer ddlConn.Recycle() 321 // If MySQL fails, then we will Rollback the changes. 322 return ddlConn.Exec(qre.ctx, sqlparser.String(qre.plan.FullStmt), 1000, true) 323 } 324 325 func (qre *QueryExecutor) execCreateViewDDL(conn *StatefulConnection, stmt *sqlparser.CreateView) (*sqltypes.Result, error) { 326 bindVars := generateBindVarsForViewDDLInsert(stmt) 327 sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, bindVars) 328 if err != nil { 329 return nil, err 330 } 331 qr, err := execWithDDLView(qre.ctx, conn, sql) 332 if err != nil { 333 sqlErr, isSQLErr := mysql.NewSQLErrorFromError(err).(*mysql.SQLError) 334 // If it is a MySQL error and its code is of duplicate entry, 335 // then we would return duplicate create view error. 336 if isSQLErr && sqlErr.Number() == mysql.ERDupEntry { 337 return nil, vterrors.Errorf(vtrpcpb.Code_ALREADY_EXISTS, "Table '%s' already exists", stmt.ViewName.Name.String()) 338 } 339 return nil, err 340 } 341 return qr, nil 342 } 343 344 func (qre *QueryExecutor) execAlterViewDDL(conn *StatefulConnection, stmt *sqlparser.AlterView) (*sqltypes.Result, error) { 345 createViewDDL := &sqlparser.CreateView{ 346 ViewName: stmt.ViewName, 347 Algorithm: stmt.Algorithm, 348 Definer: stmt.Definer, 349 Security: stmt.Security, 350 Columns: stmt.Columns, 351 Select: stmt.Select, 352 CheckOption: stmt.CheckOption, 353 Comments: stmt.Comments, 354 } 355 bindVars := generateBindVarsForViewDDLInsert(createViewDDL) 356 sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, bindVars) 357 if err != nil { 358 return nil, err 359 } 360 qr, err := execWithDDLView(qre.ctx, conn, sql) 361 if err != nil { 362 return nil, err 363 } 364 if qr.RowsAffected == 0 { 365 return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "Table '%s' does not exist", stmt.ViewName.Name.String()) 366 } 367 return qr, nil 368 } 369 370 func (qre *QueryExecutor) execDropViewDDL(conn *StatefulConnection) (*sqltypes.Result, error) { 371 viewsMap := make(map[string]int) 372 stmt := qre.plan.FullStmt.(*sqlparser.DropView) 373 var viewNames []string 374 for pos, view := range stmt.FromTables { 375 viewName := view.Name.String() 376 if _, exists := viewsMap[viewName]; exists { 377 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Not unique view: '%s'", viewName) 378 } 379 viewNames = append(viewNames, viewName) 380 viewsMap[viewName] = pos 381 } 382 viewNamesBV, err := sqltypes.BuildBindVariable(viewNames) 383 if err != nil { 384 return nil, err 385 } 386 bindVars := map[string]*querypb.BindVariable{ 387 "table_name": viewNamesBV, 388 } 389 390 sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, bindVars) 391 if err != nil { 392 return nil, err 393 } 394 _, err = execWithDDLView(qre.ctx, conn, sql) 395 if err != nil { 396 return nil, err 397 } 398 399 // Drop the view on MySQL too. 400 return conn.Exec(qre.ctx, sqlparser.String(qre.plan.FullStmt), 1000, true) 401 } 402 403 func execWithDDLView(ctx context.Context, conn *StatefulConnection, sql string) (*sqltypes.Result, error) { 404 return conn.Exec(ctx, sql, 10000, true) 405 } 406 407 // Stream performs a streaming query execution. 408 func (qre *QueryExecutor) Stream(callback StreamCallback) error { 409 qre.logStats.PlanType = qre.plan.PlanID.String() 410 411 defer func(start time.Time) { 412 qre.tsv.stats.QueryTimings.Record(qre.plan.PlanID.String(), start) 413 qre.recordUserQuery("Stream", int64(time.Since(start))) 414 }(time.Now()) 415 416 if err := qre.checkPermissions(); err != nil { 417 return err 418 } 419 420 switch qre.plan.PlanID { 421 case p.PlanSelectStream: 422 if qre.bindVars[sqltypes.BvReplaceSchemaName] != nil { 423 qre.bindVars[sqltypes.BvSchemaName] = sqltypes.StringBindVariable(qre.tsv.config.DB.DBName) 424 } 425 } 426 427 sql, sqlWithoutComments, err := qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) 428 if err != nil { 429 return err 430 } 431 432 var replaceKeyspace string 433 if sqltypes.IncludeFieldsOrDefault(qre.options) == querypb.ExecuteOptions_ALL && qre.tsv.sm.target.Keyspace != qre.tsv.config.DB.DBName { 434 replaceKeyspace = qre.tsv.sm.target.Keyspace 435 } 436 437 if consolidator := qre.tsv.qe.streamConsolidator; consolidator != nil { 438 if qre.connID == 0 && qre.plan.PlanID == p.PlanSelectStream && qre.shouldConsolidate() { 439 return consolidator.Consolidate(qre.logStats, sqlWithoutComments, callback, 440 func(callback StreamCallback) error { 441 dbConn, err := qre.getStreamConn() 442 if err != nil { 443 return err 444 } 445 defer dbConn.Recycle() 446 return qre.execStreamSQL(dbConn, qre.connID != 0, sql, func(result *sqltypes.Result) error { 447 // this stream result is potentially used by more than one client, so 448 // the consolidator will return it to the pool once it knows it's no longer 449 // being shared 450 451 if replaceKeyspace != "" { 452 result.ReplaceKeyspace(replaceKeyspace) 453 } 454 return callback(result) 455 }) 456 }) 457 } 458 } 459 460 // if we have a transaction id, let's use the txPool for this query 461 var conn *connpool.DBConn 462 if qre.connID != 0 { 463 txConn, err := qre.tsv.te.txPool.GetAndLock(qre.connID, "for streaming query") 464 if err != nil { 465 return err 466 } 467 defer txConn.Unlock() 468 if qre.setting != nil { 469 if err = txConn.ApplySetting(qre.ctx, qre.setting); err != nil { 470 return vterrors.Wrap(err, "failed to execute system setting on the connection") 471 } 472 } 473 conn = txConn.UnderlyingDBConn() 474 } else { 475 dbConn, err := qre.getStreamConn() 476 if err != nil { 477 return err 478 } 479 defer dbConn.Recycle() 480 conn = dbConn 481 } 482 483 return qre.execStreamSQL(conn, qre.connID != 0, sql, func(result *sqltypes.Result) error { 484 // this stream result is only used by the calling client, so it can be 485 // returned to the pool once the callback has fully returned 486 defer returnStreamResult(result) 487 488 if replaceKeyspace != "" { 489 result.ReplaceKeyspace(replaceKeyspace) 490 } 491 return callback(result) 492 }) 493 } 494 495 // MessageStream streams messages from a message table. 496 func (qre *QueryExecutor) MessageStream(callback StreamCallback) error { 497 qre.logStats.OriginalSQL = qre.query 498 qre.logStats.PlanType = qre.plan.PlanID.String() 499 500 defer func(start time.Time) { 501 qre.tsv.stats.QueryTimings.Record(qre.plan.PlanID.String(), start) 502 qre.recordUserQuery("MessageStream", int64(time.Since(start))) 503 }(time.Now()) 504 505 if err := qre.checkPermissions(); err != nil { 506 return err 507 } 508 509 done, err := qre.tsv.messager.Subscribe(qre.ctx, qre.plan.TableName().String(), func(r *sqltypes.Result) error { 510 select { 511 case <-qre.ctx.Done(): 512 return io.EOF 513 default: 514 } 515 return callback(r) 516 }) 517 if err != nil { 518 return err 519 } 520 <-done 521 return nil 522 } 523 524 // checkPermissions returns an error if the query does not pass all checks 525 // (denied query, table ACL). 526 func (qre *QueryExecutor) checkPermissions() error { 527 // Skip permissions check if the context is local. 528 if tabletenv.IsLocalContext(qre.ctx) { 529 return nil 530 } 531 532 // Check if the query relates to a table that is in the denylist. 533 remoteAddr := "" 534 username := "" 535 ci, ok := callinfo.FromContext(qre.ctx) 536 if ok { 537 remoteAddr = ci.RemoteAddr() 538 username = ci.Username() 539 } 540 541 bufferingTimeoutCtx, cancel := context.WithTimeout(qre.ctx, maxQueryBufferDuration) 542 defer cancel() 543 544 action, ruleCancelCtx, desc := qre.plan.Rules.GetAction(remoteAddr, username, qre.bindVars, qre.marginComments) 545 switch action { 546 case rules.QRFail: 547 return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "disallowed due to rule: %s", desc) 548 case rules.QRFailRetry: 549 return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "disallowed due to rule: %s", desc) 550 case rules.QRBuffer: 551 if ruleCancelCtx != nil { 552 // We buffer up to some timeout. The timeout is determined by ctx.Done(). 553 // If we're not at timeout yet, we fail the query 554 select { 555 case <-ruleCancelCtx.Done(): 556 // good! We have buffered the query, and buffering is completed 557 case <-bufferingTimeoutCtx.Done(): 558 // Sorry, timeout while waiting for buffering to complete 559 return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "buffer timeout in rule: %s", desc) 560 } 561 } 562 default: 563 // no rules against this query. Good to proceed 564 } 565 // Skip ACL check for queries against the dummy dual table 566 if qre.plan.TableName().String() == "dual" { 567 return nil 568 } 569 570 // Skip the ACL check if the connecting user is an exempted superuser. 571 if qre.tsv.qe.exemptACL != nil && qre.tsv.qe.exemptACL.IsMember(&querypb.VTGateCallerID{Username: username}) { 572 qre.tsv.qe.tableaclExemptCount.Add(1) 573 return nil 574 } 575 576 callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) 577 if callerID == nil { 578 if qre.tsv.qe.strictTableACL { 579 return vterrors.Errorf(vtrpcpb.Code_UNAUTHENTICATED, "missing caller id") 580 } 581 return nil 582 } 583 584 // Skip the ACL check if the caller id is an exempted superuser. 585 if qre.tsv.qe.exemptACL != nil && qre.tsv.qe.exemptACL.IsMember(callerID) { 586 qre.tsv.qe.tableaclExemptCount.Add(1) 587 return nil 588 } 589 590 for i, auth := range qre.plan.Authorized { 591 if err := qre.checkAccess(auth, qre.plan.Permissions[i].TableName, callerID); err != nil { 592 return err 593 } 594 } 595 596 return nil 597 } 598 599 func (qre *QueryExecutor) checkAccess(authorized *tableacl.ACLResult, tableName string, callerID *querypb.VTGateCallerID) error { 600 statsKey := []string{tableName, authorized.GroupName, qre.plan.PlanID.String(), callerID.Username} 601 if !authorized.IsMember(callerID) { 602 if qre.tsv.qe.enableTableACLDryRun { 603 qre.tsv.Stats().TableaclPseudoDenied.Add(statsKey, 1) 604 return nil 605 } 606 607 // Skip ACL check for queries against the dummy dual table 608 if tableName == "dual" { 609 return nil 610 } 611 612 if qre.tsv.qe.strictTableACL { 613 groupStr := "" 614 if len(callerID.Groups) > 0 { 615 groupStr = fmt.Sprintf(", in groups [%s],", strings.Join(callerID.Groups, ", ")) 616 } 617 errStr := fmt.Sprintf("%s command denied to user '%s'%s for table '%s' (ACL check error)", qre.plan.PlanID.String(), callerID.Username, groupStr, tableName) 618 qre.tsv.Stats().TableaclDenied.Add(statsKey, 1) 619 qre.tsv.qe.accessCheckerLogger.Infof("%s", errStr) 620 return vterrors.Errorf(vtrpcpb.Code_PERMISSION_DENIED, "%s", errStr) 621 } 622 return nil 623 } 624 qre.tsv.Stats().TableaclAllowed.Add(statsKey, 1) 625 return nil 626 } 627 628 func (qre *QueryExecutor) execDDL(conn *StatefulConnection) (*sqltypes.Result, error) { 629 // Let's see if this is a normal DDL statement or an Online DDL statement. 630 // An Online DDL statement is identified by /*vt+ .. */ comment with expected directives, like uuid etc. 631 if onlineDDL, err := schema.OnlineDDLFromCommentedStatement(qre.plan.FullStmt); err == nil { 632 // Parsing is successful. 633 if !onlineDDL.Strategy.IsDirect() { 634 // This is an online DDL. 635 return qre.tsv.onlineDDLExecutor.SubmitMigration(qre.ctx, qre.plan.FullStmt) 636 } 637 } 638 639 isTemporaryTable := false 640 if ddlStmt, ok := qre.plan.FullStmt.(sqlparser.DDLStatement); ok { 641 isTemporaryTable = ddlStmt.IsTemporary() 642 } 643 if !isTemporaryTable { 644 // Temporary tables are limited to the session creating them. There is no need to Reload() 645 // the table because other connections will not be able to see the table anyway. 646 defer func() { 647 // Call se.Reload() with includeStats=false as obtaining table 648 // size stats involves joining `information_schema.tables`, 649 // which can be very costly on systems with a large number of 650 // tables. 651 // 652 // Instead of synchronously recalculating table size stats 653 // after every DDL, let them be outdated until the periodic 654 // schema reload fixes it. 655 if err := qre.tsv.se.ReloadAtEx(qre.ctx, mysql.Position{}, false); err != nil { 656 log.Errorf("failed to reload schema %v", err) 657 } 658 }() 659 } 660 sql := qre.query 661 // If FullQuery is not nil, then the DDL query was fully parsed 662 // and we should use the ast to generate the query instead. 663 if qre.plan.FullQuery != nil { 664 var err error 665 sql, _, err = qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) 666 if err != nil { 667 return nil, err 668 } 669 } 670 result, err := qre.execStatefulConn(conn, sql, true) 671 if err != nil { 672 return nil, err 673 } 674 // Only perform this operation when the connection has transaction open. 675 // TODO: This actually does not retain the old transaction. We should see how to provide correct behaviour to client. 676 if conn.txProps != nil { 677 err = qre.BeginAgain(qre.ctx, conn) 678 if err != nil { 679 return nil, err 680 } 681 } 682 return result, nil 683 } 684 685 func (qre *QueryExecutor) execLoad(conn *StatefulConnection) (*sqltypes.Result, error) { 686 result, err := qre.execStatefulConn(conn, qre.query, true) 687 if err != nil { 688 return nil, err 689 } 690 return result, nil 691 } 692 693 // BeginAgain commits the existing transaction and begins a new one 694 func (*QueryExecutor) BeginAgain(ctx context.Context, dc *StatefulConnection) error { 695 if dc.IsClosed() || dc.TxProperties().Autocommit { 696 return nil 697 } 698 if _, err := dc.Exec(ctx, "commit", 1, false); err != nil { 699 return err 700 } 701 if _, err := dc.Exec(ctx, "begin", 1, false); err != nil { 702 return err 703 } 704 return nil 705 } 706 707 func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { 708 env := evalengine.EnvWithBindVars(qre.bindVars, collations.Unknown) 709 result, err := env.Evaluate(qre.plan.NextCount) 710 if err != nil { 711 return nil, err 712 } 713 tableName := qre.plan.TableName() 714 v := result.Value() 715 inc, err := v.ToInt64() 716 if err != nil || inc < 1 { 717 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid increment for sequence %s: %s", tableName, v.String()) 718 } 719 720 t := qre.plan.Table 721 t.SequenceInfo.Lock() 722 defer t.SequenceInfo.Unlock() 723 if t.SequenceInfo.NextVal == 0 || t.SequenceInfo.NextVal+inc > t.SequenceInfo.LastVal { 724 _, err := qre.execAsTransaction(func(conn *StatefulConnection) (*sqltypes.Result, error) { 725 query := fmt.Sprintf("select next_id, cache from %s where id = 0 for update", sqlparser.String(tableName)) 726 qr, err := qre.execStatefulConn(conn, query, false) 727 if err != nil { 728 return nil, err 729 } 730 if len(qr.Rows) != 1 { 731 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected rows from reading sequence %s (possible mis-route): %d", tableName, len(qr.Rows)) 732 } 733 nextID, err := evalengine.ToInt64(qr.Rows[0][0]) 734 if err != nil { 735 return nil, vterrors.Wrapf(err, "error loading sequence %s", tableName) 736 } 737 // If LastVal does not match next ID, then either: 738 // VTTablet just started, and we're initializing the cache, or 739 // Someone reset the id underneath us. 740 if t.SequenceInfo.LastVal != nextID { 741 if nextID < t.SequenceInfo.LastVal { 742 log.Warningf("Sequence next ID value %v is below the currently cached max %v, updating it to max", nextID, t.SequenceInfo.LastVal) 743 nextID = t.SequenceInfo.LastVal 744 } 745 t.SequenceInfo.NextVal = nextID 746 t.SequenceInfo.LastVal = nextID 747 } 748 cache, err := evalengine.ToInt64(qr.Rows[0][1]) 749 if err != nil { 750 return nil, vterrors.Wrapf(err, "error loading sequence %s", tableName) 751 } 752 if cache < 1 { 753 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid cache value for sequence %s: %d", tableName, cache) 754 } 755 newLast := nextID + cache 756 for newLast < t.SequenceInfo.NextVal+inc { 757 newLast += cache 758 } 759 query = fmt.Sprintf("update %s set next_id = %d where id = 0", sqlparser.String(tableName), newLast) 760 conn.TxProperties().RecordQuery(query) 761 _, err = qre.execStatefulConn(conn, query, false) 762 if err != nil { 763 return nil, err 764 } 765 t.SequenceInfo.LastVal = newLast 766 return nil, nil 767 }) 768 if err != nil { 769 return nil, err 770 } 771 } 772 ret := t.SequenceInfo.NextVal 773 t.SequenceInfo.NextVal += inc 774 return &sqltypes.Result{ 775 Fields: sequenceFields, 776 Rows: [][]sqltypes.Value{{ 777 sqltypes.NewInt64(ret), 778 }}, 779 }, nil 780 } 781 782 // execSelect sends a query to mysql only if another identical query is not running. Otherwise, it waits and 783 // reuses the result. If the plan is missing field info, it sends the query to mysql requesting full info. 784 func (qre *QueryExecutor) execSelect() (*sqltypes.Result, error) { 785 sql, sqlWithoutComments, err := qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) 786 if err != nil { 787 return nil, err 788 } 789 // Check tablet type. 790 if qre.shouldConsolidate() { 791 q, original := qre.tsv.qe.consolidator.Create(sqlWithoutComments) 792 if original { 793 defer q.Broadcast() 794 conn, err := qre.getConn() 795 796 if err != nil { 797 q.Err = err 798 } else { 799 defer conn.Recycle() 800 q.Result, q.Err = qre.execDBConn(conn, sql, true) 801 } 802 } else { 803 qre.logStats.QuerySources |= tabletenv.QuerySourceConsolidator 804 startTime := time.Now() 805 q.Wait() 806 qre.tsv.stats.WaitTimings.Record("Consolidations", startTime) 807 } 808 if q.Err != nil { 809 return nil, q.Err 810 } 811 return q.Result.(*sqltypes.Result), nil 812 } 813 conn, err := qre.getConn() 814 if err != nil { 815 return nil, err 816 } 817 defer conn.Recycle() 818 res, err := qre.execDBConn(conn, sql, true) 819 if err != nil { 820 return nil, err 821 } 822 return res, nil 823 } 824 825 func (qre *QueryExecutor) execDMLLimit(conn *StatefulConnection) (*sqltypes.Result, error) { 826 maxrows := qre.tsv.qe.maxResultSize.Get() 827 qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) 828 result, err := qre.txFetch(conn, true) 829 if err != nil { 830 return nil, err 831 } 832 if err := qre.verifyRowCount(int64(result.RowsAffected), maxrows); err != nil { 833 defer qre.logStats.AddRewrittenSQL("rollback", time.Now()) 834 _ = qre.tsv.te.txPool.Rollback(qre.ctx, conn) 835 return nil, err 836 } 837 return result, nil 838 } 839 840 func (qre *QueryExecutor) verifyRowCount(count, maxrows int64) error { 841 if count > maxrows { 842 callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) 843 return vterrors.Errorf(vtrpcpb.Code_ABORTED, "caller id: %s: row count exceeded %d", callerID.Username, maxrows) 844 } 845 warnThreshold := qre.tsv.qe.warnResultSize.Get() 846 if warnThreshold > 0 && count > warnThreshold { 847 callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) 848 qre.tsv.Stats().Warnings.Add("ResultsExceeded", 1) 849 log.Warningf("caller id: %s row count %v exceeds warning threshold %v: %q", callerID.Username, count, warnThreshold, queryAsString(qre.plan.FullQuery.Query, qre.bindVars, qre.tsv.Config().SanitizeLogMessages)) 850 } 851 return nil 852 } 853 854 func (qre *QueryExecutor) execOther() (*sqltypes.Result, error) { 855 conn, err := qre.getConn() 856 if err != nil { 857 return nil, err 858 } 859 defer conn.Recycle() 860 return qre.execDBConn(conn, qre.query, true) 861 } 862 863 func (qre *QueryExecutor) getConn() (*connpool.DBConn, error) { 864 span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.getConn") 865 defer span.Finish() 866 867 start := time.Now() 868 conn, err := qre.tsv.qe.conns.Get(ctx, qre.setting) 869 870 switch err { 871 case nil: 872 qre.logStats.WaitingForConnection += time.Since(start) 873 return conn, nil 874 case connpool.ErrConnPoolClosed: 875 return nil, err 876 } 877 return nil, err 878 } 879 880 func (qre *QueryExecutor) getStreamConn() (*connpool.DBConn, error) { 881 span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.getStreamConn") 882 defer span.Finish() 883 884 start := time.Now() 885 conn, err := qre.tsv.qe.streamConns.Get(ctx, qre.setting) 886 switch err { 887 case nil: 888 qre.logStats.WaitingForConnection += time.Since(start) 889 return conn, nil 890 case connpool.ErrConnPoolClosed: 891 return nil, err 892 } 893 return nil, err 894 } 895 896 // txFetch fetches from a TxConnection. 897 func (qre *QueryExecutor) txFetch(conn *StatefulConnection, record bool) (*sqltypes.Result, error) { 898 sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) 899 if err != nil { 900 return nil, err 901 } 902 qr, err := qre.execStatefulConn(conn, sql, true) 903 if err != nil { 904 return nil, err 905 } 906 // Only record successful queries. 907 if record { 908 conn.TxProperties().RecordQuery(sql) 909 } 910 return qr, nil 911 } 912 913 func (qre *QueryExecutor) generateFinalSQL(parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable) (string, string, error) { 914 query, err := parsedQuery.GenerateQuery(bindVars, nil) 915 if err != nil { 916 return "", "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s", err) 917 } 918 if qre.tsv.config.AnnotateQueries { 919 username := callerid.GetPrincipal(callerid.EffectiveCallerIDFromContext(qre.ctx)) 920 if username == "" { 921 username = callerid.GetUsername(callerid.ImmediateCallerIDFromContext(qre.ctx)) 922 } 923 var buf strings.Builder 924 tabletTypeStr := qre.tsv.sm.target.TabletType.String() 925 buf.Grow(8 + len(username) + len(tabletTypeStr)) 926 buf.WriteString("/* ") 927 buf.WriteString(username) 928 buf.WriteString("@") 929 buf.WriteString(tabletTypeStr) 930 buf.WriteString(" */ ") 931 buf.WriteString(qre.marginComments.Leading) 932 qre.marginComments.Leading = buf.String() 933 } 934 935 if qre.marginComments.Leading == "" && qre.marginComments.Trailing == "" { 936 return query, query, nil 937 } 938 939 var buf strings.Builder 940 buf.Grow(len(qre.marginComments.Leading) + len(query) + len(qre.marginComments.Trailing)) 941 buf.WriteString(qre.marginComments.Leading) 942 buf.WriteString(query) 943 buf.WriteString(qre.marginComments.Trailing) 944 return buf.String(), query, nil 945 } 946 947 func rewriteOUTParamError(err error) error { 948 sqlErr, ok := err.(*mysql.SQLError) 949 if !ok { 950 return err 951 } 952 if sqlErr.Num == mysql.ErSPNotVarArg { 953 return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "OUT and INOUT parameters are not supported") 954 } 955 return err 956 } 957 958 func (qre *QueryExecutor) execCallProc() (*sqltypes.Result, error) { 959 conn, err := qre.getConn() 960 if err != nil { 961 return nil, err 962 } 963 defer conn.Recycle() 964 sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) 965 if err != nil { 966 return nil, err 967 } 968 969 qr, err := qre.execDBConn(conn, sql, true) 970 if err != nil { 971 return nil, rewriteOUTParamError(err) 972 } 973 if !qr.IsMoreResultsExists() { 974 if qr.IsInTransaction() { 975 conn.Close() 976 return nil, vterrors.New(vtrpcpb.Code_CANCELED, "Transaction not concluded inside the stored procedure, leaking transaction from stored procedure is not allowed") 977 } 978 return qr, nil 979 } 980 err = qre.drainResultSetOnConn(conn) 981 if err != nil { 982 return nil, err 983 } 984 return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "Multi-Resultset not supported in stored procedure") 985 } 986 987 func (qre *QueryExecutor) execProc(conn *StatefulConnection) (*sqltypes.Result, error) { 988 beforeInTx := conn.IsInTransaction() 989 sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) 990 if err != nil { 991 return nil, err 992 } 993 qr, err := qre.execStatefulConn(conn, sql, true) 994 if err != nil { 995 return nil, rewriteOUTParamError(err) 996 } 997 if !qr.IsMoreResultsExists() { 998 afterInTx := qr.IsInTransaction() 999 if beforeInTx != afterInTx { 1000 conn.Close() 1001 return nil, vterrors.New(vtrpcpb.Code_CANCELED, "Transaction state change inside the stored procedure is not allowed") 1002 } 1003 return qr, nil 1004 } 1005 err = qre.drainResultSetOnConn(conn.UnderlyingDBConn()) 1006 if err != nil { 1007 return nil, err 1008 } 1009 return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "Multi-Resultset not supported in stored procedure") 1010 } 1011 1012 func (qre *QueryExecutor) execAlterMigration() (*sqltypes.Result, error) { 1013 alterMigration, ok := qre.plan.FullStmt.(*sqlparser.AlterMigration) 1014 if !ok { 1015 return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "Expecting ALTER VITESS_MIGRATION plan") 1016 } 1017 1018 switch alterMigration.Type { 1019 case sqlparser.RetryMigrationType: 1020 return qre.tsv.onlineDDLExecutor.RetryMigration(qre.ctx, alterMigration.UUID) 1021 case sqlparser.CleanupMigrationType: 1022 return qre.tsv.onlineDDLExecutor.CleanupMigration(qre.ctx, alterMigration.UUID) 1023 case sqlparser.LaunchMigrationType: 1024 return qre.tsv.onlineDDLExecutor.LaunchMigration(qre.ctx, alterMigration.UUID, alterMigration.Shards) 1025 case sqlparser.LaunchAllMigrationType: 1026 return qre.tsv.onlineDDLExecutor.LaunchMigrations(qre.ctx) 1027 case sqlparser.CompleteMigrationType: 1028 return qre.tsv.onlineDDLExecutor.CompleteMigration(qre.ctx, alterMigration.UUID) 1029 case sqlparser.CompleteAllMigrationType: 1030 return qre.tsv.onlineDDLExecutor.CompletePendingMigrations(qre.ctx) 1031 case sqlparser.CancelMigrationType: 1032 return qre.tsv.onlineDDLExecutor.CancelMigration(qre.ctx, alterMigration.UUID, "CANCEL issued by user", true) 1033 case sqlparser.CancelAllMigrationType: 1034 return qre.tsv.onlineDDLExecutor.CancelPendingMigrations(qre.ctx, "CANCEL ALL issued by user", true) 1035 case sqlparser.ThrottleMigrationType: 1036 return qre.tsv.onlineDDLExecutor.ThrottleMigration(qre.ctx, alterMigration.UUID, alterMigration.Expire, alterMigration.Ratio) 1037 case sqlparser.ThrottleAllMigrationType: 1038 return qre.tsv.onlineDDLExecutor.ThrottleAllMigrations(qre.ctx, alterMigration.Expire, alterMigration.Ratio) 1039 case sqlparser.UnthrottleMigrationType: 1040 return qre.tsv.onlineDDLExecutor.UnthrottleMigration(qre.ctx, alterMigration.UUID) 1041 case sqlparser.UnthrottleAllMigrationType: 1042 return qre.tsv.onlineDDLExecutor.UnthrottleAllMigrations(qre.ctx) 1043 } 1044 return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "ALTER VITESS_MIGRATION not implemented") 1045 } 1046 1047 func (qre *QueryExecutor) execRevertMigration() (*sqltypes.Result, error) { 1048 if _, ok := qre.plan.FullStmt.(*sqlparser.RevertMigration); !ok { 1049 return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "Expecting REVERT VITESS_MIGRATION plan") 1050 } 1051 return qre.tsv.onlineDDLExecutor.SubmitMigration(qre.ctx, qre.plan.FullStmt) 1052 } 1053 1054 func (qre *QueryExecutor) execShowMigrationLogs() (*sqltypes.Result, error) { 1055 if showMigrationLogsStmt, ok := qre.plan.FullStmt.(*sqlparser.ShowMigrationLogs); ok { 1056 return qre.tsv.onlineDDLExecutor.ShowMigrationLogs(qre.ctx, showMigrationLogsStmt) 1057 } 1058 return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "Expecting SHOW VITESS_MIGRATION plan") 1059 } 1060 1061 func (qre *QueryExecutor) execShowThrottledApps() (*sqltypes.Result, error) { 1062 if err := qre.tsv.lagThrottler.CheckIsReady(); err != nil { 1063 return nil, err 1064 } 1065 if _, ok := qre.plan.FullStmt.(*sqlparser.ShowThrottledApps); !ok { 1066 return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "Expecting SHOW VITESS_THROTTLED_APPS plan") 1067 } 1068 result := &sqltypes.Result{ 1069 Fields: []*querypb.Field{ 1070 { 1071 Name: "app", 1072 Type: sqltypes.VarChar, 1073 }, 1074 { 1075 Name: "expire_at", 1076 Type: sqltypes.Timestamp, 1077 }, 1078 { 1079 Name: "ratio", 1080 Type: sqltypes.Decimal, 1081 }, 1082 }, 1083 Rows: [][]sqltypes.Value{}, 1084 } 1085 for _, t := range qre.tsv.lagThrottler.ThrottledApps() { 1086 result.Rows = append(result.Rows, 1087 []sqltypes.Value{ 1088 sqltypes.NewVarChar(t.AppName), 1089 sqltypes.NewTimestamp(t.ExpireAt.Format(sqltypes.TimestampFormat)), 1090 sqltypes.NewDecimal(fmt.Sprintf("%v", t.Ratio)), 1091 }) 1092 } 1093 return result, nil 1094 } 1095 1096 func (qre *QueryExecutor) execShowThrottlerStatus() (*sqltypes.Result, error) { 1097 if _, ok := qre.plan.FullStmt.(*sqlparser.ShowThrottlerStatus); !ok { 1098 return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "Expecting SHOW VITESS_THROTTLER STATUS plan") 1099 } 1100 var enabled int32 1101 if err := qre.tsv.lagThrottler.CheckIsReady(); err == nil { 1102 enabled = 1 1103 } 1104 result := &sqltypes.Result{ 1105 Fields: []*querypb.Field{ 1106 { 1107 Name: "shard", 1108 Type: sqltypes.VarChar, 1109 }, 1110 { 1111 Name: "enabled", 1112 Type: sqltypes.Int32, 1113 }, 1114 { 1115 Name: "threshold", 1116 Type: sqltypes.Float64, 1117 }, 1118 { 1119 Name: "query", 1120 Type: sqltypes.VarChar, 1121 }, 1122 }, 1123 Rows: [][]sqltypes.Value{ 1124 { 1125 sqltypes.NewVarChar(qre.tsv.sm.target.Shard), 1126 sqltypes.NewInt32(enabled), 1127 sqltypes.NewFloat64(qre.tsv.lagThrottler.MetricsThreshold.Get()), 1128 sqltypes.NewVarChar(qre.tsv.lagThrottler.GetMetricsQuery()), 1129 }, 1130 }, 1131 } 1132 return result, nil 1133 } 1134 1135 func (qre *QueryExecutor) drainResultSetOnConn(conn *connpool.DBConn) error { 1136 more := true 1137 for more { 1138 qr, err := conn.FetchNext(qre.ctx, int(qre.getSelectLimit()), true) 1139 if err != nil { 1140 return err 1141 } 1142 more = qr.IsMoreResultsExists() 1143 } 1144 return nil 1145 } 1146 1147 func (qre *QueryExecutor) getSelectLimit() int64 { 1148 return qre.tsv.qe.maxResultSize.Get() 1149 } 1150 1151 func (qre *QueryExecutor) execDBConn(conn *connpool.DBConn, sql string, wantfields bool) (*sqltypes.Result, error) { 1152 span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.execDBConn") 1153 defer span.Finish() 1154 1155 defer qre.logStats.AddRewrittenSQL(sql, time.Now()) 1156 1157 qd := NewQueryDetail(qre.logStats.Ctx, conn) 1158 qre.tsv.statelessql.Add(qd) 1159 defer qre.tsv.statelessql.Remove(qd) 1160 1161 return conn.Exec(ctx, sql, int(qre.tsv.qe.maxResultSize.Get()), wantfields) 1162 } 1163 1164 func (qre *QueryExecutor) execStatefulConn(conn *StatefulConnection, sql string, wantfields bool) (*sqltypes.Result, error) { 1165 span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.execStatefulConn") 1166 defer span.Finish() 1167 1168 defer qre.logStats.AddRewrittenSQL(sql, time.Now()) 1169 1170 qd := NewQueryDetail(qre.logStats.Ctx, conn) 1171 qre.tsv.statefulql.Add(qd) 1172 defer qre.tsv.statefulql.Remove(qd) 1173 1174 return conn.Exec(ctx, sql, int(qre.tsv.qe.maxResultSize.Get()), wantfields) 1175 } 1176 1177 func (qre *QueryExecutor) execStreamSQL(conn *connpool.DBConn, isTransaction bool, sql string, callback func(*sqltypes.Result) error) error { 1178 span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.execStreamSQL") 1179 trace.AnnotateSQL(span, sqlparser.Preview(sql)) 1180 callBackClosingSpan := func(result *sqltypes.Result) error { 1181 defer span.Finish() 1182 return callback(result) 1183 } 1184 1185 start := time.Now() 1186 defer qre.logStats.AddRewrittenSQL(sql, start) 1187 1188 // Add query detail object into QueryExecutor TableServer list w.r.t if it is a transactional or not. Previously we were adding it 1189 // to olapql list regardless but that resulted in problems, where long-running stream queries which can be stateful (or transactional) 1190 // weren't getting cleaned up during unserveCommon>handleShutdownGracePeriod in state_manager.go. 1191 // This change will ensure that long-running streaming stateful queries get gracefully shutdown during ServingTypeChange 1192 // once their grace period is over. 1193 qd := NewQueryDetail(qre.logStats.Ctx, conn) 1194 if isTransaction { 1195 qre.tsv.statefulql.Add(qd) 1196 defer qre.tsv.statefulql.Remove(qd) 1197 return conn.StreamOnce(ctx, sql, callBackClosingSpan, allocStreamResult, int(qre.tsv.qe.streamBufferSize.Get()), sqltypes.IncludeFieldsOrDefault(qre.options)) 1198 } 1199 qre.tsv.olapql.Add(qd) 1200 defer qre.tsv.olapql.Remove(qd) 1201 return conn.Stream(ctx, sql, callBackClosingSpan, allocStreamResult, int(qre.tsv.qe.streamBufferSize.Get()), sqltypes.IncludeFieldsOrDefault(qre.options)) 1202 } 1203 1204 func (qre *QueryExecutor) recordUserQuery(queryType string, duration int64) { 1205 username := callerid.GetPrincipal(callerid.EffectiveCallerIDFromContext(qre.ctx)) 1206 if username == "" { 1207 username = callerid.GetUsername(callerid.ImmediateCallerIDFromContext(qre.ctx)) 1208 } 1209 tableName := qre.plan.TableName().String() 1210 qre.tsv.Stats().UserTableQueryCount.Add([]string{tableName, username, queryType}, 1) 1211 qre.tsv.Stats().UserTableQueryTimesNs.Add([]string{tableName, username, queryType}, duration) 1212 } 1213 1214 func generateBindVarsForViewDDLInsert(createView *sqlparser.CreateView) map[string]*querypb.BindVariable { 1215 bindVars := make(map[string]*querypb.BindVariable) 1216 bindVars["table_name"] = sqltypes.StringBindVariable(createView.ViewName.Name.String()) 1217 bindVars["create_statement"] = sqltypes.StringBindVariable(sqlparser.String(createView)) 1218 return bindVars 1219 } 1220 1221 func (qre *QueryExecutor) GetSchemaDefinitions(tableType querypb.SchemaTableType, tableNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error { 1222 switch tableType { 1223 case querypb.SchemaTableType_VIEWS: 1224 return qre.getViewDefinitions(tableNames, callback) 1225 } 1226 return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid table type %v", tableType) 1227 } 1228 1229 func (qre *QueryExecutor) getViewDefinitions(viewNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error { 1230 query := mysql.FetchViews 1231 var bindVars map[string]*querypb.BindVariable 1232 if len(viewNames) > 0 { 1233 query = mysql.FetchUpdatedViews 1234 bindVars = map[string]*querypb.BindVariable{ 1235 "viewnames": sqltypes.StringBindVariable(strings.Join(viewNames, ",")), 1236 } 1237 } 1238 return qre.generateFinalQueryAndStreamExecute(query, bindVars, func(result *sqltypes.Result) error { 1239 schemaDef := make(map[string]string) 1240 for _, row := range result.Rows { 1241 schemaDef[row[0].ToString()] = row[1].ToString() 1242 } 1243 return callback(&querypb.GetSchemaResponse{TableDefinition: schemaDef}) 1244 }) 1245 } 1246 1247 func (qre *QueryExecutor) generateFinalQueryAndStreamExecute(query string, bindVars map[string]*querypb.BindVariable, callback func(result *sqltypes.Result) error) error { 1248 sql := query 1249 if len(bindVars) > 0 { 1250 stmt, err := sqlparser.Parse(query) 1251 if err != nil { 1252 return err 1253 } 1254 sql, _, err = qre.generateFinalSQL(sqlparser.NewParsedQuery(stmt), bindVars) 1255 if err != nil { 1256 return err 1257 } 1258 } 1259 1260 conn, err := qre.getStreamConn() 1261 if err != nil { 1262 return err 1263 } 1264 defer conn.Recycle() 1265 1266 return qre.execStreamSQL(conn, false /* isTransaction */, sql, callback) 1267 }