Skip to content

Commit 3dd8853

Browse files
committed
sql/*: load external statement hints from the hints cache
When making a `sql.Statement`, call `MaybeGetStatementHints` to load any applicable external statement hints from the hint cache. We don't yet do anything with the hints once they're loaded, but we do save them in prepared statements. A few notable code paths are not yet loading hints from the hints cache: - SQL statements within UDFs and SPs - Views Informs: #153633 Release note: None
1 parent 94e5872 commit 3dd8853

21 files changed

+228
-39
lines changed

pkg/sql/conn_executor_exec.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/cockroachdb/cockroach/pkg/sql/contentionpb"
3232
"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
3333
"github.com/cockroachdb/cockroach/pkg/sql/execstats"
34+
"github.com/cockroachdb/cockroach/pkg/sql/hints"
3435
"github.com/cockroachdb/cockroach/pkg/sql/isql"
3536
"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
3637
"github.com/cockroachdb/cockroach/pkg/sql/opt/exec/explain"
@@ -383,12 +384,16 @@ func (ex *connExecutor) execStmtInOpenState(
383384

384385
isExtendedProtocol := prepared != nil
385386
stmtFingerprintFmtMask := tree.FmtHideConstants | tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&ex.server.cfg.Settings.SV))
387+
var statementHintsCache *hints.StatementHintsCache
388+
if ex.executorType != executorTypeInternal {
389+
statementHintsCache = ex.server.cfg.StatementHintsCache
390+
}
386391

387392
var stmt Statement
388393
if isExtendedProtocol {
389-
stmt = makeStatementFromPrepared(prepared, queryID)
394+
stmt = makeStatementFromPrepared(ctx, prepared, queryID, stmtFingerprintFmtMask, statementHintsCache)
390395
} else {
391-
stmt = makeStatement(parserStmt, queryID, stmtFingerprintFmtMask)
396+
stmt = makeStatement(ctx, parserStmt, queryID, stmtFingerprintFmtMask, statementHintsCache)
392397
}
393398

394399
if len(stmt.QueryTags) > 0 {
@@ -557,6 +562,10 @@ func (ex *connExecutor) execStmtInOpenState(
557562
stmt.ExpectedTypes = ps.Columns
558563
stmt.StmtNoConstants = ps.StatementNoConstants
559564
stmt.StmtSummary = ps.StatementSummary
565+
stmt.Hints = ps.Hints
566+
stmt.HintIDs = ps.HintIDs
567+
stmt.HintsGeneration = ps.HintsGeneration
568+
stmt.ReloadHintsIfStale(ctx, stmtFingerprintFmtMask, statementHintsCache)
560569
res.ResetStmtType(ps.AST)
561570

562571
if e.DiscardRows {
@@ -888,7 +897,12 @@ func (ex *connExecutor) execStmtInOpenState(
888897
typeHints[i] = resolved
889898
}
890899
}
900+
var statementHintsCache *hints.StatementHintsCache
901+
if ex.executorType != executorTypeInternal {
902+
statementHintsCache = ex.server.cfg.StatementHintsCache
903+
}
891904
prepStmt := makeStatement(
905+
ctx,
892906
statements.Statement[tree.Statement]{
893907
// We need the SQL string just for the part that comes after
894908
// "PREPARE ... AS",
@@ -901,6 +915,7 @@ func (ex *connExecutor) execStmtInOpenState(
901915
},
902916
ex.server.cfg.GenerateID(),
903917
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&ex.server.cfg.Settings.SV)),
918+
statementHintsCache,
904919
)
905920
var rawTypeHints []oid.Oid
906921

@@ -1252,11 +1267,15 @@ func (ex *connExecutor) execStmtInOpenStateWithPausablePortal(
12521267

12531268
isExtendedProtocol := portal != nil && portal.Stmt != nil
12541269
stmtFingerprintFmtMask := tree.FmtHideConstants | tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&ex.server.cfg.Settings.SV))
1270+
var statementHintsCache *hints.StatementHintsCache
1271+
if ex.executorType != executorTypeInternal {
1272+
statementHintsCache = ex.server.cfg.StatementHintsCache
1273+
}
12551274

12561275
if isExtendedProtocol {
1257-
vars.stmt = makeStatementFromPrepared(portal.Stmt, queryID)
1276+
vars.stmt = makeStatementFromPrepared(ctx, portal.Stmt, queryID, stmtFingerprintFmtMask, statementHintsCache)
12581277
} else {
1259-
vars.stmt = makeStatement(parserStmt, queryID, stmtFingerprintFmtMask)
1278+
vars.stmt = makeStatement(ctx, parserStmt, queryID, stmtFingerprintFmtMask, statementHintsCache)
12601279
}
12611280

12621281
var queryTimeoutTicker *time.Timer
@@ -1443,6 +1462,10 @@ func (ex *connExecutor) execStmtInOpenStateWithPausablePortal(
14431462
vars.stmt.ExpectedTypes = ps.Columns
14441463
vars.stmt.StmtNoConstants = ps.StatementNoConstants
14451464
vars.stmt.StmtSummary = ps.StatementSummary
1465+
vars.stmt.Hints = ps.Hints
1466+
vars.stmt.HintIDs = ps.HintIDs
1467+
vars.stmt.HintsGeneration = ps.HintsGeneration
1468+
vars.stmt.ReloadHintsIfStale(ctx, stmtFingerprintFmtMask, statementHintsCache)
14461469
res.ResetStmtType(ps.AST)
14471470

14481471
if e.DiscardRows {
@@ -1852,7 +1875,12 @@ func (ex *connExecutor) execStmtInOpenStateWithPausablePortal(
18521875
typeHints[i] = resolved
18531876
}
18541877
}
1878+
var statementHintsCache *hints.StatementHintsCache
1879+
if ex.executorType != executorTypeInternal {
1880+
statementHintsCache = ex.server.cfg.StatementHintsCache
1881+
}
18551882
prepStmt := makeStatement(
1883+
ctx,
18561884
statements.Statement[tree.Statement]{
18571885
// We need the SQL string just for the part that comes after
18581886
// "PREPARE ... AS",
@@ -1865,6 +1893,7 @@ func (ex *connExecutor) execStmtInOpenStateWithPausablePortal(
18651893
},
18661894
ex.server.cfg.GenerateID(),
18671895
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&ex.server.cfg.Settings.SV)),
1896+
statementHintsCache,
18681897
)
18691898
var rawTypeHints []oid.Oid
18701899

@@ -3463,8 +3492,11 @@ func (ex *connExecutor) execStmtInNoTxnState(
34633492
}
34643493

34653494
p := &ex.planner
3466-
stmt := makeStatement(parserStmt, ex.server.cfg.GenerateID(),
3467-
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&ex.server.cfg.Settings.SV)))
3495+
stmt := makeStatement(
3496+
ctx, parserStmt, ex.server.cfg.GenerateID(),
3497+
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&ex.server.cfg.Settings.SV)),
3498+
nil, /* statementHintsCache */
3499+
)
34683500
p.stmt = stmt
34693501
p.semaCtx.Annotations = tree.MakeAnnotations(stmt.NumAnnotations)
34703502
p.extendedEvalCtx.Annotations = &p.semaCtx.Annotations

pkg/sql/conn_executor_prepare.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"context"
1010

1111
"github.com/cockroachdb/cockroach/pkg/kv"
12+
"github.com/cockroachdb/cockroach/pkg/sql/hints"
1213
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
1314
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
1415
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase"
@@ -72,8 +73,15 @@ func (ex *connExecutor) execPrepare(
7273
ex.deletePreparedStmt(ctx, "")
7374
}
7475

75-
stmt := makeStatement(parseCmd.Statement, ex.server.cfg.GenerateID(),
76-
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(ex.server.cfg.SV())))
76+
var statementHintsCache *hints.StatementHintsCache
77+
if ex.executorType != executorTypeInternal {
78+
statementHintsCache = ex.server.cfg.StatementHintsCache
79+
}
80+
stmt := makeStatement(
81+
ctx, parseCmd.Statement, ex.server.cfg.GenerateID(),
82+
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(ex.server.cfg.SV())),
83+
statementHintsCache,
84+
)
7785
_, err := ex.addPreparedStmt(
7886
ctx,
7987
parseCmd.Name,
@@ -236,6 +244,9 @@ func (ex *connExecutor) prepare(
236244
prepared.Statement.NumPlaceholders = origNumPlaceholders
237245
prepared.StatementNoConstants = stmt.StmtNoConstants
238246
prepared.StatementSummary = stmt.StmtSummary
247+
prepared.Hints = stmt.Hints
248+
prepared.HintIDs = stmt.HintIDs
249+
prepared.HintsGeneration = stmt.HintsGeneration
239250

240251
// Point to the prepared state, which can be further populated during query
241252
// preparation.

pkg/sql/distsql_plan_changefeed.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,14 @@ func PlanCDCExpression(
7878
opt.apply(&cfg)
7979
}
8080

81-
p.stmt = makeStatement(statements.Statement[tree.Statement]{
82-
AST: cdcExpr,
83-
SQL: tree.AsString(cdcExpr),
84-
}, clusterunique.ID{}, /* queryID */
81+
p.stmt = makeStatement(
82+
ctx,
83+
statements.Statement[tree.Statement]{
84+
AST: cdcExpr,
85+
SQL: tree.AsString(cdcExpr),
86+
}, clusterunique.ID{}, /* queryID */
8587
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&p.execCfg.Settings.SV)),
88+
nil, /* statementHintsCache */
8689
)
8790

8891
p.curPlan.init(&p.stmt, &p.instrumentation)

pkg/sql/distsql_running_test.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,11 @@ func TestDistSQLRunningInAbortedTxn(t *testing.T) {
167167

168168
// We need to re-plan every time, since the plan is closed automatically
169169
// by PlanAndRun() below making it unusable across retries.
170-
p.stmt = makeStatement(stmt, clusterunique.ID{},
171-
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&execCfg.Settings.SV)))
170+
p.stmt = makeStatement(
171+
ctx, stmt, clusterunique.ID{},
172+
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&execCfg.Settings.SV)),
173+
nil, /* statementHintsCache */
174+
)
172175
if err := p.makeOptimizerPlan(ctx); err != nil {
173176
t.Fatal(err)
174177
}
@@ -285,8 +288,11 @@ func TestDistSQLRunningParallelFKChecksAfterAbort(t *testing.T) {
285288
p.ExtendedEvalContext().Tracing,
286289
)
287290

288-
p.stmt = makeStatement(stmt, clusterunique.ID{},
289-
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&s.ClusterSettings().SV)))
291+
p.stmt = makeStatement(
292+
ctx, stmt, clusterunique.ID{},
293+
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&s.ClusterSettings().SV)),
294+
nil, /* statementHintsCache */
295+
)
290296
if err := p.makeOptimizerPlan(ctx); err != nil {
291297
t.Fatal(err)
292298
}

pkg/sql/explain_tree_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@ func TestPlanToTreeAndPlanToString(t *testing.T) {
7171
ih.codec = execCfg.Codec
7272
ih.collectBundle = true
7373

74-
p.stmt = makeStatement(stmt, clusterunique.ID{},
75-
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&execCfg.Settings.SV)))
74+
p.stmt = makeStatement(
75+
ctx, stmt, clusterunique.ID{},
76+
tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&execCfg.Settings.SV)),
77+
nil, /* statementHintsCache */
78+
)
7679
if err := p.makeOptimizerPlan(ctx); err != nil {
7780
t.Fatal(err)
7881
}

pkg/sql/hints/hint_cache.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ func NewStatementHintsCache(
135135
return s > int(cacheSize.Get(&st.SV))
136136
},
137137
})
138+
hintsCache.generation.Store(1)
138139
return hintsCache
139140
}
140141

pkg/sql/hints/hint_cache_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ func TestHintCacheGeneration(t *testing.T) {
416416
}
417417

418418
// The initial scan should increment the generation.
419-
waitForGenerationInc(0)
419+
waitForGenerationInc(1)
420420
generationAfterInitialScan := getGenerationAssertNoChange()
421421

422422
// Insert a hint - generation should increment.

pkg/sql/opt/optbuilder/routine.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ func (b *Builder) buildRoutine(
450450
bodyTags = make([]string, len(stmts))
451451

452452
for i := range stmts {
453+
// TODO(michae2): We should be checking the statement hints cache here to
454+
// find any external statement hints that could apply to this statement.
453455
stmtScope := b.buildStmtAtRootWithScope(stmts[i].AST, nil /* desiredTypes */, bodyScope)
454456

455457
// The last statement produces the output of the UDF.

pkg/sql/opt/optbuilder/select.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ func (b *Builder) buildView(
335335
if !ok {
336336
panic(errors.AssertionFailedf("expected SELECT statement"))
337337
}
338+
// TODO(michae2): We should be checking the statement hints cache here to
339+
// find any external statement hints that could apply to the view statement.
338340

339341
b.views[view] = sel
340342

pkg/sql/plan_opt.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package sql
77

88
import (
99
"context"
10+
"slices"
1011
"strings"
1112

1213
"github.com/cockroachdb/cockroach/pkg/server/telemetry"
@@ -139,6 +140,8 @@ func (p *planner) prepareUsingOptimizer(
139140
// Check that the type hints match (the type hints affect type checking).
140141
if !pm.TypeHints.Identical(p.semaCtx.Placeholders.TypeHints) {
141142
opc.log(ctx, "query cache hit but type hints don't match")
143+
} else if pm.HintsGeneration != stmt.Prepared.HintsGeneration && !slices.Equal(pm.HintIDs, stmt.Prepared.HintIDs) {
144+
opc.log(ctx, "query cache hit but external statement hints don't match")
142145
} else {
143146
isStale, err := cachedData.Memo.IsStale(ctx, p.EvalContext(), opc.catalog)
144147
if err != nil {
@@ -150,6 +153,9 @@ func (p *planner) prepareUsingOptimizer(
150153
stmt.Prepared.StatementNoConstants = pm.StatementNoConstants
151154
stmt.Prepared.Columns = pm.Columns
152155
stmt.Prepared.Types = pm.Types
156+
stmt.Hints = pm.Hints
157+
stmt.HintIDs = pm.HintIDs
158+
stmt.HintsGeneration = pm.HintsGeneration
153159
if cachedData.Memo.IsOptimized() {
154160
// A cache, fully optimized memo is an "ideal generic
155161
// memo".

0 commit comments

Comments
 (0)