Skip to content

Commit

Permalink
opt: add optimizer_min_row_count session setting
Browse files Browse the repository at this point in the history
Informs cockroachdb#64570
Informs cockroachdb#130201

Release note (sql change): The `optimizer_min_row_count` session setting
has been added which sets a lower bound on row count estimates for
relational expressions during query planning. A value of zero, which is
the default, indicates no lower bound. Note that if this is set to a
value greater than zero, a row count of zero can still be estimated for
expressions with a cardinality of zero, e.g., for a contradictory
filter. Setting this to a value higher than 0, such as 1, may yield
better query plans in some cases, such as when statistics are frequently
stale and inaccurate.
  • Loading branch information
mgartner committed Jan 31, 2025
1 parent 64bad8e commit 526e15f
Show file tree
Hide file tree
Showing 20 changed files with 313 additions and 70 deletions.
4 changes: 4 additions & 0 deletions pkg/sql/exec_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4011,6 +4011,10 @@ func (m *sessionDataMutator) SetOptimizerPreferBoundedCardinality(b bool) {
m.data.OptimizerPreferBoundedCardinality = b
}

func (m *sessionDataMutator) SetOptimizerMinRowCount(val float64) {
m.data.OptimizerMinRowCount = val
}

// 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_min_row_count 0
optimizer_prefer_bounded_cardinality off
optimizer_prove_implication_with_virtual_computed_columns on
optimizer_push_limit_into_project_filtered_scan 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_min_row_count 0 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
Expand Down Expand Up @@ -3213,6 +3214,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_min_row_count 0 NULL user NULL 0 0
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
Expand Down Expand Up @@ -3418,6 +3420,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_min_row_count 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
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_min_row_count 0
optimizer_prefer_bounded_cardinality off
optimizer_prove_implication_with_virtual_computed_columns on
optimizer_push_limit_into_project_filtered_scan on
Expand Down
10 changes: 5 additions & 5 deletions pkg/sql/opt/exec/execbuilder/testdata/explain_redact
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ upsert bc
query T
EXPLAIN (OPT, MEMO, REDACT) INSERT INTO bc SELECT a::float + 1 FROM a ON CONFLICT (b) DO UPDATE SET b = bc.b + 100
----
memo (optimized, ~36KB, required=[presentation: info:25] [distribution: test])
memo (optimized, ~37KB, required=[presentation: info:25] [distribution: test])
├── G1: (explain G2 [distribution: test])
│ └── [presentation: info:25] [distribution: test]
│ ├── best: (explain G2="[distribution: test]" [distribution: test])
Expand Down Expand Up @@ -1117,7 +1117,7 @@ update ab
query T
EXPLAIN (OPT, MEMO, REDACT) UPDATE ab SET a = a || 'ab' WHERE a > 'a'
----
memo (optimized, ~14KB, required=[presentation: info:15] [distribution: test])
memo (optimized, ~15KB, required=[presentation: info:15] [distribution: test])
├── G1: (explain G2 [distribution: test])
│ └── [presentation: info:15] [distribution: test]
│ ├── best: (explain G2="[distribution: test]" [distribution: test])
Expand Down Expand Up @@ -1355,7 +1355,7 @@ update e
query T
EXPLAIN (OPT, MEMO, REDACT) UPDATE e SET e = 'eee' WHERE e > 'a'
----
memo (optimized, ~17KB, required=[presentation: info:17] [distribution: test])
memo (optimized, ~18KB, required=[presentation: info:17] [distribution: test])
├── G1: (explain G2 [distribution: test])
│ └── [presentation: info:17] [distribution: test]
│ ├── best: (explain G2="[distribution: test]" [distribution: test])
Expand Down Expand Up @@ -1686,7 +1686,7 @@ project
query T
EXPLAIN (OPT, MEMO, REDACT) SELECT * FROM bc WHERE b >= 1.0 AND b < 2.0
----
memo (optimized, ~12KB, required=[presentation: info:7] [distribution: test])
memo (optimized, ~13KB, required=[presentation: info:7] [distribution: test])
├── G1: (explain G2 [presentation: b:1,c:2] [distribution: test])
│ └── [presentation: info:7] [distribution: test]
│ ├── best: (explain G2="[presentation: b:1,c:2] [distribution: test]" [presentation: b:1,c:2] [distribution: test])
Expand Down Expand Up @@ -2435,7 +2435,7 @@ project
query T
EXPLAIN (OPT, MEMO, REDACT) SELECT * FROM bc JOIN f ON b = f + 1
----
memo (optimized, ~28KB, required=[presentation: info:14] [distribution: test])
memo (optimized, ~29KB, required=[presentation: info:14] [distribution: test])
├── G1: (explain G2 [presentation: b:1,c:2,f:7] [distribution: test])
│ └── [presentation: info:14] [distribution: test]
│ ├── best: (explain G2="[presentation: b:1,c:2,f:7] [distribution: test]" [presentation: b:1,c:2,f:7] [distribution: test])
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 @@ -201,6 +201,7 @@ type Memo struct {
unsafeAllowTriggersModifyingCascades bool
legacyVarcharTyping bool
preferBoundedCardinality bool
minRowCount float64
internal bool

// txnIsoLevel is the isolation level under which the plan was created. This
Expand Down Expand Up @@ -295,6 +296,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
unsafeAllowTriggersModifyingCascades: evalCtx.SessionData().UnsafeAllowTriggersModifyingCascades,
legacyVarcharTyping: evalCtx.SessionData().LegacyVarcharTyping,
preferBoundedCardinality: evalCtx.SessionData().OptimizerPreferBoundedCardinality,
minRowCount: evalCtx.SessionData().OptimizerMinRowCount,
internal: evalCtx.SessionData().Internal,
txnIsoLevel: evalCtx.TxnIsoLevel,
}
Expand Down Expand Up @@ -466,6 +468,7 @@ func (m *Memo) IsStale(
m.unsafeAllowTriggersModifyingCascades != evalCtx.SessionData().UnsafeAllowTriggersModifyingCascades ||
m.legacyVarcharTyping != evalCtx.SessionData().LegacyVarcharTyping ||
m.preferBoundedCardinality != evalCtx.SessionData().OptimizerPreferBoundedCardinality ||
m.minRowCount != evalCtx.SessionData().OptimizerMinRowCount ||
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 @@ -543,6 +543,11 @@ func TestMemoIsStale(t *testing.T) {
evalCtx.SessionData().OptimizerPreferBoundedCardinality = false
notStale()

evalCtx.SessionData().OptimizerMinRowCount = 1.0
stale()
evalCtx.SessionData().OptimizerMinRowCount = 0
notStale()

evalCtx.SessionData().Internal = true
stale()
evalCtx.SessionData().Internal = false
Expand Down
62 changes: 32 additions & 30 deletions pkg/sql/opt/memo/statistics_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,18 +228,20 @@ const (
//
// See props/statistics.go for more details.
type statisticsBuilder struct {
ctx context.Context
evalCtx *eval.Context
md *opt.Metadata
ctx context.Context
evalCtx *eval.Context
md *opt.Metadata
minRowCount float64
}

func (sb *statisticsBuilder) init(ctx context.Context, evalCtx *eval.Context, md *opt.Metadata) {
// This initialization pattern ensures that fields are not unwittingly
// reused. Field reuse must be explicit.
*sb = statisticsBuilder{
ctx: ctx,
evalCtx: evalCtx,
md: md,
ctx: ctx,
evalCtx: evalCtx,
md: md,
minRowCount: evalCtx.SessionData().OptimizerMinRowCount,
}
}

Expand Down Expand Up @@ -836,7 +838,7 @@ func (sb *statisticsBuilder) colAvgSize(tabID opt.TableID, col opt.ColumnID) uin

func (sb *statisticsBuilder) buildScan(scan *ScanExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -1085,7 +1087,7 @@ func (sb *statisticsBuilder) colStatScan(colSet opt.ColSet, scan *ScanExpr) *pro

func (sb *statisticsBuilder) buildSelect(sel *SelectExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -1126,7 +1128,7 @@ func (sb *statisticsBuilder) colStatSelect(

func (sb *statisticsBuilder) buildProject(prj *ProjectExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -1228,7 +1230,7 @@ func (sb *statisticsBuilder) buildInvertedFilter(
invFilter *InvertedFilterExpr, relProps *props.Relational,
) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -1289,7 +1291,7 @@ func (sb *statisticsBuilder) buildJoin(
}

s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -1820,7 +1822,7 @@ func (sb *statisticsBuilder) colStatFromJoinRight(

func (sb *statisticsBuilder) buildIndexJoin(indexJoin *IndexJoinExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -1901,7 +1903,7 @@ func (sb *statisticsBuilder) buildZigzagJoin(
zigzag *ZigzagJoinExpr, relProps *props.Relational, h *joinPropsHelper,
) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2002,7 +2004,7 @@ func (sb *statisticsBuilder) buildZigzagJoin(

func (sb *statisticsBuilder) buildGroupBy(groupNode RelExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2114,7 +2116,7 @@ func (sb *statisticsBuilder) colStatGroupBy(

func (sb *statisticsBuilder) buildSetNode(setNode RelExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2219,7 +2221,7 @@ func (sb *statisticsBuilder) colStatSetNodeImpl(
// buildValues builds the statistics for a VALUES expression.
func (sb *statisticsBuilder) buildValues(values ValuesContainer, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2324,7 +2326,7 @@ func (sb *statisticsBuilder) colStatLiteralValues(

func (sb *statisticsBuilder) buildLimit(limit *LimitExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2367,7 +2369,7 @@ func (sb *statisticsBuilder) colStatLimit(

func (sb *statisticsBuilder) buildTopK(topK *TopKExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2409,7 +2411,7 @@ func (sb *statisticsBuilder) colStatTopK(colSet opt.ColSet, topK *TopKExpr) *pro

func (sb *statisticsBuilder) buildOffset(offset *OffsetExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2462,7 +2464,7 @@ func (sb *statisticsBuilder) colStatOffset(

func (sb *statisticsBuilder) buildMax1Row(max1Row *Max1RowExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2492,7 +2494,7 @@ func (sb *statisticsBuilder) colStatMax1Row(

func (sb *statisticsBuilder) buildOrdinality(ord *OrdinalityExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2537,7 +2539,7 @@ func (sb *statisticsBuilder) colStatOrdinality(

func (sb *statisticsBuilder) buildWindow(window *WindowExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2605,7 +2607,7 @@ func (sb *statisticsBuilder) buildProjectSet(
projectSet *ProjectSetExpr, relProps *props.Relational,
) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2738,7 +2740,7 @@ func (sb *statisticsBuilder) buildWithScan(
withScan *WithScanExpr, relProps, bindingProps *props.Relational,
) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2780,7 +2782,7 @@ func (sb *statisticsBuilder) colStatWithScan(

func (sb *statisticsBuilder) buildMutation(mutation RelExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2818,7 +2820,7 @@ func (sb *statisticsBuilder) colStatMutation(

func (sb *statisticsBuilder) buildLock(lock *LockExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2852,7 +2854,7 @@ func (sb *statisticsBuilder) buildVectorSearch(
search *VectorSearchExpr, relProps *props.Relational,
) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2909,7 +2911,7 @@ func (sb *statisticsBuilder) buildVectorPartitionSearch(
search *VectorPartitionSearchExpr, relProps *props.Relational,
) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2944,7 +2946,7 @@ func (sb *statisticsBuilder) colStatVectorPartitionSearch(

func (sb *statisticsBuilder) buildBarrier(barrier *BarrierExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short cut if cardinality is 0.
return
}
Expand Down Expand Up @@ -2978,7 +2980,7 @@ func (sb *statisticsBuilder) colStatBarrier(

func (sb *statisticsBuilder) buildCall(call *CallExpr, relProps *props.Relational) {
s := relProps.Statistics()
if zeroCardinality := s.Init(relProps); zeroCardinality {
if zeroCardinality := s.Init(relProps, sb.minRowCount); zeroCardinality {
// Short-cut if cardinality is 0.
return
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/sql/opt/memo/statistics_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ func TestGetStatsFromConstraint(t *testing.T) {
relProps := &props.Relational{Cardinality: props.AnyCardinality}
relProps.NotNullCols = cs.ExtractNotNullCols(ctx, &evalCtx)
s := relProps.Statistics()
s.Init(relProps)
const minRowCount = 0
s.Init(relProps, minRowCount)

// Calculate distinct counts.
sb.applyConstraintSet(cs, true /* tight */, sel, relProps, relProps.Statistics())
Expand Down
Loading

0 comments on commit 526e15f

Please sign in to comment.