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

Code before:
		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)
	} else {
		result.MinEst = minEst
		result.Est = estRows
	}
	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()

Review comment (security):
The comment is misleading about how tidb_opt_risk_range_skew_ratio works. When skewRatio > 0, it doesn't simply replace the 0.5 multiplier - instead, it triggers a completely different calculation method using CalculateSkewRatioCounts at line 1227. The comment should clarify that skewRatio > 0 enables a skew-aware estimation strategy that provides min/max bounds, rather than just adjusting the multiplier.
```suggestion
		// Multiplying addedRows by 0.5 encodes the default assumption that 50% of "addedRows"
		// fall inside the histogram range and 50% are out-of-range. The session variable
		// `tidb_opt_risk_range_skew_ratio` controls skew-aware out-of-range estimation; when set
		// to a positive value it provides a custom risk factor and can enable a skew-sensitive
		// estimation strategy that derives more conservative (min/max) bounds.
```

Code after:
	}

	// 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
		// larger than the default 0.5.
		if skewRatio > 0 {
			addedRowMultiplier = skewRatio
		}
		estRows = (addedRows * addedRowMultiplier) * totalPercent
	}

	// Step 11: Calculate a potential worst case for use in final MaxEst
	// 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).
		maxAddedRows = max(maxAddedRows, float64(realtimeRowCount)/outOfRangeBetweenRate)
	}
	if maxTotalPercent > 0 {