Skip to content

Commit de08acb

Browse files
committed
sql,*: record UDF/SP stats
Updates UDF and SP execution to support recording statement statistics and insights. Now, each sub statement executed in a UDF and SP will have latencies and execution stats stored in the sql stats subsystem. Additionally, these statements will be appear in the transaction statistics details page in table containing "statement fingerprints for this transaction". Resolves: CRDB-57025 Epic: CRDB-55081 Release note (sql change, ui change): SQL Statements executed in stored procedures and user defined functions will now have sql stats recorded and should appear in the sql activity and insights pages in db console.
1 parent 019bb62 commit de08acb

File tree

12 files changed

+200
-30
lines changed

12 files changed

+200
-30
lines changed

pkg/sql/conn_executor.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,8 +1311,6 @@ func (s *Server) newConnExecutor(
13111311

13121312
ex.applicationName.Store(ex.sessionData().ApplicationName)
13131313
ex.applicationStats = applicationStats
1314-
// We ignore statements and transactions run by the internal executor by
1315-
// passing a nil writer.
13161314
ex.statsCollector = sslocal.NewStatsCollector(
13171315
s.cfg.Settings,
13181316
applicationStats,
@@ -4020,7 +4018,7 @@ func (ex *connExecutor) initPlanner(ctx context.Context, p *planner) {
40204018
p.cancelChecker.Reset(ctx)
40214019

40224020
ex.initEvalCtx(ctx, &p.extendedEvalCtx, p)
4023-
4021+
p.statsCollector = ex.statsCollector
40244022
p.sessionDataMutatorIterator = ex.dataMutatorIterator
40254023
p.noticeSender = nil
40264024
p.preparedStatements = ex.getPrepStmtsAccessor()

pkg/sql/opt/exec/execbuilder/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ go_library(
1717
deps = [
1818
"//pkg/kv/kvserver/concurrency/isolation",
1919
"//pkg/server/telemetry",
20+
"//pkg/sql/appstatspb",
2021
"//pkg/sql/catalog/colinfo",
2122
"//pkg/sql/catalog/descpb",
2223
"//pkg/sql/lexbase",
@@ -45,6 +46,7 @@ go_library(
4546
"//pkg/sql/sem/tree/treewindow",
4647
"//pkg/sql/sem/volatility",
4748
"//pkg/sql/sqlerrors",
49+
"//pkg/sql/sqlstats",
4850
"//pkg/sql/sqltelemetry",
4951
"//pkg/sql/types",
5052
"//pkg/util/buildutil",

pkg/sql/opt/exec/execbuilder/relational.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3731,6 +3731,7 @@ func (b *Builder) buildCall(c *memo.CallExpr) (_ execPlan, outputCols colOrdMap,
37313731
udf.Def.BodyProps,
37323732
udf.Def.BodyStmts,
37333733
udf.Def.BodyTags,
3734+
udf.Def.BodyASTs,
37343735
false, /* allowOuterWithRefs */
37353736
nil, /* wrapRootExpr */
37363737
0, /* resultBufferID */

pkg/sql/opt/exec/execbuilder/scalar.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package execbuilder
88
import (
99
"context"
1010

11+
"github.com/cockroachdb/cockroach/pkg/sql/appstatspb"
1112
"github.com/cockroachdb/cockroach/pkg/sql/opt"
1213
"github.com/cockroachdb/cockroach/pkg/sql/opt/exec"
1314
"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
@@ -24,6 +25,7 @@ import (
2425
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree/treecmp"
2526
"github.com/cockroachdb/cockroach/pkg/sql/sem/volatility"
2627
"github.com/cockroachdb/cockroach/pkg/sql/sqlerrors"
28+
"github.com/cockroachdb/cockroach/pkg/sql/sqlstats"
2729
"github.com/cockroachdb/cockroach/pkg/sql/types"
2830
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
2931
"github.com/cockroachdb/cockroach/pkg/util/log"
@@ -695,6 +697,7 @@ func (b *Builder) buildExistsSubquery(
695697
stmtProps,
696698
nil, /* stmtStr */
697699
make([]string, len(stmts)),
700+
nil, /* stmtASTs */
698701
true, /* allowOuterWithRefs */
699702
wrapRootExpr,
700703
0, /* resultBufferID */
@@ -822,6 +825,7 @@ func (b *Builder) buildSubquery(
822825
stmtProps,
823826
nil, /* stmtStr */
824827
make([]string, len(stmts)),
828+
nil, /* stmtASTs */
825829
true, /* allowOuterWithRefs */
826830
nil, /* wrapRootExpr */
827831
0, /* resultBufferID */
@@ -900,7 +904,7 @@ func (b *Builder) buildSubquery(
900904
if err != nil {
901905
return err
902906
}
903-
err = fn(plan, "" /* stmtForDistSQLDiagram */, true /* isFinalPlan */)
907+
err = fn(plan, nil, "" /* stmtForDistSQLDiagram */, true /* isFinalPlan */)
904908
if err != nil {
905909
return err
906910
}
@@ -1017,8 +1021,9 @@ func (b *Builder) buildUDF(ctx *buildScalarCtx, scalar opt.ScalarExpr) (tree.Typ
10171021
udf.Def.BodyProps,
10181022
udf.Def.BodyStmts,
10191023
udf.Def.BodyTags,
1020-
false, /* allowOuterWithRefs */
1021-
nil, /* wrapRootExpr */
1024+
udf.Def.BodyASTs, /* stmtASTs */
1025+
false, /* allowOuterWithRefs */
1026+
nil, /* wrapRootExpr */
10221027
udf.Def.ResultBufferID,
10231028
)
10241029

@@ -1091,6 +1096,7 @@ func (b *Builder) initRoutineExceptionHandler(
10911096
action.BodyProps,
10921097
action.BodyStmts,
10931098
action.BodyTags,
1099+
nil, /* stmtASTs */
10941100
false, /* allowOuterWithRefs */
10951101
nil, /* wrapRootExpr */
10961102
0, /* resultBufferID */
@@ -1141,6 +1147,7 @@ func (b *Builder) buildRoutinePlanGenerator(
11411147
stmtProps []*physical.Required,
11421148
stmtStr []string,
11431149
stmtTags []string,
1150+
stmtASTs []tree.Statement,
11441151
allowOuterWithRefs bool,
11451152
wrapRootExpr wrapRootExprFn,
11461153
resultBufferID memo.RoutineResultBufferID,
@@ -1207,7 +1214,12 @@ func (b *Builder) buildRoutinePlanGenerator(
12071214
dbName := b.evalCtx.SessionData().Database
12081215
appName := b.evalCtx.SessionData().ApplicationName
12091216

1217+
format := tree.FmtHideConstants | tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&b.evalCtx.Settings.SV))
12101218
for i := range stmts {
1219+
var statsBuilder *sqlstats.StatsBuilderWithLatencyRecorder
1220+
latencyRecorder := sqlstats.NewStatementLatencyRecorder()
1221+
sqlstats.RecordStatementPhase(latencyRecorder, sqlstats.StatementStarted)
1222+
sqlstats.RecordStatementPhase(latencyRecorder, sqlstats.StatementStartParsing)
12111223
stmt := stmts[i]
12121224
props := stmtProps[i]
12131225
var tag string
@@ -1216,6 +1228,22 @@ func (b *Builder) buildRoutinePlanGenerator(
12161228
if i < len(stmtTags) {
12171229
tag = stmtTags[i]
12181230
}
1231+
if i < len(stmtASTs) {
1232+
fingerprint := tree.FormatStatementHideConstants(stmtASTs[i], format)
1233+
fpId := appstatspb.ConstructStatementFingerprintID(fingerprint, b.evalCtx.TxnImplicit, dbName)
1234+
summary := tree.FormatStatementSummary(stmtASTs[i], format)
1235+
stmtType := stmtASTs[i].StatementType()
1236+
builder := sqlstats.NewRecordedStatementStatsBuilder(
1237+
fpId, dbName, fingerprint, summary, stmtType, appName,
1238+
)
1239+
1240+
statsBuilder = &sqlstats.StatsBuilderWithLatencyRecorder{
1241+
StatsBuilder: builder,
1242+
LatencyRecorder: latencyRecorder,
1243+
}
1244+
}
1245+
sqlstats.RecordStatementPhase(latencyRecorder, sqlstats.StatementEndParsing)
1246+
sqlstats.RecordStatementPhase(latencyRecorder, sqlstats.StatementStartPlanning)
12191247
o.Init(ctx, b.evalCtx, b.catalog)
12201248
f := o.Factory()
12211249

@@ -1324,7 +1352,8 @@ func (b *Builder) buildRoutinePlanGenerator(
13241352
stmtForDistSQLDiagram = stmtStr[i]
13251353
}
13261354
incrementRoutineStmtCounter(b.evalCtx.StartedRoutineStatementCounters, dbName, appName, tag)
1327-
err = fn(plan, stmtForDistSQLDiagram, isFinalPlan)
1355+
sqlstats.RecordStatementPhase(latencyRecorder, sqlstats.StatementEndPlanning)
1356+
err = fn(plan, statsBuilder, stmtForDistSQLDiagram, isFinalPlan)
13281357
if err != nil {
13291358
return err
13301359
}

pkg/sql/opt/memo/expr.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@ type UDFDefinition struct {
742742
// at the same position in Body.
743743
BodyProps []*physical.Required
744744

745+
BodyASTs []tree.Statement
746+
745747
// BodyStmts, if set, is the string representation of each statement in
746748
// Body. It is only populated when verbose tracing is enabled.
747749
BodyStmts []string

pkg/sql/opt/optbuilder/plpgsql.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
678678
if b.options.insideDataSource && b.setReturnType.Family() == types.TupleFamily {
679679
retNextScope = b.ob.expandRoutineTupleIntoCols(retNextScope)
680680
}
681-
b.appendBodyStmtFromScope(&retCon, retNextScope, "" /* stmtTag */)
681+
b.appendBodyStmtFromScope(&retCon, retNextScope, nil /* stmt */)
682682
b.appendPlpgSQLStmts(&retCon, stmts[i+1:])
683683
return b.callContinuation(&retCon, s)
684684

@@ -720,7 +720,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
720720
if !b.options.insideDataSource && b.setReturnType.Family() == types.TupleFamily {
721721
retQueryScope = b.ob.combineRoutineColsIntoTuple(retQueryScope)
722722
}
723-
b.appendBodyStmtFromScope(&retCon, retQueryScope, t.SqlStmt.StatementTag())
723+
b.appendBodyStmtFromScope(&retCon, retQueryScope, t.SqlStmt)
724724
b.appendPlpgSQLStmts(&retCon, stmts[i+1:])
725725
return b.callContinuation(&retCon, s)
726726

@@ -949,7 +949,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
949949
// crdb_internal.plpgsql_raise builtin function.
950950
con := b.makeContinuation("_stmt_raise")
951951
con.def.Volatility = volatility.Volatile
952-
b.appendBodyStmtFromScope(&con, b.buildPLpgSQLRaise(con.s, b.getRaiseArgs(con.s, t)), "" /* stmtTag */)
952+
b.appendBodyStmtFromScope(&con, b.buildPLpgSQLRaise(con.s, b.getRaiseArgs(con.s, t)), nil /* stmt */)
953953
b.appendPlpgSQLStmts(&con, stmts[i+1:])
954954
return b.callContinuation(&con, s)
955955

@@ -970,7 +970,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
970970
if len(t.Target) == 0 {
971971
// When there is no INTO target, build the SQL statement into a body
972972
// statement that is only executed for its side effects.
973-
b.appendBodyStmtFromScope(&execCon, stmtScope, t.SqlStmt.StatementTag())
973+
b.appendBodyStmtFromScope(&execCon, stmtScope, t.SqlStmt)
974974
b.appendPlpgSQLStmts(&execCon, stmts[i+1:])
975975
return b.callContinuation(&execCon, s)
976976
}
@@ -1028,7 +1028,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
10281028
intoScope = b.callContinuation(&retCon, intoScope)
10291029

10301030
// Step 3: call the INTO continuation from the parent scope.
1031-
b.appendBodyStmtFromScope(&execCon, intoScope, t.SqlStmt.StatementTag())
1031+
b.appendBodyStmtFromScope(&execCon, intoScope, t.SqlStmt)
10321032
return b.callContinuation(&execCon, s)
10331033

10341034
case *ast.Open:
@@ -1068,7 +1068,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
10681068
// Cursors with mutations are invalid.
10691069
panic(cursorMutationErr)
10701070
}
1071-
b.appendBodyStmtFromScope(&openCon, openScope, query.StatementTag())
1071+
b.appendBodyStmtFromScope(&openCon, openScope, query)
10721072
b.appendPlpgSQLStmts(&openCon, stmts[i+1:])
10731073

10741074
// Build a statement to generate a unique name for the cursor if one
@@ -1078,7 +1078,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
10781078
nameCon := b.makeContinuation("_gen_cursor_name")
10791079
nameCon.def.Volatility = volatility.Volatile
10801080
nameScope := b.buildCursorNameGen(&nameCon, t.CurVar)
1081-
b.appendBodyStmtFromScope(&nameCon, b.callContinuation(&openCon, nameScope), "" /* stmtTag */)
1081+
b.appendBodyStmtFromScope(&nameCon, b.callContinuation(&openCon, nameScope), nil /* stmt */)
10821082
return b.callContinuation(&nameCon, s)
10831083

10841084
case *ast.Close:
@@ -1118,7 +1118,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
11181118
closeScope := closeCon.s.push()
11191119
b.ob.synthesizeColumn(closeScope, closeColName, types.Int, nil /* expr */, closeCall)
11201120
b.ob.constructProjectForScope(closeCon.s, closeScope)
1121-
b.appendBodyStmtFromScope(&closeCon, closeScope, "" /* stmtTag */)
1121+
b.appendBodyStmtFromScope(&closeCon, closeScope, nil /* stmt */)
11221122
b.appendPlpgSQLStmts(&closeCon, stmts[i+1:])
11231123
return b.callContinuation(&closeCon, s)
11241124

@@ -1142,7 +1142,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
11421142
fetchCon.def.Volatility = volatility.Volatile
11431143
fetchScope := b.buildFetch(fetchCon.s, t)
11441144
if t.IsMove {
1145-
b.appendBodyStmtFromScope(&fetchCon, fetchScope, "" /* stmtTag */)
1145+
b.appendBodyStmtFromScope(&fetchCon, fetchScope, nil /* stmt */)
11461146
b.appendPlpgSQLStmts(&fetchCon, stmts[i+1:])
11471147
return b.callContinuation(&fetchCon, s)
11481148
}
@@ -1173,7 +1173,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
11731173
intoScope = b.callContinuation(&retCon, intoScope)
11741174

11751175
// Add the built statement to the FETCH continuation.
1176-
b.appendBodyStmtFromScope(&fetchCon, intoScope, "" /* stmtTag */)
1176+
b.appendBodyStmtFromScope(&fetchCon, intoScope, nil /* stmt */)
11771177
return b.callContinuation(&fetchCon, s)
11781178

11791179
case *ast.Null:
@@ -1275,7 +1275,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
12751275
if len(target) == 0 {
12761276
// When there is no INTO target, build the nested procedure call into a
12771277
// body statement that is only executed for its side effects.
1278-
b.appendBodyStmtFromScope(&callCon, callScope, "" /* stmtTag */)
1278+
b.appendBodyStmtFromScope(&callCon, callScope, nil /* stmt */)
12791279
b.appendPlpgSQLStmts(&callCon, stmts[i+1:])
12801280
return b.callContinuation(&callCon, s)
12811281
}
@@ -1290,7 +1290,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
12901290
intoScope = b.callContinuation(&retCon, intoScope)
12911291

12921292
// Add the built statement to the CALL continuation.
1293-
b.appendBodyStmtFromScope(&callCon, intoScope, "" /* stmtTag */)
1293+
b.appendBodyStmtFromScope(&callCon, intoScope, nil /* stmt */)
12941294
return b.callContinuation(&callCon, s)
12951295

12961296
case *ast.DoBlock:
@@ -1306,7 +1306,7 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope)
13061306
doCon := b.makeContinuation("_stmt_do")
13071307
doCon.def.Volatility = volatility.Volatile
13081308
bodyScope := b.ob.buildPLpgSQLDoBody(t)
1309-
b.appendBodyStmtFromScope(&doCon, bodyScope, "" /* stmtTag */)
1309+
b.appendBodyStmtFromScope(&doCon, bodyScope, nil /* stmt */)
13101310
b.appendPlpgSQLStmts(&doCon, stmts[i+1:])
13111311
return b.callContinuation(&doCon, s)
13121312

@@ -1456,7 +1456,7 @@ func (b *plpgsqlBuilder) handleIntForLoop(
14561456
)
14571457
// Call recursively into the loop body continuation.
14581458
incScope = b.callContinuation(&loopCon, incScope)
1459-
b.appendBodyStmtFromScope(&incrementCon, incScope, "" /* stmtTag */)
1459+
b.appendBodyStmtFromScope(&incrementCon, incScope, nil /* stmt */)
14601460

14611461
// Notably, we call the loop body continuation here, rather than the
14621462
// increment continuation, because the counter should not be incremented
@@ -2020,7 +2020,7 @@ func (b *plpgsqlBuilder) buildEndOfFunctionRaise(con *continuation) {
20202020
pgcode.RoutineExceptionFunctionExecutedNoReturnStatement.String(), /* code */
20212021
)
20222022
con.def.Volatility = volatility.Volatile
2023-
b.appendBodyStmtFromScope(con, b.buildPLpgSQLRaise(con.s, args), "" /* stmtTag */)
2023+
b.appendBodyStmtFromScope(con, b.buildPLpgSQLRaise(con.s, args), nil /* stmt */)
20242024

20252025
// Build a dummy statement that returns NULL. It won't be executed, but
20262026
// ensures that the continuation routine's return type is correct.
@@ -2029,7 +2029,7 @@ func (b *plpgsqlBuilder) buildEndOfFunctionRaise(con *continuation) {
20292029
typedNull := b.ob.factory.ConstructNull(b.returnType)
20302030
b.ob.synthesizeColumn(eofScope, eofColName, b.returnType, nil /* expr */, typedNull)
20312031
b.ob.constructProjectForScope(con.s, eofScope)
2032-
b.appendBodyStmtFromScope(con, eofScope, "" /* stmtTag */)
2032+
b.appendBodyStmtFromScope(con, eofScope, nil /* stmt */)
20332033
}
20342034

20352035
// addOneRowCheck handles INTO STRICT, where a SQL statement is required to
@@ -2280,7 +2280,7 @@ func (b *plpgsqlBuilder) makeContinuationWithTyp(
22802280
// routine definitions, which need to push the continuation before it is
22812281
// finished. The separation also allows for appending multiple body statements.
22822282
func (b *plpgsqlBuilder) appendBodyStmtFromScope(
2283-
con *continuation, bodyScope *scope, stmtTag string,
2283+
con *continuation, bodyScope *scope, stmt tree.Statement,
22842284
) {
22852285
// Set the volatility of the continuation routine to the least restrictive
22862286
// volatility level in the Relational properties of the body statements.
@@ -2290,8 +2290,11 @@ func (b *plpgsqlBuilder) appendBodyStmtFromScope(
22902290
con.def.Volatility = vol
22912291
}
22922292
con.def.Body = append(con.def.Body, bodyExpr)
2293-
con.def.BodyTags = append(con.def.BodyTags, stmtTag)
22942293
con.def.BodyProps = append(con.def.BodyProps, bodyScope.makePhysicalProps())
2294+
if stmt != nil {
2295+
con.def.BodyTags = append(con.def.BodyTags, stmt.StatementTag())
2296+
con.def.BodyASTs = append(con.def.BodyASTs, stmt)
2297+
}
22952298
}
22962299

22972300
// appendPlpgSQLStmts builds the given PLpgSQL statements into a relational
@@ -2301,7 +2304,7 @@ func (b *plpgsqlBuilder) appendPlpgSQLStmts(con *continuation, stmts []ast.State
23012304
// Make sure to push s before constructing the continuation scope to ensure
23022305
// that the parameter columns are not projected.
23032306
continuationScope := b.buildPLpgSQLStatements(stmts, con.s.push())
2304-
b.appendBodyStmtFromScope(con, continuationScope, "" /* stmtTag */)
2307+
b.appendBodyStmtFromScope(con, continuationScope, nil /* stmt */)
23052308
}
23062309

23072310
// callContinuation adds a column that projects the result of calling the

pkg/sql/opt/optbuilder/routine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,15 @@ func (b *Builder) buildRoutine(
420420
var bodyProps []*physical.Required
421421
var bodyStmts []string
422422
var bodyTags []string
423+
var bodyASTs []tree.Statement
423424
switch o.Language {
424425
case tree.RoutineLangSQL:
425426
// Parse the function body.
426427
stmts, err := parser.Parse(o.Body)
428+
bodyASTs = make([]tree.Statement, len(stmts))
429+
for i := range stmts {
430+
bodyASTs[i] = stmts[i].AST
431+
}
427432
if err != nil {
428433
panic(err)
429434
}
@@ -539,6 +544,7 @@ func (b *Builder) buildRoutine(
539544
BodyProps: bodyProps,
540545
BodyStmts: bodyStmts,
541546
BodyTags: bodyTags,
547+
BodyASTs: bodyASTs,
542548
Params: params,
543549
ResultBufferID: resultBufferID,
544550
},

pkg/sql/planner.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ type planner struct {
224224

225225
instrumentation instrumentationHelper
226226

227+
statsCollector *sslocal.StatsCollector
228+
227229
// Contexts for different stages of planning and execution.
228230
semaCtx tree.SemaContext
229231
extendedEvalCtx extendedEvalContext
@@ -495,6 +497,7 @@ func newInternalPlanner(
495497
p.schemaResolver.authAccessor = p
496498
p.evalCatalogBuiltins.Init(execCfg.Codec, p.txn, p.Descriptors())
497499
p.extendedEvalCtx.CatalogBuiltins = &p.evalCatalogBuiltins
500+
p.statsCollector = &sslocal.StatsCollector{}
498501

499502
return p, func() {
500503
// Note that we capture ctx here. This is only valid as long as we create

0 commit comments

Comments
 (0)