Repository: duckdb/duckdb
File: src/core_functions/scalar/map/map_entries.cpp (C++)

Code before:
	auto &map = arguments[0]->return_type;

	if (map.id() == LogicalTypeId::UNKNOWN) {
		// Prepared statement
		bound_function.arguments.emplace_back(LogicalTypeId::UNKNOWN);
		bound_function.return_type = LogicalType(LogicalTypeId::SQLNULL);
		return nullptr;
	}

	if (map.id() == LogicalTypeId::SQLNULL) {
		// Input is NULL, output is STRUCT(NULL, NULL)[]
		auto map_type = LogicalType::MAP(LogicalTypeId::SQLNULL, LogicalTypeId::SQLNULL);
		bound_function.return_type = CreateReturnType(map_type);
		return make_uniq<VariableReturnBindData>(bound_function.return_type);
	}

	if (map.id() != LogicalTypeId::MAP) {
		throw InvalidInputException("The provided argument is not a map");
	}
	bound_function.return_type = CreateReturnType(map);
	return make_uniq<VariableReturnBindData>(bound_function.return_type);
}

ScalarFunction MapEntriesFun::GetFunction() {
	//! the arguments and return types are actually set in the binder function
	ScalarFunction fun({}, LogicalTypeId::LIST, MapEntriesFunction, MapEntriesBind);
	fun.null_handling = FunctionNullHandling::SPECIAL_HANDLING;
	fun.varargs = LogicalType::ANY;
	return fun;
}

} // namespace duckdb


Review comment (suggestion):
These functions should not use special handling - this seems like a work-around for another work-around in `src/function/function_binder.cpp` where we don't return the output type but convert it to `SQLNULL` as a fallback.

Can we apply this diff to fix that, and turn this back into `DEFAULT_NULL_HANDLING`

```diff
diff --git a/src/function/function_binder.cpp b/src/function/function_binder.cpp
index 267f589d8f..87eacee685 100644
--- a/src/function/function_binder.cpp
+++ b/src/function/function_binder.cpp
@@ -306,13 +306,20 @@ unique_ptr<Expression> FunctionBinder::BindScalarFunction(ScalarFunctionCatalogE
        LogicalType return_type_if_null;
        switch (bound_function.return_type.id()) {
        case LogicalTypeId::ANY:
+               return_type_if_null = LogicalType::SQLNULL;
+               break;
        case LogicalTypeId::DECIMAL:
        case LogicalTypeId::STRUCT:
        case LogicalTypeId::LIST:
        case LogicalTypeId::MAP:
        case LogicalTypeId::UNION:
        case LogicalTypeId::ARRAY:
-               return_type_if_null = LogicalType::SQLNULL;
+               if (bound_function.return_type.AuxInfo()) {
+                       // this is a complete and valid type - return it
+                       return_type_if_null = bound_function.return_type;
+               } else {
+                       return_type_if_null = LogicalType::SQLNULL;
+               }
                break;
        default:
                return_type_if_null = bound_function.return_type;

```

Code after:

	if (map.id() == LogicalTypeId::UNKNOWN) {
		// Prepared statement
		bound_function.arguments.emplace_back(LogicalTypeId::UNKNOWN);
		bound_function.return_type = LogicalType(LogicalTypeId::SQLNULL);
		return nullptr;
	}

	if (map.id() == LogicalTypeId::SQLNULL) {
		// Input is NULL, output is STRUCT(NULL, NULL)[]
		auto map_type = LogicalType::MAP(LogicalTypeId::SQLNULL, LogicalTypeId::SQLNULL);
		bound_function.return_type = CreateReturnType(map_type);
		return make_uniq<VariableReturnBindData>(bound_function.return_type);
	}

	if (map.id() != LogicalTypeId::MAP) {
		throw InvalidInputException("The provided argument is not a map");
	}
	bound_function.return_type = CreateReturnType(map);
	return make_uniq<VariableReturnBindData>(bound_function.return_type);
}

ScalarFunction MapEntriesFun::GetFunction() {
	//! the arguments and return types are actually set in the binder function
	ScalarFunction fun({}, LogicalTypeId::LIST, MapEntriesFunction, MapEntriesBind);
	fun.null_handling = FunctionNullHandling::SPECIAL_HANDLING;
	fun.varargs = LogicalType::ANY;
	return fun;
}

} // namespace duckdb