From 01b416857fa773dc823da2f3f8ec2a63ec8700b7 Mon Sep 17 00:00:00 2001 From: Azhng Date: Fri, 8 Apr 2022 20:23:35 +0000 Subject: [PATCH] sql: add builtin to request statement bundles Makes #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. --- docs/generated/sql/functions.md | 1 + pkg/server/status_test.go | 45 +++++++++++++++++++---------- pkg/sql/conn_executor.go | 29 ++++++++++--------- pkg/sql/planner.go | 25 ++++++++-------- pkg/sql/sem/builtins/builtins.go | 49 ++++++++++++++++++++++++++++++++ pkg/sql/sem/tree/eval.go | 12 ++++++++ 6 files changed, 120 insertions(+), 41 deletions(-) diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index 2102ae3408dd..87dde7aa4a9d 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -3033,6 +3033,7 @@ SELECT * FROM crdb_internal.check_consistency(true, ‘\x02’, ‘\x04’)

crdb_internal.repair_ttl_table_scheduled_job(oid: oid) → void

Repairs the scheduled job for a TTL table if it is missing.

+crdb_internal.request_statement_bundle(stmtFingerprint: string, minExecutionLatency: interval, expiresAfter: interval) → bool crdb_internal.reset_index_usage_stats() → bool

This function is used to clear the collected index usage statistics.

crdb_internal.reset_sql_stats() → bool

This function is used to clear the collected SQL statistics.

diff --git a/pkg/server/status_test.go b/pkg/server/status_test.go index 8906c6d01b2e..96d560f8ac7c 100644 --- a/pkg/server/status_test.go +++ b/pkg/server/status_test.go @@ -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) { diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index 4402de7fc5be..06ddb2f1ce24 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -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, diff --git a/pkg/sql/planner.go b/pkg/sql/planner.go index bcb9f0fac928..eb549077bb01 100644 --- a/pkg/sql/planner.go +++ b/pkg/sql/planner.go @@ -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, diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index f70d904116d0..2e5d80ca6ed9 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -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 { diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index dca01f93c447..2eaebfc8260b 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -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. // @@ -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.