Repository: duckdb/duckdb
File: src/optimizer/statistics/operator/propagate_aggregate.cpp (C++)

Code before:
		return make_uniq<MinValueComp>();
	}
	D_ASSERT(fun_name == "max");
	return make_uniq<MaxValueComp>();
}

bool TryGetValueFromStats(const PartitionStatistics &stats, const column_t column_index,
                          const ValueComparator &comparator, Value &result) {
	if (stats.count_type == CountType::COUNT_APPROXIMATE) {
		// we cannot get an exact count
		return false;
	}
	if (!stats.partition_row_group) {
		return false;
	}
	auto column_stats = stats.partition_row_group->GetColumnStatistics(column_index);
	if (!NumericStats::HasMinMax(*column_stats)) {
		// TODO: This also returns if an entire row group is null. In that case, we could skip/compare null
		return false;
	}
	result = comparator.GetVal(*column_stats);
	return true;
}

} // namespace

void StatisticsPropagator::TryExecuteAggregates(LogicalAggregate &aggr, unique_ptr<LogicalOperator> &node_ptr) {
	if (!aggr.groups.empty()) {
		// not possible with groups
		return;
	}
	// check if all aggregates are COUNT(*), MIN or MAX
	vector<idx_t> count_star_idxs;
	vector<ColumnBinding> min_max_bindings;
	vector<unique_ptr<ValueComparator>> comparators;

	for (idx_t i = 0; i < aggr.expressions.size(); i++) {
		auto &aggr_ref = aggr.expressions[i];
		if (aggr_ref->GetExpressionClass() != ExpressionClass::BOUND_AGGREGATE) {
			// not an aggregate
			return;
		}
		auto &aggr_expr = aggr_ref->Cast<BoundAggregateExpression>();
		if (aggr_expr.filter) {
			// aggregate has a filter - bail
			return;
		}
		const string &fun_name = aggr_expr.function.name;
		if (fun_name == "min" || fun_name == "max") {
			if (aggr_expr.children.size() != 1 || aggr_expr.children[0]->type != ExpressionType::BOUND_COLUMN_REF) {
				return;

Review comment (bug):
We need a callback to signify whether or not the `min/max` are exact or not, e.g. `stats.partition_row_group->MinMaxIsExact()`. For example, in Parquet the stats contain `is_min_value_exact / is_max_value_exact`. If these are not set or are set to false we can't assume the stats are contained in the data and we can't safely perform this optimization.

For our own stats, numeric stats are accurate only if there are no deletes / updates. For example, this can provide wrong stats:

```sql
create table t as from range(100) t(r);
update t set r=20;
select min(r), max(r), first(stats(r)) from t;
┌────────┬────────┬──────────────────────────────────────────────────────────────────────────┐
│ min(r) │ max(r) │                            "first"(stats(r))                             │
│ int64  │ int64  │                                 varchar                                  │
├────────┼────────┼──────────────────────────────────────────────────────────────────────────┤
│   20   │   20   │ [Min: 0, Max: 99][Has Null: false, Has No Null: true][Approx Unique: 98] │
└────────┴────────┴──────────────────────────────────────────────────────────────────────────┘
```

Code after:
};

template <typename StatsType>
unique_ptr<ValueComparator> GetComparator(const string &fun_name) {
	if (fun_name == "min") {
		return make_uniq<MinValueComp<StatsType>>();
	}
	D_ASSERT(fun_name == "max");
	return make_uniq<MaxValueComp<StatsType>>();
}

unique_ptr<ValueComparator> GetComparator(const string &fun_name, const LogicalType &type) {
	if (type == LogicalType::VARCHAR) {
		return GetComparator<StringStats>(fun_name);
	} else if (type.IsNumeric() || type.IsTemporal()) {
		return GetComparator<NumericStats>(fun_name);
	}
	return nullptr;
}

bool TryGetValueFromStats(const PartitionStatistics &stats, const column_t column_index,
                          const ValueComparator &comparator, Value &result) {
	if (!stats.partition_row_group) {
		return false;
	}
	auto column_stats = stats.partition_row_group->GetColumnStatistics(column_index);
	if (!stats.partition_row_group->MinMaxIsExact(*column_stats)) {
		return false;
	}
	if (column_stats->GetStatsType() == StatisticsType::NUMERIC_STATS) {
		if (!NumericStats::HasMinMax(*column_stats)) {
			// TODO: This also returns if an entire row group is null. In that case, we could skip/compare null
			return false;
		}
	} else {
		D_ASSERT(column_stats->GetStatsType() == StatisticsType::STRING_STATS);
		if (StringStats::Min(*column_stats) > StringStats::Max(*column_stats)) {
			// No min/max statistics availabe
			return false;
		}
	}
	result = comparator.GetVal(*column_stats);
	return true;
}

} // namespace

void StatisticsPropagator::TryExecuteAggregates(LogicalAggregate &aggr, unique_ptr<LogicalOperator> &node_ptr) {
	if (!aggr.groups.empty()) {
		// not possible with groups
		return;