// updateStmtCounts updates metrics for the number of times the different types of SQL // statements have been received by this node. func (e *Executor) updateStmtCounts(stmt parser.Statement) { e.queryCount.Inc(1) switch stmt.(type) { case *parser.BeginTransaction: e.txnBeginCount.Inc(1) case *parser.Select: e.selectCount.Inc(1) case *parser.Update: e.updateCount.Inc(1) case *parser.Insert: e.insertCount.Inc(1) case *parser.Delete: e.deleteCount.Inc(1) case *parser.CommitTransaction: e.txnCommitCount.Inc(1) case *parser.RollbackTransaction: e.txnRollbackCount.Inc(1) default: if stmt.StatementType() == parser.DDL { e.ddlCount.Inc(1) } else { e.miscCount.Inc(1) } } }
// the current transaction might have been committed/rolled back when this returns. func (e *Executor) execStmt( stmt parser.Statement, planMaker *planner, autoCommit bool, ) (Result, error) { var result Result plan, err := planMaker.makePlan(stmt, nil, autoCommit) if err != nil { return result, err } if err := plan.Start(); err != nil { return result, err } result.PGTag = stmt.StatementTag() result.Type = stmt.StatementType() switch result.Type { case parser.RowsAffected: result.RowsAffected += countRowsAffected(plan) case parser.Rows: result.Columns = plan.Columns() for _, c := range result.Columns { if err := checkResultDatum(c.Typ); err != nil { return result, err } } // valuesAlloc is used to allocate the backing storage for the // ResultRow.Values slices in chunks. var valuesAlloc []parser.Datum const maxChunkSize = 64 // Arbitrary, could use tuning. chunkSize := 4 // Arbitrary as well. for plan.Next() { // The plan.Values DTuple needs to be copied on each iteration. values := plan.Values() n := len(values) if len(valuesAlloc) < n { valuesAlloc = make([]parser.Datum, len(result.Columns)*chunkSize) if chunkSize < maxChunkSize { chunkSize *= 2 } } row := ResultRow{Values: valuesAlloc[:0:n]} valuesAlloc = valuesAlloc[n:] for _, val := range values { if err := checkResultDatum(val); err != nil { return result, err } row.Values = append(row.Values, val) } result.Rows = append(result.Rows, row) } } return result, plan.Err() }
// updateStmtCounts updates metrics for the number of times the different types of SQL // statements have been received by this node. func (e *Executor) updateStmtCounts(stmt parser.Statement) { switch stmt.(type) { case *parser.Select: e.selectCount.Inc(1) case *parser.Update: e.updateCount.Inc(1) case *parser.Insert: e.insertCount.Inc(1) case *parser.Delete: e.deleteCount.Inc(1) default: if stmt.StatementType() == parser.DDL { e.ddlCount.Inc(1) } else { e.miscCount.Inc(1) } } }
// the current transaction might have been committed/rolled back when this returns. func (e *Executor) execStmt( stmt parser.Statement, planMaker *planner, timestamp time.Time, autoCommit bool) (Result, *roachpb.Error) { var result Result plan, pErr := planMaker.makePlan(stmt, autoCommit) if pErr != nil { return result, pErr } result.PGTag = stmt.StatementTag() result.Type = stmt.StatementType() switch result.Type { case parser.RowsAffected: result.RowsAffected += countRowsAffected(plan) case parser.Rows: result.Columns = plan.Columns() for _, c := range result.Columns { if err := checkResultDatum(c.Typ); err != nil { return result, roachpb.NewError(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 result, roachpb.NewError(err) } row.Values = append(row.Values, val) } result.Rows = append(result.Rows, row) } } return result, plan.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 }
// makePlan creates the query plan for a single SQL statement. The returned // plan needs to be iterated over using planNode.Next() and planNode.Values() // in order to retrieve matching rows. If autoCommit is true, the plan is // allowed (but not required) to commit the transaction along with other KV // operations. // // Note: The autoCommit parameter enables operations to enable the 1PC // optimization. This is a bit hackish/preliminary at present. func (p *planner) makePlan(stmt parser.Statement, autoCommit bool) (planNode, *roachpb.Error) { // This will set the system DB trigger for transactions containing // DDL statements that have no effect, such as // `BEGIN; INSERT INTO ...; CREATE TABLE IF NOT EXISTS ...; COMMIT;` // where the table already exists. This will generate some false // refreshes, but that's expected to be quite rare in practice. if stmt.StatementType() == parser.DDL { p.txn.SetSystemConfigTrigger() } switch n := stmt.(type) { case *parser.AlterTable: return p.AlterTable(n) case *parser.BeginTransaction: return p.BeginTransaction(n) case *parser.CommitTransaction: return p.CommitTransaction(n) case *parser.CreateDatabase: return p.CreateDatabase(n) case *parser.CreateIndex: return p.CreateIndex(n) case *parser.CreateTable: return p.CreateTable(n) case *parser.Delete: return p.Delete(n) case *parser.DropDatabase: return p.DropDatabase(n) case *parser.DropIndex: return p.DropIndex(n) case *parser.DropTable: return p.DropTable(n) case *parser.Explain: return p.Explain(n) case *parser.Grant: return p.Grant(n) case *parser.Insert: return p.Insert(n, autoCommit) case *parser.ParenSelect: return p.makePlan(n.Select, autoCommit) case *parser.RenameColumn: return p.RenameColumn(n) case *parser.RenameDatabase: return p.RenameDatabase(n) case *parser.RenameIndex: return p.RenameIndex(n) case *parser.RenameTable: return p.RenameTable(n) case *parser.Revoke: return p.Revoke(n) case *parser.RollbackTransaction: return p.RollbackTransaction(n) case *parser.Select: return p.Select(n) case *parser.Set: return p.Set(n) case *parser.SetTimeZone: return p.SetTimeZone(n) case *parser.SetTransaction: return p.SetTransaction(n) case *parser.Show: return p.Show(n) case *parser.ShowColumns: return p.ShowColumns(n) case *parser.ShowDatabases: return p.ShowDatabases(n) case *parser.ShowGrants: return p.ShowGrants(n) case *parser.ShowIndex: return p.ShowIndex(n) case *parser.ShowTables: return p.ShowTables(n) case *parser.Truncate: return p.Truncate(n) case *parser.Update: return p.Update(n) case parser.Values: return p.Values(n) default: return nil, roachpb.NewErrorf("unknown statement type: %T", stmt) } }
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 }