Skip to content

Commit 64bad8e

Browse files
committed
opt: add optimizer_prefer_bounded_cardinality session setting
Release note (sql change): The `optimizer_prefer_bounded_cardinality` session setting has been added which instructs the optimizer to prefer query plans where every expression has a guaranteed upper-bound on the number of rows it will process. This may help the optimizer produce better query plans in some cases. This setting is disabled by default.
1 parent bdb558b commit 64bad8e

File tree

14 files changed

+555
-3
lines changed

14 files changed

+555
-3
lines changed

pkg/sql/exec_util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4007,6 +4007,10 @@ func (m *sessionDataMutator) SetCatalogDigestStalenessCheckEnabled(b bool) {
40074007
m.data.CatalogDigestStalenessCheckEnabled = b
40084008
}
40094009

4010+
func (m *sessionDataMutator) SetOptimizerPreferBoundedCardinality(b bool) {
4011+
m.data.OptimizerPreferBoundedCardinality = b
4012+
}
4013+
40104014
// Utility functions related to scrubbing sensitive information on SQL Stats.
40114015

40124016
// quantizeCounts ensures that the Count field in the

pkg/sql/logictest/testdata/logic_test/information_schema

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4007,6 +4007,7 @@ optimizer on
40074007
optimizer_always_use_histograms on
40084008
optimizer_hoist_uncorrelated_equality_subqueries on
40094009
optimizer_merge_joins_enabled on
4010+
optimizer_prefer_bounded_cardinality off
40104011
optimizer_prove_implication_with_virtual_computed_columns on
40114012
optimizer_push_limit_into_project_filtered_scan on
40124013
optimizer_push_offset_into_index_join on

pkg/sql/logictest/testdata/logic_test/pg_catalog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3007,6 +3007,7 @@ opt_split_scan_limit 2048 N
30073007
optimizer_always_use_histograms on NULL NULL NULL string
30083008
optimizer_hoist_uncorrelated_equality_subqueries on NULL NULL NULL string
30093009
optimizer_merge_joins_enabled on NULL NULL NULL string
3010+
optimizer_prefer_bounded_cardinality off NULL NULL NULL string
30103011
optimizer_prove_implication_with_virtual_computed_columns on NULL NULL NULL string
30113012
optimizer_push_limit_into_project_filtered_scan on NULL NULL NULL string
30123013
optimizer_push_offset_into_index_join on NULL NULL NULL string
@@ -3212,6 +3213,7 @@ opt_split_scan_limit 2048 N
32123213
optimizer_always_use_histograms on NULL user NULL on on
32133214
optimizer_hoist_uncorrelated_equality_subqueries on NULL user NULL on on
32143215
optimizer_merge_joins_enabled on NULL user NULL on on
3216+
optimizer_prefer_bounded_cardinality off NULL user NULL off off
32153217
optimizer_prove_implication_with_virtual_computed_columns on NULL user NULL on on
32163218
optimizer_push_limit_into_project_filtered_scan on NULL user NULL on on
32173219
optimizer_push_offset_into_index_join on NULL user NULL on on
@@ -3416,6 +3418,7 @@ optimizer NULL NULL NULL
34163418
optimizer_always_use_histograms NULL NULL NULL NULL NULL
34173419
optimizer_hoist_uncorrelated_equality_subqueries NULL NULL NULL NULL NULL
34183420
optimizer_merge_joins_enabled NULL NULL NULL NULL NULL
3421+
optimizer_prefer_bounded_cardinality NULL NULL NULL NULL NULL
34193422
optimizer_prove_implication_with_virtual_computed_columns NULL NULL NULL NULL NULL
34203423
optimizer_push_limit_into_project_filtered_scan NULL NULL NULL NULL NULL
34213424
optimizer_push_offset_into_index_join 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
@@ -138,6 +138,7 @@ opt_split_scan_limit 2048
138138
optimizer_always_use_histograms on
139139
optimizer_hoist_uncorrelated_equality_subqueries on
140140
optimizer_merge_joins_enabled on
141+
optimizer_prefer_bounded_cardinality off
141142
optimizer_prove_implication_with_virtual_computed_columns on
142143
optimizer_push_limit_into_project_filtered_scan on
143144
optimizer_push_offset_into_index_join on

pkg/sql/opt/memo/cost.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ type Cost struct {
1919
// group members during testing, by setting their cost so high that any other
2020
// member will have a lower cost.
2121
var MaxCost = Cost{
22-
C: math.Inf(+1),
23-
Flags: CostFlags{FullScanPenalty: true, HugeCostPenalty: true},
22+
C: math.Inf(+1),
23+
Flags: CostFlags{
24+
FullScanPenalty: true,
25+
HugeCostPenalty: true,
26+
UnboundedCardinality: true,
27+
},
2428
}
2529

2630
// Less returns true if this cost is lower than the given cost.
@@ -57,6 +61,10 @@ type CostFlags struct {
5761
// used when the optimizer is forced to use a particular plan, and will error
5862
// if it cannot be used.
5963
HugeCostPenalty bool
64+
// UnboundedCardinality is true if the operator or any of its descendants
65+
// have no guaranteed upperbound on the number of rows that they can
66+
// produce. See props.AnyCardinality.
67+
UnboundedCardinality bool
6068
}
6169

6270
// Less returns true if these flags indicate a lower penalty than the other
@@ -71,16 +79,20 @@ func (c CostFlags) Less(other CostFlags) bool {
7179
if c.FullScanPenalty != other.FullScanPenalty {
7280
return !c.FullScanPenalty
7381
}
82+
if c.UnboundedCardinality != other.UnboundedCardinality {
83+
return !c.UnboundedCardinality
84+
}
7485
return false
7586
}
7687

7788
// Add adds the other flags to these flags.
7889
func (c *CostFlags) Add(other CostFlags) {
7990
c.FullScanPenalty = c.FullScanPenalty || other.FullScanPenalty
8091
c.HugeCostPenalty = c.HugeCostPenalty || other.HugeCostPenalty
92+
c.UnboundedCardinality = c.UnboundedCardinality || other.UnboundedCardinality
8193
}
8294

8395
// Empty returns true if these flags are empty.
8496
func (c CostFlags) Empty() bool {
85-
return !c.FullScanPenalty && !c.HugeCostPenalty
97+
return !c.FullScanPenalty && !c.HugeCostPenalty && !c.UnboundedCardinality
8698
}

pkg/sql/opt/memo/cost_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ func TestCostLess(t *testing.T) {
3535
{memo.MaxCost, memo.MaxCost, false},
3636
{memo.MaxCost, memo.Cost{C: 1.0, Flags: memo.CostFlags{FullScanPenalty: true}}, false},
3737
{memo.Cost{C: 1.0, Flags: memo.CostFlags{HugeCostPenalty: true}}, memo.MaxCost, true},
38+
{memo.Cost{C: 2.0, Flags: memo.CostFlags{}}, memo.Cost{C: 1.0, Flags: memo.CostFlags{UnboundedCardinality: true}}, true},
39+
{memo.Cost{C: 1.0, Flags: memo.CostFlags{UnboundedCardinality: true}}, memo.Cost{C: 2.0, Flags: memo.CostFlags{}}, false},
3840
}
3941
for _, tc := range testCases {
4042
if tc.left.Less(tc.right) != tc.expected {
@@ -72,6 +74,8 @@ func TestCostFlagsLess(t *testing.T) {
7274
{memo.CostFlags{FullScanPenalty: true, HugeCostPenalty: true}, memo.CostFlags{FullScanPenalty: true, HugeCostPenalty: true}, false},
7375
{memo.CostFlags{FullScanPenalty: false}, memo.CostFlags{FullScanPenalty: true}, true},
7476
{memo.CostFlags{HugeCostPenalty: false}, memo.CostFlags{HugeCostPenalty: true}, true},
77+
{memo.CostFlags{UnboundedCardinality: false}, memo.CostFlags{UnboundedCardinality: true}, true},
78+
{memo.CostFlags{UnboundedCardinality: true}, memo.CostFlags{UnboundedCardinality: false}, false},
7579
}
7680
for _, tc := range testCases {
7781
if tc.left.Less(tc.right) != tc.expected {

pkg/sql/opt/memo/expr_format.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,9 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) {
913913
if cost.Flags.HugeCostPenalty {
914914
b.WriteString(" huge-cost-penalty")
915915
}
916+
if cost.Flags.UnboundedCardinality {
917+
b.WriteString(" unbounded-cardinality")
918+
}
916919
tp.Child(b.String())
917920
}
918921
}

pkg/sql/opt/memo/memo.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ type Memo struct {
200200
pushLimitIntoProjectFilteredScan bool
201201
unsafeAllowTriggersModifyingCascades bool
202202
legacyVarcharTyping bool
203+
preferBoundedCardinality bool
203204
internal bool
204205

205206
// txnIsoLevel is the isolation level under which the plan was created. This
@@ -293,6 +294,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
293294
pushLimitIntoProjectFilteredScan: evalCtx.SessionData().OptimizerPushLimitIntoProjectFilteredScan,
294295
unsafeAllowTriggersModifyingCascades: evalCtx.SessionData().UnsafeAllowTriggersModifyingCascades,
295296
legacyVarcharTyping: evalCtx.SessionData().LegacyVarcharTyping,
297+
preferBoundedCardinality: evalCtx.SessionData().OptimizerPreferBoundedCardinality,
296298
internal: evalCtx.SessionData().Internal,
297299
txnIsoLevel: evalCtx.TxnIsoLevel,
298300
}
@@ -463,6 +465,7 @@ func (m *Memo) IsStale(
463465
m.pushLimitIntoProjectFilteredScan != evalCtx.SessionData().OptimizerPushLimitIntoProjectFilteredScan ||
464466
m.unsafeAllowTriggersModifyingCascades != evalCtx.SessionData().UnsafeAllowTriggersModifyingCascades ||
465467
m.legacyVarcharTyping != evalCtx.SessionData().LegacyVarcharTyping ||
468+
m.preferBoundedCardinality != evalCtx.SessionData().OptimizerPreferBoundedCardinality ||
466469
m.internal != evalCtx.SessionData().Internal ||
467470
m.txnIsoLevel != evalCtx.TxnIsoLevel {
468471
return true, nil

pkg/sql/opt/memo/memo_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,11 @@ func TestMemoIsStale(t *testing.T) {
538538
evalCtx.SessionData().LegacyVarcharTyping = false
539539
notStale()
540540

541+
evalCtx.SessionData().OptimizerPreferBoundedCardinality = true
542+
stale()
543+
evalCtx.SessionData().OptimizerPreferBoundedCardinality = false
544+
notStale()
545+
541546
evalCtx.SessionData().Internal = true
542547
stale()
543548
evalCtx.SessionData().Internal = false

pkg/sql/opt/xform/coster.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,8 +625,13 @@ func (c *coster) ComputeCost(candidate memo.RelExpr, required *physical.Required
625625
// Add a one-time cost for any operator with unbounded cardinality. This
626626
// ensures we prefer plans that push limits as far down the tree as possible,
627627
// all else being equal.
628+
//
629+
// Also add a cost flag for unbounded cardinality.
628630
if candidate.Relational().Cardinality.IsUnbounded() {
629631
cost.C += cpuCostFactor
632+
if c.evalCtx.SessionData().OptimizerPreferBoundedCardinality {
633+
cost.Flags.UnboundedCardinality = true
634+
}
630635
}
631636

632637
if !cost.Less(memo.MaxCost) {

0 commit comments

Comments
 (0)