Repository: duckdb/duckdb
File: extension/core_functions/scalar/map/switch.cpp (C++)

Code before:
	explicit SwitchFunctionBindData(const LogicalType &return_type_p, idx_t map_index_p)
	    : return_type(std::move(return_type_p)), map_index(std::move(map_index_p)) {
	}

	LogicalType return_type;
	idx_t map_index;

	bool Equals(const FunctionData &other_p) const override {
		const auto &other = other_p.Cast<SwitchFunctionBindData>();
		if (return_type != other.return_type) {
			return false;
		}
		if (map_index != other.map_index) {
			return false;
		}
		return true;
	}

	unique_ptr<FunctionData> Copy() const override {
		return make_uniq<SwitchFunctionBindData>(return_type, map_index);
	}
};

idx_t FindMapArgumentIndex(const vector<unique_ptr<Expression>> &arguments) {
	for (idx_t i = 0; i < arguments.size(); i++) {
		if (arguments[i]->return_type.id() == LogicalTypeId::MAP) {
			return i;
		}
	}
	return DConstants::INVALID_INDEX;
}

unique_ptr<FunctionData> SwitchBindReturnType(ClientContext &context, ScalarFunction &function,
                                              vector<unique_ptr<Expression>> &arguments) {
	auto map_index = FindMapArgumentIndex(arguments);
	if (map_index == DConstants::INVALID_INDEX) {
		throw BinderException("Switch: No map argument found");
	}
	auto &cases = arguments[map_index];
	if (cases->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) {
		throw BinderException("SWITCH expected a constant map for the cases");
	}
	auto &func = cases->Cast<BoundFunctionExpression>();
	if (func.function.name != "map") {
		throw BinderException("SWITCH expected a constant map for the cases");
	}
	auto map_value = ExpressionExecutor::EvaluateScalar(context, *cases);
	auto values_type = MapType::ValueType(map_value.type());
	return make_uniq<SwitchFunctionBindData>(values_type, map_index);
}


Review comment (performance):
Can we benchmark this against `CASE` with a few different map/case sizes?

If the performance is worse than `CASE`, we should just evaluate this using `CASE`. We can convert this in the bind.

Code after:
namespace {
struct SwitchFunctionBindData : FunctionData {
	explicit SwitchFunctionBindData(const LogicalType &return_type_p, idx_t map_index_p)
	    : return_type(std::move(return_type_p)), map_index(std::move(map_index_p)) {
	}

	LogicalType return_type;
	idx_t map_index;

	bool Equals(const FunctionData &other_p) const override {
		const auto &other = other_p.Cast<SwitchFunctionBindData>();
		if (return_type != other.return_type) {
			return false;
		}
		if (map_index != other.map_index) {
			return false;
		}
		return true;
	}

	unique_ptr<FunctionData> Copy() const override {
		return make_uniq<SwitchFunctionBindData>(return_type, map_index);
	}
};

idx_t FindMapArgumentIndex(const vector<unique_ptr<Expression>> &arguments) {
	for (idx_t i = 0; i < arguments.size(); i++) {
		if (arguments[i]->return_type.id() == LogicalTypeId::MAP) {
			return i;
		}
	}
	return DConstants::INVALID_INDEX;
}

unique_ptr<FunctionData> SwitchBindReturnType(ClientContext &context, ScalarFunction &function,
                                              vector<unique_ptr<Expression>> &arguments) {
	auto map_index = FindMapArgumentIndex(arguments);
	if (map_index == DConstants::INVALID_INDEX) {
		throw BinderException("Switch: No map argument found");
	}
	auto &cases = arguments[map_index];
	if (cases->GetExpressionClass() != ExpressionClass::BOUND_FUNCTION) {
		throw BinderException("SWITCH expected a constant map for the cases");
	}
	auto &func = cases->Cast<BoundFunctionExpression>();
	if (func.function.name != "map") {
		throw BinderException("SWITCH expected a constant map for the cases");
	}
	auto map_value = ExpressionExecutor::EvaluateScalar(context, *cases);
	auto values_type = MapType::ValueType(map_value.type());
	return make_uniq<SwitchFunctionBindData>(values_type, map_index);