Skip to content

Commit

Permalink
sql: add builtin to request statement bundles
Browse files Browse the repository at this point in the history
Makes cockroachdb#79547 less painful.

Previously, there was no way to request statement bundle builtins for
internal statements.
This commit introduces crdb_internal.request_statement_bundle builtin
which allows statement bundle being requested from SQL CLI. The new
builtin takes three parameters:
* statement fingerprint text
* minimum execution latency for the statement
* length of duration the statement bundle request will stay valid for
VIEWACTIVITY or VIEWACTIVITYREDACTED permissions are required to use
this builtin.

Release note (sql change): new crdb_internal.request_statement_bundle builtin
allows statement bundle being requested from SQL CLI. The new builtin takes
three parameters:
* statement fingerprint text
* minimum execution latency for the statement
* length of duration the statement bundle request will stay valid for.
VIEWACTIVITY or VIEWACTIVITYREDACTED permissions are required to use
this builtin.
  • Loading branch information
Azhng committed Apr 11, 2022
1 parent d53a4c0 commit 01b4168
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 41 deletions.
1 change: 1 addition & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3033,6 +3033,7 @@ SELECT * FROM crdb_internal.check_consistency(true, ‘\x02’, ‘\x04’)</p>
</span></td></tr>
<tr><td><a name="crdb_internal.repair_ttl_table_scheduled_job"></a><code>crdb_internal.repair_ttl_table_scheduled_job(oid: oid) &rarr; void</code></td><td><span class="funcdesc"><p>Repairs the scheduled job for a TTL table if it is missing.</p>
</span></td></tr>
<tr><td><a name="crdb_internal.request_statement_bundle"></a><code>crdb_internal.request_statement_bundle(stmtFingerprint: <a href="string.html">string</a>, minExecutionLatency: <a href="interval.html">interval</a>, expiresAfter: <a href="interval.html">interval</a>) &rarr; <a href="bool.html">bool</a></code></td><td></td></tr>
<tr><td><a name="crdb_internal.reset_index_usage_stats"></a><code>crdb_internal.reset_index_usage_stats() &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>This function is used to clear the collected index usage statistics.</p>
</span></td></tr>
<tr><td><a name="crdb_internal.reset_sql_stats"></a><code>crdb_internal.reset_sql_stats() &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>This function is used to clear the collected SQL statistics.</p>
Expand Down
45 changes: 30 additions & 15 deletions pkg/server/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2728,25 +2728,40 @@ func TestCreateStatementDiagnosticsReport(t *testing.T) {
defer leaktest.AfterTest(t)()
defer log.Scope(t).Close(t)

s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
s, conn, _ := serverutils.StartServer(t, base.TestServerArgs{})
defer s.Stopper().Stop(context.Background())

req := &serverpb.CreateStatementDiagnosticsReportRequest{
StatementFingerprint: "INSERT INTO test VALUES (_)",
}
var resp serverpb.CreateStatementDiagnosticsReportResponse
if err := postStatusJSONProto(s, "stmtdiagreports", req, &resp); err != nil {
t.Fatal(err)
}
t.Run("http", func(t *testing.T) {
req := &serverpb.CreateStatementDiagnosticsReportRequest{
StatementFingerprint: "INSERT INTO test VALUES (_)",
}
var resp serverpb.CreateStatementDiagnosticsReportResponse
if err := postStatusJSONProto(s, "stmtdiagreports", req, &resp); err != nil {
t.Fatal(err)
}

var respGet serverpb.StatementDiagnosticsReportsResponse
if err := getStatusJSONProto(s, "stmtdiagreports", &respGet); err != nil {
t.Fatal(err)
}
var respGet serverpb.StatementDiagnosticsReportsResponse
if err := getStatusJSONProto(s, "stmtdiagreports", &respGet); err != nil {
t.Fatal(err)
}

if respGet.Reports[0].StatementFingerprint != req.StatementFingerprint {
t.Fatal("statement diagnostics request was not persisted")
}
if respGet.Reports[0].StatementFingerprint != req.StatementFingerprint {
t.Fatal("statement diagnostics request was not persisted")
}
})

t.Run("builtin", func(t *testing.T) {
sqlConn := sqlutils.MakeSQLRunner(conn)
sqlConn.CheckQueryResults(t,
"SELECT crdb_internal.request_statement_bundle('SELECT _', 0::INTERVAL, 0::INTERVAL)",
[][]string{{"true"}})

sqlConn.CheckQueryResults(t, `
SELECT count(*)
FROM system.statement_diagnostics_requests
WHERE statement_fingerprint = 'SELECT _'
`, [][]string{{"1"}})
})
}

func TestCreateStatementDiagnosticsReportWithViewActivityOptions(t *testing.T) {
Expand Down
29 changes: 15 additions & 14 deletions pkg/sql/conn_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2652,20 +2652,21 @@ func (ex *connExecutor) asOfClauseWithSessionDefault(expr tree.AsOfClause) tree.
func (ex *connExecutor) initEvalCtx(ctx context.Context, evalCtx *extendedEvalContext, p *planner) {
*evalCtx = extendedEvalContext{
EvalContext: tree.EvalContext{
Planner: p,
PrivilegedAccessor: p,
SessionAccessor: p,
JobExecContext: p,
ClientNoticeSender: p,
Sequence: p,
Tenant: p,
Regions: p,
JoinTokenCreator: p,
PreparedStatementState: &ex.extraTxnState.prepStmtsNamespace,
SessionDataStack: ex.sessionDataStack,
ReCache: ex.server.reCache,
SQLStatsController: ex.server.sqlStatsController,
IndexUsageStatsController: ex.server.indexUsageStatsController,
Planner: p,
PrivilegedAccessor: p,
SessionAccessor: p,
JobExecContext: p,
ClientNoticeSender: p,
Sequence: p,
Tenant: p,
Regions: p,
JoinTokenCreator: p,
PreparedStatementState: &ex.extraTxnState.prepStmtsNamespace,
SessionDataStack: ex.sessionDataStack,
ReCache: ex.server.reCache,
SQLStatsController: ex.server.sqlStatsController,
IndexUsageStatsController: ex.server.indexUsageStatsController,
StmtDiagnosticsRequestInserter: ex.server.cfg.StmtDiagnosticsRecorder.InsertRequest,
},
Tracing: &ex.sessionTracing,
MemMetrics: &ex.memMetrics,
Expand Down
25 changes: 13 additions & 12 deletions pkg/sql/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,18 +471,19 @@ func internalExtendedEvalCtx(
}
ret := extendedEvalContext{
EvalContext: tree.EvalContext{
Txn: txn,
SessionDataStack: sds,
TxnReadOnly: false,
TxnImplicit: true,
TxnIsSingleStmt: true,
Context: ctx,
Mon: plannerMon,
TestingKnobs: evalContextTestingKnobs,
StmtTimestamp: stmtTimestamp,
TxnTimestamp: txnTimestamp,
SQLStatsController: sqlStatsController,
IndexUsageStatsController: indexUsageStatsController,
Txn: txn,
SessionDataStack: sds,
TxnReadOnly: false,
TxnImplicit: true,
TxnIsSingleStmt: true,
Context: ctx,
Mon: plannerMon,
TestingKnobs: evalContextTestingKnobs,
StmtTimestamp: stmtTimestamp,
TxnTimestamp: txnTimestamp,
SQLStatsController: sqlStatsController,
IndexUsageStatsController: indexUsageStatsController,
StmtDiagnosticsRequestInserter: execCfg.StmtDiagnosticsRecorder.InsertRequest,
},
Tracing: &SessionTracing{},
Descs: tables,
Expand Down
49 changes: 49 additions & 0 deletions pkg/sql/sem/builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -6876,6 +6876,55 @@ specified store on the node it's run from. One of 'mvccGC', 'merge', 'split',
Volatility: tree.VolatilityVolatile,
},
),

"crdb_internal.request_statement_bundle": makeBuiltin(
tree.FunctionProperties{
Category: categorySystemInfo,
DistsqlBlocklist: true, // applicable only on the gateway
},
tree.Overload{
Types: tree.ArgTypes{
{"stmtFingerprint", types.String},
{"minExecutionLatency", types.Interval},
{"expiresAfter", types.Interval},
},
ReturnType: tree.FixedReturnType(types.Bool),
Fn: func(evalCtx *tree.EvalContext, args tree.Datums) (tree.Datum, error) {
hasViewActivityRedacted, err := evalCtx.SessionAccessor.HasRoleOption(
evalCtx.Ctx(), roleoption.VIEWACTIVITYREDACTED)
if err != nil {
return nil, err
}

hasViewActivity, err := evalCtx.SessionAccessor.HasRoleOption(
evalCtx.Ctx(), roleoption.VIEWACTIVITY)
if err != nil {
return nil, err
}

if !hasViewActivity && !hasViewActivityRedacted {
return nil, errors.New("requesting statement bundle requires either " +
"VIEWACTIVITY or VIEWACTIVITYREDACTED option")
}

stmtFingerprint := string(tree.MustBeDString(args[0]))
minExecutionLatency := time.Duration(tree.MustBeDInterval(args[1]).Nanos())
expiresAfter := time.Duration(tree.MustBeDInterval(args[2]).Nanos())

if err := evalCtx.StmtDiagnosticsRequestInserter(
evalCtx.Ctx(),
stmtFingerprint,
minExecutionLatency,
expiresAfter,
); err != nil {
return nil, err
}

return tree.DBoolTrue, nil
},
Volatility: tree.VolatilityVolatile,
},
),
}

var lengthImpls = func(incBitOverload bool) builtinDefinition {
Expand Down
12 changes: 12 additions & 0 deletions pkg/sql/sem/tree/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -3579,6 +3579,16 @@ type IndexUsageStatsController interface {
ResetIndexUsageStats(ctx context.Context) error
}

// StmtDiagnosticsRequestInsertFunc is an interface embedded in EvalCtx that can
// be used by the builtins to insert statement diagnostics request. This
// interface is introduced to avoid circular dependency.
type StmtDiagnosticsRequestInsertFunc func(
ctx context.Context,
stmtFingerprint string,
minExecutionLatency time.Duration,
expiresAfter time.Duration,
) error

// EvalContext defines the context in which to evaluate an expression, allowing
// the retrieval of state such as the node ID or statement start time.
//
Expand Down Expand Up @@ -3735,6 +3745,8 @@ type EvalContext struct {
// KVStoresIterator is used by various crdb_internal builtins to directly
// access stores on this node.
KVStoresIterator kvserverbase.StoresIterator

StmtDiagnosticsRequestInserter StmtDiagnosticsRequestInsertFunc
}

// MakeTestingEvalContext returns an EvalContext that includes a MemoryMonitor.
Expand Down

0 comments on commit 01b4168

Please sign in to comment.