// ExecuteStatements executes the given statement(s) and returns a response. // On error, the returned integer is an HTTP error code. func (e *Executor) ExecuteStatements(user string, session Session, stmts string, params []parser.Datum) (Response, int, error) { planMaker := plannerPool.Get().(*planner) defer plannerPool.Put(planMaker) *planMaker = planner{ user: user, evalCtx: parser.EvalContext{ NodeID: e.nodeID, ReCache: e.reCache, // Copy existing GetLocation closure. See plannerPool.New() for the // initial setting. GetLocation: planMaker.evalCtx.GetLocation, }, leaseMgr: e.leaseMgr, systemConfig: e.getSystemConfig(), session: session, } // Resume a pending transaction if present. if planMaker.session.Txn != nil { txn := client.NewTxn(e.db) txn.Proto = planMaker.session.Txn.Txn txn.UserPriority = planMaker.session.Txn.UserPriority if planMaker.session.MutatesSystemConfig { txn.SetSystemConfigTrigger() } planMaker.setTxn(txn, planMaker.session.Txn.Timestamp.GoTime()) } // Send the Request for SQL execution and set the application-level error // for each result in the reply. planMaker.params = parameters(params) reply := e.execStmts(stmts, planMaker) // Send back the session state even if there were application-level errors. // Add transaction to session state. if planMaker.txn != nil { // 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) planMaker.session.Txn = &Session_Transaction{ Txn: planMaker.txn.Proto, Timestamp: driver.Timestamp(planMaker.evalCtx.TxnTimestamp.Time), UserPriority: planMaker.txn.UserPriority, } planMaker.session.MutatesSystemConfig = planMaker.txn.SystemConfigTrigger() } else { planMaker.session.Txn = nil planMaker.session.MutatesSystemConfig = false } bytes, err := proto.Marshal(&planMaker.session) if err != nil { return Response{}, http.StatusInternalServerError, err } reply.Session = bytes return reply, 0, nil }
// Execute the statement(s) in the given request and return a response. // On error, the returned integer is an HTTP error code. func (e *Executor) Execute(args driver.Request) (driver.Response, int, error) { planMaker := plannerPool.Get().(*planner) defer plannerPool.Put(planMaker) *planMaker = planner{ user: args.GetUser(), evalCtx: parser.EvalContext{ NodeID: e.nodeID, ReCache: e.reCache, // Copy existing GetLocation closure. See plannerPool.New() for the // initial setting. GetLocation: planMaker.evalCtx.GetLocation, }, leaseMgr: e.leaseMgr, systemConfig: e.getSystemConfig(), } // Pick up current session state. if err := proto.Unmarshal(args.Session, &planMaker.session); err != nil { return args.CreateReply(), http.StatusBadRequest, err } // Resume a pending transaction if present. if planMaker.session.Txn != nil { txn := client.NewTxn(e.db) txn.Proto = planMaker.session.Txn.Txn if planMaker.session.MutatesSystemDB { txn.SetSystemDBTrigger() } planMaker.setTxn(txn, planMaker.session.Txn.Timestamp.GoTime()) } // Send the Request for SQL execution and set the application-level error // for each result in the reply. planMaker.params = parameters(args.Params) reply := e.execStmts(args.Sql, planMaker) // Send back the session state even if there were application-level errors. // Add transaction to session state. if planMaker.txn != nil { planMaker.session.Txn = &Session_Transaction{Txn: planMaker.txn.Proto, Timestamp: driver.Timestamp(planMaker.evalCtx.TxnTimestamp.Time)} planMaker.session.MutatesSystemDB = planMaker.txn.SystemDBTrigger() } else { planMaker.session.Txn = nil planMaker.session.MutatesSystemDB = false } bytes, err := proto.Marshal(&planMaker.session) if err != nil { return args.CreateReply(), http.StatusInternalServerError, err } reply.Session = bytes return reply, 0, nil }
func protoFromDatum(datum parser.Datum) driver.Datum { if datum == parser.DNull { return driver.Datum{} } switch vt := datum.(type) { case parser.DBool: return driver.Datum{ Payload: &driver.Datum_BoolVal{BoolVal: bool(vt)}, } case parser.DInt: return driver.Datum{ Payload: &driver.Datum_IntVal{IntVal: int64(vt)}, } case parser.DFloat: return driver.Datum{ Payload: &driver.Datum_FloatVal{FloatVal: float64(vt)}, } case *parser.DDecimal: return driver.Datum{ Payload: &driver.Datum_DecimalVal{DecimalVal: vt.Dec.String()}, } case parser.DBytes: return driver.Datum{ Payload: &driver.Datum_BytesVal{BytesVal: []byte(vt)}, } case parser.DString: return driver.Datum{ Payload: &driver.Datum_StringVal{StringVal: string(vt)}, } case parser.DDate: return driver.Datum{ Payload: &driver.Datum_DateVal{DateVal: int64(vt)}, } case parser.DTimestamp: wireTimestamp := driver.Timestamp(vt.Time) return driver.Datum{ Payload: &driver.Datum_TimeVal{ TimeVal: &wireTimestamp, }, } case parser.DInterval: return driver.Datum{ Payload: &driver.Datum_IntervalVal{IntervalVal: vt.Nanoseconds()}, } default: panic(util.Errorf("unsupported result type: %s", datum.Type())) } }
func makeDriverDatum(datum parser.Datum) (driver.Datum, error) { if datum == parser.DNull { return driver.Datum{}, nil } switch vt := datum.(type) { case parser.DBool: return driver.Datum{ Payload: &driver.Datum_BoolVal{BoolVal: bool(vt)}, }, nil case parser.DInt: return driver.Datum{ Payload: &driver.Datum_IntVal{IntVal: int64(vt)}, }, nil case parser.DFloat: return driver.Datum{ Payload: &driver.Datum_FloatVal{FloatVal: float64(vt)}, }, nil case parser.DBytes: return driver.Datum{ Payload: &driver.Datum_BytesVal{BytesVal: []byte(vt)}, }, nil case parser.DString: return driver.Datum{ Payload: &driver.Datum_StringVal{StringVal: string(vt)}, }, nil case parser.DDate: return driver.Datum{ Payload: &driver.Datum_DateVal{DateVal: int64(vt)}, }, nil case parser.DTimestamp: wireTimestamp := driver.Timestamp(vt.Time) return driver.Datum{ Payload: &driver.Datum_TimeVal{ TimeVal: &wireTimestamp, }, }, nil case parser.DInterval: return driver.Datum{ Payload: &driver.Datum_IntervalVal{IntervalVal: vt.Nanoseconds()}, }, nil default: return driver.Datum{}, fmt.Errorf("unsupported result type: %s", datum.Type()) } }
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, 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 }