// Show a session-local variable name. func (p *planner) Show(n *parser.Show) (planNode, error) { name := strings.ToUpper(n.Name) v := &valuesNode{columns: []ResultColumn{{Name: name, Typ: parser.TypeString}}} switch name { case `DATABASE`: v.rows = append(v.rows, []parser.Datum{parser.NewDString(p.session.Database)}) case `TIME ZONE`: v.rows = append(v.rows, []parser.Datum{parser.NewDString(p.session.Location.String())}) case `SYNTAX`: v.rows = append(v.rows, []parser.Datum{parser.NewDString(parser.Syntax(p.session.Syntax).String())}) case `DEFAULT_TRANSACTION_ISOLATION`: level := p.session.DefaultIsolationLevel.String() v.rows = append(v.rows, []parser.Datum{parser.NewDString(level)}) case `TRANSACTION ISOLATION LEVEL`: v.rows = append(v.rows, []parser.Datum{parser.NewDString(p.txn.Proto.Isolation.String())}) case `TRANSACTION PRIORITY`: v.rows = append(v.rows, []parser.Datum{parser.NewDString(p.txn.UserPriority.String())}) default: return nil, fmt.Errorf("unknown variable: %q", name) } return v, nil }
// Prepare returns the result types of the given statement. Args may be a // partially populated val args map. Prepare will populate the missing val // args. The column result types are returned (or nil if there are no results). func (e *Executor) Prepare(query string, session *Session, args parser.MapArgs) ( []ResultColumn, *roachpb.Error) { stmt, err := parser.ParseOne(query, parser.Syntax(session.Syntax)) if err != nil { return nil, roachpb.NewError(err) } session.planner.resetForBatch(e) session.planner.evalCtx.Args = args session.planner.evalCtx.PrepareOnly = true // TODO(andrei): does the prepare phase really need a Txn? txn := client.NewTxn(*e.ctx.DB) txn.Proto.Isolation = session.DefaultIsolationLevel session.planner.setTxn(txn) defer session.planner.setTxn(nil) plan, pErr := session.planner.prepare(stmt) if pErr != nil { return nil, pErr } if plan == nil { return nil, nil } cols := plan.Columns() for _, c := range cols { if err := checkResultDatum(c.Typ); err != nil { return nil, roachpb.NewError(err) } } return cols, nil }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (e *Executor) execStmts(sql string, planMaker *planner) driver.Response { var resp driver.Response stmts, err := planMaker.parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. resp.Results = append(resp.Results, makeResultFromError(planMaker, err)) return resp } for _, stmt := range stmts { result, err := e.execStmt(stmt, planMaker) if err != nil { result = makeResultFromError(planMaker, err) } resp.Results = append(resp.Results, result) // Release the leases once a transaction is complete. if planMaker.txn == nil { planMaker.releaseLeases(e.db) // The previous transaction finished executing some schema changes. Wait for // the schema changes to propagate to all nodes, so that once the executor // returns the new schema are live everywhere. This is not needed for // correctness but is done to make the UI experience/tests predictable. if err := e.waitForCompletedSchemaChangesToPropagate(planMaker); err != nil { log.Warning(err) } } } return resp }
// Show a session-local variable name. func (p *planner) Show(n *parser.Show) (planNode, error) { name := strings.ToUpper(n.Name) v := &valuesNode{columns: []ResultColumn{{Name: name, Typ: parser.DummyString}}} switch name { case `DATABASE`: v.rows = append(v.rows, []parser.Datum{parser.DString(p.session.Database)}) case `TIME ZONE`: loc, err := p.evalCtx.GetLocation() if err != nil { return nil, err } v.rows = append(v.rows, []parser.Datum{parser.DString(loc.String())}) case `SYNTAX`: v.rows = append(v.rows, []parser.Datum{parser.DString(parser.Syntax(p.session.Syntax).String())}) case `DEFAULT_TRANSACTION_ISOLATION`: v.rows = append(v.rows, []parser.Datum{parser.DString("SERIALIZABLE")}) case `TRANSACTION ISOLATION LEVEL`: v.rows = append(v.rows, []parser.Datum{parser.DString(p.txn.Proto.Isolation.String())}) case `TRANSACTION PRIORITY`: v.rows = append(v.rows, []parser.Datum{parser.DString(p.txn.UserPriority.String())}) default: return nil, fmt.Errorf("unknown variable: %q", name) } return v, nil }
// Prepare returns the result types of the given statement. pinfo may // contain partial type information for placeholders. Prepare will // populate the missing types. The column result types are returned (or // nil if there are no results). func (e *Executor) Prepare( query string, session *Session, pinfo parser.PlaceholderTypes, ) ([]ResultColumn, error) { if log.V(2) { log.Infof(session.Ctx(), "preparing: %s", query) } else if traceSQL { log.Tracef(session.Ctx(), "preparing: %s", query) } stmt, err := parser.ParseOne(query, parser.Syntax(session.Syntax)) if err != nil { return nil, err } if err = pinfo.ProcessPlaceholderAnnotations(stmt); err != nil { return nil, err } protoTS, err := isAsOf(&session.planner, stmt, e.ctx.Clock.Now()) if err != nil { return nil, err } session.planner.resetForBatch(e) session.planner.semaCtx.Placeholders.SetTypes(pinfo) session.planner.evalCtx.PrepareOnly = true // Prepare needs a transaction because it needs to retrieve db/table // descriptors for type checking. txn := client.NewTxn(session.Ctx(), *e.ctx.DB) txn.Proto.Isolation = session.DefaultIsolationLevel session.planner.setTxn(txn) defer session.planner.setTxn(nil) if protoTS != nil { session.planner.asOf = true defer func() { session.planner.asOf = false }() setTxnTimestamps(txn, *protoTS) } plan, err := session.planner.prepare(stmt) if err != nil { return nil, err } if plan == nil { return nil, nil } cols := plan.Columns() for _, c := range cols { if err := checkResultDatum(c.Typ); err != nil { return nil, err } } return cols, nil }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (e *Executor) execStmts(sql string, planMaker *planner) Response { var resp Response stmts, err := planMaker.parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. resp.Results = append(resp.Results, makeResultFromError(planMaker, roachpb.NewError(err))) return resp } for _, stmt := range stmts { result, err := e.execStmt(stmt, planMaker) if err != nil { result = makeResultFromError(planMaker, err) } // Release the leases once a transaction is complete. if planMaker.txn == nil { planMaker.releaseLeases(e.db) // Execute any schema changes that were scheduled. if len(planMaker.schemaChangers) > 0 && // Disable execution in some tests. !disableSyncSchemaChangeExec { retryOpts := retry.Options{ InitialBackoff: 20 * time.Millisecond, MaxBackoff: 200 * time.Millisecond, Multiplier: 2, } for _, sc := range planMaker.schemaChangers { sc.db = e.db for r := retry.Start(retryOpts); r.Next(); { if done, err := sc.IsDone(); err != nil { log.Warning(err) break } else if done { break } if pErr := sc.exec(); pErr != nil { if _, ok := pErr.GoError().(*roachpb.ExistingSchemaChangeLeaseError); ok { // Try again. continue } // All other errors can be reported. result = makeResultFromError(planMaker, pErr) } break } } } } resp.Results = append(resp.Results, result) } return resp }
// Show a session-local variable name. func (p *planner) Show(n *parser.Show) (planNode, error) { name := strings.ToUpper(n.Name) v := &valuesNode{columns: []string{name}} switch name { case `DATABASE`: v.rows = append(v.rows, []parser.Datum{parser.DString(p.session.Database)}) case `SYNTAX`: v.rows = append(v.rows, []parser.Datum{parser.DString(parser.Syntax(p.session.Syntax).String())}) default: return nil, fmt.Errorf("unknown variable: %q", name) } return v, nil }
// Prepare returns the result types of the given statement. Args may be a // partially populated val args map. Prepare will populate the missing val // args. The column result types are returned (or nil if there are no results). func (e *Executor) Prepare(user string, query string, session *Session, args parser.MapArgs) ( []ResultColumn, *roachpb.Error) { stmt, err := parser.ParseOne(query, parser.Syntax(session.Syntax)) if err != nil { return nil, roachpb.NewError(err) } planMaker := plannerPool.Get().(*planner) defer releasePlanner(planMaker) cfg, cache := e.getSystemConfig() *planMaker = planner{ user: user, evalCtx: parser.EvalContext{ NodeID: e.nodeID, ReCache: e.reCache, GetLocation: session.getLocation, Args: args, PrepareOnly: true, }, leaseMgr: e.ctx.LeaseManager, systemConfig: cfg, databaseCache: cache, session: session, execCtx: &e.ctx, } txn := client.NewTxn(*e.ctx.DB) txn.Proto.Isolation = session.DefaultIsolationLevel planMaker.setTxn(txn) plan, pErr := planMaker.prepare(stmt) if pErr != nil { return nil, pErr } if plan == nil { return nil, nil } cols := plan.Columns() for _, c := range cols { if err := checkResultDatum(c.Typ); err != nil { return nil, roachpb.NewError(err) } } return cols, nil }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (s server) execStmts(sql string, params parameters, planMaker *planner) driver.Response { var resp driver.Response stmts, err := parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { // A parse error occured: we can't determine if there were multiple // statements or only one, so just pretend there was one. resp.Results = append(resp.Results, rollbackTxnAndReturnResultWithError(planMaker, err)) return resp } for _, stmt := range stmts { result, err := s.execStmt(stmt, params, planMaker) if err != nil { result = rollbackTxnAndReturnResultWithError(planMaker, err) } resp.Results = append(resp.Results, result) } return resp }
// Prepare returns the result types of the given statement. Args may be a // partially populated val args map. Prepare will populate the missing val // args. The column result types are returned (or nil if there are no results). func (e *Executor) Prepare(user string, query string, session *Session, args parser.MapArgs) ( []ResultColumn, *roachpb.Error) { stmt, err := parser.ParseOne(query, parser.Syntax(session.Syntax)) if err != nil { return nil, roachpb.NewError(err) } planMaker := plannerPool.Get().(*planner) defer releasePlanner(planMaker) cfg, cache := e.getSystemConfig() *planMaker = planner{ user: user, evalCtx: parser.EvalContext{ NodeID: e.nodeID, ReCache: e.reCache, GetLocation: session.getLocation, Args: args, }, leaseMgr: e.leaseMgr, systemConfig: cfg, databaseCache: cache, session: session, } timestamp := time.Now() txn := e.newTxn(session) planMaker.setTxn(txn, timestamp) planMaker.evalCtx.StmtTimestamp = parser.DTimestamp{Time: timestamp} plan, pErr := planMaker.prepare(stmt) if pErr != nil { return nil, pErr } if plan == nil { return nil, nil } cols := plan.Columns() for _, c := range cols { if err := checkResultDatum(c.Typ); err != nil { return nil, roachpb.NewError(err) } } return cols, nil }
// Prepare returns the result types of the given statement. pinfo may // contain partial type information for placeholders. Prepare will // populate the missing types. The column result types are returned (or // nil if there are no results). func (e *Executor) Prepare( ctx context.Context, query string, session *Session, pinfo parser.PlaceholderTypes, ) ([]ResultColumn, error) { stmt, err := parser.ParseOne(query, parser.Syntax(session.Syntax)) if err != nil { return nil, err } if err = pinfo.ProcessPlaceholderAnnotations(stmt); err != nil { return nil, err } session.planner.resetForBatch(e) session.planner.semaCtx.Placeholders.SetTypes(pinfo) session.planner.evalCtx.PrepareOnly = true // Prepare needs a transaction because it needs to retrieve db/table // descriptors for type checking. txn := client.NewTxn(ctx, *e.ctx.DB) txn.Proto.Isolation = session.DefaultIsolationLevel session.planner.setTxn(txn) defer session.planner.setTxn(nil) plan, err := session.planner.prepare(stmt) if err != nil { return nil, err } if plan == nil { return nil, nil } cols := plan.Columns() for _, c := range cols { if err := checkResultDatum(c.Typ); err != nil { return nil, err } } return cols, nil }
// Show a session-local variable name. func (p *planner) Show(n *parser.Show) (planNode, *roachpb.Error) { name := strings.ToUpper(n.Name) v := &valuesNode{columns: []column{{name: name, typ: parser.DummyString}}} switch name { case `DATABASE`: v.rows = append(v.rows, []parser.Datum{parser.DString(p.session.Database)}) case `TIME ZONE`: loc, err := p.evalCtx.GetLocation() if err != nil { return nil, roachpb.NewError(err) } v.rows = append(v.rows, []parser.Datum{parser.DString(loc.String())}) case `SYNTAX`: v.rows = append(v.rows, []parser.Datum{parser.DString(parser.Syntax(p.session.Syntax).String())}) case `TRANSACTION ISOLATION LEVEL`: v.rows = append(v.rows, []parser.Datum{parser.DString(p.txn.Proto.Isolation.String())}) default: return nil, roachpb.NewUErrorf("unknown variable: %q", name) } return v, nil }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (e *Executor) execStmts(sql string, params parameters, planMaker *planner) driver.Response { var resp driver.Response stmts, err := parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. resp.Results = append(resp.Results, makeResultFromError(planMaker, err)) return resp } for _, stmt := range stmts { result, err := e.execStmt(stmt, params, planMaker) if err != nil { result = makeResultFromError(planMaker, err) } resp.Results = append(resp.Results, result) // TODO(pmattis): Is this the correct time to be releasing leases acquired // during execution of the statement? // // TODO(pmattis): Need to record the leases used by a transaction within // the transaction state and restore it when the transaction is restored. planMaker.releaseLeases(e.db) } return resp }
// 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 }
// 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 { // 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.updateStateAndCleanupOnErr(err, e) } res.ResultList = append(res.ResultList, Result{Err: err}) 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 // If protoTS is set, the transaction proto sets its Orig and Max timestamps // to it each retry. var protoTS *hlc.Timestamp // 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 { execOpt.MinInitialTimestamp = e.ctx.Clock.Now() // Detect implicit transactions. if _, isBegin := stmts[0].(*parser.BeginTransaction); !isBegin { execOpt.AutoCommit = true stmtsToExec = stmtsToExec[:1] // Check for AS OF SYSTEM TIME. If it is present but not detected here, // it will raise an error later on. protoTS, err = isAsOf(planMaker, stmtsToExec[0], execOpt.MinInitialTimestamp) if err != nil { res.ResultList = append(res.ResultList, Result{Err: err}) return res } if protoTS != nil { planMaker.asOf = true defer func() { planMaker.asOf = false }() } } txnState.reset(session.Ctx(), e, session) txnState.State = Open txnState.autoRetry = true txnState.sqlTimestamp = e.ctx.Clock.PhysicalTime() 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) 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 if protoTS != nil { setTxnTimestamps(txnState.txn, *protoTS) } var err error results, remainingStmts, err = runTxnAttempt(e, planMaker, origState, txnState, opt, stmtsToExec) return err } // 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. err := txn.Exec(execOpt, txnClosure) // Update the Err field of the last result if the error was coming from // auto commit. The error was generated outside of the txn closure, so it was not // set in any result. if err != nil { lastResult := &results[len(results)-1] if aErr, ok := err.(*client.AutoCommitError); ok { // Until #7881 fixed. if txn == nil { log.Errorf(session.Ctx(), "AutoCommitError on nil txn: %+v, "+ "txnState %+v, execOpt %+v, stmts %+v, remaining %+v", err, txnState, execOpt, stmts, remainingStmts) } lastResult.Err = aErr e.txnAbortCount.Inc(1) txn.CleanupOnError(err) } if lastResult.Err == nil { log.Fatalf(session.Ctx(), "error (%s) was returned, but it was not set in the last result (%v)", err, lastResult) } } res.ResultList = append(res.ResultList, results...) // Now make sense of the state we got into and update txnState. if (txnState.State == RestartWait || txnState.State == Aborted) && txnState.commitSeen { // A COMMIT got an error (retryable or not). 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(err) 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[:len(stmtsToExec)-len(remainingStmts)] if txnState.State != Open { planMaker.checkTestingVerifyMetadataInitialOrDie(e, stmts) planMaker.checkTestingVerifyMetadataOrDie(e, stmtsExecuted) // Exec the schema changers (if the txn rolled back, the schema changers // will short-circuit because the corresponding descriptor mutation is not // found). planMaker.releaseLeases() txnState.schemaChangers.execSchemaChanges(e, planMaker, res.ResultList) } 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 err != 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(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 }