func protoFromResult(r Result) driver.Response_Result { drr := driver.Response_Result{} if r.PErr != nil { drr.Error = proto.String(r.PErr.String()) } switch r.Type { case parser.DDL: drr.Union = &driver.Response_Result_DDL_{ DDL: &driver.Response_Result_DDL{}, } case parser.RowsAffected: drr.Union = &driver.Response_Result_RowsAffected{ RowsAffected: uint32(r.RowsAffected), } case parser.Rows: rows := &driver.Response_Result_Rows{ Columns: make([]*driver.Response_Result_Rows_Column, 0, len(r.Columns)), Rows: make([]driver.Response_Result_Rows_Row, 0, len(r.Rows)), } for _, col := range r.Columns { rows.Columns = append(rows.Columns, protoFromColumn(col)) } for _, row := range r.Rows { rows.Rows = append(rows.Rows, protoFromRow(row)) } drr.Union = &driver.Response_Result_Rows_{ Rows: rows, } } return drr }
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: var resultRows driver.Response_Result_Rows for _, column := range plan.Columns() { datum, err := makeDriverDatum(column.typ) if err != nil { return err } resultRows.Columns = append(resultRows.Columns, &driver.Response_Result_Rows_Column{ Name: column.name, Typ: datum, }) } 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 { datum, err := makeDriverDatum(val) if err != nil { return err } row.Values = append(row.Values, datum) } 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 }
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 }