// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (s *Server) exec(req driver.Request) (driver.Response, error) { var resp driver.Response // Pick up current session state. planner := planner{db: s.db} if req.Session != nil { // TODO(tschottdorf) will have to validate the Session information (for // instance, whether access to the stored database is permitted). if err := gogoproto.Unmarshal(req.Session, &planner.session); err != nil { return resp, err } } stmts, err := parser.Parse(req.Sql) if err != nil { return resp, err } for _, stmt := range stmts { // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, parameters(req.Params)); err != nil { return resp, err } var plan planNode if plan, err = planner.makePlan(stmt); err != nil { return resp, err } result := driver.Result{ Columns: plan.Columns(), } for plan.Next() { values := plan.Values() row := driver.Result_Row{} row.Values = make([]driver.Datum, 0, len(values)) for _, val := range values { switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)}) case parser.DNull: row.Values = append(row.Values, driver.Datum{}) default: return resp, util.Errorf("unsupported datum: %T", val) } } result.Rows = append(result.Rows, row) } resp.Results = append(resp.Results, result) } // Update session state. resp.Session, err = gogoproto.Marshal(&planner.session) return resp, err }
func (p *planner) query(sql string, args ...interface{}) (planNode, *roachpb.Error) { stmt, err := parser.ParseOneTraditional(sql) if err != nil { return nil, roachpb.NewError(err) } if err := parser.FillArgs(stmt, golangParameters(args)); err != nil { return nil, roachpb.NewError(err) } return p.makePlan(stmt, false) }
// execStmtInOpenTxn executes one statement in the context // of the planner's transaction (which is assumed to exist). // It handles statements that affect the transaction state (BEGIN, COMMIT) // and delegates everything else to `execStmt`. // It binds placeholders. // // The current transaction might be committed/rolled back when this returns. // // Args: // abortedMode: if set, we're in a transaction that has encountered errors, so we // must reject the statement unless it's a COMMIT/ROLLBACK. // implicitTxn: set if the current transaction was implicitly // created by the system (i.e. the client sent the statement outside of // a transaction). // COMMIT/ROLLBACK statements are rejected if set. Also, the transaction // might be auto-committed in this function. // firstInTxn: set for the first statement in a transaction. Used // so that nested BEGIN statements are caught. // stmtTimestamp: Used as the statement_timestamp(). // // Returns: // - a Result // - an error, if any. In case of error, the result returned also reflects this error. func (e *Executor) execStmtInOpenTxn( stmt parser.Statement, planMaker *planner, implicitTxn bool, firstInTxn bool, stmtTimestamp parser.DTimestamp, txnState *txnState) (Result, *roachpb.Error) { if txnState.state() != openTransaction { panic("execStmtInOpenTxn called outside of an open txn") } if planMaker.txn == nil { panic("execStmtInOpenTxn called with the a txn not set on the planner") } planMaker.evalCtx.StmtTimestamp = stmtTimestamp // TODO(cdo): Figure out how to not double count on retries. e.updateStmtCounts(stmt) switch stmt.(type) { case *parser.BeginTransaction: if !firstInTxn { txnState.aborted = true pErr := roachpb.NewError(errTransactionInProgress) return Result{PErr: pErr}, pErr } case *parser.CommitTransaction, *parser.RollbackTransaction, *parser.SetTransaction: if implicitTxn { txnState.aborted = true pErr := roachpb.NewError(errNoTransactionInProgress) return Result{PErr: pErr}, pErr } } // Bind all the placeholder variables in the stmt to actual values. stmt, err := parser.FillArgs(stmt, &planMaker.params) if err != nil { txnState.aborted = true pErr := roachpb.NewError(err) return Result{PErr: pErr}, pErr } result, pErr := e.execStmt(stmt, planMaker, timeutil.Now(), implicitTxn /* autoCommit */) txnDone := planMaker.txn == nil if pErr != nil { result = Result{PErr: pErr} txnState.aborted = true } if txnDone { txnState.aborted = false txnState.txn = nil } return result, pErr }
func (e *Executor) execStmt(stmt parser.Statement, planMaker *planner) (Result, *roachpb.Error) { var result Result switch stmt.(type) { case *parser.BeginTransaction: if planMaker.txn != nil { return result, roachpb.NewError(errTransactionInProgress) } // Start a transaction here and not in planMaker to prevent begin // transaction from being called within an auto-transaction below. planMaker.setTxn(client.NewTxn(e.db), time.Now()) planMaker.txn.SetDebugName("sql", 0) case *parser.CommitTransaction, *parser.RollbackTransaction: if planMaker.txn == nil { return result, roachpb.NewError(errNoTransactionInProgress) } else if planMaker.txn.Proto.Status == roachpb.ABORTED { // Reset to allow starting a new transaction. planMaker.resetTxn() return result, nil } case *parser.SetTransaction: if planMaker.txn == nil { return result, roachpb.NewError(errNoTransactionInProgress) } default: if planMaker.txn != nil && planMaker.txn.Proto.Status == roachpb.ABORTED { return result, roachpb.NewError(&roachpb.SqlTransactionAbortedError{}) } } // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, &planMaker.params); err != nil { return result, roachpb.NewError(err) } // Create a function which both makes and executes the plan, populating // result. // // TODO(pmattis): Should this be a separate function? Perhaps we should move // some of the common code back out into execStmts and have execStmt contain // only the body of this closure. f := func(timestamp time.Time, autoCommit bool) *roachpb.Error { planMaker.evalCtx.StmtTimestamp = parser.DTimestamp{Time: timestamp} plan, pErr := planMaker.makePlan(stmt, autoCommit) if pErr != nil { return pErr } switch result.Type = stmt.StatementType(); result.Type { case parser.RowsAffected: for plan.Next() { result.RowsAffected++ } case parser.Rows: result.Columns = plan.Columns() for _, c := range result.Columns { if err := checkResultDatum(c.Typ); err != nil { return err } } for plan.Next() { // The plan.Values DTuple needs to be copied on each iteration. values := plan.Values() row := ResultRow{Values: make([]parser.Datum, 0, len(values))} for _, val := range values { if err := checkResultDatum(val); err != nil { return err } row.Values = append(row.Values, val) } result.Rows = append(result.Rows, row) } } return plan.PErr() } // If there is a pending transaction. if planMaker.txn != nil { pErr := f(time.Now(), false) return result, pErr } if testingWaitForMetadata { // We might need to verify metadata. Lock the system config so that // no gossip updates sneak in under us. // 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. // This can be addressed when we move to a connection-oriented // protocol and server-side transactions. e.systemConfigCond.L.Lock() defer e.systemConfigCond.L.Unlock() } // No transaction. Run the command as a retryable block in an // auto-transaction. if pErr := e.db.Txn(func(txn *client.Txn) *roachpb.Error { timestamp := time.Now() planMaker.setTxn(txn, timestamp) pErr := f(timestamp, true) planMaker.resetTxn() return pErr }); pErr != nil { return result, pErr } if testingWaitForMetadata { if verify := planMaker.testingVerifyMetadata; verify != nil { // In the case of a multi-statement request, avoid reusing this // callback. planMaker.testingVerifyMetadata = nil for i := 0; ; i++ { if verify(e.systemConfig) != nil { e.systemConfigCond.Wait() } else { if i == 0 { return result, roachpb.NewErrorf("expected %q to require a gossip update, but it did not", stmt) } else if i > 1 { log.Infof("%q unexpectedly required %d gossip updates", stmt, i) } break } } } } return result, nil }
func (s server) execStmt(stmt parser.Statement, params parameters, planMaker *planner) (driver.Result, error) { var result driver.Result if planMaker.txn == nil { if _, ok := stmt.(*parser.BeginTransaction); ok { // Start a transaction here and not in planMaker to prevent begin // transaction from being called within an auto-transaction below. planMaker.txn = client.NewTxn(s.db) planMaker.txn.SetDebugName("sql", 0) } } else if planMaker.txn.Proto.Status == proto.ABORTED { switch stmt.(type) { case *parser.CommitTransaction, *parser.RollbackTransaction: // Reset to allow starting a new transaction. planMaker.txn = nil return result, nil default: return result, errTransactionAborted } } // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, params); err != nil { return result, err } var plan planNode // If there is a pending transaction. if planMaker.txn != nil { // Run in transaction planMaker.txn var err error if plan, err = planMaker.makePlan(stmt); err != nil { return result, err } } else { // No transaction. Run the command as a retryable block in an // auto-transaction. if err := s.db.Txn(func(txn *client.Txn) error { planMaker.txn = txn var err error plan, err = planMaker.makePlan(stmt) planMaker.txn = nil return err }); err != nil { return result, err } } result.Columns = plan.Columns() for plan.Next() { values := plan.Values() row := driver.Result_Row{Values: make([]driver.Datum, 0, len(values))} for _, val := range values { if val == parser.DNull { row.Values = append(row.Values, driver.Datum{}) } else { switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)}) case parser.DDate: s := vt.String() row.Values = append(row.Values, driver.Datum{StringVal: &s}) case parser.DTimestamp: s := vt.String() row.Values = append(row.Values, driver.Datum{StringVal: &s}) case parser.DInterval: s := vt.String() row.Values = append(row.Values, driver.Datum{StringVal: &s}) default: return result, util.Errorf("unsupported datum: %T", val) } } } result.Rows = append(result.Rows, row) } if err := plan.Err(); err != nil { return result, err } return result, nil }
func (e *Executor) execStmt(stmt parser.Statement, params parameters, planMaker *planner) (driver.Response_Result, error) { var result driver.Response_Result switch stmt.(type) { case *parser.BeginTransaction: if planMaker.txn != nil { return result, errTransactionInProgress } // Start a transaction here and not in planMaker to prevent begin // transaction from being called within an auto-transaction below. planMaker.setTxn(client.NewTxn(e.db), time.Now()) planMaker.txn.SetDebugName("sql", 0) case *parser.CommitTransaction, *parser.RollbackTransaction: if planMaker.txn == nil { return result, errNoTransactionInProgress } else if planMaker.txn.Proto.Status == roachpb.ABORTED { // Reset to allow starting a new transaction. planMaker.resetTxn() return result, nil } case *parser.SetTransaction: if planMaker.txn == nil { return result, errNoTransactionInProgress } default: if planMaker.txn != nil && planMaker.txn.Proto.Status == roachpb.ABORTED { return result, errTransactionAborted } } // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, params); err != nil { return result, err } // Create a function which both makes and executes the plan, populating // result. // // TODO(pmattis): Should this be a separate function? Perhaps we should move // some of the common code back out into execStmts and have execStmt contain // only the body of this closure. f := func(timestamp time.Time) error { planMaker.evalCtx.StmtTimestamp = parser.DTimestamp{Time: timestamp} plan, err := planMaker.makePlan(stmt) if err != nil { return err } switch stmt.StatementType() { case parser.DDL: result.Union = &driver.Response_Result_DDL_{DDL: &driver.Response_Result_DDL{}} case parser.RowsAffected: resultRowsAffected := driver.Response_Result_RowsAffected{} result.Union = &resultRowsAffected for plan.Next() { resultRowsAffected.RowsAffected++ } case parser.Rows: resultRows := &driver.Response_Result_Rows{ Columns: plan.Columns(), } result.Union = &driver.Response_Result_Rows_{ Rows: resultRows, } for plan.Next() { values := plan.Values() row := driver.Response_Result_Rows_Row{Values: make([]driver.Datum, 0, len(values))} for _, val := range values { if val == parser.DNull { row.Values = append(row.Values, driver.Datum{}) continue } switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_BoolVal{BoolVal: bool(vt)}, }) case parser.DInt: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_IntVal{IntVal: int64(vt)}, }) case parser.DFloat: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_FloatVal{FloatVal: float64(vt)}, }) case parser.DBytes: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_BytesVal{BytesVal: []byte(vt)}, }) case parser.DString: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_StringVal{StringVal: string(vt)}, }) case parser.DDate: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_DateVal{DateVal: int64(vt)}, }) case parser.DTimestamp: wireTimestamp := driver.Timestamp(vt.Time) row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_TimeVal{ TimeVal: &wireTimestamp, }, }) case parser.DInterval: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_IntervalVal{IntervalVal: vt.Nanoseconds()}, }) default: return fmt.Errorf("unsupported result type: %s", val.Type()) } } resultRows.Rows = append(resultRows.Rows, row) } } return plan.Err() } // If there is a pending transaction. if planMaker.txn != nil { err := f(time.Now()) return result, err } // No transaction. Run the command as a retryable block in an // auto-transaction. err := e.db.Txn(func(txn *client.Txn) error { timestamp := time.Now() planMaker.setTxn(txn, timestamp) err := f(timestamp) planMaker.resetTxn() return err }) return result, err }
func (e Executor) execStmt(stmt parser.Statement, params parameters, planMaker *planner) (driver.Result, error) { var result driver.Result switch stmt.(type) { case *parser.BeginTransaction: if planMaker.txn != nil { return result, errTransactionInProgress } // Start a transaction here and not in planMaker to prevent begin // transaction from being called within an auto-transaction below. planMaker.txn = client.NewTxn(e.db) planMaker.txn.SetDebugName("sql", 0) case *parser.CommitTransaction, *parser.RollbackTransaction: if planMaker.txn != nil { if planMaker.txn.Proto.Status == proto.ABORTED { // Reset to allow starting a new transaction. planMaker.txn = nil return result, nil } } else { return result, errNoTransactionInProgress } case *parser.SetTransaction: if planMaker.txn == nil { return result, errNoTransactionInProgress } default: if planMaker.txn != nil && planMaker.txn.Proto.Status == proto.ABORTED { return result, errTransactionAborted } } // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, params); err != nil { return result, err } // Create a function which both makes and executes the plan, populating // result. // // TODO(pmattis): Should this be a separate function? Perhaps we should move // some of the common code back out into execStmts and have execStmt contain // only the body of this closure. f := func() error { plan, err := planMaker.makePlan(stmt) if err != nil { return err } result.Columns = plan.Columns() for plan.Next() { values := plan.Values() row := driver.Result_Row{Values: make([]driver.Datum, 0, len(values))} for _, val := range values { if val == parser.DNull { row.Values = append(row.Values, driver.Datum{}) } else { switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_BoolVal{BoolVal: bool(vt)}, }) case parser.DInt: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_IntVal{IntVal: int64(vt)}, }) case parser.DFloat: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_FloatVal{FloatVal: float64(vt)}, }) case parser.DBytes: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_BytesVal{BytesVal: []byte(vt)}, }) case parser.DString: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_StringVal{StringVal: string(vt)}, }) case parser.DDate: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_TimeVal{ TimeVal: &driver.Datum_Timestamp{ Sec: vt.Unix(), Nsec: uint32(vt.Nanosecond()), }, }, }) case parser.DTimestamp: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_TimeVal{ TimeVal: &driver.Datum_Timestamp{ Sec: vt.Unix(), Nsec: uint32(vt.Nanosecond()), }, }, }) case parser.DInterval: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_StringVal{StringVal: vt.String()}, }) default: return util.Errorf("unsupported datum: %T", val) } } } result.Rows = append(result.Rows, row) } return plan.Err() } // If there is a pending transaction. if planMaker.txn != nil { err := f() return result, err } // No transaction. Run the command as a retryable block in an // auto-transaction. err := e.db.Txn(func(txn *client.Txn) error { planMaker.txn = txn err := f() planMaker.txn = nil return err }) return result, err }
// execStmtInOpenTxn executes one statement in the context // of the planner's transaction (which is assumed to exist). // It handles statements that affect the transaction state (BEGIN, COMMIT) // and delegates everything else to `execStmt`. // It binds placeholders. // // The current transaction might be committed/rolled back when this returns. // It might also have transitioned to the aborted or RestartWait state. // // Args: // implicitTxn: set if the current transaction was implicitly // created by the system (i.e. the client sent the statement outside of // a transaction). // COMMIT/ROLLBACK statements are rejected if set. Also, the transaction // might be auto-committed in this function. // firstInTxn: set for the first statement in a transaction. Used // so that nested BEGIN statements are caught. // stmtTimestamp: Used as the statement_timestamp(). // // Returns: // - a Result // - an error, if any. In case of error, the result returned also reflects this error. func (e *Executor) execStmtInOpenTxn( stmt parser.Statement, planMaker *planner, implicitTxn bool, firstInTxn bool, stmtTimestamp roachpb.Timestamp, txnState *txnState) (Result, *roachpb.Error) { if txnState.State != Open { panic("execStmtInOpenTxn called outside of an open txn") } if planMaker.txn == nil { panic("execStmtInOpenTxn called with the a txn not set on the planner") } planMaker.evalCtx.SetStmtTimestamp(stmtTimestamp) // TODO(cdo): Figure out how to not double count on retries. e.updateStmtCounts(stmt) switch s := stmt.(type) { case *parser.BeginTransaction: if !firstInTxn { txnState.resetStateAndTxn(Aborted) pErr := roachpb.NewError(errTransactionInProgress) return Result{PErr: pErr}, pErr } case *parser.CommitTransaction: if implicitTxn { return e.noTransactionHelper(txnState) } // CommitTransaction is executed fully here; there's no planNode for it // and the planner is not involved at all. return commitSQLTransaction(txnState, planMaker, commit, e) case *parser.ReleaseSavepoint: if implicitTxn { return e.noTransactionHelper(txnState) } if pErr := parser.ValidateRestartCheckpoint(s.Savepoint); pErr != nil { return Result{PErr: pErr}, pErr } // ReleaseSavepoint is executed fully here; there's no planNode for it // and the planner is not involved at all. return commitSQLTransaction(txnState, planMaker, release, e) case *parser.RollbackTransaction: if implicitTxn { return e.noTransactionHelper(txnState) } // RollbackTransaction is executed fully here; there's no planNode for it // and the planner is not involved at all. // Notice that we don't return any errors on rollback. return rollbackSQLTransaction(txnState, planMaker), nil case *parser.SetTransaction: if implicitTxn { return e.noTransactionHelper(txnState) } case *parser.Savepoint: if implicitTxn { return e.noTransactionHelper(txnState) } if pErr := parser.ValidateRestartCheckpoint(s.Name); pErr != nil { return Result{PErr: pErr}, pErr } // We check if the transaction has "started" already by looking inside the txn proto. // The executor should not be doing that. But it's also what the planner does for // SET TRANSACTION ISOLATION ... It feels ever more wrong here. // TODO(andrei): find a better way to track this running state. // TODO(andrei): the check for retrying is a hack - we erroneously allow // SAVEPOINT to be issued at any time during a retry, not just in the // beginning. We should figure out how to track whether we started using the // transaction during a retry. if txnState.txn.Proto.IsInitialized() && !txnState.retrying { pErr := roachpb.NewError(util.Errorf( "SAVEPOINT %s needs to be the first statement in a transaction", parser.RestartSavepointName)) return Result{PErr: pErr}, pErr } // Note that Savepoint doesn't have a corresponding plan node. // This here is all the execution there is. txnState.retryIntent = true return Result{}, nil case *parser.RollbackToSavepoint: if pErr := parser.ValidateRestartCheckpoint(s.Savepoint); pErr != nil { return Result{PErr: pErr}, pErr } // Can't restart if we didn't get an error first, which would've put the // txn in a different state. txnState.resetStateAndTxn(Aborted) pErr := roachpb.NewError(errNotRetriable) return Result{PErr: pErr}, pErr } // Bind all the placeholder variables in the stmt to actual values. stmt, err := parser.FillArgs(stmt, &planMaker.params) if err != nil { txnState.resetStateAndTxn(Aborted) pErr := roachpb.NewError(err) return Result{PErr: pErr}, pErr } if txnState.tr != nil { txnState.tr.LazyLog(stmt, true /* sensitive */) } result, pErr := e.execStmt(stmt, planMaker, timeutil.Now(), implicitTxn /* autoCommit */) if pErr != nil { if txnState.tr != nil { txnState.tr.LazyPrintf("ERROR: %v", pErr) } txnState.updateStateAndCleanupOnErr(pErr, e) result = Result{PErr: pErr} } else if txnState.tr != nil { tResult := &traceResult{tag: result.PGTag, count: -1} switch result.Type { case parser.RowsAffected: tResult.count = result.RowsAffected case parser.Rows: tResult.count = len(result.Rows) } txnState.tr.LazyLog(tResult, false) } return result, pErr }
func (e *Executor) execStmt(stmt parser.Statement, planMaker *planner) (driver.Response_Result, error) { var result driver.Response_Result switch stmt.(type) { case *parser.BeginTransaction: if planMaker.txn != nil { return result, errTransactionInProgress } // Start a transaction here and not in planMaker to prevent begin // transaction from being called within an auto-transaction below. planMaker.setTxn(client.NewTxn(e.db), time.Now()) planMaker.txn.SetDebugName("sql", 0) case *parser.CommitTransaction, *parser.RollbackTransaction: if planMaker.txn == nil { return result, errNoTransactionInProgress } else if planMaker.txn.Proto.Status == roachpb.ABORTED { // Reset to allow starting a new transaction. planMaker.resetTxn() return result, nil } case *parser.SetTransaction: if planMaker.txn == nil { return result, errNoTransactionInProgress } default: if planMaker.txn != nil && planMaker.txn.Proto.Status == roachpb.ABORTED { return result, errTransactionAborted } } // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, &planMaker.params); err != nil { return result, err } // Create a function which both makes and executes the plan, populating // result. // // TODO(pmattis): Should this be a separate function? Perhaps we should move // some of the common code back out into execStmts and have execStmt contain // only the body of this closure. f := func(timestamp time.Time) error { planMaker.evalCtx.StmtTimestamp = parser.DTimestamp{Time: timestamp} plan, err := planMaker.makePlan(stmt) if err != nil { return err } switch stmt.StatementType() { case parser.DDL: result.Union = &driver.Response_Result_DDL_{DDL: &driver.Response_Result_DDL{}} case parser.RowsAffected: resultRowsAffected := driver.Response_Result_RowsAffected{} result.Union = &resultRowsAffected for plan.Next() { resultRowsAffected.RowsAffected++ } case parser.Rows: resultRows := &driver.Response_Result_Rows{ Columns: plan.Columns(), } result.Union = &driver.Response_Result_Rows_{ Rows: resultRows, } for plan.Next() { values := plan.Values() row := driver.Response_Result_Rows_Row{Values: make([]driver.Datum, 0, len(values))} for _, val := range values { if val == parser.DNull { row.Values = append(row.Values, driver.Datum{}) continue } switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_BoolVal{BoolVal: bool(vt)}, }) case parser.DInt: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_IntVal{IntVal: int64(vt)}, }) case parser.DFloat: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_FloatVal{FloatVal: float64(vt)}, }) case parser.DBytes: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_BytesVal{BytesVal: []byte(vt)}, }) case parser.DString: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_StringVal{StringVal: string(vt)}, }) case parser.DDate: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_DateVal{DateVal: int64(vt)}, }) case parser.DTimestamp: wireTimestamp := driver.Timestamp(vt.Time) row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_TimeVal{ TimeVal: &wireTimestamp, }, }) case parser.DInterval: row.Values = append(row.Values, driver.Datum{ Payload: &driver.Datum_IntervalVal{IntervalVal: vt.Nanoseconds()}, }) default: return fmt.Errorf("unsupported result type: %s", val.Type()) } } resultRows.Rows = append(resultRows.Rows, row) } } return plan.Err() } // If there is a pending transaction. if planMaker.txn != nil { err := f(time.Now()) return result, err } if testingWaitForMetadata { // We might need to verify metadata. Lock the system config so that // no gossip updates sneak in under us. // 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. // This can be addressed when we move to a connection-oriented // protocol and server-side transactions. e.systemConfigCond.L.Lock() defer e.systemConfigCond.L.Unlock() } // No transaction. Run the command as a retryable block in an // auto-transaction. err := e.db.Txn(func(txn *client.Txn) error { timestamp := time.Now() planMaker.setTxn(txn, timestamp) err := f(timestamp) planMaker.resetTxn() return err }) if testingWaitForMetadata && err == nil { if verify := planMaker.testingVerifyMetadata; verify != nil { // In the case of a multi-statement request, avoid reusing this // callback. planMaker.testingVerifyMetadata = nil for i := 0; ; i++ { if verify(e.systemConfig) != nil { e.systemConfigCond.Wait() } else { if i == 0 { err = util.Errorf("expected %q to require a gossip update, but it did not", stmt) } else if i > 1 { log.Infof("%q unexpectedly required %d gossip updates", stmt, i) } break } } } } return result, err }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (s *Server) exec(req driver.Request) (resp driver.Response, err error) { // Pick up current session state. // The request user is validated in ServeHTTP. Even in insecure mode, // it is guaranteed not to be empty. planMaker := planner{user: req.GetUser()} defer func() { // Update session state even if an error occurs. if bytes, err := gogoproto.Marshal(&planMaker.session); err != nil { // Marshaling a `Session` never errors (known from reading the code). panic(err) } else { resp.Session = bytes } }() if req.Session != nil { // TODO(tschottdorf) will have to validate the Session information (for // instance, whether access to the stored database is permitted). if err = gogoproto.Unmarshal(req.Session, &planMaker.session); err != nil { return } } var stmts parser.StatementList if stmts, err = parser.Parse(req.Sql); err != nil { return } for _, stmt := range stmts { // Bind all the placeholder variables in the stmt to actual values. if err = parser.FillArgs(stmt, parameters(req.Params)); err != nil { return } var plan planNode if err = s.db.Txn(func(txn *client.Txn) error { planMaker.txn = txn plan, err = planMaker.makePlan(stmt) planMaker.txn = nil return err }); err != nil { return } result := driver.Result{ Columns: plan.Columns(), } for plan.Next() { values := plan.Values() row := driver.Result_Row{} row.Values = make([]driver.Datum, 0, len(values)) for _, val := range values { if val == parser.DNull { row.Values = append(row.Values, driver.Datum{}) } else { switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)}) default: err = util.Errorf("unsupported datum: %T", val) return } } } result.Rows = append(result.Rows, row) } if err = plan.Err(); err != nil { return } resp.Results = append(resp.Results, result) } return }