// 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) } } }
func (p *planner) prepare(stmt parser.Statement) (planNode, *roachpb.Error) { p.prepareOnly = true switch n := stmt.(type) { case *parser.Delete: return p.Delete(n) case *parser.Insert: return p.Insert(n, false) case *parser.Select: return p.Select(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.Update: return p.Update(n) default: return nil, roachpb.NewUErrorf("prepare statement not supported: %s", stmt.StatementTag()) // TODO(mjibson): add support for parser.Values. // Broken because it conflicts with INSERT's use of VALUES. } }
// 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() }
func fuzzSingle(stmt parser.Statement) (interestingness int) { var lastExpr parser.Expr rcvr := func() { if r := recover(); r != nil { if !expected(fmt.Sprintf("%v", r)) { fmt.Printf("Stmt: %s\n%s", stmt, spew.Sdump(stmt)) if lastExpr != nil { fmt.Printf("Expr: %s", spew.Sdump(lastExpr)) } panic(r) } // Anything that has expected errors in it is fine, but not as // interesting as things that go through. interestingness = 1 } } defer rcvr() data0 := stmt.String() // TODO(tschottdorf): again, this is since we're ignoring stuff in the // grammar instead of erroring out on unsupported language. See: // https://github.com/cockroachdb/cockroach/issues/1949 if strings.Contains(data0, "%!s(<nil>)") { return 0 } stmt1, err := parser.Parse(data0) if err != nil { fmt.Printf("AST: %s", spew.Sdump(stmt)) fmt.Printf("data0: %q\n", data0) panic(err) } interestingness = 2 data1 := stmt1.String() // TODO(tschottdorf): due to the ignoring issue again. // if !reflect.DeepEqual(stmt, stmt1) { if data1 != data0 { fmt.Printf("data0: %q\n", data0) fmt.Printf("AST: %s", spew.Sdump(stmt)) fmt.Printf("data1: %q\n", data1) fmt.Printf("AST: %s", spew.Sdump(stmt1)) panic("not equal") } var v visitorFunc = func(e parser.Expr) parser.Expr { lastExpr = e if _, err := parser.EvalExpr(e); err != nil { panic(err) } return e } parser.WalkStmt(v, stmt) return }
func (c *conn) query(stmt parser.Statement, args []driver.Value) (*rows, error) { // TODO(pmattis): Apply the args to the statement. switch p := stmt.(type) { case *parser.CreateDatabase: return c.CreateDatabase(p, args) case *parser.CreateTable: return c.CreateTable(p, args) case *parser.Delete: return c.Delete(p, args) case *parser.Insert: return c.Insert(p, args) case *parser.Select: return c.Select(p, args) case *parser.ShowColumns: return c.Send(createCall(stmt.String(), args)) case *parser.ShowDatabases: return c.Send(createCall(stmt.String(), args)) case *parser.ShowIndex: return c.Send(createCall(stmt.String(), args)) case *parser.ShowTables: return c.Send(createCall(stmt.String(), args)) case *parser.Update: return c.Update(p, args) case *parser.Use: c.database = p.Name return c.Send(createCall(stmt.String(), args)) case *parser.AlterTable: case *parser.AlterView: case *parser.CreateIndex: case *parser.CreateView: case *parser.DropDatabase: case *parser.DropIndex: case *parser.DropTable: case *parser.DropView: case *parser.RenameTable: case *parser.Set: case *parser.TruncateTable: case *parser.Union: // Various unimplemented statements. default: return nil, fmt.Errorf("unknown statement type: %T", stmt) } return nil, fmt.Errorf("TODO(pmattis): unimplemented: %T %s", stmt, stmt) }
// 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 }