Esempio n. 1
0
// 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)
		}
	}
}
Esempio n. 2
0
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.
	}
}
Esempio n. 3
0
// 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()
}
Esempio n. 4
0
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
}
Esempio n. 5
0
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)
}
Esempio n. 6
0
// 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)
		}
	}
}
Esempio n. 7
0
// 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()
}
Esempio n. 8
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
}
Esempio n. 9
0
// 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)
	}
}
Esempio n. 10
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
}
Esempio n. 11
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
}