Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: use the new EXPLAIN infrastructure for UI plans #52956

Merged
merged 3 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 21 additions & 30 deletions pkg/sql/app_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ import (
type stmtKey struct {
stmt string
failed bool
distSQLUsed bool
vectorized bool
implicitTxn bool
}

Expand All @@ -56,6 +54,14 @@ type stmtStats struct {
syncutil.Mutex

data roachpb.StatementStatistics

// distSQLUsed records whether the last instance of this statement used
// distribution.
distSQLUsed bool

// vectorized records whether the last instance of this statement used
// vectorization.
vectorized bool
}

// transactionStats holds per-application transaction statistics.
Expand Down Expand Up @@ -105,18 +111,10 @@ var logicalPlanCollectionPeriod = settings.RegisterPublicNonNegativeDurationSett
)

func (s stmtKey) String() string {
return s.flags() + s.stmt
}

func (s stmtKey) flags() string {
var b bytes.Buffer
if s.failed {
b.WriteByte('!')
}
if s.distSQLUsed {
b.WriteByte('+')
return "!" + s.stmt
}
return b.String()
return s.stmt
}

// recordStatement saves per-statement statistics.
Expand Down Expand Up @@ -144,7 +142,7 @@ func (a *appStats) recordStatement(

// Get the statistics object.
s := a.getStatsForStmt(
stmt, distSQLUsed, vectorized, implicitTxn,
stmt, implicitTxn,
err, true, /* createIfNonexistent */
)

Expand Down Expand Up @@ -172,24 +170,19 @@ func (a *appStats) recordStatement(
s.data.OverheadLat.Record(s.data.Count, ovhLat)
s.data.BytesRead.Record(s.data.Count, float64(stats.bytesRead))
s.data.RowsRead.Record(s.data.Count, float64(stats.rowsRead))
s.distSQLUsed = distSQLUsed
s.vectorized = vectorized
s.Unlock()
}

// getStatsForStmt retrieves the per-stmt stat object.
func (a *appStats) getStatsForStmt(
stmt *Statement,
distSQLUsed bool,
vectorized bool,
implicitTxn bool,
err error,
createIfNonexistent bool,
stmt *Statement, implicitTxn bool, err error, createIfNonexistent bool,
) *stmtStats {
// Extend the statement key with various characteristics, so
// that we use separate buckets for the different situations.
key := stmtKey{
failed: err != nil,
distSQLUsed: distSQLUsed,
vectorized: vectorized,
implicitTxn: implicitTxn,
}
if stmt.AnonymizedStr != "" {
Expand Down Expand Up @@ -299,16 +292,14 @@ func (a *appStats) recordTransaction(txnTimeSec float64, ev txnEvent, implicit b
// sample logical plan for its corresponding fingerprint. We use
// `logicalPlanCollectionPeriod` to assess how frequently to sample logical
// plans.
func (a *appStats) shouldSaveLogicalPlanDescription(
stmt *Statement, useDistSQL bool, vectorized bool, implicitTxn bool, err error,
) bool {
func (a *appStats) shouldSaveLogicalPlanDescription(stmt *Statement, implicitTxn bool) bool {
if !sampleLogicalPlans.Get(&a.st.SV) {
return false
}
stats := a.getStatsForStmt(
stmt, useDistSQL, vectorized, implicitTxn,
err, false, /* createIfNonexistent */
)
// We don't know yet if we will hit an error, so we assume we don't. The worst
// that can happen is that for statements that always error out, we will
// always save the tree plan.
stats := a.getStatsForStmt(stmt, implicitTxn, nil /* error */, false /* createIfNonexistent */)
if stats == nil {
// Save logical plan the first time we see new statement fingerprint.
return true
Expand Down Expand Up @@ -506,9 +497,9 @@ func (s *sqlStats) getStmtStats(
if ok {
k := roachpb.StatementStatisticsKey{
Query: maybeScrubbed,
DistSQL: q.distSQLUsed,
DistSQL: stats.distSQLUsed,
Opt: true,
Vec: q.vectorized,
Vec: stats.vectorized,
ImplicitTxn: q.implicitTxn,
Failed: q.failed,
App: maybeHashedAppName,
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/apply_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func (a *applyJoinNode) Next(params runParams) (bool, error) {
// the right side of the join using the optimizer, with all outer columns
// in the right side replaced by the bindings that were defined by the most
// recently read left row.
p, err := a.planRightSideFn(leftRow)
p, err := a.planRightSideFn(newExecFactory(params.p), leftRow)
if err != nil {
return false, err
}
Expand Down
10 changes: 6 additions & 4 deletions pkg/sql/conn_executor_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -909,10 +909,12 @@ func (ex *connExecutor) dispatchToExecutionEngine(
// makeExecPlan creates an execution plan and populates planner.curPlan using
// the cost-based optimizer.
func (ex *connExecutor) makeExecPlan(ctx context.Context, planner *planner) error {
planner.curPlan.init(planner.stmt, ex.appStats)
if planner.collectBundle {
planner.curPlan.savePlanString = true
}
savePlanString := planner.collectBundle
planner.curPlan.init(
planner.stmt,
ex.appStats,
savePlanString,
)

if err := planner.makeOptimizerPlan(ctx); err != nil {
log.VEventf(ctx, 1, "optimizer plan failed: %v", err)
Expand Down
9 changes: 8 additions & 1 deletion pkg/sql/crdb_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,10 +778,17 @@ CREATE TABLE crdb_internal.node_statement_statistics (
if s.data.SensitiveInfo.LastErr != "" {
errString = tree.NewDString(s.data.SensitiveInfo.LastErr)
}
var flags string
if s.distSQLUsed {
flags = "+"
}
if stmtKey.failed {
flags = "!" + flags
}
err := addRow(
tree.NewDInt(tree.DInt(nodeID)),
tree.NewDString(appName),
tree.NewDString(stmtKey.flags()),
tree.NewDString(flags),
tree.NewDString(stmtKey.stmt),
anonymized,
tree.NewDInt(tree.DInt(s.data.Count)),
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/executor_statement_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (ex *connExecutor) recordStatementSummary(
}

ex.statsCollector.recordStatement(
stmt, planner.curPlan.savedPlanForStats,
stmt, planner.curPlan.planForStats,
flags.IsDistributed(), flags.IsSet(planFlagVectorized),
flags.IsSet(planFlagImplicitTxn), automaticRetryCount, rowsAffected, err,
parseLat, planLat, runLat, svcLat, execOverhead, stats,
Expand Down
4 changes: 0 additions & 4 deletions pkg/sql/explain_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ const (
// explainSubqueryFmtFlags is the format for subqueries within `EXPLAIN SQL` statements.
// Since these are individually run, we don't need to scrub any data from subqueries.
explainSubqueryFmtFlags = tree.FmtSimple

// sampledLogicalPlanFmtFlags is the format for sampled logical plans. Because these exposed
// in the Admin UI, sampled plans should be scrubbed of sensitive information.
sampledLogicalPlanFmtFlags = tree.FmtHideConstants
)

// explainPlanNode wraps the logic for EXPLAIN as a planNode.
Expand Down
86 changes: 0 additions & 86 deletions pkg/sql/explain_tree.go

This file was deleted.

8 changes: 4 additions & 4 deletions pkg/sql/explain_tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ func TestPlanToTreeAndPlanToString(t *testing.T) {

p.stmt = &Statement{Statement: stmt}
p.curPlan.savePlanString = true
p.curPlan.savePlanForStats = true
if err := p.makeOptimizerPlan(ctx); err != nil {
t.Fatal(err)
}
p.curPlan.flags.Set(planFlagExecDone)
p.curPlan.close(ctx)
if d.Cmd == "plan-string" {
p.curPlan.flags.Set(planFlagExecDone)
p.curPlan.close(ctx)
return p.curPlan.planString
}
tree := planToTree(ctx, &p.curPlan)
treeYaml, err := yaml.Marshal(tree)
treeYaml, err := yaml.Marshal(p.curPlan.planForStats)
if err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 0 additions & 2 deletions pkg/sql/logictest/testdata/logic_test/statement_statistics
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ SELECT sqrt(_) !
SELECT x FROM (VALUES (_, _, __more1__), (__more1__)) AS t (x) ·
SELECT x FROM test WHERE y = (_ / z) !+
SELECT x FROM test WHERE y IN (_, _, _ + x, _, _) ·
SELECT x FROM test WHERE y IN (_, _, __more3__) ·
SELECT x FROM test WHERE y IN (_, _, __more3__) +
SELECT x FROM test WHERE y NOT IN (_, _, __more3__) ·
SET CLUSTER SETTING "debug.panic_on_failed_assertions" = DEFAULT ·
Expand All @@ -147,7 +146,6 @@ SELECT _ FROM (VALUES (_, _, __more1__), (__more1__)) AS _ (_)
SELECT _ FROM _ WHERE _ = (_ / _)
SELECT _ FROM _ WHERE _ IN (_, _, _ + _, _, _)
SELECT _ FROM _ WHERE _ IN (_, _, __more3__)
SELECT _ FROM _ WHERE _ IN (_, _, __more3__)
SELECT _ FROM _ WHERE _ NOT IN (_, _, __more3__)
SET CLUSTER SETTING "debug.panic_on_failed_assertions" = DEFAULT
SET CLUSTER SETTING "debug.panic_on_failed_assertions" = _
Expand Down
8 changes: 4 additions & 4 deletions pkg/sql/opt/exec/execbuilder/relational.go
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ func (b *Builder) buildApplyJoin(join memo.RelExpr) (execPlan, error) {
//
// Note: we put o outside of the function so we allocate it only once.
var o xform.Optimizer
planRightSideFn := func(leftRow tree.Datums) (exec.Plan, error) {
planRightSideFn := func(ef exec.Factory, leftRow tree.Datums) (exec.Plan, error) {
o.Init(b.evalCtx, b.catalog)
f := o.Factory()

Expand All @@ -746,7 +746,7 @@ func (b *Builder) buildApplyJoin(join memo.RelExpr) (execPlan, error) {
return nil, err
}

eb := New(b.factory, f.Memo(), b.catalog, newRightSide, b.evalCtx, false /* allowAutoCommit */)
eb := New(ef, f.Memo(), b.catalog, newRightSide, b.evalCtx, false /* allowAutoCommit */)
eb.disableTelemetry = true
plan, err := eb.Build()
if err != nil {
Expand Down Expand Up @@ -1795,7 +1795,6 @@ func (b *Builder) buildRecursiveCTE(rec *memo.RecursiveCTEExpr) (execPlan, error
// To implement exec.RecursiveCTEIterationFn, we create a special Builder.

innerBldTemplate := &Builder{
factory: b.factory,
mem: b.mem,
catalog: b.catalog,
evalCtx: b.evalCtx,
Expand All @@ -1806,9 +1805,10 @@ func (b *Builder) buildRecursiveCTE(rec *memo.RecursiveCTEExpr) (execPlan, error
withExprs: b.withExprs[:len(b.withExprs):len(b.withExprs)],
}

fn := func(bufferRef exec.Node) (exec.Plan, error) {
fn := func(ef exec.Factory, bufferRef exec.Node) (exec.Plan, error) {
// Use a separate builder each time.
innerBld := *innerBldTemplate
innerBld.factory = ef
innerBld.addBuiltWithExpr(rec.WithID, initial.outputCols, bufferRef)
plan, err := innerBld.build(rec.Recursive)
if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion pkg/sql/opt/exec/explain/emit.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,15 @@ func (e *emitter) emitSpans(
e.ob.Attr(field, "FULL SCAN")
}
} else {
e.ob.Attr(field, e.spanFormatFn(table, index, scanParams))
if e.ob.flags.HideValues {
n := len(scanParams.InvertedConstraint)
if scanParams.IndexConstraint != nil {
n = scanParams.IndexConstraint.Spans.Count()
}
e.ob.Attrf(field, "%d span%s", n, util.Pluralize(int64(n)))
} else {
e.ob.Attr(field, e.spanFormatFn(table, index, scanParams))
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/sql/opt/exec/explain/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ type Flags struct {
// ShowTypes indicates that the types of columns are shown.
// If ShowTypes is true, then Verbose is also true.
ShowTypes bool
// If HideValues is true, we hide fields that may contain values from the
// query (e.g. spans). Used internally for the plan visible in the UI.
// If HideValues is true, then Verbose must be false.
HideValues bool
}

// MakeFlags crates Flags from ExplainOptions.
Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/opt/exec/explain/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ func (ob *OutputBuilder) Expr(key string, expr tree.TypedExpr, varColumns sqlbas
if ob.flags.ShowTypes {
flags |= tree.FmtShowTypes
}
if ob.flags.HideValues {
flags |= tree.FmtHideConstants
}
f := tree.NewFmtCtx(flags)
f.SetIndexedVarFormat(func(ctx *tree.FmtCtx, idx int) {
// Ensure proper quoting.
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/opt/exec/explain/result_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func getResultColumns(
}
return sqlbase.ShowTraceColumns, nil

case createTableOp, createTableAsOp, createViewOp, controlJobsOp,
case createTableOp, createTableAsOp, createViewOp, controlJobsOp, controlSchedulesOp,
cancelQueriesOp, cancelSessionsOp, errorIfRowsOp, deleteRangeOp:
// These operations produce no columns.
return nil, nil
Expand Down
Loading