Repository: duckdb/duckdb
File: src/planner/table_filter_state.cpp (C++)

Code before:
#include "duckdb/planner/table_filter_state.hpp"
#include "duckdb/planner/filter/bloom_filter.hpp"
#include "duckdb/planner/filter/conjunction_filter.hpp"
#include "duckdb/planner/filter/expression_filter.hpp"
#include "duckdb/planner/filter/selectivity_optional_filter.hpp"
#include "duckdb/planner/filter/struct_filter.hpp"
#include "duckdb/planner/expression/bound_function_expression.hpp"
#include "duckdb/execution/expression_executor_state.hpp"

namespace duckdb {

ExpressionFilterState::ExpressionFilterState(ClientContext &context, const Expression &expression) : executor(context) {
	executor.AddExpression(expression);
	if (expression.type == ExpressionType::BOUND_FUNCTION) {
		auto &func_expr = expression.Cast<BoundFunctionExpression>();
		if (func_expr.function.HasFilterPrunerCallbacks() && func_expr.function.init_local_state) {
			ExpressionExecutorState exec_state;
			exec_state.executor = &executor;
			ExpressionState expr_state(expression, exec_state);
			extension_state = func_expr.function.init_local_state(expr_state, func_expr, func_expr.bind_info.get());
		}
	}
}

unique_ptr<TableFilterState> TableFilterState::Initialize(ClientContext &context, const TableFilter &filter) {
	switch (filter.filter_type) {
	case TableFilterType::BLOOM_FILTER: {
		auto &bf = filter.Cast<BFTableFilter>();
		return make_uniq<BFTableFilterState>(bf.GetKeyType());
	}
	case TableFilterType::OPTIONAL_FILTER: {
		// the optional filter may be executed if it is a SelectivityOptionalFilter
		auto &optional_filter = filter.Cast<OptionalFilter>();
		return optional_filter.InitializeState(context);
	}

	case TableFilterType::STRUCT_EXTRACT: {
		auto &struct_filter = filter.Cast<StructFilter>();
		return Initialize(context, *struct_filter.child_filter);
	}
	case TableFilterType::CONJUNCTION_OR: {
		auto &conj_filter = filter.Cast<ConjunctionOrFilter>();
		auto result = make_uniq<ConjunctionOrFilterState>();
		for (auto &child_filter : conj_filter.child_filters) {
			result->child_states.push_back(Initialize(context, *child_filter));

Review comment (suggestion):
This should already be present / called in the `ExpressionExecutor`. If the root is a function the expression state there will be a `ExecuteFunctionState` - which will have the local state.

Code after:
#include "duckdb/planner/table_filter_state.hpp"
#include "duckdb/planner/filter/bloom_filter.hpp"
#include "duckdb/planner/filter/conjunction_filter.hpp"
#include "duckdb/planner/filter/expression_filter.hpp"
#include "duckdb/planner/filter/selectivity_optional_filter.hpp"
#include "duckdb/planner/filter/struct_filter.hpp"

namespace duckdb {

ExpressionFilterState::ExpressionFilterState(ClientContext &context, const Expression &expression) : executor(context) {
	executor.AddExpression(expression);
}

unique_ptr<TableFilterState> TableFilterState::Initialize(ClientContext &context, const TableFilter &filter) {
	switch (filter.filter_type) {
	case TableFilterType::BLOOM_FILTER: {
		auto &bf = filter.Cast<BFTableFilter>();
		return make_uniq<BFTableFilterState>(bf.GetKeyType());
	}
	case TableFilterType::OPTIONAL_FILTER: {
		// the optional filter may be executed if it is a SelectivityOptionalFilter
		auto &optional_filter = filter.Cast<OptionalFilter>();
		return optional_filter.InitializeState(context);
	}

	case TableFilterType::STRUCT_EXTRACT: {
		auto &struct_filter = filter.Cast<StructFilter>();
		return Initialize(context, *struct_filter.child_filter);
	}
	case TableFilterType::CONJUNCTION_OR: {
		auto &conj_filter = filter.Cast<ConjunctionOrFilter>();
		auto result = make_uniq<ConjunctionOrFilterState>();
		for (auto &child_filter : conj_filter.child_filters) {
			result->child_states.push_back(Initialize(context, *child_filter));
		}
		return std::move(result);
	}
	case TableFilterType::CONJUNCTION_AND: {
		auto &conj_filter = filter.Cast<ConjunctionAndFilter>();
		auto result = make_uniq<ConjunctionAndFilterState>();
		for (auto &child_filter : conj_filter.child_filters) {
			result->child_states.push_back(Initialize(context, *child_filter));
		}
		return std::move(result);
	}