Skip to content

Commit b61eb64

Browse files
committed
sql/*: invalidate cached memos after hint injection changes
If we build a memo with hint injection, and then later we realize that memo won't work (maybe because we discover the hint is unsatisfiable during execution of a prepared statement) we need to invalidate the cached memo. To do this, add a usingHintInjection field which tells the memo staleness check whether we're trying with or without hint injection. Also, in a related but separate change, this commit adds all matching HintIDs to the optimizer metadata so that we don't invalidate cached memos if the hintsGeneration changed due to some unrelated statement hints changing. Informs: #153633 Release note: None
1 parent 8cc65a7 commit b61eb64

File tree

8 files changed

+319
-10
lines changed

8 files changed

+319
-10
lines changed

pkg/sql/faketreeeval/evalctx.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,16 @@ func (ep *DummyEvalPlanner) InsertStatementHint(
603603
return 0, nil
604604
}
605605

606+
// UsingHintInjection is part of the eval.Planner interface.
607+
func (ep *DummyEvalPlanner) UsingHintInjection() bool {
608+
return false
609+
}
610+
611+
// GetHintIDs is part of the eval.Planner interface.
612+
func (ep *DummyEvalPlanner) GetHintIDs() []int64 {
613+
return nil
614+
}
615+
606616
// DummyPrivilegedAccessor implements the tree.PrivilegedAccessor interface by returning errors.
607617
type DummyPrivilegedAccessor struct{}
608618

pkg/sql/logictest/testdata/logic_test/statement_hint_builtins

Lines changed: 255 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,9 @@ DEALLOCATE ALL
188188
statement ok
189189
CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT, INDEX (b))
190190

191-
# Temporary, until next commit.
192-
statement ok
193-
SET CLUSTER SETTING sql.query_cache.enabled = off
194-
195191
# Try some simple hint injections. First, an index hint.
196192

193+
onlyif config local
197194
query T
198195
EXPLAIN SELECT a FROM abc WHERE a = 10
199196
----
@@ -231,6 +228,7 @@ WHERE message LIKE '%injected hints%'
231228
injected hints from external statement hint x
232229
trying planning with injected hints
233230

231+
onlyif config local
234232
query T
235233
EXPLAIN SELECT a FROM abc WHERE a = 10
236234
----
@@ -248,6 +246,7 @@ statement hints count: 1
248246

249247
# Try injecting a join hint.
250248

249+
onlyif config local
251250
query T
252251
EXPLAIN SELECT a, x FROM abc JOIN xy ON y = b WHERE a = 10
253252
----
@@ -289,6 +288,7 @@ WHERE message LIKE '%injected hints%'
289288
injected hints from external statement hint x
290289
trying planning with injected hints
291290

291+
onlyif config local
292292
query T
293293
EXPLAIN SELECT a, x FROM abc JOIN xy ON y = b WHERE a = 10
294294
----
@@ -338,6 +338,7 @@ WHERE message LIKE '%injected hints%'
338338
injected hints from external statement hint x
339339
trying planning with injected hints
340340

341+
onlyif config local
341342
query T
342343
EXPLAIN SELECT a FROM abc@abc_pkey WHERE b = 10
343344
----
@@ -380,6 +381,7 @@ trying planning with injected hints
380381
planning with injected hints failed with: index "foo" not found
381382
falling back to planning without injected hints
382383

384+
onlyif config local
383385
query T
384386
EXPLAIN SELECT a + 1 FROM abc WHERE a = 10
385387
----
@@ -424,6 +426,7 @@ trying planning with injected hints
424426
planning with injected hints failed with: could not produce a query plan conforming to the LOOKUP JOIN hint
425427
falling back to planning without injected hints
426428

429+
onlyif config local
427430
query T
428431
EXPLAIN SELECT c FROM xy JOIN abc ON c = y WHERE x = 10
429432
----
@@ -447,7 +450,7 @@ statement hints count: 1
447450

448451
# Try a prepared statement with an injected hint.
449452

450-
statement ok
453+
let $hint_p1
451454
SELECT crdb_internal.inject_hint(
452455
'SELECT c FROM abc WHERE b > _',
453456
'SELECT c FROM abc@{NO_INDEX_JOIN} WHERE b > _'
@@ -460,10 +463,10 @@ statement ok
460463
SET tracing = on
461464

462465
statement ok
463-
PREPARE p AS SELECT c FROM abc WHERE b > $1
466+
PREPARE p1 AS SELECT c FROM abc WHERE b > $1
464467

465468
statement ok
466-
EXECUTE p (5)
469+
EXECUTE p1 (5)
467470

468471
statement ok
469472
SET tracing = off
@@ -478,6 +481,249 @@ injected hints from external statement hint x
478481
trying preparing with injected hints
479482
trying planning with injected hints
480483

481-
# Temporary, until next commit.
484+
# Try injecting a hint between prepare and execute.
485+
486+
statement ok
487+
SET tracing = on
488+
489+
statement ok
490+
PREPARE p2 AS SELECT c + 1 FROM abc WHERE b > $1
491+
492+
statement ok
493+
SET tracing = off
494+
495+
query empty
496+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
497+
FROM [SHOW TRACE FOR SESSION]
498+
WHERE message LIKE '%injected hints%'
499+
500+
statement ok
501+
SELECT crdb_internal.inject_hint(
502+
'SELECT c + _ FROM abc WHERE b > _',
503+
'SELECT c + _ FROM abc@{FORCE_INDEX=abc_pkey,DESC} WHERE b > _'
504+
)
505+
506+
statement ok
507+
SELECT crdb_internal.await_statement_hints_cache()
508+
509+
statement ok
510+
SET tracing = on
511+
512+
statement ok
513+
EXECUTE p2 (5)
514+
515+
statement ok
516+
SET tracing = off
517+
518+
query T
519+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
520+
FROM [SHOW TRACE FOR SESSION]
521+
WHERE message LIKE '%injected hints%'
522+
----
523+
injected hints from external statement hint x
524+
trying planning with injected hints
525+
526+
# Try removing an injected hint between prepare and execute.
527+
528+
# (Re-use p1.)
529+
statement ok
530+
DELETE FROM system.statement_hints WHERE row_id = $hint_p1
531+
532+
statement ok
533+
SELECT crdb_internal.await_statement_hints_cache()
534+
535+
statement ok
536+
SET tracing = on
537+
538+
statement ok
539+
EXECUTE p1 (6)
540+
541+
statement ok
542+
SET tracing = off
543+
544+
query empty
545+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
546+
FROM [SHOW TRACE FOR SESSION]
547+
WHERE message LIKE '%injected hints%'
548+
549+
# Check that we do not use an invalid index hint injected into a prepared
550+
# statement.
551+
552+
statement ok
553+
SELECT crdb_internal.inject_hint(
554+
'SELECT sum(a) FROM abc WHERE c = _',
555+
'SELECT sum(a) FROM abc@abc_foo WHERE c = _'
556+
)
557+
558+
statement ok
559+
SELECT crdb_internal.await_statement_hints_cache()
560+
561+
statement ok
562+
SET tracing = on
563+
564+
statement ok
565+
PREPARE p3 AS SELECT sum(a) FROM abc WHERE c = $1
566+
567+
statement ok
568+
EXECUTE p3 (5)
569+
570+
statement ok
571+
SET tracing = off
572+
573+
query T
574+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
575+
FROM [SHOW TRACE FOR SESSION]
576+
WHERE message LIKE '%injected hints%'
577+
----
578+
injected hints from external statement hint x
579+
injected hints from external statement hint x
580+
trying preparing with injected hints
581+
preparing with injected hints failed with: index "abc_foo" not found
582+
falling back to preparing without injected hints
583+
trying planning with injected hints
584+
planning with injected hints failed with: index "abc_foo" not found
585+
falling back to planning without injected hints
586+
587+
# Check that we do not use an unsatisfiable hint injected into a prepared
588+
# statement.
589+
590+
statement ok
591+
SELECT crdb_internal.inject_hint(
592+
'SELECT max(a) FROM abc WHERE (b = _) AND (c = _)',
593+
'SELECT max(a) FROM abc@{FORCE_ZIGZAG} WHERE (b = _) AND (c = _)'
594+
)
595+
596+
statement ok
597+
SELECT crdb_internal.await_statement_hints_cache()
598+
599+
statement ok
600+
SET tracing = on
601+
602+
statement ok
603+
PREPARE p4 AS SELECT max(a) FROM abc WHERE b = $1 AND c = $2
604+
482605
statement ok
483-
RESET CLUSTER SETTING sql.query_cache.enabled
606+
EXECUTE p4 (5, 6)
607+
608+
statement ok
609+
SET tracing = off
610+
611+
query T
612+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
613+
FROM [SHOW TRACE FOR SESSION]
614+
WHERE message LIKE '%injected hints%'
615+
----
616+
injected hints from external statement hint x
617+
injected hints from external statement hint x
618+
trying preparing with injected hints
619+
trying planning with injected hints
620+
planning with injected hints failed with: could not produce a query plan conforming to the FORCE_ZIGZAG hint
621+
falling back to planning without injected hints
622+
623+
# Check that we can inject hints into generic query plans.
624+
625+
statement ok
626+
SET plan_cache_mode = force_generic_plan
627+
628+
statement ok
629+
SELECT crdb_internal.inject_hint(
630+
'SELECT a * _ FROM abc WHERE b > _',
631+
'SELECT a * _ FROM abc@abc_pkey WHERE b > _'
632+
)
633+
634+
statement ok
635+
SELECT crdb_internal.await_statement_hints_cache()
636+
637+
statement ok
638+
SET tracing = on
639+
640+
statement ok
641+
PREPARE p5 AS SELECT a * 2 FROM abc WHERE b > $1
642+
643+
statement ok
644+
EXECUTE p5 (5)
645+
646+
statement ok
647+
EXECUTE p5 (6)
648+
649+
statement ok
650+
SET tracing = off
651+
652+
query T
653+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
654+
FROM [SHOW TRACE FOR SESSION]
655+
WHERE message LIKE '%injected hints%' OR message LIKE '%(generic)%'
656+
----
657+
injected hints from external statement hint x
658+
injected hints from external statement hint x
659+
trying preparing with injected hints
660+
trying planning with injected hints
661+
optimizing (generic)
662+
trying planning with injected hints
663+
664+
# Check that we can inject hints when using plan_cache_mode=auto.
665+
666+
statement ok
667+
SET plan_cache_mode = auto
668+
669+
statement ok
670+
SELECT crdb_internal.inject_hint(
671+
'SELECT a - b FROM abc WHERE b = _',
672+
'SELECT a - b FROM abc@abc_pkey WHERE b = _'
673+
)
674+
675+
statement ok
676+
SELECT crdb_internal.await_statement_hints_cache()
677+
678+
statement ok
679+
SET tracing = on
680+
681+
statement ok
682+
PREPARE p6 AS SELECT a - b FROM abc WHERE b = $1
683+
684+
statement ok
685+
EXECUTE p6 (5)
686+
687+
statement ok
688+
EXECUTE p6 (6)
689+
690+
statement ok
691+
EXECUTE p6 (7)
692+
693+
statement ok
694+
EXECUTE p6 (8)
695+
696+
statement ok
697+
EXECUTE p6 (9)
698+
699+
statement ok
700+
EXECUTE p6 (10)
701+
702+
statement ok
703+
EXECUTE p6 (11)
704+
705+
statement ok
706+
SET tracing = off
707+
708+
query T
709+
SELECT regexp_replace(split_part(message, ': SELECT', 1), E'\\d+', 'x')
710+
FROM [SHOW TRACE FOR SESSION]
711+
WHERE message LIKE '%injected hints%' OR message LIKE '%(generic)%'
712+
----
713+
injected hints from external statement hint x
714+
injected hints from external statement hint x
715+
trying preparing with injected hints
716+
trying planning with injected hints
717+
trying planning with injected hints
718+
trying planning with injected hints
719+
trying planning with injected hints
720+
trying planning with injected hints
721+
trying planning with injected hints
722+
optimizing (generic)
723+
trying planning with injected hints
724+
725+
statement ok
726+
RESET plan_cache_mode
727+
728+
statement ok
729+
DEALLOCATE ALL

pkg/sql/opt/memo/memo.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ type Memo struct {
215215
clampLowHistogramSelectivity bool
216216
clampInequalitySelectivity bool
217217
useMaxFrequencySelectivity bool
218+
usingHintInjection bool
218219

219220
// txnIsoLevel is the isolation level under which the plan was created. This
220221
// affects the planning of some locking operations, so it must be included in
@@ -330,6 +331,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
330331
clampLowHistogramSelectivity: evalCtx.SessionData().OptimizerClampLowHistogramSelectivity,
331332
clampInequalitySelectivity: evalCtx.SessionData().OptimizerClampInequalitySelectivity,
332333
useMaxFrequencySelectivity: evalCtx.SessionData().OptimizerUseMaxFrequencySelectivity,
334+
usingHintInjection: evalCtx.Planner != nil && evalCtx.Planner.UsingHintInjection(),
333335
txnIsoLevel: evalCtx.TxnIsoLevel,
334336
}
335337
m.metadata.Init()
@@ -509,6 +511,7 @@ func (m *Memo) IsStale(
509511
m.clampLowHistogramSelectivity != evalCtx.SessionData().OptimizerClampLowHistogramSelectivity ||
510512
m.clampInequalitySelectivity != evalCtx.SessionData().OptimizerClampInequalitySelectivity ||
511513
m.useMaxFrequencySelectivity != evalCtx.SessionData().OptimizerUseMaxFrequencySelectivity ||
514+
m.usingHintInjection != (evalCtx.Planner != nil && evalCtx.Planner.UsingHintInjection()) ||
512515
m.txnIsoLevel != evalCtx.TxnIsoLevel {
513516
return true, nil
514517
}

0 commit comments

Comments
 (0)