Repository: pingcap/tidb
File: pkg/planner/core/stats.go (Go)

Code before:
		}
		return nil
	}
	path.AccessConds = res.AccessConds
	path.TableFilters = res.RemainedConds
	path.EqCondCount = res.EqCondCount
	path.EqOrInCondCount = res.EqOrInCount
	path.IsDNFCond = res.IsDNFCond
	path.MinAccessCondsForDNFCond = res.MinAccessCondsForDNFCond
	path.ConstCols = make([]bool, len(path.IdxCols))
	if res.ColumnValues != nil {
		for i := range path.ConstCols {
			path.ConstCols[i] = res.ColumnValues[i] != nil
		}
	}
	indexCols := path.IdxCols
	if len(indexCols) > len(path.Index.Columns) { // remove clustered primary key if it has been added to path.IdxCols
		indexCols = indexCols[0:len(path.Index.Columns)]
	}
	count, err := cardinality.GetRowCountByIndexRanges(sctx, histColl, path.Index.ID, path.Ranges, indexCols)
	path.CountAfterAccess, path.MinCountAfterAccess, path.MaxCountAfterAccess = count.Est, count.MinEst, count.MaxEst
	return err
}

// fixTiCIIndexRangesForIntHandle fixes the TiCI index ranges for int handle.
// For normal index range, it's min value is NULL or the MinNotNull, and max value is MaxValue.
// But for TiCI index range which directly uses table's int pk range, we should set the min value to the min int value and max value to max int value.
func fixTiCIIndexRangesForIntHandle(ranges []*ranger.Range, isUnsigned bool) {
	var minDatum, maxDatum types.Datum
	// isUnsigned indicates whether the handle is unsigned.
	// Now we just cast the unsigned int to int and then store the int value inside the Datum.
	// We wrap the uint64/int64 with Datum here to keep us untouched with Datum's ineternal representation.
	if isUnsigned {
		minDatum = types.NewUintDatum(0)
		maxDatum = types.NewUintDatum(math.MaxUint64)
	} else {
		minDatum = types.NewIntDatum(math.MinInt64)
		maxDatum = types.NewIntDatum(math.MaxInt64)
	}
	for i := len(ranges) - 1; i >= 0; i-- {
		ran := ranges[i]
		if ran.HighVal[0].IsNull() {
			ranges = append(ranges[:i], ranges[i+1:]...)
			continue
		}
		if ran.LowVal[0].IsNull() || ran.LowVal[0].Kind() == types.KindMinNotNull {
			ran.LowVal[0].SetInt64(minDatum.GetInt64())
			ran.LowExclude = false
		}
		if ran.HighVal[0].Kind() == types.KindMaxValue {
			ran.HighVal[0].SetInt64(maxDatum.GetInt64())

Review comment (suggestion):
Grammar: "it's min value" should be "its min value" (possessive, not contraction).
```suggestion
// For normal index range, its min value is NULL or the MinNotNull, and max value is MaxValue.
```

Code after:
		}
		return nil
	}
	path.AccessConds = res.AccessConds
	path.TableFilters = res.RemainedConds
	path.EqCondCount = res.EqCondCount
	path.EqOrInCondCount = res.EqOrInCount
	path.IsDNFCond = res.IsDNFCond
	path.MinAccessCondsForDNFCond = res.MinAccessCondsForDNFCond
	path.ConstCols = make([]bool, len(path.IdxCols))
	if res.ColumnValues != nil {
		for i := range path.ConstCols {
			path.ConstCols[i] = res.ColumnValues[i] != nil
		}
	}
	indexCols := path.IdxCols
	if len(indexCols) > len(path.Index.Columns) { // remove clustered primary key if it has been added to path.IdxCols
		indexCols = indexCols[0:len(path.Index.Columns)]
	}
	count, err := cardinality.GetRowCountByIndexRanges(sctx, histColl, path.Index.ID, path.Ranges, indexCols)
	path.CountAfterAccess, path.MinCountAfterAccess, path.MaxCountAfterAccess = count.Est, count.MinEst, count.MaxEst
	return err
}

// fixTiCIIndexRangesForIntHandle fixes the TiCI index ranges for int handle.
// For normal index range, its min value is NULL or the MinNotNull, and max value is MaxValue.
// But for TiCI index range which directly uses table's int pk range, we should set the min value to the min int value and max value to max int value.
// And unlike the normal index, the code converting the int pk's range to the final kv.KeyRange doesn't handle the NULL.
// So after calling the function of extract normal index ranges, we need to fix the ranges for TiCI index with int handle.
func fixTiCIIndexRangesForIntHandle(ranges []*ranger.Range, isUnsigned bool) []*ranger.Range {
	var minDatum, maxDatum types.Datum
	// isUnsigned indicates whether the handle is unsigned.
	// Now we just cast the unsigned int to int and then store the int value inside the Datum.
	// We wrap the uint64/int64 with Datum here to keep us untouched with Datum's internal representation.
	if isUnsigned {
		minDatum = types.NewUintDatum(0)
		maxDatum = types.NewUintDatum(math.MaxUint64)
	} else {
		minDatum = types.NewIntDatum(math.MinInt64)
		maxDatum = types.NewIntDatum(math.MaxInt64)
	}
	for i := len(ranges) - 1; i >= 0; i-- {
		ran := ranges[i]
		// Remove the [NULL, NULL] which is invalid when generating int handle's kv.KeyRange.
		if ran.HighVal[0].IsNull() {
			ranges = slices.Delete(ranges, i, i+1)
			continue
		}
		// Convert the min and max value to the int min/max value.
		if ran.LowVal[0].IsNull() || ran.LowVal[0].Kind() == types.KindMinNotNull {
			ran.LowVal[0].SetInt64(minDatum.GetInt64())