Skip to content

Commit 5a5e657

Browse files
committed
sql,opt: add session variable to disable optimizer rules
Fixes #88609 Release note (sql change): Added a new session variable, disable_optimizer_rules, which allows users to provide a comma- separated list of optimizer rules to disable during query optimization. This allows users to avoid rules that are known to create a suboptimal query plan for specific queries.
1 parent 74d58a6 commit 5a5e657

File tree

12 files changed

+237
-5
lines changed

12 files changed

+237
-5
lines changed

pkg/sql/exec_util.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"strings"
2121
"time"
2222

23-
apd "github.com/cockroachdb/apd/v3"
23+
"github.com/cockroachdb/apd/v3"
2424
"github.com/cockroachdb/cockroach/pkg/base"
2525
"github.com/cockroachdb/cockroach/pkg/cloud/externalconn"
2626
"github.com/cockroachdb/cockroach/pkg/clusterversion"
@@ -3909,6 +3909,10 @@ func (m *sessionDataMutator) SetTestingOptimizerDisableRuleProbability(val float
39093909
m.data.TestingOptimizerDisableRuleProbability = val
39103910
}
39113911

3912+
func (m *sessionDataMutator) SetDisableOptimizerRules(val []string) {
3913+
m.data.DisableOptimizerRules = val
3914+
}
3915+
39123916
func (m *sessionDataMutator) SetTrigramSimilarityThreshold(val float64) {
39133917
m.data.TrigramSimilarityThreshold = val
39143918
}

pkg/sql/logictest/testdata/logic_test/information_schema

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3972,6 +3972,7 @@ default_with_oids off
39723972
descriptor_validation on
39733973
disable_changefeed_replication off
39743974
disable_hoist_projection_in_join_limitation off
3975+
disable_optimizer_rules ·
39753976
disable_partially_distributed_plans off
39763977
disable_plan_gists off
39773978
disable_vec_union_eager_cancellation off

pkg/sql/logictest/testdata/logic_test/pg_catalog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,6 +2975,7 @@ default_with_oids off
29752975
descriptor_validation on NULL NULL NULL string
29762976
disable_changefeed_replication off NULL NULL NULL string
29772977
disable_hoist_projection_in_join_limitation off NULL NULL NULL string
2978+
disable_optimizer_rules · NULL NULL NULL string
29782979
disable_partially_distributed_plans off NULL NULL NULL string
29792980
disable_plan_gists off NULL NULL NULL string
29802981
disable_vec_union_eager_cancellation off NULL NULL NULL string
@@ -3215,6 +3216,7 @@ default_with_oids off
32153216
descriptor_validation on NULL user NULL on on
32163217
disable_changefeed_replication off NULL user NULL off off
32173218
disable_hoist_projection_in_join_limitation off NULL user NULL off off
3219+
disable_optimizer_rules · NULL user NULL · ·
32183220
disable_partially_distributed_plans off NULL user NULL off off
32193221
disable_plan_gists off NULL user NULL off off
32203222
disable_vec_union_eager_cancellation off NULL user NULL off off
@@ -3442,6 +3444,7 @@ descriptor_validation NULL NULL
34423444
direct_columnar_scans_enabled NULL NULL NULL NULL NULL
34433445
disable_changefeed_replication NULL NULL NULL NULL NULL
34443446
disable_hoist_projection_in_join_limitation NULL NULL NULL NULL NULL
3447+
disable_optimizer_rules NULL NULL NULL NULL NULL
34453448
disable_partially_distributed_plans NULL NULL NULL NULL NULL
34463449
disable_plan_gists NULL NULL NULL NULL NULL
34473450
disable_vec_union_eager_cancellation NULL NULL NULL NULL NULL

pkg/sql/logictest/testdata/logic_test/show_source

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ default_with_oids off
7979
descriptor_validation on
8080
disable_changefeed_replication off
8181
disable_hoist_projection_in_join_limitation off
82+
disable_optimizer_rules ·
8283
disable_partially_distributed_plans off
8384
disable_plan_gists off
8485
disable_vec_union_eager_cancellation off
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# LogicTest: local
2+
3+
# Tests for the disable_optimizer_rules session variable.
4+
# This variable allows disabling specific optimizer rules by name.
5+
6+
statement ok
7+
CREATE TABLE t (a INT, b INT, c INT, INDEX (b))
8+
9+
# Basic test with the variable unset (default).
10+
query T
11+
EXPLAIN SELECT * FROM t WHERE b = 1
12+
----
13+
distribution: local
14+
vectorized: true
15+
·
16+
• index join
17+
│ table: t@t_pkey
18+
19+
└── • scan
20+
missing stats
21+
table: t@t_b_idx
22+
spans: [/1 - /1]
23+
24+
# Set the variable with a rule that doesn't affect this query.
25+
statement ok
26+
SET disable_optimizer_rules = 'PushFilterIntoJoinLeft'
27+
28+
# Query should still use the index.
29+
query T
30+
EXPLAIN SELECT * FROM t WHERE b = 1
31+
----
32+
distribution: local
33+
vectorized: true
34+
·
35+
• index join
36+
│ table: t@t_pkey
37+
38+
└── • scan
39+
missing stats
40+
table: t@t_b_idx
41+
spans: [/1 - /1]
42+
43+
# Set the variable to disable the rule to generate constrained scans.
44+
statement ok
45+
SET disable_optimizer_rules = 'GenerateConstrainedScans'
46+
47+
# Now the query should not use the index and instead use a full table scan.
48+
query T
49+
EXPLAIN SELECT * FROM t WHERE b = 1
50+
----
51+
distribution: local
52+
vectorized: true
53+
·
54+
• filter
55+
│ filter: b = 1
56+
57+
└── • scan
58+
missing stats
59+
table: t@t_pkey
60+
spans: FULL SCAN
61+
62+
# Test with multiple rules.
63+
statement ok
64+
SET disable_optimizer_rules = 'GenerateConstrainedScans,PushFilterIntoJoinLeft'
65+
66+
query T
67+
SHOW disable_optimizer_rules
68+
----
69+
GenerateConstrainedScans, PushFilterIntoJoinLeft
70+
71+
# The query should still not use the index.
72+
query T
73+
EXPLAIN SELECT * FROM t WHERE b = 1
74+
----
75+
distribution: local
76+
vectorized: true
77+
·
78+
• filter
79+
│ filter: b = 1
80+
81+
└── • scan
82+
missing stats
83+
table: t@t_pkey
84+
spans: FULL SCAN
85+
86+
# Test with non-existent rule (should generate a notice to the client).
87+
# Spaces should be trimmed so the other rule should still take effect.
88+
# The rules are case-insensitive, so generateConstrainedScans is equivalent
89+
# to GenerateConstrainedScans.
90+
statement notice NOTICE: rule NonExistentRule does not exist and will be ignored
91+
SET disable_optimizer_rules = 'NonExistentRule, generateConstrainedScans'
92+
93+
query T
94+
SHOW disable_optimizer_rules
95+
----
96+
generateConstrainedScans
97+
98+
# The query should still not use the index.
99+
query T
100+
EXPLAIN SELECT * FROM t WHERE b = 1
101+
----
102+
distribution: local
103+
vectorized: true
104+
·
105+
• filter
106+
│ filter: b = 1
107+
108+
└── • scan
109+
missing stats
110+
table: t@t_pkey
111+
spans: FULL SCAN
112+
113+
# Reset the variable.
114+
statement ok
115+
RESET disable_optimizer_rules
116+
117+
# Query should use the index again.
118+
query T
119+
EXPLAIN SELECT * FROM t WHERE b = 1
120+
----
121+
distribution: local
122+
vectorized: true
123+
·
124+
• index join
125+
│ table: t@t_pkey
126+
127+
└── • scan
128+
missing stats
129+
table: t@t_b_idx
130+
spans: [/1 - /1]
131+
132+
# Cleanup.
133+
statement ok
134+
DROP TABLE t

pkg/sql/opt/exec/execbuilder/tests/local/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/opt/memo/memo.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package memo
99
import (
1010
"bytes"
1111
"context"
12+
"reflect"
1213

1314
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/isolation"
1415
"github.com/cockroachdb/cockroach/pkg/sql/opt"
@@ -167,6 +168,7 @@ type Memo struct {
167168
testingOptimizerRandomSeed int64
168169
testingOptimizerCostPerturbation float64
169170
testingOptimizerDisableRuleProbability float64
171+
disableOptimizerRules []string
170172
enforceHomeRegion bool
171173
variableInequalityLookupJoinEnabled bool
172174
allowOrdinalColumnReferences bool
@@ -272,6 +274,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
272274
testingOptimizerRandomSeed: evalCtx.SessionData().TestingOptimizerRandomSeed,
273275
testingOptimizerCostPerturbation: evalCtx.SessionData().TestingOptimizerCostPerturbation,
274276
testingOptimizerDisableRuleProbability: evalCtx.SessionData().TestingOptimizerDisableRuleProbability,
277+
disableOptimizerRules: evalCtx.SessionData().DisableOptimizerRules,
275278
enforceHomeRegion: evalCtx.SessionData().EnforceHomeRegion,
276279
variableInequalityLookupJoinEnabled: evalCtx.SessionData().VariableInequalityLookupJoinEnabled,
277280
allowOrdinalColumnReferences: evalCtx.SessionData().AllowOrdinalColumnReferences,
@@ -450,6 +453,7 @@ func (m *Memo) IsStale(
450453
m.testingOptimizerRandomSeed != evalCtx.SessionData().TestingOptimizerRandomSeed ||
451454
m.testingOptimizerCostPerturbation != evalCtx.SessionData().TestingOptimizerCostPerturbation ||
452455
m.testingOptimizerDisableRuleProbability != evalCtx.SessionData().TestingOptimizerDisableRuleProbability ||
456+
!reflect.DeepEqual(m.disableOptimizerRules, evalCtx.SessionData().DisableOptimizerRules) ||
453457
m.enforceHomeRegion != evalCtx.SessionData().EnforceHomeRegion ||
454458
m.variableInequalityLookupJoinEnabled != evalCtx.SessionData().VariableInequalityLookupJoinEnabled ||
455459
m.allowOrdinalColumnReferences != evalCtx.SessionData().AllowOrdinalColumnReferences ||

pkg/sql/opt/memo/memo_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,12 @@ func TestMemoIsStale(t *testing.T) {
373373
evalCtx.SessionData().TestingOptimizerDisableRuleProbability = 0
374374
notStale()
375375

376+
// Stale disable_optimizer_rules.
377+
evalCtx.SessionData().DisableOptimizerRules = []string{"some_rule"}
378+
stale()
379+
evalCtx.SessionData().DisableOptimizerRules = nil
380+
notStale()
381+
376382
// Stale allow_ordinal_column_references.
377383
evalCtx.SessionData().AllowOrdinalColumnReferences = true
378384
stale()

pkg/sql/opt/rule_name.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@
55

66
package opt
77

8+
import "strings"
9+
10+
// RuleNameMap is a map from lower-case rule name strings to RuleName values.
11+
var RuleNameMap = make(map[string]RuleName, NumRuleNames)
12+
13+
func init() {
14+
for i := RuleName(1); i < NumRuleNames; i++ {
15+
if i == startAutoRule || i == startExploreRule {
16+
// Skip these markers.
17+
continue
18+
}
19+
RuleNameMap[strings.ToLower(i.String())] = i
20+
}
21+
}
22+
823
// RuleName enumerates the names of all the optimizer rules. Manual rule names
924
// are defined in this file and rule names generated by Optgen are defined in
1025
// rule_name.og.go.

pkg/sql/opt/xform/optimizer.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package xform
88
import (
99
"context"
1010
"math/rand"
11+
"strings"
1112

1213
"github.com/cockroachdb/cockroach/pkg/sql/opt"
1314
"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
@@ -133,6 +134,16 @@ func (o *Optimizer) Init(ctx context.Context, evalCtx *eval.Context, catalog cat
133134
o.mem = o.f.Memo()
134135
o.explorer.init(o)
135136

137+
var disabledRules RuleSet
138+
// If the DisableOptimizerRules session variable is set, then disable
139+
// the specified rules.
140+
if ruleNames := evalCtx.SessionData().DisableOptimizerRules; len(ruleNames) > 0 {
141+
for _, ruleName := range ruleNames {
142+
if rule, ok := opt.RuleNameMap[strings.ToLower(ruleName)]; ok {
143+
disabledRules.Add(int(rule))
144+
}
145+
}
146+
}
136147
if seed := evalCtx.SessionData().TestingOptimizerRandomSeed; seed != 0 {
137148
o.rng = rand.New(rand.NewSource(seed))
138149
}
@@ -153,7 +164,10 @@ func (o *Optimizer) Init(ctx context.Context, evalCtx *eval.Context, catalog cat
153164
o.defaultCoster.Init(ctx, evalCtx, o.mem, costPerturbation, o.rng, o)
154165
o.coster = &o.defaultCoster
155166
if disableRuleProbability > 0 {
156-
o.disableRulesRandom(disableRuleProbability)
167+
o.disableRulesRandom(disableRuleProbability, &disabledRules)
168+
}
169+
if disabledRules.Len() > 0 {
170+
o.disableRules(disabledRules)
157171
}
158172
}
159173

@@ -1067,7 +1081,7 @@ func (a *groupStateAlloc) allocate() *groupState {
10671081
}
10681082

10691083
// disableRulesRandom disables rules with the given probability for testing.
1070-
func (o *Optimizer) disableRulesRandom(probability float64) {
1084+
func (o *Optimizer) disableRulesRandom(probability float64, disabledRules *RuleSet) {
10711085
essentialRules := intsets.MakeFast(
10721086
// Needed to prevent constraint building from failing.
10731087
int(opt.NormalizeInConst),
@@ -1115,7 +1129,6 @@ func (o *Optimizer) disableRulesRandom(probability float64) {
11151129
int(opt.ConvertUncorrelatedExistsToCoalesceSubquery),
11161130
)
11171131

1118-
var disabledRules RuleSet
11191132
for i := opt.RuleName(1); i < opt.NumRuleNames; i++ {
11201133
var r float64
11211134
if o.rng == nil {
@@ -1127,12 +1140,14 @@ func (o *Optimizer) disableRulesRandom(probability float64) {
11271140
disabledRules.Add(int(i))
11281141
}
11291142
}
1143+
}
11301144

1145+
func (o *Optimizer) disableRules(disabledRules RuleSet) {
11311146
o.f.SetDisabledRules(disabledRules)
11321147

11331148
o.NotifyOnMatchedRule(func(ruleName opt.RuleName) bool {
11341149
if disabledRules.Contains(int(ruleName)) {
1135-
log.Infof(o.ctx, "disabled rule matched: %s", ruleName.String())
1150+
log.VEventf(o.ctx, 2, "disabled rule matched: %s", ruleName.String())
11361151
return false
11371152
}
11381153
return true

0 commit comments

Comments
 (0)