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

Code before:
		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;
			}
			const auto &col_ref = aggr_expr.children[0]->Cast<BoundColumnRefExpression>();
			if (!col_ref.return_type.IsNumeric() && !col_ref.return_type.IsTemporal()) {
				// Only use NumericStats (StringStats min/max may be truncated)
				return;
			}

			min_max_bindings.push_back(col_ref.binding);
			comparators.push_back(GetComparator(fun_name));
		} else if (fun_name == "count_star") {
			count_star_idxs.push_back(i);
		} else {
			// aggregate is not count star, min or max - bail
			return;
		}
	}

	// skip any projections
	reference<LogicalOperator> child_ref = *aggr.children[0];
	while (child_ref.get().type == LogicalOperatorType::LOGICAL_PROJECTION) {
		for (auto &binding : min_max_bindings) {
			auto &expr = child_ref.get().expressions[binding.column_index];
			if (expr->type != ExpressionType::BOUND_COLUMN_REF) {
				return;
			}
			binding = expr->Cast<BoundColumnRefExpression>().binding;
		}
		child_ref = *child_ref.get().children[0];
	}

Review comment (suggestion):
This shouldn't be assumed for string stats either - they don't have to be truncated - they just can be truncated. This should be handled by the stats provider in `MinMaxIsExact`

Code after:
	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;
	}
	// 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;
			}
			const auto &col_ref = aggr_expr.children[0]->Cast<BoundColumnRefExpression>();
			min_max_bindings.push_back(col_ref.binding);
			auto comparator = GetComparator(fun_name, col_ref.return_type);
			if (!comparator) {
				// Type has no min max statistics
				return;
			}