Пример #1
0
// exec executes the request. Any error encountered is returned; it is
// the caller's responsibility to update the response.
func (s *Server) exec(req driver.Request) (driver.Response, error) {
	var resp driver.Response

	// Pick up current session state.
	planner := planner{db: s.db}
	if req.Session != nil {
		// TODO(tschottdorf) will have to validate the Session information (for
		// instance, whether access to the stored database is permitted).
		if err := gogoproto.Unmarshal(req.Session, &planner.session); err != nil {
			return resp, err
		}
	}
	stmts, err := parser.Parse(req.Sql)
	if err != nil {
		return resp, err
	}
	for _, stmt := range stmts {
		// Bind all the placeholder variables in the stmt to actual values.
		if err := parser.FillArgs(stmt, parameters(req.Params)); err != nil {
			return resp, err
		}
		var plan planNode
		if plan, err = planner.makePlan(stmt); err != nil {
			return resp, err
		}

		result := driver.Result{
			Columns: plan.Columns(),
		}
		for plan.Next() {
			values := plan.Values()
			row := driver.Result_Row{}
			row.Values = make([]driver.Datum, 0, len(values))
			for _, val := range values {
				switch vt := val.(type) {
				case parser.DBool:
					row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)})
				case parser.DInt:
					row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)})
				case parser.DFloat:
					row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)})
				case parser.DString:
					row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)})
				case parser.DNull:
					row.Values = append(row.Values, driver.Datum{})
				default:
					return resp, util.Errorf("unsupported datum: %T", val)
				}
			}
			result.Rows = append(result.Rows, row)
		}
		resp.Results = append(resp.Results, result)
	}

	// Update session state.
	resp.Session, err = gogoproto.Marshal(&planner.session)
	return resp, err
}
Пример #2
0
func (p *planner) query(sql string, args ...interface{}) (planNode, *roachpb.Error) {
	stmt, err := parser.ParseOneTraditional(sql)
	if err != nil {
		return nil, roachpb.NewError(err)
	}
	if err := parser.FillArgs(stmt, golangParameters(args)); err != nil {
		return nil, roachpb.NewError(err)
	}
	return p.makePlan(stmt, false)
}
Пример #3
0
// execStmtInOpenTxn executes one statement in the context
// of the planner's transaction (which is assumed to exist).
// It handles statements that affect the transaction state (BEGIN, COMMIT)
// and delegates everything else to `execStmt`.
// It binds placeholders.
//
// The current transaction might be committed/rolled back when this returns.
//
// Args:
// abortedMode: if set, we're in a transaction that has encountered errors, so we
//  must reject the statement unless it's a COMMIT/ROLLBACK.
// implicitTxn: set if the current transaction was implicitly
//  created by the system (i.e. the client sent the statement outside of
//  a transaction).
//  COMMIT/ROLLBACK statements are rejected if set. Also, the transaction
//  might be auto-committed in this function.
// firstInTxn: set for the first statement in a transaction. Used
//  so that nested BEGIN statements are caught.
// stmtTimestamp: Used as the statement_timestamp().
//
// Returns:
// - a Result
// - an error, if any. In case of error, the result returned also reflects this error.
func (e *Executor) execStmtInOpenTxn(
	stmt parser.Statement, planMaker *planner,
	implicitTxn bool,
	firstInTxn bool,
	stmtTimestamp parser.DTimestamp,
	txnState *txnState) (Result, *roachpb.Error) {
	if txnState.state() != openTransaction {
		panic("execStmtInOpenTxn called outside of an open txn")
	}
	if planMaker.txn == nil {
		panic("execStmtInOpenTxn called with the a txn not set on the planner")
	}

	planMaker.evalCtx.StmtTimestamp = stmtTimestamp

	// TODO(cdo): Figure out how to not double count on retries.
	e.updateStmtCounts(stmt)
	switch stmt.(type) {
	case *parser.BeginTransaction:
		if !firstInTxn {
			txnState.aborted = true
			pErr := roachpb.NewError(errTransactionInProgress)
			return Result{PErr: pErr}, pErr
		}
	case *parser.CommitTransaction, *parser.RollbackTransaction, *parser.SetTransaction:
		if implicitTxn {
			txnState.aborted = true
			pErr := roachpb.NewError(errNoTransactionInProgress)
			return Result{PErr: pErr}, pErr
		}
	}

	// Bind all the placeholder variables in the stmt to actual values.
	stmt, err := parser.FillArgs(stmt, &planMaker.params)
	if err != nil {
		txnState.aborted = true
		pErr := roachpb.NewError(err)
		return Result{PErr: pErr}, pErr
	}

	result, pErr := e.execStmt(stmt, planMaker, timeutil.Now(),
		implicitTxn /* autoCommit */)
	txnDone := planMaker.txn == nil
	if pErr != nil {
		result = Result{PErr: pErr}
		txnState.aborted = true
	}
	if txnDone {
		txnState.aborted = false
		txnState.txn = nil
	}
	return result, pErr
}
Пример #4
0
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
}
Пример #5
0
func (s server) execStmt(stmt parser.Statement, params parameters, planMaker *planner) (driver.Result, error) {
	var result driver.Result
	if planMaker.txn == nil {
		if _, ok := stmt.(*parser.BeginTransaction); ok {
			// Start a transaction here and not in planMaker to prevent begin
			// transaction from being called within an auto-transaction below.
			planMaker.txn = client.NewTxn(s.db)
			planMaker.txn.SetDebugName("sql", 0)
		}
	} else if planMaker.txn.Proto.Status == proto.ABORTED {
		switch stmt.(type) {
		case *parser.CommitTransaction, *parser.RollbackTransaction:
			// Reset to allow starting a new transaction.
			planMaker.txn = nil
			return result, nil
		default:
			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
	}
	var plan planNode
	// If there is a pending transaction.
	if planMaker.txn != nil {
		// Run in transaction planMaker.txn
		var err error
		if plan, err = planMaker.makePlan(stmt); err != nil {
			return result, err
		}
	} else {
		// No transaction. Run the command as a retryable block in an
		// auto-transaction.
		if err := s.db.Txn(func(txn *client.Txn) error {
			planMaker.txn = txn
			var err error
			plan, err = planMaker.makePlan(stmt)
			planMaker.txn = nil
			return err
		}); err != nil {
			return result, err
		}
	}
	result.Columns = plan.Columns()
	for plan.Next() {
		values := plan.Values()
		row := driver.Result_Row{Values: make([]driver.Datum, 0, len(values))}
		for _, val := range values {
			if val == parser.DNull {
				row.Values = append(row.Values, driver.Datum{})
			} else {
				switch vt := val.(type) {
				case parser.DBool:
					row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)})
				case parser.DInt:
					row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)})
				case parser.DFloat:
					row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)})
				case parser.DString:
					row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)})
				case parser.DDate:
					s := vt.String()
					row.Values = append(row.Values, driver.Datum{StringVal: &s})
				case parser.DTimestamp:
					s := vt.String()
					row.Values = append(row.Values, driver.Datum{StringVal: &s})
				case parser.DInterval:
					s := vt.String()
					row.Values = append(row.Values, driver.Datum{StringVal: &s})
				default:
					return result, util.Errorf("unsupported datum: %T", val)
				}
			}
		}
		result.Rows = append(result.Rows, row)
	}
	if err := plan.Err(); err != nil {
		return result, err
	}
	return result, nil
}
Пример #6
0
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
}
Пример #7
0
func (e Executor) execStmt(stmt parser.Statement, params parameters, planMaker *planner) (driver.Result, error) {
	var result driver.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.txn = client.NewTxn(e.db)
		planMaker.txn.SetDebugName("sql", 0)
	case *parser.CommitTransaction, *parser.RollbackTransaction:
		if planMaker.txn != nil {
			if planMaker.txn.Proto.Status == proto.ABORTED {
				// Reset to allow starting a new transaction.
				planMaker.txn = nil
				return result, nil
			}
		} else {
			return result, errNoTransactionInProgress
		}
	case *parser.SetTransaction:
		if planMaker.txn == nil {
			return result, errNoTransactionInProgress
		}
	default:
		if planMaker.txn != nil && planMaker.txn.Proto.Status == proto.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() error {
		plan, err := planMaker.makePlan(stmt)
		if err != nil {
			return err
		}

		result.Columns = plan.Columns()
		for plan.Next() {
			values := plan.Values()
			row := driver.Result_Row{Values: make([]driver.Datum, 0, len(values))}
			for _, val := range values {
				if val == parser.DNull {
					row.Values = append(row.Values, driver.Datum{})
				} else {
					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_TimeVal{
								TimeVal: &driver.Datum_Timestamp{
									Sec:  vt.Unix(),
									Nsec: uint32(vt.Nanosecond()),
								},
							},
						})
					case parser.DTimestamp:
						row.Values = append(row.Values, driver.Datum{
							Payload: &driver.Datum_TimeVal{
								TimeVal: &driver.Datum_Timestamp{
									Sec:  vt.Unix(),
									Nsec: uint32(vt.Nanosecond()),
								},
							},
						})
					case parser.DInterval:
						row.Values = append(row.Values, driver.Datum{
							Payload: &driver.Datum_StringVal{StringVal: vt.String()},
						})
					default:
						return util.Errorf("unsupported datum: %T", val)
					}
				}
			}
			result.Rows = append(result.Rows, row)
		}

		return plan.Err()
	}

	// If there is a pending transaction.
	if planMaker.txn != nil {
		err := f()
		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 {
		planMaker.txn = txn
		err := f()
		planMaker.txn = nil
		return err
	})
	return result, err
}
Пример #8
0
// execStmtInOpenTxn executes one statement in the context
// of the planner's transaction (which is assumed to exist).
// It handles statements that affect the transaction state (BEGIN, COMMIT)
// and delegates everything else to `execStmt`.
// It binds placeholders.
//
// The current transaction might be committed/rolled back when this returns.
// It might also have transitioned to the aborted or RestartWait state.
//
// Args:
// implicitTxn: set if the current transaction was implicitly
//  created by the system (i.e. the client sent the statement outside of
//  a transaction).
//  COMMIT/ROLLBACK statements are rejected if set. Also, the transaction
//  might be auto-committed in this function.
// firstInTxn: set for the first statement in a transaction. Used
//  so that nested BEGIN statements are caught.
// stmtTimestamp: Used as the statement_timestamp().
//
// Returns:
// - a Result
// - an error, if any. In case of error, the result returned also reflects this error.
func (e *Executor) execStmtInOpenTxn(
	stmt parser.Statement, planMaker *planner,
	implicitTxn bool,
	firstInTxn bool,
	stmtTimestamp roachpb.Timestamp,
	txnState *txnState) (Result, *roachpb.Error) {
	if txnState.State != Open {
		panic("execStmtInOpenTxn called outside of an open txn")
	}
	if planMaker.txn == nil {
		panic("execStmtInOpenTxn called with the a txn not set on the planner")
	}

	planMaker.evalCtx.SetStmtTimestamp(stmtTimestamp)

	// TODO(cdo): Figure out how to not double count on retries.
	e.updateStmtCounts(stmt)
	switch s := stmt.(type) {
	case *parser.BeginTransaction:
		if !firstInTxn {
			txnState.resetStateAndTxn(Aborted)
			pErr := roachpb.NewError(errTransactionInProgress)
			return Result{PErr: pErr}, pErr
		}
	case *parser.CommitTransaction:
		if implicitTxn {
			return e.noTransactionHelper(txnState)
		}
		// CommitTransaction is executed fully here; there's no planNode for it
		// and the planner is not involved at all.
		return commitSQLTransaction(txnState, planMaker, commit, e)
	case *parser.ReleaseSavepoint:
		if implicitTxn {
			return e.noTransactionHelper(txnState)
		}
		if pErr := parser.ValidateRestartCheckpoint(s.Savepoint); pErr != nil {
			return Result{PErr: pErr}, pErr
		}
		// ReleaseSavepoint is executed fully here; there's no planNode for it
		// and the planner is not involved at all.
		return commitSQLTransaction(txnState, planMaker, release, e)
	case *parser.RollbackTransaction:
		if implicitTxn {
			return e.noTransactionHelper(txnState)
		}
		// RollbackTransaction is executed fully here; there's no planNode for it
		// and the planner is not involved at all.
		// Notice that we don't return any errors on rollback.
		return rollbackSQLTransaction(txnState, planMaker), nil
	case *parser.SetTransaction:
		if implicitTxn {
			return e.noTransactionHelper(txnState)
		}
	case *parser.Savepoint:
		if implicitTxn {
			return e.noTransactionHelper(txnState)
		}
		if pErr := parser.ValidateRestartCheckpoint(s.Name); pErr != nil {
			return Result{PErr: pErr}, pErr
		}
		// We check if the transaction has "started" already by looking inside the txn proto.
		// The executor should not be doing that. But it's also what the planner does for
		// SET TRANSACTION ISOLATION ... It feels ever more wrong here.
		// TODO(andrei): find a better way to track this running state.
		// TODO(andrei): the check for retrying is a hack - we erroneously allow
		// SAVEPOINT to be issued at any time during a retry, not just in the
		// beginning. We should figure out how to track whether we started using the
		// transaction during a retry.
		if txnState.txn.Proto.IsInitialized() && !txnState.retrying {
			pErr := roachpb.NewError(util.Errorf(
				"SAVEPOINT %s needs to be the first statement in a transaction",
				parser.RestartSavepointName))
			return Result{PErr: pErr}, pErr
		}
		// Note that Savepoint doesn't have a corresponding plan node.
		// This here is all the execution there is.
		txnState.retryIntent = true
		return Result{}, nil
	case *parser.RollbackToSavepoint:
		if pErr := parser.ValidateRestartCheckpoint(s.Savepoint); pErr != nil {
			return Result{PErr: pErr}, pErr
		}
		// Can't restart if we didn't get an error first, which would've put the
		// txn in a different state.
		txnState.resetStateAndTxn(Aborted)
		pErr := roachpb.NewError(errNotRetriable)
		return Result{PErr: pErr}, pErr
	}

	// Bind all the placeholder variables in the stmt to actual values.
	stmt, err := parser.FillArgs(stmt, &planMaker.params)
	if err != nil {
		txnState.resetStateAndTxn(Aborted)
		pErr := roachpb.NewError(err)
		return Result{PErr: pErr}, pErr
	}

	if txnState.tr != nil {
		txnState.tr.LazyLog(stmt, true /* sensitive */)
	}
	result, pErr := e.execStmt(stmt, planMaker, timeutil.Now(),
		implicitTxn /* autoCommit */)
	if pErr != nil {
		if txnState.tr != nil {
			txnState.tr.LazyPrintf("ERROR: %v", pErr)
		}
		txnState.updateStateAndCleanupOnErr(pErr, e)
		result = Result{PErr: pErr}
	} else if txnState.tr != nil {
		tResult := &traceResult{tag: result.PGTag, count: -1}
		switch result.Type {
		case parser.RowsAffected:
			tResult.count = result.RowsAffected
		case parser.Rows:
			tResult.count = len(result.Rows)
		}
		txnState.tr.LazyLog(tResult, false)
	}
	return result, pErr
}
Пример #9
0
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
}
Пример #10
0
// exec executes the request. Any error encountered is returned; it is
// the caller's responsibility to update the response.
func (s *Server) exec(req driver.Request) (resp driver.Response, err error) {

	// Pick up current session state.
	// The request user is validated in ServeHTTP. Even in insecure mode,
	// it is guaranteed not to be empty.
	planMaker := planner{user: req.GetUser()}
	defer func() {
		// Update session state even if an error occurs.
		if bytes, err := gogoproto.Marshal(&planMaker.session); err != nil {
			// Marshaling a `Session` never errors (known from reading the code).
			panic(err)
		} else {
			resp.Session = bytes
		}
	}()

	if req.Session != nil {
		// TODO(tschottdorf) will have to validate the Session information (for
		// instance, whether access to the stored database is permitted).
		if err = gogoproto.Unmarshal(req.Session, &planMaker.session); err != nil {
			return
		}
	}
	var stmts parser.StatementList
	if stmts, err = parser.Parse(req.Sql); err != nil {
		return
	}
	for _, stmt := range stmts {
		// Bind all the placeholder variables in the stmt to actual values.
		if err = parser.FillArgs(stmt, parameters(req.Params)); err != nil {
			return
		}
		var plan planNode
		if err = s.db.Txn(func(txn *client.Txn) error {
			planMaker.txn = txn
			plan, err = planMaker.makePlan(stmt)
			planMaker.txn = nil
			return err
		}); err != nil {
			return
		}

		result := driver.Result{
			Columns: plan.Columns(),
		}
		for plan.Next() {
			values := plan.Values()
			row := driver.Result_Row{}
			row.Values = make([]driver.Datum, 0, len(values))
			for _, val := range values {
				if val == parser.DNull {
					row.Values = append(row.Values, driver.Datum{})
				} else {
					switch vt := val.(type) {
					case parser.DBool:
						row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)})
					case parser.DInt:
						row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)})
					case parser.DFloat:
						row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)})
					case parser.DString:
						row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)})
					default:
						err = util.Errorf("unsupported datum: %T", val)
						return
					}
				}
			}
			result.Rows = append(result.Rows, row)
		}
		if err = plan.Err(); err != nil {
			return
		}

		resp.Results = append(resp.Results, result)
	}

	return
}