Repository: pingcap/tidb
File: pkg/statistics/histogram.go (Go)

Code before:
			addedRowMultiplier = skewRatio
		}
		estRows = (addedRows * addedRowMultiplier) * totalPercent
	}

	// We may have missed the true lowest/highest values due to sampling OR there could be a delay in
	// updates to modifyCount (meaning modifyCount is incorrectly set to 0). So ensure we always
	// account for at least 1% of the total row count as a worst case for "addedRows".
	// We inflate this here so ONLY to impact the MaxEst value.
	if modifyCount == 0 || addedRows == 0 {
		if realtimeRowCount <= 0 {
			realtimeRowCount = int64(hg.TotalRowCount())
		}
		// Use outOfRangeBetweenRate as a divisor to get a small percentage of the approximate
		// modifyCount (since outOfRangeBetweenRate has a default value of 100).
		addedRows = max(addedRows, float64(realtimeRowCount)/outOfRangeBetweenRate)
	}
	maxAddedRows := addedRows
	if maxTotalPercent > 0 {
		// Always apply maxTotalPercent to maxAddedRows (matching old behavior where addedRows was always scaled)
		maxAddedRows *= maxTotalPercent
	}

	minEst := min(estRows, oneValue)
	if skewRatio > 0 {
		result = CalculateSkewRatioCounts(minEst, maxAddedRows, skewRatio)
	}
	result.Est = max(result.Est, oneValue)
	result.MaxEst = max(result.Est, maxAddedRows)

	return result
}

// Copy deep copies the histogram.
func (hg *Histogram) Copy() *Histogram {
	if hg == nil {
		return nil
	}
	newHist := *hg
	if hg.Bounds != nil {
		newHist.Bounds = hg.Bounds.CopyConstruct()
	}
	newHist.Buckets = make([]Bucket, 0, len(hg.Buckets))
	newHist.Buckets = append(newHist.Buckets, hg.Buckets...)
	return &newHist
}

// TruncateHistogram truncates the histogram to `numBkt` buckets.
func (hg *Histogram) TruncateHistogram(numBkt int) *Histogram {
	hist := hg.Copy()
	hist.Buckets = hist.Buckets[:numBkt]

Review comment (suggestion):
When skewRatio is less than or equal to 0, the result variable is never initialized, causing two problems:

1. result.Est starts at 0, so line 1210 sets it to oneValue, ignoring the estRows calculation from lines 1174-1186
2. result.MinEst remains at 0, even though minEst was calculated on line 1206

The fix should initialize result before line 1210. For example:
```
if skewRatio > 0 {
    result = CalculateSkewRatioCounts(minEst, maxAddedRows, skewRatio)
} else {
    result = RowEstimate{Est: estRows, MinEst: minEst, MaxEst: maxAddedRows}
}
```
Then lines 1210-1211 would apply the necessary bounds to ensure Est >= oneValue and MaxEst >= Est.
```suggestion
		result = CalculateSkewRatioCounts(minEst, maxAddedRows, skewRatio)
	} else {
		result = RowEstimate{Est: estRows, MinEst: minEst, MaxEst: maxAddedRows}
```

Code after:
	}
	if math.IsInf(histWidth, 1) {
		histWidth = 0
	}
	boundL := histL - histWidth
	boundR := histR + histWidth

	// Step 7: Calculate the width of the predicate
	// TODO: If predWidth == 0, it may be because the common prefix is too large.
	// In future - out of range for equal predicates should also use this logic
	// for consistency. We need to handle both "equal" and "large common prefix".
	predWidth := r - l
	if predWidth < 0 {
		// This should never happen.
		intest.Assert(false, "Right bound should not be less than left bound")

		return DefaultRowEst(0)
	} else if predWidth == 0 {
		// Set histWidth to 0 so that we can still return a minimum of oneValue,
		// and return the max as worst case.
		histWidth = 0
	}

	// Step 8: Calculate the out of range percentages
	// Calculate left overlap percentage if the range overlaps with (boundL, histL)
	leftPercent := calculateLeftOverlapPercent(l, r, boundL, histL, histWidth)
	// Calculate right overlap percentage if the range overlaps with (histR, boundR)
	rightPercent := calculateRightOverlapPercent(l, r, histR, boundR, histWidth)

	totalPercent := min(leftPercent*0.5+rightPercent*0.5, 1.0)
	maxTotalPercent := min(leftPercent+rightPercent, 1.0)

	// Step 9: Calculate the added rows
	// Use absolute value to account for the case where rows may have been added on one side,
	// but deleted from the other, resulting in qualifying out of range rows even though
	// realtimeRowCount is less than histogram count
	addedRows := hg.AbsRowCountDifference(realtimeRowCount)
	maxAddedRows := addedRows

	// Step 10: Calculate the estimated rows
	estRows := oneValue
	skewRatio := sctx.GetSessionVars().RiskRangeSkewRatio
	sctx.GetSessionVars().RecordRelevantOptVar(vardef.TiDBOptRiskRangeSkewRatio)
	if totalPercent > 0 {
		// Multiplying addedRows by 0.5 provides the assumption that 50% "addedRows" are inside
		// the histogram range, and 50% (0.5) are out-of-range. Users can adjust this
		// magic number by setting the session variable `tidb_opt_risk_range_skew_ratio`.
		// When skewRatio > 0, estRows sets the starting point for the skew ratio calculation.
		addedRowMultiplier := 0.5
		// NOTE: Skew ratio is used twice in this function.
		// This first usage allows a user to specify a ratio that is smaller or