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 f28b341 commit 5918578
Show file tree
Hide file tree
Showing 14 changed files with 554 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 @@ -3851,6 +3851,10 @@ func (m *sessionDataMutator) SetLegacyVarcharTyping(val bool) {
m.data.LegacyVarcharTyping = val
}

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 @@ -6172,6 +6172,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 off
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 @@ -2947,6 +2947,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 off NULL NULL NULL string
optimizer_push_offset_into_index_join on NULL NULL NULL string
Expand Down Expand Up @@ -3138,6 +3139,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 off NULL user NULL off off
optimizer_push_offset_into_index_join on NULL user NULL on on
Expand Down Expand Up @@ -3328,6 +3330,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 @@ -128,6 +128,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 off
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 @@ -861,6 +861,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 @@ -196,6 +196,7 @@ type Memo struct {
usePolymorphicParameterFix bool
pushLimitIntoProjectFilteredScan bool
legacyVarcharTyping bool
preferBoundedCardinality bool

// txnIsoLevel is the isolation level under which the plan was created. This
// affects the planning of some locking operations, so it must be included in
Expand Down Expand Up @@ -283,6 +284,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
usePolymorphicParameterFix: evalCtx.SessionData().OptimizerUsePolymorphicParameterFix,
pushLimitIntoProjectFilteredScan: evalCtx.SessionData().OptimizerPushLimitIntoProjectFilteredScan,
legacyVarcharTyping: evalCtx.SessionData().LegacyVarcharTyping,
preferBoundedCardinality: evalCtx.SessionData().OptimizerPreferBoundedCardinality,
txnIsoLevel: evalCtx.TxnIsoLevel,
}
m.metadata.Init()
Expand Down Expand Up @@ -448,6 +450,7 @@ func (m *Memo) IsStale(
m.usePolymorphicParameterFix != evalCtx.SessionData().OptimizerUsePolymorphicParameterFix ||
m.pushLimitIntoProjectFilteredScan != evalCtx.SessionData().OptimizerPushLimitIntoProjectFilteredScan ||
m.legacyVarcharTyping != evalCtx.SessionData().LegacyVarcharTyping ||
m.preferBoundedCardinality != evalCtx.SessionData().OptimizerPreferBoundedCardinality ||
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 @@ -507,6 +507,11 @@ func TestMemoIsStale(t *testing.T) {
evalCtx.SessionData().LegacyVarcharTyping = false
notStale()

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

// User no longer has access to view.
catalog.View(tree.NewTableNameWithSchema("t", catconstants.PublicSchemaName, "abcview")).Revoked = true
_, err = o.Memo().IsStale(ctx, &evalCtx, catalog)
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 @@ -619,8 +619,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 5918578

Please sign in to comment.