Skip to content

Commit

Permalink
opt: add optimizer_prefer_bounded_cardinality session setting
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mgartner committed Jan 31, 2025
1 parent bdb558b commit 64bad8e
Show file tree
Hide file tree
Showing 14 changed files with 555 additions and 3 deletions.
4 changes: 4 additions & 0 deletions pkg/sql/exec_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4007,6 +4007,10 @@ func (m *sessionDataMutator) SetCatalogDigestStalenessCheckEnabled(b bool) {
m.data.CatalogDigestStalenessCheckEnabled = b
}

func (m *sessionDataMutator) SetOptimizerPreferBoundedCardinality(b bool) {
m.data.OptimizerPreferBoundedCardinality = b
}

// Utility functions related to scrubbing sensitive information on SQL Stats.

// quantizeCounts ensures that the Count field in the
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/logictest/testdata/logic_test/information_schema
Original file line number Diff line number Diff line change
Expand Up @@ -4007,6 +4007,7 @@ optimizer on
optimizer_always_use_histograms on
optimizer_hoist_uncorrelated_equality_subqueries on
optimizer_merge_joins_enabled on
optimizer_prefer_bounded_cardinality off
optimizer_prove_implication_with_virtual_computed_columns on
optimizer_push_limit_into_project_filtered_scan on
optimizer_push_offset_into_index_join on
Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/pg_catalog
Original file line number Diff line number Diff line change
Expand Up @@ -3007,6 +3007,7 @@ opt_split_scan_limit 2048 N
optimizer_always_use_histograms on NULL NULL NULL string
optimizer_hoist_uncorrelated_equality_subqueries on NULL NULL NULL string
optimizer_merge_joins_enabled on NULL NULL NULL string
optimizer_prefer_bounded_cardinality off NULL NULL NULL string
optimizer_prove_implication_with_virtual_computed_columns on NULL NULL NULL string
optimizer_push_limit_into_project_filtered_scan on NULL NULL NULL string
optimizer_push_offset_into_index_join on NULL NULL NULL string
Expand Down Expand Up @@ -3212,6 +3213,7 @@ opt_split_scan_limit 2048 N
optimizer_always_use_histograms on NULL user NULL on on
optimizer_hoist_uncorrelated_equality_subqueries on NULL user NULL on on
optimizer_merge_joins_enabled on NULL user NULL on on
optimizer_prefer_bounded_cardinality off NULL user NULL off off
optimizer_prove_implication_with_virtual_computed_columns on NULL user NULL on on
optimizer_push_limit_into_project_filtered_scan on NULL user NULL on on
optimizer_push_offset_into_index_join on NULL user NULL on on
Expand Down Expand Up @@ -3416,6 +3418,7 @@ optimizer NULL NULL NULL
optimizer_always_use_histograms NULL NULL NULL NULL NULL
optimizer_hoist_uncorrelated_equality_subqueries NULL NULL NULL NULL NULL
optimizer_merge_joins_enabled NULL NULL NULL NULL NULL
optimizer_prefer_bounded_cardinality NULL NULL NULL NULL NULL
optimizer_prove_implication_with_virtual_computed_columns NULL NULL NULL NULL NULL
optimizer_push_limit_into_project_filtered_scan NULL NULL NULL NULL NULL
optimizer_push_offset_into_index_join NULL NULL NULL NULL NULL
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/logictest/testdata/logic_test/show_source
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ opt_split_scan_limit 2048
optimizer_always_use_histograms on
optimizer_hoist_uncorrelated_equality_subqueries on
optimizer_merge_joins_enabled on
optimizer_prefer_bounded_cardinality off
optimizer_prove_implication_with_virtual_computed_columns on
optimizer_push_limit_into_project_filtered_scan on
optimizer_push_offset_into_index_join on
Expand Down
18 changes: 15 additions & 3 deletions pkg/sql/opt/memo/cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ type Cost struct {
// group members during testing, by setting their cost so high that any other
// member will have a lower cost.
var MaxCost = Cost{
C: math.Inf(+1),
Flags: CostFlags{FullScanPenalty: true, HugeCostPenalty: true},
C: math.Inf(+1),
Flags: CostFlags{
FullScanPenalty: true,
HugeCostPenalty: true,
UnboundedCardinality: true,
},
}

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

// Less returns true if these flags indicate a lower penalty than the other
Expand All @@ -71,16 +79,20 @@ func (c CostFlags) Less(other CostFlags) bool {
if c.FullScanPenalty != other.FullScanPenalty {
return !c.FullScanPenalty
}
if c.UnboundedCardinality != other.UnboundedCardinality {
return !c.UnboundedCardinality
}
return false
}

// Add adds the other flags to these flags.
func (c *CostFlags) Add(other CostFlags) {
c.FullScanPenalty = c.FullScanPenalty || other.FullScanPenalty
c.HugeCostPenalty = c.HugeCostPenalty || other.HugeCostPenalty
c.UnboundedCardinality = c.UnboundedCardinality || other.UnboundedCardinality
}

// Empty returns true if these flags are empty.
func (c CostFlags) Empty() bool {
return !c.FullScanPenalty && !c.HugeCostPenalty
return !c.FullScanPenalty && !c.HugeCostPenalty && !c.UnboundedCardinality
}
4 changes: 4 additions & 0 deletions pkg/sql/opt/memo/cost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ func TestCostLess(t *testing.T) {
{memo.MaxCost, memo.MaxCost, false},
{memo.MaxCost, memo.Cost{C: 1.0, Flags: memo.CostFlags{FullScanPenalty: true}}, false},
{memo.Cost{C: 1.0, Flags: memo.CostFlags{HugeCostPenalty: true}}, memo.MaxCost, true},
{memo.Cost{C: 2.0, Flags: memo.CostFlags{}}, memo.Cost{C: 1.0, Flags: memo.CostFlags{UnboundedCardinality: true}}, true},
{memo.Cost{C: 1.0, Flags: memo.CostFlags{UnboundedCardinality: true}}, memo.Cost{C: 2.0, Flags: memo.CostFlags{}}, false},
}
for _, tc := range testCases {
if tc.left.Less(tc.right) != tc.expected {
Expand Down Expand Up @@ -72,6 +74,8 @@ func TestCostFlagsLess(t *testing.T) {
{memo.CostFlags{FullScanPenalty: true, HugeCostPenalty: true}, memo.CostFlags{FullScanPenalty: true, HugeCostPenalty: true}, false},
{memo.CostFlags{FullScanPenalty: false}, memo.CostFlags{FullScanPenalty: true}, true},
{memo.CostFlags{HugeCostPenalty: false}, memo.CostFlags{HugeCostPenalty: true}, true},
{memo.CostFlags{UnboundedCardinality: false}, memo.CostFlags{UnboundedCardinality: true}, true},
{memo.CostFlags{UnboundedCardinality: true}, memo.CostFlags{UnboundedCardinality: false}, false},
}
for _, tc := range testCases {
if tc.left.Less(tc.right) != tc.expected {
Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/opt/memo/expr_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,9 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) {
if cost.Flags.HugeCostPenalty {
b.WriteString(" huge-cost-penalty")
}
if cost.Flags.UnboundedCardinality {
b.WriteString(" unbounded-cardinality")
}
tp.Child(b.String())
}
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/opt/memo/memo.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ type Memo struct {
pushLimitIntoProjectFilteredScan bool
unsafeAllowTriggersModifyingCascades bool
legacyVarcharTyping bool
preferBoundedCardinality bool
internal bool

// txnIsoLevel is the isolation level under which the plan was created. This
Expand Down Expand Up @@ -293,6 +294,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
pushLimitIntoProjectFilteredScan: evalCtx.SessionData().OptimizerPushLimitIntoProjectFilteredScan,
unsafeAllowTriggersModifyingCascades: evalCtx.SessionData().UnsafeAllowTriggersModifyingCascades,
legacyVarcharTyping: evalCtx.SessionData().LegacyVarcharTyping,
preferBoundedCardinality: evalCtx.SessionData().OptimizerPreferBoundedCardinality,
internal: evalCtx.SessionData().Internal,
txnIsoLevel: evalCtx.TxnIsoLevel,
}
Expand Down Expand Up @@ -463,6 +465,7 @@ func (m *Memo) IsStale(
m.pushLimitIntoProjectFilteredScan != evalCtx.SessionData().OptimizerPushLimitIntoProjectFilteredScan ||
m.unsafeAllowTriggersModifyingCascades != evalCtx.SessionData().UnsafeAllowTriggersModifyingCascades ||
m.legacyVarcharTyping != evalCtx.SessionData().LegacyVarcharTyping ||
m.preferBoundedCardinality != evalCtx.SessionData().OptimizerPreferBoundedCardinality ||
m.internal != evalCtx.SessionData().Internal ||
m.txnIsoLevel != evalCtx.TxnIsoLevel {
return true, nil
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/opt/memo/memo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,11 @@ func TestMemoIsStale(t *testing.T) {
evalCtx.SessionData().LegacyVarcharTyping = false
notStale()

evalCtx.SessionData().OptimizerPreferBoundedCardinality = true
stale()
evalCtx.SessionData().OptimizerPreferBoundedCardinality = false
notStale()

evalCtx.SessionData().Internal = true
stale()
evalCtx.SessionData().Internal = false
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/opt/xform/coster.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,8 +625,13 @@ func (c *coster) ComputeCost(candidate memo.RelExpr, required *physical.Required
// Add a one-time cost for any operator with unbounded cardinality. This
// ensures we prefer plans that push limits as far down the tree as possible,
// all else being equal.
//
// Also add a cost flag for unbounded cardinality.
if candidate.Relational().Cardinality.IsUnbounded() {
cost.C += cpuCostFactor
if c.evalCtx.SessionData().OptimizerPreferBoundedCardinality {
cost.Flags.UnboundedCardinality = true
}
}

if !cost.Less(memo.MaxCost) {
Expand Down
Loading

0 comments on commit 64bad8e

Please sign in to comment.