diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index d190284d1b60..e298e1d5b2ee 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -4458,7 +4458,7 @@ func (ex *connExecutor) serialize() serverpb.Session { "serialization") continue } - sqlNoConstants := truncateSQL(formatStatementHideConstants(parsed.AST)) + sqlNoConstants := truncateSQL(tree.FormatStatementHideConstants(parsed.AST)) nPlaceholders := 0 if query.placeholders != nil { nPlaceholders = len(query.placeholders.Values) @@ -4483,7 +4483,7 @@ func (ex *connExecutor) serialize() serverpb.Session { ElapsedTime: elapsedTime, Sql: sql, SqlNoConstants: sqlNoConstants, - SqlSummary: formatStatementSummary(parsed.AST), + SqlSummary: tree.FormatStatementSummary(parsed.AST), Placeholders: placeholders, IsDistributed: query.isDistributed, Phase: (serverpb.ActiveQuery_Phase)(query.phase), @@ -4498,7 +4498,7 @@ func (ex *connExecutor) serialize() serverpb.Session { lastActiveQueryNoConstants := "" if ex.mu.LastActiveQuery != nil { lastActiveQuery = truncateSQL(ex.mu.LastActiveQuery.String()) - lastActiveQueryNoConstants = truncateSQL(formatStatementHideConstants(ex.mu.LastActiveQuery)) + lastActiveQueryNoConstants = truncateSQL(tree.FormatStatementHideConstants(ex.mu.LastActiveQuery)) } status := serverpb.Session_IDLE if len(activeQueries) > 0 { diff --git a/pkg/sql/conn_executor_exec.go b/pkg/sql/conn_executor_exec.go index f7a67cd7b0f0..e18c0a4247cd 100644 --- a/pkg/sql/conn_executor_exec.go +++ b/pkg/sql/conn_executor_exec.go @@ -4395,7 +4395,7 @@ func (ex *connExecutor) execWithProfiling( if prepared != nil { stmtNoConstants = prepared.StatementNoConstants } else { - stmtNoConstants = formatStatementHideConstants(ast) + stmtNoConstants = tree.FormatStatementHideConstants(ast) } labels := pprof.Labels( "appname", ex.sessionData().ApplicationName, diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index 581425374fbc..57bdfb49f47d 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -3481,35 +3481,6 @@ func HashForReporting(secret, appName string) string { return hex.EncodeToString(hash.Sum(nil)[:4]) } -// formatStatementHideConstants formats the statement using -// tree.FmtHideConstants. It does *not* anonymize the statement, since -// the result will still contain names and identifiers. -func formatStatementHideConstants(ast tree.Statement, optFlags ...tree.FmtFlags) string { - if ast == nil { - return "" - } - fmtFlags := tree.FmtHideConstants - for _, f := range optFlags { - fmtFlags |= f - } - return tree.AsStringWithFlags(ast, fmtFlags) -} - -// formatStatementSummary formats the statement using tree.FmtSummary -// and tree.FmtHideConstants. This returns a summarized version of the -// query. It does *not* anonymize the statement, since the result will -// still contain names and identifiers. -func formatStatementSummary(ast tree.Statement, optFlags ...tree.FmtFlags) string { - if ast == nil { - return "" - } - fmtFlags := tree.FmtSummary | tree.FmtHideConstants - for _, f := range optFlags { - fmtFlags |= f - } - return tree.AsStringWithFlags(ast, fmtFlags) -} - // DescsTxn is a convenient method for running a transaction on descriptors // when you have an ExecutorConfig. // diff --git a/pkg/sql/executor_statement_metrics_test.go b/pkg/sql/executor_statement_metrics_test.go index ed577ef7f99e..62600aa9712e 100644 --- a/pkg/sql/executor_statement_metrics_test.go +++ b/pkg/sql/executor_statement_metrics_test.go @@ -138,7 +138,7 @@ func makeTestQuery(i int) (*Statement, error) { } return &Statement{ - StmtNoConstants: formatStatementHideConstants(stmt.AST, tree.FmtCollapseLists|tree.FmtConstantsAsUnderscores), + StmtNoConstants: tree.FormatStatementHideConstants(stmt.AST, tree.FmtCollapseLists|tree.FmtConstantsAsUnderscores), Statement: stmt, }, nil } diff --git a/pkg/sql/opt/exec/execbuilder/statement.go b/pkg/sql/opt/exec/execbuilder/statement.go index ae3f83768aa6..9837caed04be 100644 --- a/pkg/sql/opt/exec/execbuilder/statement.go +++ b/pkg/sql/opt/exec/execbuilder/statement.go @@ -168,6 +168,15 @@ func (b *Builder) buildExplain( return b.buildExplainOpt(explainExpr) } + if explainExpr.Options.Mode == tree.ExplainFingerprint { + var ep execPlan + ep.root, err = b.factory.ConstructExplainOpt(explainExpr.Fingerprint, exec.ExplainEnvData{}) + if err != nil { + return execPlan{}, colOrdMap{}, err + } + return ep, b.outputColsFromList(explainExpr.ColList), nil + } + var ep execPlan ep.root, err = b.factory.ConstructExplain( &explainExpr.Options, diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain b/pkg/sql/opt/exec/execbuilder/testdata/explain index 0bea47759527..0748d62e0351 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain @@ -2347,7 +2347,7 @@ quality of service: regular query T EXPLAIN (OPT, MEMO) SELECT * FROM tc JOIN t ON k=a ---- -memo (optimized, ~19KB, required=[presentation: info:14] [distribution: test]) +memo (optimized, ~20KB, required=[presentation: info:14] [distribution: test]) ├── G1: (explain G2 [presentation: a:1,b:2,k:8,v:9] [distribution: test]) │ └── [presentation: info:14] [distribution: test] │ ├── best: (explain G2="[presentation: a:1,b:2,k:8,v:9] [distribution: test]" [presentation: a:1,b:2,k:8,v:9] [distribution: test]) @@ -2428,7 +2428,7 @@ TABLE t ├── crdb_internal_origin_timestamp decimal [hidden] [system] └── PRIMARY INDEX t_pkey └── k int not null -memo (optimized, ~19KB, required=[presentation: info:14] [distribution: test]) +memo (optimized, ~20KB, required=[presentation: info:14] [distribution: test]) ├── G1: (explain G2 [presentation: a:1,b:2,k:8,v:9] [distribution: test]) │ └── [presentation: info:14] [distribution: test] │ ├── best: (explain G2="[presentation: a:1,b:2,k:8,v:9] [distribution: test]" [presentation: a:1,b:2,k:8,v:9] [distribution: test]) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain_fingerprint b/pkg/sql/opt/exec/execbuilder/testdata/explain_fingerprint new file mode 100644 index 000000000000..74b5ff45116a --- /dev/null +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain_fingerprint @@ -0,0 +1,249 @@ +# LogicTest: local + +statement ok +CREATE TABLE t (a INT PRIMARY KEY, b STRING, c FLOAT) + +statement ok +CREATE TABLE u (x INT, y STRING, INDEX idx_y (y)) + +# Basic EXPLAIN (FINGERPRINT) test +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t +---- +SELECT * FROM t + +# Test with constants - they should be replaced with placeholders +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a = 123 +---- +SELECT * FROM t WHERE a = _ + +# Test with string constants +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE b = 'hello' +---- +SELECT * FROM t WHERE b = _ + +# Test with multiple constants +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a = 1 AND b = 'test' AND c = 3.14 +---- +SELECT * FROM t WHERE ((a = _) AND (b = _)) AND (c = _) + +# Test with JOIN +query T +EXPLAIN (FINGERPRINT) SELECT t.a, u.y FROM t JOIN u ON t.a = u.x +---- +SELECT t.a, u.y FROM t JOIN u ON t.a = u.x + +# Test with GROUP BY and aggregation +query T +EXPLAIN (FINGERPRINT) SELECT b, count(*) FROM t GROUP BY b +---- +SELECT b, count(*) FROM t GROUP BY b + +# Test with ORDER BY +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t ORDER BY a DESC +---- +SELECT * FROM t ORDER BY a DESC + +# Test with LIMIT +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t LIMIT 10 +---- +SELECT * FROM t LIMIT _ + +# Test with subquery +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a IN (SELECT x FROM u WHERE y = 'test') +---- +SELECT * FROM t WHERE a IN (SELECT x FROM u WHERE y = _) + +# Test with INSERT +query T +EXPLAIN (FINGERPRINT) INSERT INTO t VALUES (1, 'foo', 2.5) +---- +INSERT INTO t VALUES (_, __more__) + +# Test with UPDATE +query T +EXPLAIN (FINGERPRINT) UPDATE t SET b = 'updated' WHERE a = 100 +---- +UPDATE t SET b = _ WHERE a = _ + +# Test with DELETE +query T +EXPLAIN (FINGERPRINT) DELETE FROM t WHERE a > 50 +---- +DELETE FROM t WHERE a > _ + +# Test with index hint +query T +EXPLAIN (FINGERPRINT) SELECT * FROM u@idx_y WHERE y = 'search' +---- +SELECT * FROM u@idx_y WHERE y = _ + +# Test with UNION +query T +EXPLAIN (FINGERPRINT) SELECT a FROM t UNION SELECT x FROM u +---- +SELECT a FROM t UNION SELECT x FROM u + +# Test with CTE +query T +EXPLAIN (FINGERPRINT) WITH cte AS (SELECT * FROM t WHERE a < 10) SELECT * FROM cte +---- +WITH cte AS (SELECT * FROM t WHERE a < _) SELECT * FROM cte + +# Test prepared statement functionality +statement ok +PREPARE stmt1 AS SELECT * FROM t WHERE a = $1 + +query T +EXPLAIN (FINGERPRINT) EXECUTE stmt1 +---- +EXECUTE stmt1 + +statement ok +PREPARE stmt2 AS INSERT INTO t VALUES ($1, $2, $3) + +query T +EXPLAIN (FINGERPRINT) EXECUTE stmt2 +---- +EXECUTE stmt2 + +statement ok +PREPARE stmt3 AS SELECT t.a, u.y FROM t JOIN u ON t.a = u.x WHERE t.b = $1 + +query T +EXPLAIN (FINGERPRINT) EXECUTE stmt3 +---- +EXECUTE stmt3 + +# Test prepare of EXPLAIN (FINGERPRINT) +statement ok +PREPARE stmt4 AS EXPLAIN (FINGERPRINT) SELECT 1 + +query T +EXECUTE stmt4 +---- +SELECT _ + +# Test that invalid combinations are rejected +statement error EXPLAIN \(FINGERPRINT\) cannot be used with VERBOSE +EXPLAIN (FINGERPRINT, VERBOSE) SELECT * FROM t + +statement error EXPLAIN \(FINGERPRINT\) cannot be used with TYPES +EXPLAIN (FINGERPRINT, TYPES) SELECT * FROM t + +statement error pq: at or near "EOF": syntax error: the JSON flag can only be used with DISTSQL +EXPLAIN (FINGERPRINT, JSON) SELECT * FROM t + +# Test that regular EXPLAIN EXECUTE is still rejected while FINGERPRINT works +statement error EXPLAIN EXECUTE is not supported; use EXPLAIN ANALYZE +EXPLAIN EXECUTE stmt1 + +# But EXPLAIN (FINGERPRINT) EXECUTE should work (already tested above) + +# Test with more complex queries +query T +EXPLAIN (FINGERPRINT) +SELECT t.a, t.b, count(u.x) as cnt +FROM t +LEFT JOIN u ON t.a = u.x +WHERE t.c > 1.0 AND (t.b LIKE 'prefix%' OR t.b IS NULL) +GROUP BY t.a, t.b +HAVING count(u.x) > 5 +ORDER BY cnt DESC, t.a ASC +LIMIT 20 OFFSET 10 +---- +SELECT t.a, t.b, count(u.x) AS cnt FROM t LEFT JOIN u ON t.a = u.x WHERE (t.c > _) AND ((t.b LIKE _) OR (t.b IS NULL)) GROUP BY t.a, t.b HAVING count(u.x) > _ ORDER BY cnt DESC, t.a ASC LIMIT _ OFFSET _ + +# Test with window functions +query T +EXPLAIN (FINGERPRINT) +SELECT a, b, row_number() OVER (PARTITION BY b ORDER BY a) as rn +FROM t +---- +SELECT a, b, row_number() OVER (PARTITION BY b ORDER BY a) AS rn FROM t + +query T +EXPLAIN (FINGERPRINT) +SELECT + department_id, + employee_id, + salary, + avg(salary) OVER ( + PARTITION BY department_id + ORDER BY employee_id + ROWS BETWEEN 2 PRECEDING AND CURRENT ROW + ) AS moving_avg_salary, + round(avg(salary) OVER ( + PARTITION BY department_id + ), 2) AS dept_avg_salary, + 1.05 * salary AS adjusted_salary, + salary + 1000 AS bonus_salary +FROM employees +---- +SELECT department_id, employee_id, salary, avg(salary) OVER (PARTITION BY department_id ORDER BY employee_id ROWS BETWEEN _ PRECEDING AND CURRENT ROW) AS moving_avg_salary, round(avg(salary) OVER (PARTITION BY department_id), _) AS dept_avg_salary, _ * salary AS adjusted_salary, salary + _ AS bonus_salary FROM employees + + +# Test with CASE expression +query T +EXPLAIN (FINGERPRINT) +SELECT a, + CASE + WHEN a > 100 THEN 'high' + WHEN a > 50 THEN 'medium' + ELSE 'low' + END as category +FROM t +---- +SELECT a, CASE WHEN a > _ THEN _ WHEN a > _ THEN _ ELSE _ END AS category FROM t + +# Test that different constant values produce the same fingerprint +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a = 999 +---- +SELECT * FROM t WHERE a = _ + +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a = 111 +---- +SELECT * FROM t WHERE a = _ + +# Test with cluster setting sql.stats.statement_fingerprint.format_mask set to 0 +statement ok +SET CLUSTER SETTING sql.stats.statement_fingerprint.format_mask = 0 + +# With format_mask=0, long lists should not be collapsed to __more__ +query T +EXPLAIN (FINGERPRINT) INSERT INTO t VALUES (1, 'a', 1.1), (2, 'b', 2.2), (3, 'c', 3.3), (4, 'd', 4.4), (5, 'e', 5.5) +---- +INSERT INTO t VALUES (_, '_', __more1_10__), (__more1_10__) + +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +---- +SELECT * FROM t WHERE a IN (_, _, __more1_10__) + +query T +EXPLAIN (FINGERPRINT) UPDATE t SET b = 'updated' WHERE a IN (10, 20, 30, 40, 50, 60, 70, 80, 90, 100) +---- +UPDATE t SET b = '_' WHERE a IN (_, _, __more1_10__) + +# Reset cluster setting to default +statement ok +RESET CLUSTER SETTING sql.stats.statement_fingerprint.format_mask + +# Verify default behavior is restored (long lists should be collapsed) +query T +EXPLAIN (FINGERPRINT) INSERT INTO t VALUES (1, 'a', 1.1), (2, 'b', 2.2), (3, 'c', 3.3), (4, 'd', 4.4), (5, 'e', 5.5) +---- +INSERT INTO t VALUES (_, __more__), (__more__) + +query T +EXPLAIN (FINGERPRINT) SELECT * FROM t WHERE a IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +---- +SELECT * FROM t WHERE a IN (_, __more__) diff --git a/pkg/sql/opt/exec/execbuilder/tests/local/generated_test.go b/pkg/sql/opt/exec/execbuilder/tests/local/generated_test.go index f6adc124d6e9..12f7f8b6cfcc 100644 --- a/pkg/sql/opt/exec/execbuilder/tests/local/generated_test.go +++ b/pkg/sql/opt/exec/execbuilder/tests/local/generated_test.go @@ -218,6 +218,13 @@ func TestExecBuild_explain_env( runExecBuildLogicTest(t, "explain_env") } +func TestExecBuild_explain_fingerprint( + t *testing.T, +) { + defer leaktest.AfterTest(t)() + runExecBuildLogicTest(t, "explain_fingerprint") +} + func TestExecBuild_explain_gist( t *testing.T, ) { diff --git a/pkg/sql/opt/ops/statement.opt b/pkg/sql/opt/ops/statement.opt index fa3d53dd1e3f..5c3edeed74d7 100644 --- a/pkg/sql/opt/ops/statement.opt +++ b/pkg/sql/opt/ops/statement.opt @@ -129,6 +129,9 @@ define ExplainPrivate { # StmtType stores the type of the statement return we are explaining. StmtType StatementReturnType + + # Fingerprint stores the statement fingerprint for EXPLAIN (FINGERPRINT). + Fingerprint string } # ShowTraceForSession returns the current session traces. diff --git a/pkg/sql/opt/optbuilder/explain.go b/pkg/sql/opt/optbuilder/explain.go index 84d27ffe0c1f..1e81a1982e16 100644 --- a/pkg/sql/opt/optbuilder/explain.go +++ b/pkg/sql/opt/optbuilder/explain.go @@ -18,12 +18,25 @@ import ( func (b *Builder) buildExplain(explain *tree.Explain, inScope *scope) (outScope *scope) { if _, ok := explain.Statement.(*tree.Execute); ok { - panic(pgerror.New( - pgcode.FeatureNotSupported, "EXPLAIN EXECUTE is not supported; use EXPLAIN ANALYZE", - )) + // EXPLAIN (FINGERPRINT) EXECUTE is supported, but other modes are not. + if explain.Mode != tree.ExplainFingerprint { + panic(pgerror.New( + pgcode.FeatureNotSupported, "EXPLAIN EXECUTE is not supported; use EXPLAIN ANALYZE", + )) + } } - stmtScope := b.buildStmtAtRoot(explain.Statement, nil /* desiredTypes */) + var stmtScope *scope + if explain.Mode == tree.ExplainFingerprint { + // We don't actually need to build the statement for EXPLAIN (FINGERPRINT), + // so don't. This allows someone to run EXPLAIN (FINGERPRINT) for statements + // they don't have permission to execute, for example. Instead, we create a + // dummy empty VALUES clause as input. + emptyValues := &tree.LiteralValuesClause{Rows: tree.RawRows{}} + stmtScope = b.buildLiteralValuesClause(emptyValues, nil /* desiredTypes */, inScope) + } else { + stmtScope = b.buildStmtAtRoot(explain.Statement, nil /* desiredTypes */) + } outScope = inScope.push() @@ -56,6 +69,9 @@ func (b *Builder) buildExplain(explain *tree.Explain, inScope *scope) (outScope case tree.ExplainGist: telemetry.Inc(sqltelemetry.ExplainGist) + case tree.ExplainFingerprint: + telemetry.Inc(sqltelemetry.ExplainFingerprint) + default: panic(errors.Errorf("EXPLAIN mode %s not supported", explain.Mode)) } @@ -68,6 +84,10 @@ func (b *Builder) buildExplain(explain *tree.Explain, inScope *scope) (outScope Props: stmtScope.makePhysicalProps(), StmtType: explain.Statement.StatementReturnType(), } + if explain.Mode == tree.ExplainFingerprint { + stmtFingerprintFmtMask := tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&b.evalCtx.Settings.SV)) + private.Fingerprint = tree.FormatStatementHideConstants(explain.Statement, stmtFingerprintFmtMask) + } outScope.expr = b.factory.ConstructExplain(input, &private) return outScope } diff --git a/pkg/sql/sem/tree/explain.go b/pkg/sql/sem/tree/explain.go index d4dcaaa1ee92..11655aface72 100644 --- a/pkg/sql/sem/tree/explain.go +++ b/pkg/sql/sem/tree/explain.go @@ -70,17 +70,21 @@ const ( // ExplainGist generates a plan "gist". ExplainGist + // ExplainFingerprint returns the statement fingerprint. + ExplainFingerprint + numExplainModes = iota ) var explainModeStrings = [...]string{ - ExplainPlan: "PLAN", - ExplainDistSQL: "DISTSQL", - ExplainOpt: "OPT", - ExplainVec: "VEC", - ExplainDebug: "DEBUG", - ExplainDDL: "DDL", - ExplainGist: "GIST", + ExplainPlan: "PLAN", + ExplainDistSQL: "DISTSQL", + ExplainOpt: "OPT", + ExplainVec: "VEC", + ExplainDebug: "DEBUG", + ExplainDDL: "DDL", + ExplainGist: "GIST", + ExplainFingerprint: "FINGERPRINT", } var explainModeStringMap = func() map[string]ExplainMode { @@ -304,6 +308,15 @@ func MakeExplain(options []string, stmt Statement) (Statement, error) { }, nil } + if opts.Mode == ExplainFingerprint { + // FINGERPRINT mode doesn't support any flags + for f := ExplainFlag(1); f <= numExplainFlags; f++ { + if opts.Flags[f] { + return nil, pgerror.Newf(pgcode.Syntax, "EXPLAIN (FINGERPRINT) cannot be used with %s", f) + } + } + } + if opts.Mode == ExplainDebug { return nil, pgerror.Newf(pgcode.Syntax, "DEBUG flag can only be used with EXPLAIN ANALYZE") } diff --git a/pkg/sql/sem/tree/format.go b/pkg/sql/sem/tree/format.go index 21963be48d67..39631bd5a685 100644 --- a/pkg/sql/sem/tree/format.go +++ b/pkg/sql/sem/tree/format.go @@ -874,6 +874,35 @@ func SerializeForDisplay(n NodeFormatter) string { return AsStringWithFlags(n, FmtParsable) } +// FormatStatementHideConstants formats the statement using FmtHideConstants. It +// does *not* anonymize the statement, since the result will still contain names +// and identifiers. +func FormatStatementHideConstants(ast Statement, optFlags ...FmtFlags) string { + if ast == nil { + return "" + } + fmtFlags := FmtHideConstants + for _, f := range optFlags { + fmtFlags |= f + } + return AsStringWithFlags(ast, fmtFlags) +} + +// FormatStatementSummary formats the statement using FmtSummary and +// FmtHideConstants. This returns a summarized version of the query. It does +// *not* anonymize the statement, since the result will still contain names and +// identifiers. +func FormatStatementSummary(ast Statement, optFlags ...FmtFlags) string { + if ast == nil { + return "" + } + fmtFlags := FmtSummary | FmtHideConstants + for _, f := range optFlags { + fmtFlags |= f + } + return AsStringWithFlags(ast, fmtFlags) +} + var fmtCtxPool = sync.Pool{ New: func() interface{} { return &FmtCtx{} diff --git a/pkg/sql/sqltelemetry/planning.go b/pkg/sql/sqltelemetry/planning.go index 8bb7ffdac882..b2d48a98e4b6 100644 --- a/pkg/sql/sqltelemetry/planning.go +++ b/pkg/sql/sqltelemetry/planning.go @@ -122,6 +122,10 @@ var ExplainOptVerboseUseCounter = telemetry.GetCounterOnce("sql.plan.explain-opt // EXPLAIN (GIST) is run. var ExplainGist = telemetry.GetCounterOnce("sql.plan.explain-gist") +// ExplainFingerprint is to be incremented whenever +// EXPLAIN (FINGERPRINT) is run. +var ExplainFingerprint = telemetry.GetCounterOnce("sql.plan.explain-fingerprint") + // CreateStatisticsUseCounter is to be incremented whenever a non-automatic // run of CREATE STATISTICS occurs. var CreateStatisticsUseCounter = telemetry.GetCounterOnce("sql.plan.stats.created") diff --git a/pkg/sql/statement.go b/pkg/sql/statement.go index b958655e0137..2d5705b9be8f 100644 --- a/pkg/sql/statement.go +++ b/pkg/sql/statement.go @@ -54,8 +54,8 @@ func makeStatement( return Statement{ Statement: parserStmt, - StmtNoConstants: formatStatementHideConstants(parserStmt.AST, fmtFlags), - StmtSummary: formatStatementSummary(parserStmt.AST, fmtFlags), + StmtNoConstants: tree.FormatStatementHideConstants(parserStmt.AST, fmtFlags), + StmtSummary: tree.FormatStatementSummary(parserStmt.AST, fmtFlags), QueryID: queryID, QueryTags: tags, }