// execRequest executes the request using the provided planner. // It parses the sql into statements, iterates through the statements, creates // KV transactions and automatically retries them when possible, executes the // (synchronous attempt of) schema changes. // It will accumulate a result in Response for each statement. // It will resume a SQL transaction, if one was previously open for this client. // // execRequest handles the mismatch between the SQL interface that the Executor // provides, based on statements being streamed from the client in the context // of a session, and the KV client.Txn interface, based on (possibly-retriable) // callbacks passed to be executed in the context of a transaction. Actual // execution of statements in the context of a KV txn is delegated to // runTxnAttempt(). // // Args: // txnState: State about about ongoing transaction (if any). The state will be // updated. func (e *Executor) execRequest(session *Session, sql string) StatementResults { var res StatementResults txnState := &session.TxnState planMaker := &session.planner stmts, err := planMaker.parser.Parse(sql, parser.Syntax(session.Syntax)) if err != nil { pErr := roachpb.NewError(err) // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. if txnState.txn != nil { // Rollback the txn. txnState.txn.CleanupOnError(pErr) txnState.resetStateAndTxn(Aborted) } res.ResultList = append(res.ResultList, Result{PErr: pErr}) return res } if len(stmts) == 0 { res.Empty = true return res } // If the planMaker wants config updates to be blocked, then block them. defer planMaker.blockConfigUpdatesMaybe(e)() for len(stmts) > 0 { // Each iteration consumes a transaction's worth of statements. inTxn := txnState.State != NoTxn var execOpt client.TxnExecOptions // Figure out the statements out of which we're going to try to consume // this iteration. If we need to create an implicit txn, only one statement // can be consumed. stmtsToExec := stmts // We can AutoRetry the next batch of statements if we're in a clean state // (i.e. the next statements we're going to see are the first statements in // a transaction). if !inTxn { // Detect implicit transactions. if _, isBegin := stmts[0].(*parser.BeginTransaction); !isBegin { execOpt.AutoCommit = true stmtsToExec = stmtsToExec[0:1] } txnState.reset(e, session) txnState.State = Open txnState.autoRetry = true execOpt.MinInitialTimestamp = e.ctx.Clock.Now() if execOpt.AutoCommit { txnState.txn.SetDebugName(sqlImplicitTxnName, 0) } else { txnState.txn.SetDebugName(sqlTxnName, 0) } } else { txnState.autoRetry = false } execOpt.AutoRetry = txnState.autoRetry if txnState.State == NoTxn { panic("we failed to initialize a txn") } // Now actually run some statements. var remainingStmts parser.StatementList var results []Result origState := txnState.State txnClosure := func(txn *client.Txn, opt *client.TxnExecOptions) *roachpb.Error { if txnState.State == Open && txnState.txn != txn { panic(fmt.Sprintf("closure wasn't called in the txn we set up for it."+ "\ntxnState.txn:%+v\ntxn:%+v\ntxnState:%+v", txnState.txn, txn, txnState)) } txnState.txn = txn return runTxnAttempt(e, planMaker, origState, txnState, opt, stmtsToExec, &results, &remainingStmts) } // This is where the magic happens - we ask db to run a KV txn and possibly retry it. txn := txnState.txn // this might be nil if the txn was already aborted. pErr := txnState.txn.Exec(execOpt, txnClosure) res.ResultList = append(res.ResultList, results...) // Now make sense of the state we got into and update txnState. if txnState.State == RestartWait && txnState.commitSeen { // A COMMIT got a retriable error. Too bad, this txn is toast. After we // return a result for COMMIT (with the COMMIT pgwire tag), the user can't // send any more commands. e.txnAbortCount.Inc(1) txn.CleanupOnError(pErr) txnState.resetStateAndTxn(NoTxn) } if execOpt.AutoCommit { // If execOpt.AutoCommit was set, then the txn no longer exists at this point. txnState.resetStateAndTxn(NoTxn) } // If the txn is in any state but Open, exec the schema changes. They'll // short-circuit themselves if the mutation that queued them has been // rolled back from the table descriptor. stmtsExecuted := stmts[0 : len(stmtsToExec)-len(remainingStmts)] if txnState.State != Open { planMaker.releaseLeases() // Exec the schema changers (if the txn rolled back, the schema changers // will short-circuit because the corresponding descriptor mutation is not // found). txnState.schemaChangers.execSchemaChanges(e, planMaker, res.ResultList) planMaker.checkTestingVerifyMetadataInitialOrDie(e, stmts) planMaker.checkTestingVerifyMetadataOrDie(e, stmtsExecuted) } else { // We're still in a txn, so we only check that the verifyMetadata callback // fails the first time it's run. The gossip update that will make the // callback succeed only happens when the txn is done. planMaker.checkTestingVerifyMetadataInitialOrDie(e, stmtsExecuted) } // Figure out what statements to run on the next iteration. if pErr != nil { // Don't execute anything further. stmts = nil } else if execOpt.AutoCommit { stmts = stmts[1:] } else { stmts = remainingStmts } } return res }
// execRequest executes the request using the provided planner. // It parses the sql into statements, iterates through the statements, creates // KV transactions and automatically retries them when possible, executes the // (synchronous attempt of) schema changes. // It will accumulate a result in Response for each statement. // It will resume a SQL transaction, if one was previously open for this client. // // execRequest handles the mismatch between the SQL interface that the Executor // provides, based on statements being streamed from the client in the context // of a session, and the KV client.Txn interface, based on (possibly-retriable) // callbacks passed to be executed in the context of a transaction. Actual // execution of statements in the context of a KV txn is delegated to // runTxnAttempt(). // // Args: // txnState: State about about ongoing transaction (if any). The state will be // updated. func (e *Executor) execRequest( txnState *txnState, sql string, planMaker *planner) StatementResults { var res StatementResults stmts, err := planMaker.parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { pErr := roachpb.NewError(err) // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. if txnState.txn != nil { // Rollback the txn. txnState.txn.Cleanup(pErr) txnState.aborted = true txnState.txn = nil } res.ResultList = append(res.ResultList, Result{PErr: pErr}) return res } if len(stmts) == 0 { res.Empty = true return res } if e.ctx.TestingMocker.WaitForGossipUpdate { // We might need to verify metadata. Lock the system config so that no // gossip updates sneak in under us. The point is to be able to assert // that the verify callback only succeeds after a gossip update. // // This lock does not change semantics. Even outside of tests, the // planner is initialized with a static systemConfig, so locking // the Executor's systemConfig cannot change the semantics of the // SQL operation being performed under lock. // // The case of a multi-request transaction is not handled here, // because those transactions outlive the verification callback. // TODO(andrei): consider putting this callback on the Session, not // on the executor, after Session is not a proto any more. Also, #4646. e.systemConfigCond.L.Lock() defer func() { e.systemConfigCond.L.Unlock() }() } for len(stmts) > 0 { // Each iteration consumes a transaction's worth of statements. inTxn := txnState.state() != noTransaction var execOpt client.TxnExecOptions // Figure out the statements out of which we're going to try to consume // this iteration. If we need to create an implicit txn, only one statement // can be consumed. stmtsToExec := stmts // We can AutoRetry the next batch of statements if we're in a clean state // (i.e. the next statements we're going to see are the first statements in // a transaction). if !inTxn { // Detect implicit transactions. if _, isBegin := stmts[0].(*parser.BeginTransaction); !isBegin { execOpt.AutoCommit = true stmtsToExec = stmtsToExec[0:1] } txnState.txn = e.newTxn(planMaker.session) execOpt.AutoRetry = true execOpt.MinInitialTimestamp = e.ctx.Clock.Now() txnState.txn.SetDebugName(fmt.Sprintf("sql implicit: %t", execOpt.AutoCommit), 0) } if txnState.state() == noTransaction { panic("we failed to initialize a txn") } // Now actually run some statements. var remainingStmts parser.StatementList var results []Result origAborted := txnState.state() == abortedTransaction txnClosure := func(txn *client.Txn, opt *client.TxnExecOptions) *roachpb.Error { return runTxnAttempt(e, planMaker, origAborted, txnState, txn, opt, stmtsToExec, &results, &remainingStmts) } // This is where the magic happens - we ask db to run a KV txn and possibly retry it. pErr := txnState.txn.Exec(execOpt, txnClosure) res.ResultList = append(res.ResultList, results...) // Now make sense of the state we got into and update txnState. if pErr != nil { // If we got an error, the txn has been aborted (or it might be already // done if the error was encountered when executing the COMMIT/ROLLBACK. // There's nothing we can use it for any more. // TODO(andrei): once txn.Exec() doesn't abort retriable txns any more, // we need to be more nuanced here. txnState.txn = nil e.txnAbortCount.Inc(1) } if execOpt.AutoCommit { // If execOpt.AutoCommit was set, then the txn no longer exists at this point. txnState.txn = nil txnState.aborted = false } // If the txn is in a final state (committed, rolled back or aborted), exec // the schema changes. if txnState.state() != openTransaction { planMaker.releaseLeases() // Exec the schema changers (if the txn rolled back, the schema changers // will short-circuit because the corresponding descriptor mutation is not // found). txnState.schemaChangers.execSchemaChanges(e, planMaker, res.ResultList) stmtsExecuted := stmts[0 : len(stmtsToExec)-len(remainingStmts)] e.checkTestingWaitForGossipUpdateOrDie(planMaker, stmtsExecuted) } // Figure out what statements to run on the next iteration. if pErr != nil { // Don't execute anything further. stmts = nil } else if execOpt.AutoCommit { stmts = stmts[1:] } else { stmts = remainingStmts } } return res }
// execRequest executes the request using the provided planner. // It parses the sql into statements, iterates through the statements, creates // KV transactions and automatically retries them when possible, executes the // (synchronous attempt of) schema changes. // It will accumulate a result in Response for each statement. // It will resume a SQL transaction, if one was previously open for this client. // // execRequest handles the mismatch between the SQL interface that the Executor // provides, based on statements being streamed from the client in the context // of a session, and the KV client.Txn interface, based on (possibly-retriable) // callbacks passed to be executed in the context of a transaction. Actual // execution of statements in the context of a KV txn is delegated to // runTxnAttempt(). // // Args: // txnState: State about about ongoing transaction (if any). The state will be // updated. func (e *Executor) execRequest( txnState *txnState, sql string, planMaker *planner) StatementResults { var res StatementResults stmts, err := planMaker.parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { pErr := roachpb.NewError(err) // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. if txnState.txn != nil { // Rollback the txn. txnState.txn.CleanupOnError(pErr) txnState.resetStateAndTxn(Aborted) } res.ResultList = append(res.ResultList, Result{PErr: pErr}) return res } if len(stmts) == 0 { res.Empty = true return res } if e.ctx.TestingKnobs.WaitForGossipUpdate { // We might need to verify metadata. Lock the system config so that no // gossip updates sneak in under us. The point is to be able to assert // that the verify callback only succeeds after a gossip update. // // This lock does not change semantics. Even outside of tests, the // planner is initialized with a static systemConfig, so locking // the Executor's systemConfig cannot change the semantics of the // SQL operation being performed under lock. // // The case of a multi-request transaction is not handled here, // because those transactions outlive the verification callback. // TODO(andrei): consider putting this callback on the Session, not // on the executor, after Session is not a proto any more. Also, #4646. e.systemConfigCond.L.Lock() defer func() { e.systemConfigCond.L.Unlock() }() } for len(stmts) > 0 { // Each iteration consumes a transaction's worth of statements. inTxn := txnState.State != NoTxn var execOpt client.TxnExecOptions // Figure out the statements out of which we're going to try to consume // this iteration. If we need to create an implicit txn, only one statement // can be consumed. stmtsToExec := stmts // We can AutoRetry the next batch of statements if we're in a clean state // (i.e. the next statements we're going to see are the first statements in // a transaction). if !inTxn { // Detect implicit transactions. if _, isBegin := stmts[0].(*parser.BeginTransaction); !isBegin { execOpt.AutoCommit = true stmtsToExec = stmtsToExec[0:1] } txnState.reset(e, planMaker.session) txnState.State = Open txnState.autoRetry = true execOpt.MinInitialTimestamp = e.ctx.Clock.Now() if execOpt.AutoCommit { txnState.txn.SetDebugName(sqlImplicitTxnName, 0) } else { txnState.txn.SetDebugName(sqlTxnName, 0) } } else { txnState.autoRetry = false } execOpt.AutoRetry = txnState.autoRetry if txnState.State == NoTxn { panic("we failed to initialize a txn") } // Now actually run some statements. var remainingStmts parser.StatementList var results []Result origState := txnState.State txnClosure := func(txn *client.Txn, opt *client.TxnExecOptions) *roachpb.Error { if txnState.State == Open && txnState.txn != txn { panic(fmt.Sprintf("closure wasn't called in the txn we set up for it."+ "\ntxnState.txn:%+v\ntxn:%+v\ntxnState:%+v", txnState.txn, txn, txnState)) } txnState.txn = txn return runTxnAttempt(e, planMaker, origState, txnState, opt, stmtsToExec, &results, &remainingStmts) } // This is where the magic happens - we ask db to run a KV txn and possibly retry it. txn := txnState.txn // this might be nil if the txn was already aborted. pErr := txnState.txn.Exec(execOpt, txnClosure) res.ResultList = append(res.ResultList, results...) // Now make sense of the state we got into and update txnState. if txnState.State == RestartWait && txnState.commitSeen { // A COMMIT got a retriable error. Too bad, this txn is toast. After we // return a result for COMMIT (with the COMMIT pgwire tag), the user can't // send any more commands. e.txnAbortCount.Inc(1) txn.CleanupOnError(pErr) txnState.resetStateAndTxn(NoTxn) } if execOpt.AutoCommit { // If execOpt.AutoCommit was set, then the txn no longer exists at this point. txnState.resetStateAndTxn(NoTxn) } // If the txn is in any state but Open, exec the schema changes. They'll // short-circuit themselves if the mutation that queued them has been // rolled back from the table descriptor. if txnState.State != Open { planMaker.releaseLeases() // Exec the schema changers (if the txn rolled back, the schema changers // will short-circuit because the corresponding descriptor mutation is not // found). txnState.schemaChangers.execSchemaChanges(e, planMaker, res.ResultList) stmtsExecuted := stmts[0 : len(stmtsToExec)-len(remainingStmts)] e.checkTestingWaitForGossipUpdateOrDie(planMaker, stmtsExecuted) } // Figure out what statements to run on the next iteration. if pErr != nil { // Don't execute anything further. stmts = nil } else if execOpt.AutoCommit { stmts = stmts[1:] } else { stmts = remainingStmts } } return res }