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() }
// 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 } result.PGTag = stmt.StatementTag() result.Type = stmt.StatementType() switch 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 }