vitess.io/vitess@v0.16.2/go/vt/vtgate/safe_session.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 "fmt" 21 "sort" 22 "strings" 23 "sync" 24 "time" 25 26 "google.golang.org/protobuf/proto" 27 28 "vitess.io/vitess/go/vt/sqlparser" 29 "vitess.io/vitess/go/vt/srvtopo" 30 "vitess.io/vitess/go/vt/sysvars" 31 "vitess.io/vitess/go/vt/vterrors" 32 "vitess.io/vitess/go/vt/vtgate/engine" 33 34 querypb "vitess.io/vitess/go/vt/proto/query" 35 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 36 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 37 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 38 ) 39 40 type ( 41 // SafeSession is a mutex-protected version of the Session. 42 // It is thread-safe if each thread only accesses one shard. 43 // (the use pattern is 'Find', if not found, then 'AppendOrUpdate', 44 // for a single shard) 45 SafeSession struct { 46 mu sync.Mutex 47 mustRollback bool 48 autocommitState autocommitState 49 commitOrder vtgatepb.CommitOrder 50 savepointState savepointState 51 // rollbackOnPartialExec is set if any DML was successfully 52 // executed. If there was a subsequent failure, if we have a savepoint we rollback to that. 53 // Otherwise, the transaction is rolled back. 54 rollbackOnPartialExec string 55 savepointName string 56 57 // this is a signal that found_rows has already been handles by the primitives, 58 // and doesn't have to be updated by the executor 59 foundRowsHandled bool 60 61 // queryFromVindex is used to avoid erroring out on multi-db transaction 62 // as the query that started a new transaction on the shard belong to a vindex. 63 queryFromVindex bool 64 65 logging *executeLogger 66 67 *vtgatepb.Session 68 } 69 70 executeLogger struct { 71 mu sync.Mutex 72 entries []engine.ExecuteEntry 73 lastID int 74 } 75 76 // autocommitState keeps track of whether a single round-trip 77 // commit to vttablet is possible. It starts as autocommitable 78 // if we started a transaction because of the autocommit flag 79 // being set. Otherwise, it starts as notAutocommitable. 80 // If execute is recursively called using the same session, 81 // like from a vindex, we will already be in a transaction, 82 // and this should cause the state to become notAutocommitable. 83 // 84 // SafeSession lets you request a commit token, which will 85 // be issued if the state is autocommitable, 86 // implying that no intermediate transactions were started. 87 // If so, the state transitions to autocommited, which is terminal. 88 // If the token is successfully issued, the caller has to perform 89 // the commit. If a token cannot be issued, then a traditional 90 // commit has to be performed at the outermost level where 91 // the autocommitable transition happened. 92 autocommitState int 93 94 // savepointState keeps track of whether savepoints need to be inserted 95 // before running the query. This will help us prevent rolling back the 96 // entire transaction in case of partial failures, and be closer to MySQL 97 // compatibility, by only reverting the changes from the failed statement 98 // If execute is recursively called using the same session, 99 // like from a vindex, we should not override the savePointState. 100 // It is set the first time and is then permanent for the remainder of the query 101 // execution. It should not be affected later by transactions starting or not. 102 savepointState int 103 ) 104 105 const ( 106 notAutocommittable = autocommitState(iota) 107 autocommittable 108 autocommitted 109 ) 110 111 const ( 112 savepointStateNotSet = savepointState(iota) 113 // savepointNotNeeded - savepoint is not required 114 savepointNotNeeded 115 // savepointNeeded - savepoint may be required 116 savepointNeeded 117 // savepointSet - savepoint is set on the session 118 savepointSet 119 // savepointRollbackSet - rollback to savepoint is set on the session 120 savepointRollbackSet 121 // savepointRollback - rollback happened on the savepoint 122 savepointRollback 123 ) 124 125 // NewSafeSession returns a new SafeSession based on the Session 126 func NewSafeSession(sessn *vtgatepb.Session) *SafeSession { 127 if sessn == nil { 128 sessn = &vtgatepb.Session{} 129 } 130 return &SafeSession{Session: sessn} 131 } 132 133 // NewAutocommitSession returns a SafeSession based on the original 134 // session, but with autocommit enabled. 135 func NewAutocommitSession(sessn *vtgatepb.Session) *SafeSession { 136 newSession := proto.Clone(sessn).(*vtgatepb.Session) 137 newSession.InTransaction = false 138 newSession.ShardSessions = nil 139 newSession.PreSessions = nil 140 newSession.PostSessions = nil 141 newSession.LockSession = nil 142 newSession.Autocommit = true 143 newSession.Warnings = nil 144 return NewSafeSession(newSession) 145 } 146 147 // ResetTx clears the session 148 func (session *SafeSession) ResetTx() { 149 session.mu.Lock() 150 defer session.mu.Unlock() 151 session.resetCommonLocked() 152 // If settings pools is enabled on the vttablet. 153 // This variable will be true but there will not be a shard session with reserved connection id. 154 // So, we should check the shard session and not just this variable. 155 if session.Session.InReservedConn { 156 allSessions := append(session.ShardSessions, append(session.PreSessions, session.PostSessions...)...) 157 for _, ss := range allSessions { 158 if ss.ReservedId != 0 { 159 // found that reserved connection exists. 160 // abort here, we should keep the shard sessions. 161 return 162 } 163 } 164 } 165 session.ShardSessions = nil 166 session.PreSessions = nil 167 session.PostSessions = nil 168 } 169 170 // Reset clears the session 171 func (session *SafeSession) Reset() { 172 session.mu.Lock() 173 defer session.mu.Unlock() 174 session.resetCommonLocked() 175 session.ShardSessions = nil 176 session.PreSessions = nil 177 session.PostSessions = nil 178 } 179 180 // ResetAll resets the shard sessions and lock session. 181 func (session *SafeSession) ResetAll() { 182 session.mu.Lock() 183 defer session.mu.Unlock() 184 session.resetCommonLocked() 185 session.ShardSessions = nil 186 session.PreSessions = nil 187 session.PostSessions = nil 188 session.LockSession = nil 189 session.AdvisoryLock = nil 190 } 191 192 func (session *SafeSession) resetCommonLocked() { 193 session.mustRollback = false 194 session.autocommitState = notAutocommittable 195 session.Session.InTransaction = false 196 session.commitOrder = vtgatepb.CommitOrder_NORMAL 197 session.Savepoints = nil 198 if session.Options != nil { 199 session.Options.TransactionAccessMode = nil 200 } 201 } 202 203 // SetQueryTimeout sets the query timeout 204 func (session *SafeSession) SetQueryTimeout(queryTimeout int64) { 205 session.mu.Lock() 206 defer session.mu.Unlock() 207 session.QueryTimeout = queryTimeout 208 } 209 210 // GetQueryTimeout gets the query timeout 211 func (session *SafeSession) GetQueryTimeout() int64 { 212 session.mu.Lock() 213 defer session.mu.Unlock() 214 return session.QueryTimeout 215 } 216 217 // SavePoints returns the save points of the session. It's safe to use concurrently 218 func (session *SafeSession) SavePoints() []string { 219 session.mu.Lock() 220 defer session.mu.Unlock() 221 return session.GetSavepoints() 222 } 223 224 // SetAutocommittable sets the state to autocommitable if true. 225 // Otherwise, it's notAutocommitable. 226 func (session *SafeSession) SetAutocommittable(flag bool) { 227 session.mu.Lock() 228 defer session.mu.Unlock() 229 230 if session.autocommitState == autocommitted { 231 // Unreachable. 232 return 233 } 234 235 if flag { 236 session.autocommitState = autocommittable 237 } else { 238 session.autocommitState = notAutocommittable 239 } 240 } 241 242 // AutocommitApproval returns true if we can perform a single round-trip 243 // autocommit. If so, the caller is responsible for committing their 244 // transaction. 245 func (session *SafeSession) AutocommitApproval() bool { 246 session.mu.Lock() 247 defer session.mu.Unlock() 248 249 if session.autocommitState == autocommitted { 250 // Unreachable. 251 return false 252 } 253 254 if session.autocommitState == autocommittable { 255 session.autocommitState = autocommitted 256 return true 257 } 258 return false 259 } 260 261 // SetSavepointState sets the state only once for the complete query execution life. 262 // Calling the function multiple times will have no effect, only the first call would be used. 263 // Default state is savepointStateNotSet, 264 // if savepoint needed (spNeed true) then it will be set to savepointNeeded otherwise savepointNotNeeded. 265 func (session *SafeSession) SetSavepointState(spNeed bool) { 266 session.mu.Lock() 267 defer session.mu.Unlock() 268 269 if session.savepointState != savepointStateNotSet { 270 return 271 } 272 273 if spNeed { 274 session.savepointState = savepointNeeded 275 } else { 276 session.savepointState = savepointNotNeeded 277 } 278 } 279 280 // CanAddSavepoint returns true if we should insert savepoint and there is no existing savepoint. 281 func (session *SafeSession) CanAddSavepoint() bool { 282 session.mu.Lock() 283 defer session.mu.Unlock() 284 285 return session.savepointState == savepointNeeded 286 } 287 288 // SetSavepoint stores the savepoint name to session. 289 func (session *SafeSession) SetSavepoint(name string) { 290 session.mu.Lock() 291 defer session.mu.Unlock() 292 293 session.savepointName = name 294 session.savepointState = savepointSet 295 } 296 297 // SetRollbackCommand stores the rollback command to session and executed if required. 298 func (session *SafeSession) SetRollbackCommand() { 299 session.mu.Lock() 300 defer session.mu.Unlock() 301 302 // if the rollback already happened on the savepoint. There is nothing to set or execute on later. 303 if session.savepointState == savepointRollback { 304 return 305 } 306 307 if session.savepointState == savepointSet { 308 session.rollbackOnPartialExec = fmt.Sprintf("rollback to %s", session.savepointName) 309 } else { 310 session.rollbackOnPartialExec = txRollback 311 } 312 session.savepointState = savepointRollbackSet 313 } 314 315 // SavepointRollback updates the state that transaction was rolledback to the savepoint stored in the session. 316 func (session *SafeSession) SavepointRollback() { 317 session.mu.Lock() 318 defer session.mu.Unlock() 319 320 session.savepointState = savepointRollback 321 } 322 323 // IsRollbackSet returns true if rollback to savepoint can be done. 324 func (session *SafeSession) IsRollbackSet() bool { 325 session.mu.Lock() 326 defer session.mu.Unlock() 327 328 return session.savepointState == savepointRollbackSet 329 } 330 331 // SetCommitOrder sets the commit order. 332 func (session *SafeSession) SetCommitOrder(co vtgatepb.CommitOrder) { 333 session.mu.Lock() 334 defer session.mu.Unlock() 335 session.commitOrder = co 336 } 337 338 // InTransaction returns true if we are in a transaction 339 func (session *SafeSession) InTransaction() bool { 340 session.mu.Lock() 341 defer session.mu.Unlock() 342 return session.Session.InTransaction 343 } 344 345 // FindAndChangeSessionIfInSingleTxMode returns the transactionId and tabletAlias, if any, for a session 346 // modifies the shard session in a specific case for single mode transaction. 347 func (session *SafeSession) FindAndChangeSessionIfInSingleTxMode(keyspace, shard string, tabletType topodatapb.TabletType, txMode vtgatepb.TransactionMode) (int64, int64, *topodatapb.TabletAlias, error) { 348 session.mu.Lock() 349 defer session.mu.Unlock() 350 sessions := session.ShardSessions 351 switch session.commitOrder { 352 case vtgatepb.CommitOrder_PRE: 353 sessions = session.PreSessions 354 case vtgatepb.CommitOrder_POST: 355 sessions = session.PostSessions 356 } 357 for _, shardSession := range sessions { 358 if keyspace == shardSession.Target.Keyspace && tabletType == shardSession.Target.TabletType && shard == shardSession.Target.Shard { 359 if txMode != vtgatepb.TransactionMode_SINGLE || !shardSession.VindexOnly || session.queryFromVindex { 360 return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias, nil 361 } 362 count := actualNoOfShardSession(session.ShardSessions) 363 // If the count of shard session which are non vindex only is greater than 0, then it is a 364 if count > 0 { 365 session.mustRollback = true 366 return 0, 0, nil, vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) 367 } 368 // the shard session is now used by non-vindex query as well, 369 // so it is not an exclusive vindex only shard session anymore. 370 shardSession.VindexOnly = false 371 return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias, nil 372 } 373 } 374 return 0, 0, nil, nil 375 } 376 377 func addOrUpdate(shardSession *vtgatepb.Session_ShardSession, sessions []*vtgatepb.Session_ShardSession) ([]*vtgatepb.Session_ShardSession, error) { 378 appendSession := true 379 for i, sess := range sessions { 380 targetedAtSameTablet := sess.Target.Keyspace == shardSession.Target.Keyspace && 381 sess.Target.TabletType == shardSession.Target.TabletType && 382 sess.Target.Shard == shardSession.Target.Shard 383 if targetedAtSameTablet { 384 if !proto.Equal(sess.TabletAlias, shardSession.TabletAlias) { 385 errorDetails := fmt.Sprintf("got non-matching aliases (%v vs %v) for the same target (keyspace: %v, tabletType: %v, shard: %v)", 386 sess.TabletAlias, shardSession.TabletAlias, 387 sess.Target.Keyspace, sess.Target.TabletType, sess.Target.Shard) 388 return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, errorDetails) 389 } 390 // replace the old info with the new one 391 sessions[i] = shardSession 392 appendSession = false 393 break 394 } 395 } 396 if appendSession { 397 sessions = append(sessions, shardSession) 398 } 399 400 return sessions, nil 401 } 402 403 // AppendOrUpdate adds a new ShardSession, or updates an existing one if one already exists for the given shard session 404 func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { 405 session.mu.Lock() 406 defer session.mu.Unlock() 407 408 // additional check of transaction id is required 409 // as now in autocommit mode there can be session due to reserved connection 410 // that needs to be stored as shard session. 411 if session.autocommitState == autocommitted && shardSession.TransactionId != 0 { 412 // Should be unreachable 413 return vterrors.VT13001("unexpected 'autocommitted' state in transaction") 414 } 415 if !(session.Session.InTransaction || session.Session.InReservedConn) { 416 // Should be unreachable 417 return vterrors.VT13001("current session is neither in transaction nor in reserved connection") 418 } 419 session.autocommitState = notAutocommittable 420 421 // Always append, in order for rollback to succeed. 422 switch session.commitOrder { 423 case vtgatepb.CommitOrder_NORMAL: 424 if session.queryFromVindex { 425 shardSession.VindexOnly = true 426 } 427 newSessions, err := addOrUpdate(shardSession, session.ShardSessions) 428 if err != nil { 429 return err 430 } 431 session.ShardSessions = newSessions 432 433 if session.queryFromVindex { 434 break 435 } 436 // isSingle is enforced only for normmal commit order operations. 437 if session.isSingleDB(txMode) && len(session.ShardSessions) > 1 { 438 count := actualNoOfShardSession(session.ShardSessions) 439 if count <= 1 { 440 break 441 } 442 session.mustRollback = true 443 return vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) 444 } 445 case vtgatepb.CommitOrder_PRE: 446 newSessions, err := addOrUpdate(shardSession, session.PreSessions) 447 if err != nil { 448 return err 449 } 450 session.PreSessions = newSessions 451 case vtgatepb.CommitOrder_POST: 452 newSessions, err := addOrUpdate(shardSession, session.PostSessions) 453 if err != nil { 454 return err 455 } 456 session.PostSessions = newSessions 457 default: 458 // Should be unreachable 459 return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] SafeSession.AppendOrUpdate: unexpected commitOrder") 460 } 461 462 return nil 463 } 464 465 func actualNoOfShardSession(sessions []*vtgatepb.Session_ShardSession) int { 466 actualSS := 0 467 for _, ss := range sessions { 468 if ss.VindexOnly { 469 continue 470 } 471 actualSS++ 472 } 473 return actualSS 474 } 475 476 func (session *SafeSession) isSingleDB(txMode vtgatepb.TransactionMode) bool { 477 return session.TransactionMode == vtgatepb.TransactionMode_SINGLE || 478 (session.TransactionMode == vtgatepb.TransactionMode_UNSPECIFIED && txMode == vtgatepb.TransactionMode_SINGLE) 479 } 480 481 // SetRollback sets the flag indicating that the transaction must be rolled back. 482 // The call is a no-op if the session is not in a transaction. 483 func (session *SafeSession) SetRollback() { 484 session.mu.Lock() 485 defer session.mu.Unlock() 486 if session.Session.InTransaction { 487 session.mustRollback = true 488 } 489 } 490 491 // MustRollback returns true if the transaction must be rolled back. 492 func (session *SafeSession) MustRollback() bool { 493 session.mu.Lock() 494 defer session.mu.Unlock() 495 return session.mustRollback 496 } 497 498 // RecordWarning stores the given warning in the session 499 func (session *SafeSession) RecordWarning(warning *querypb.QueryWarning) { 500 session.mu.Lock() 501 defer session.mu.Unlock() 502 session.Session.Warnings = append(session.Session.Warnings, warning) 503 } 504 505 // ClearWarnings removes all the warnings from the session 506 func (session *SafeSession) ClearWarnings() { 507 session.mu.Lock() 508 defer session.mu.Unlock() 509 session.Session.Warnings = nil 510 } 511 512 // SetUserDefinedVariable sets the user defined variable in the session. 513 func (session *SafeSession) SetUserDefinedVariable(key string, value *querypb.BindVariable) { 514 session.mu.Lock() 515 defer session.mu.Unlock() 516 if session.UserDefinedVariables == nil { 517 session.UserDefinedVariables = make(map[string]*querypb.BindVariable) 518 } 519 session.UserDefinedVariables[key] = value 520 } 521 522 // SetTargetString sets the target string in the session. 523 func (session *SafeSession) SetTargetString(target string) { 524 session.mu.Lock() 525 defer session.mu.Unlock() 526 session.TargetString = target 527 } 528 529 // SetSystemVariable sets the system variable in the session. 530 func (session *SafeSession) SetSystemVariable(name string, expr string) { 531 session.mu.Lock() 532 defer session.mu.Unlock() 533 if session.SystemVariables == nil { 534 session.SystemVariables = make(map[string]string) 535 } 536 session.SystemVariables[name] = expr 537 } 538 539 // GetSystemVariables takes a visitor function that will receive each MySQL system variable in the session. 540 // This function will only yield system variables which apply to MySQL itself; Vitess-aware system variables 541 // will be skipped. 542 func (session *SafeSession) GetSystemVariables(f func(k string, v string)) { 543 session.mu.Lock() 544 defer session.mu.Unlock() 545 for k, v := range session.SystemVariables { 546 if sysvars.IsVitessAware(k) { 547 continue 548 } 549 f(k, v) 550 } 551 } 552 553 // HasSystemVariables returns whether the session has system variables that would apply to MySQL 554 func (session *SafeSession) HasSystemVariables() (found bool) { 555 session.GetSystemVariables(func(_ string, _ string) { 556 found = true 557 }) 558 return 559 } 560 561 // SetOptions sets the options 562 func (session *SafeSession) SetOptions(options *querypb.ExecuteOptions) { 563 session.mu.Lock() 564 defer session.mu.Unlock() 565 session.Options = options 566 } 567 568 // StoreSavepoint stores the savepoint and release savepoint queries in the session 569 func (session *SafeSession) StoreSavepoint(sql string) { 570 session.mu.Lock() 571 defer session.mu.Unlock() 572 session.Savepoints = append(session.Savepoints, sql) 573 } 574 575 // InReservedConn returns true if the session needs to execute on a dedicated connection 576 func (session *SafeSession) InReservedConn() bool { 577 session.mu.Lock() 578 defer session.mu.Unlock() 579 return session.Session.InReservedConn 580 } 581 582 // SetReservedConn set the InReservedConn setting. 583 func (session *SafeSession) SetReservedConn(reservedConn bool) { 584 session.mu.Lock() 585 defer session.mu.Unlock() 586 session.Session.InReservedConn = reservedConn 587 } 588 589 // SetPreQueries returns the prequeries that need to be run when reserving a connection 590 func (session *SafeSession) SetPreQueries() []string { 591 // extract keys 592 var keys []string 593 sysVars := make(map[string]string) 594 session.GetSystemVariables(func(k string, v string) { 595 keys = append(keys, k) 596 sysVars[k] = v 597 }) 598 599 // if not system variables to set, return 600 if len(keys) == 0 { 601 return nil 602 } 603 604 // sort the keys 605 sort.Strings(keys) 606 607 // build the query using sorted keys 608 var preQuery strings.Builder 609 first := true 610 for _, k := range keys { 611 if first { 612 preQuery.WriteString(fmt.Sprintf("set %s = %s", k, sysVars[k])) 613 first = false 614 } else { 615 preQuery.WriteString(fmt.Sprintf(", %s = %s", k, sysVars[k])) 616 } 617 } 618 return []string{preQuery.String()} 619 } 620 621 // SetLockSession sets the lock session. 622 func (session *SafeSession) SetLockSession(lockSession *vtgatepb.Session_ShardSession) { 623 session.mu.Lock() 624 defer session.mu.Unlock() 625 session.LockSession = lockSession 626 session.LastLockHeartbeat = time.Now().Unix() 627 } 628 629 // UpdateLockHeartbeat updates the LastLockHeartbeat time 630 func (session *SafeSession) UpdateLockHeartbeat() { 631 session.mu.Lock() 632 defer session.mu.Unlock() 633 session.LastLockHeartbeat = time.Now().Unix() 634 } 635 636 // TriggerLockHeartBeat returns if it time to trigger next lock heartbeat 637 func (session *SafeSession) TriggerLockHeartBeat() bool { 638 session.mu.Lock() 639 defer session.mu.Unlock() 640 now := time.Now().Unix() 641 return now-session.LastLockHeartbeat >= int64(lockHeartbeatTime.Seconds()) 642 } 643 644 // InLockSession returns whether locking is used on this session. 645 func (session *SafeSession) InLockSession() bool { 646 session.mu.Lock() 647 defer session.mu.Unlock() 648 return session.LockSession != nil 649 } 650 651 // ResetLock resets the lock session 652 func (session *SafeSession) ResetLock() { 653 session.mu.Lock() 654 defer session.mu.Unlock() 655 session.LockSession = nil 656 session.AdvisoryLock = nil 657 } 658 659 // ResetShard reset the shard session for the provided tablet alias. 660 func (session *SafeSession) ResetShard(tabletAlias *topodatapb.TabletAlias) error { 661 session.mu.Lock() 662 defer session.mu.Unlock() 663 664 // Always append, in order for rollback to succeed. 665 switch session.commitOrder { 666 case vtgatepb.CommitOrder_NORMAL: 667 newSessions, err := removeShard(tabletAlias, session.ShardSessions) 668 if err != nil { 669 return err 670 } 671 session.ShardSessions = newSessions 672 case vtgatepb.CommitOrder_PRE: 673 newSessions, err := removeShard(tabletAlias, session.PreSessions) 674 if err != nil { 675 return err 676 } 677 session.PreSessions = newSessions 678 case vtgatepb.CommitOrder_POST: 679 newSessions, err := removeShard(tabletAlias, session.PostSessions) 680 if err != nil { 681 return err 682 } 683 session.PostSessions = newSessions 684 default: 685 // Should be unreachable 686 return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] SafeSession.ResetShard: unexpected commitOrder") 687 } 688 return nil 689 } 690 691 // SetDDLStrategy set the DDLStrategy setting. 692 func (session *SafeSession) SetDDLStrategy(strategy string) { 693 session.mu.Lock() 694 defer session.mu.Unlock() 695 session.DDLStrategy = strategy 696 } 697 698 // GetDDLStrategy returns the DDLStrategy value. 699 func (session *SafeSession) GetDDLStrategy() string { 700 session.mu.Lock() 701 defer session.mu.Unlock() 702 return session.DDLStrategy 703 } 704 705 // GetSessionUUID returns the SessionUUID value. 706 func (session *SafeSession) GetSessionUUID() string { 707 session.mu.Lock() 708 defer session.mu.Unlock() 709 return session.SessionUUID 710 } 711 712 // SetSessionEnableSystemSettings set the SessionEnableSystemSettings setting. 713 func (session *SafeSession) SetSessionEnableSystemSettings(allow bool) { 714 session.mu.Lock() 715 defer session.mu.Unlock() 716 session.EnableSystemSettings = allow 717 } 718 719 // GetSessionEnableSystemSettings returns the SessionEnableSystemSettings value. 720 func (session *SafeSession) GetSessionEnableSystemSettings() bool { 721 session.mu.Lock() 722 defer session.mu.Unlock() 723 return session.EnableSystemSettings 724 } 725 726 // SetReadAfterWriteGTID set the ReadAfterWriteGtid setting. 727 func (session *SafeSession) SetReadAfterWriteGTID(vtgtid string) { 728 session.mu.Lock() 729 defer session.mu.Unlock() 730 if session.ReadAfterWrite == nil { 731 session.ReadAfterWrite = &vtgatepb.ReadAfterWrite{} 732 } 733 session.ReadAfterWrite.ReadAfterWriteGtid = vtgtid 734 } 735 736 // SetReadAfterWriteTimeout set the ReadAfterWriteTimeout setting. 737 func (session *SafeSession) SetReadAfterWriteTimeout(timeout float64) { 738 session.mu.Lock() 739 defer session.mu.Unlock() 740 if session.ReadAfterWrite == nil { 741 session.ReadAfterWrite = &vtgatepb.ReadAfterWrite{} 742 } 743 session.ReadAfterWrite.ReadAfterWriteTimeout = timeout 744 } 745 746 // SetSessionTrackGtids set the SessionTrackGtids setting. 747 func (session *SafeSession) SetSessionTrackGtids(enable bool) { 748 session.mu.Lock() 749 defer session.mu.Unlock() 750 if session.ReadAfterWrite == nil { 751 session.ReadAfterWrite = &vtgatepb.ReadAfterWrite{} 752 } 753 session.ReadAfterWrite.SessionTrackGtids = enable 754 } 755 756 func removeShard(tabletAlias *topodatapb.TabletAlias, sessions []*vtgatepb.Session_ShardSession) ([]*vtgatepb.Session_ShardSession, error) { 757 idx := -1 758 for i, session := range sessions { 759 if proto.Equal(session.TabletAlias, tabletAlias) { 760 if session.TransactionId != 0 { 761 return nil, vterrors.VT13001("removing shard session when in transaction") 762 } 763 idx = i 764 } 765 } 766 if idx == -1 { 767 return nil, vterrors.VT13001("tried to remove missing shard") 768 } 769 return append(sessions[:idx], sessions[idx+1:]...), nil 770 } 771 772 // GetOrCreateOptions will return the current options struct, or create one and return it if no-one exists 773 func (session *SafeSession) GetOrCreateOptions() *querypb.ExecuteOptions { 774 if session.Session.Options == nil { 775 session.Session.Options = &querypb.ExecuteOptions{} 776 } 777 return session.Session.Options 778 } 779 780 var _ iQueryOption = (*SafeSession)(nil) 781 782 func (session *SafeSession) cachePlan() bool { 783 if session == nil || session.Options == nil { 784 return true 785 } 786 787 session.mu.Lock() 788 defer session.mu.Unlock() 789 790 return !(session.Options.SkipQueryPlanCache || session.Options.HasCreatedTempTables) 791 } 792 793 func (session *SafeSession) getSelectLimit() int { 794 if session == nil || session.Options == nil { 795 return -1 796 } 797 798 session.mu.Lock() 799 defer session.mu.Unlock() 800 801 return int(session.Options.SqlSelectLimit) 802 } 803 804 // isTxOpen returns true if there is open connection to any of the shard. 805 func (session *SafeSession) isTxOpen() bool { 806 session.mu.Lock() 807 defer session.mu.Unlock() 808 809 return len(session.ShardSessions) > 0 || len(session.PreSessions) > 0 || len(session.PostSessions) > 0 810 } 811 812 // getSessions returns the shard session for the current commit order. 813 func (session *SafeSession) getSessions() []*vtgatepb.Session_ShardSession { 814 session.mu.Lock() 815 defer session.mu.Unlock() 816 817 switch session.commitOrder { 818 case vtgatepb.CommitOrder_PRE: 819 return session.PreSessions 820 case vtgatepb.CommitOrder_POST: 821 return session.PostSessions 822 default: 823 return session.ShardSessions 824 } 825 } 826 827 func (session *SafeSession) RemoveInternalSavepoint() { 828 session.mu.Lock() 829 defer session.mu.Unlock() 830 831 if session.savepointName == "" { 832 return 833 } 834 sCount := len(session.Savepoints) 835 if sCount == 0 { 836 return 837 } 838 sLast := sCount - 1 839 if strings.Contains(session.Savepoints[sLast], session.savepointName) { 840 session.Savepoints = session.Savepoints[0:sLast] 841 } 842 } 843 844 // HasAdvisoryLock returns if any advisory lock is taken 845 func (session *SafeSession) HasAdvisoryLock() bool { 846 session.mu.Lock() 847 defer session.mu.Unlock() 848 849 return len(session.AdvisoryLock) != 0 850 } 851 852 // AddAdvisoryLock adds the advisory lock to the list. 853 func (session *SafeSession) AddAdvisoryLock(name string) { 854 session.mu.Lock() 855 defer session.mu.Unlock() 856 857 if session.AdvisoryLock == nil { 858 session.AdvisoryLock = map[string]int64{name: 1} 859 return 860 } 861 count, exists := session.AdvisoryLock[name] 862 if exists { 863 count++ 864 } 865 session.AdvisoryLock[name] = count 866 } 867 868 // RemoveAdvisoryLock removes the advisory lock from the list. 869 func (session *SafeSession) RemoveAdvisoryLock(name string) { 870 session.mu.Lock() 871 defer session.mu.Unlock() 872 873 if session.AdvisoryLock == nil { 874 return 875 } 876 count, exists := session.AdvisoryLock[name] 877 if !exists { 878 return 879 } 880 count-- 881 if count == 0 { 882 delete(session.AdvisoryLock, name) 883 return 884 } 885 session.AdvisoryLock[name] = count 886 } 887 888 // ClearAdvisoryLock clears the advisory lock list. 889 func (session *SafeSession) ClearAdvisoryLock() { 890 session.mu.Lock() 891 defer session.mu.Unlock() 892 893 session.AdvisoryLock = nil 894 } 895 896 func (session *SafeSession) EnableLogging() { 897 session.mu.Lock() 898 defer session.mu.Unlock() 899 900 session.logging = &executeLogger{} 901 } 902 903 func (l *executeLogger) log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { 904 if l == nil { 905 return 906 } 907 l.mu.Lock() 908 defer l.mu.Unlock() 909 id := l.lastID 910 l.lastID++ 911 if begin { 912 l.entries = append(l.entries, engine.ExecuteEntry{ 913 ID: id, 914 Target: target, 915 Gateway: gateway, 916 Query: "begin", 917 FiredFrom: primitive, 918 }) 919 } 920 ast, err := sqlparser.Parse(query) 921 if err != nil { 922 panic("query not able to parse. this should not happen") 923 } 924 pq := sqlparser.NewParsedQuery(ast) 925 if bv == nil { 926 bv = map[string]*querypb.BindVariable{} 927 } 928 q, err := pq.GenerateQuery(bv, nil) 929 if err != nil { 930 panic("query not able to generate query. this should not happen") 931 } 932 933 l.entries = append(l.entries, engine.ExecuteEntry{ 934 ID: id, 935 Target: target, 936 Gateway: gateway, 937 Query: q, 938 FiredFrom: primitive, 939 }) 940 } 941 942 func (l *executeLogger) GetLogs() []engine.ExecuteEntry { 943 l.mu.Lock() 944 defer l.mu.Unlock() 945 result := make([]engine.ExecuteEntry, len(l.entries)) 946 copy(result, l.entries) 947 return result 948 }