From ebfab1067bd7d73a3f65ef9239190df5bd531e88 Mon Sep 17 00:00:00 2001 From: Rebecca Taft Date: Mon, 25 Jul 2022 10:20:43 -0500 Subject: [PATCH] opt: fix error due to selectivity is NaN This commit fixes the error "selectivity is NaN" by avoiding division by zero. Fixes #84478 There is no release note since this does not manifest as an error in any existing releases (we have made our checking of valid selectivity more strict on master). Release note: None --- pkg/sql/opt/memo/statistics_builder.go | 2 +- pkg/sql/opt/memo/testdata/stats/select | 68 ++++++++++++++++++++++++++ pkg/sql/opt/props/histogram.go | 3 ++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/pkg/sql/opt/memo/statistics_builder.go b/pkg/sql/opt/memo/statistics_builder.go index 93ce220dbe92..70dd43782cc7 100644 --- a/pkg/sql/opt/memo/statistics_builder.go +++ b/pkg/sql/opt/memo/statistics_builder.go @@ -976,7 +976,7 @@ func (sb *statisticsBuilder) colStatSelect( // filter conditions were pushed down into the input after s.Selectivity // was calculated. For example, an index scan or index join created during // exploration could absorb some of the filter conditions. - selectivity := props.MakeSelectivity(s.RowCount / inputStats.RowCount) + selectivity := props.MakeSelectivityFromFraction(s.RowCount, inputStats.RowCount) colStat.ApplySelectivity(selectivity, inputStats.RowCount) if colSet.Intersects(relProps.NotNullCols) { colStat.NullCount = 0 diff --git a/pkg/sql/opt/memo/testdata/stats/select b/pkg/sql/opt/memo/testdata/stats/select index d443b6d27d07..94dd003800a8 100644 --- a/pkg/sql/opt/memo/testdata/stats/select +++ b/pkg/sql/opt/memo/testdata/stats/select @@ -3232,3 +3232,71 @@ select │ <--- 0 ------- 100000000000 └── filters └── x:1 = 10 [type=bool, outer=(1), constraints=(/1: [/10 - /10]; tight), fd=()-->(1)] + +# Regression test for #84478. Avoid error due to selectivity is NaN. +exec-ddl +CREATE TABLE t84478 ( + col0 FLOAT4, + col1 REGPROC NULL, + col2 BOX2D NOT NULL, + col3 TIMESTAMP NULL, + col4 FLOAT8 AS (col0 + NULL) VIRTUAL, + UNIQUE (col0) STORING (col1, col2, col3) +) +---- + +norm +SELECT + NULL, 1 +FROM + t84478 AS t1 + JOIN t84478 AS t2 + JOIN t84478 AS t3 ON + (t2.col4) = (t3.col0) + AND (t2.col3) = (t3.col3) + AND (t2.col2) = (t3.col2) ON (t1.tableoid) = (t2.col1) + RIGHT JOIN t84478 AS t4 ON + isnan(t1.crdb_internal_mvcc_timestamp::DECIMAL)::BOOL +GROUP BY + t1.col2 +---- +project + ├── columns: "?column?":33(unknown) "?column?":34(int!null) + ├── immutable + ├── stats: [rows=1] + ├── fd: ()-->(33,34) + ├── distinct-on + │ ├── columns: col2:3(box2d) + │ ├── grouping columns: col2:3(box2d) + │ ├── immutable + │ ├── stats: [rows=1, distinct(3)=1, null(3)=1, avgsize(3)=0] + │ ├── key: (3) + │ └── left-join (cross) + │ ├── columns: col2:3(box2d) crdb_internal_mvcc_timestamp:7(decimal) + │ ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more) + │ ├── immutable + │ ├── stats: [rows=1000, distinct(3)=1, null(3)=1000, avgsize(3)=0] + │ ├── scan t84478 + │ │ ├── computed column expressions + │ │ │ └── col4:29 + │ │ │ └── CAST(NULL AS FLOAT8) [type=float] + │ │ └── stats: [rows=1000] + │ ├── select + │ │ ├── columns: col2:3(box2d!null) crdb_internal_mvcc_timestamp:7(decimal!null) + │ │ ├── cardinality: [0 - 0] + │ │ ├── immutable + │ │ ├── stats: [rows=0, distinct(3)=0, null(3)=0, avgsize(3)=0] + │ │ ├── key: () + │ │ ├── fd: ()-->(3,7) + │ │ ├── values + │ │ │ ├── columns: col2:3(box2d!null) crdb_internal_mvcc_timestamp:7(decimal!null) + │ │ │ ├── cardinality: [0 - 0] + │ │ │ ├── stats: [rows=0, distinct(3)=0, null(3)=0, avgsize(3)=0] + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(3,7) + │ │ └── filters + │ │ └── isnan(crdb_internal_mvcc_timestamp:7) [type=bool, outer=(7), immutable, constraints=(/7: (/NULL - ])] + │ └── filters (true) + └── projections + ├── NULL [as="?column?":33, type=unknown] + └── 1 [as="?column?":34, type=int] diff --git a/pkg/sql/opt/props/histogram.go b/pkg/sql/opt/props/histogram.go index 0fff08312bdd..13be0a07ecf0 100644 --- a/pkg/sql/opt/props/histogram.go +++ b/pkg/sql/opt/props/histogram.go @@ -434,6 +434,9 @@ func (h *Histogram) addBucket(bucket *cat.HistogramBucket, desc bool) { // ApplySelectivity reduces the size of each histogram bucket according to // the given selectivity, and returns a new histogram with the results. func (h *Histogram) ApplySelectivity(selectivity Selectivity) *Histogram { + if selectivity == ZeroSelectivity { + return nil + } res := h.copy() for i := range res.buckets { b := &res.buckets[i]