Repository: duckdb/duckdb
File: tools/pythonpkg/src/python_replacement_scan.cpp (C++)

Code before:
#include "duckdb_python/python_replacement_scan.hpp"
#include "duckdb_python/pybind11/pybind_wrapper.hpp"
#include "duckdb/main/client_properties.hpp"
#include "duckdb_python/numpy/numpy_type.hpp"
#include "duckdb/parser/tableref/table_function_ref.hpp"
#include "duckdb_python/pyconnection/pyconnection.hpp"
#include "duckdb_python/pybind11/dataframe.hpp"
#include "duckdb/parser/expression/constant_expression.hpp"
#include "duckdb/parser/expression/function_expression.hpp"
#include "duckdb/common/typedefs.hpp"
#include "duckdb_python/pandas/pandas_scan.hpp"
#include "duckdb/parser/tableref/subqueryref.hpp"
#include "duckdb_python/pyrelation.hpp"

namespace duckdb {

static void CreateArrowScan(const string &name, py::object entry, TableFunctionRef &table_function,
                            vector<unique_ptr<ParsedExpression>> &children, ClientProperties &client_properties,
                            PyArrowObjectType type) {
	auto stream_factory = make_uniq<PythonTableArrowArrayStreamFactory>(entry.ptr(), client_properties);
	auto stream_factory_produce = PythonTableArrowArrayStreamFactory::Produce;
	auto stream_factory_get_schema = PythonTableArrowArrayStreamFactory::GetSchema;

	children.push_back(make_uniq<ConstantExpression>(Value::POINTER(CastPointerToValue(stream_factory.get()))));
	children.push_back(make_uniq<ConstantExpression>(Value::POINTER(CastPointerToValue(stream_factory_produce))));
	children.push_back(make_uniq<ConstantExpression>(Value::POINTER(CastPointerToValue(stream_factory_get_schema))));

	if (type == PyArrowObjectType::PyCapsule) {
		// Disable projection+filter pushdown
		table_function.function = make_uniq<FunctionExpression>("arrow_scan_dumb", std::move(children));
	} else {
		table_function.function = make_uniq<FunctionExpression>("arrow_scan", std::move(children));
	}

	auto dependency = make_uniq<ExternalDependency>();
	auto dependency_item = PythonDependencyItem::Create(make_uniq<RegisteredArrow>(std::move(stream_factory), entry));
	dependency->AddDependency("replacement_cache", std::move(dependency_item));
	table_function.external_dependency = std::move(dependency);
}

static void ThrowScanFailureError(const py::object &entry, const string &name, const string &location = "") {
	string error;
	auto py_object_type = string(py::str(entry.get_type().attr("__name__")));
	error += StringUtil::Format("Python Object \"%s\" of type \"%s\"", name, py_object_type);
	if (!location.empty()) {
		error += StringUtil::Format(" found on line \"%s\"", location);
	}

Review comment (bug):
Small nitpick: no producer should implement it wrongly of course, but it might still be good practice to verify that the returned capsule has the correct name, like you do in `DuckDBPyConnection::GetArrowType` when verifying a raw capsule as input.

Code after:
#include "duckdb_python/python_replacement_scan.hpp"
#include "duckdb_python/pybind11/pybind_wrapper.hpp"
#include "duckdb/main/client_properties.hpp"
#include "duckdb_python/numpy/numpy_type.hpp"
#include "duckdb/parser/tableref/table_function_ref.hpp"
#include "duckdb_python/pyconnection/pyconnection.hpp"
#include "duckdb_python/pybind11/dataframe.hpp"
#include "duckdb/parser/expression/constant_expression.hpp"
#include "duckdb/parser/expression/function_expression.hpp"
#include "duckdb/common/typedefs.hpp"
#include "duckdb_python/pandas/pandas_scan.hpp"
#include "duckdb/parser/tableref/subqueryref.hpp"
#include "duckdb_python/pyrelation.hpp"

namespace duckdb {

static void CreateArrowScan(const string &name, py::object entry, TableFunctionRef &table_function,
                            vector<unique_ptr<ParsedExpression>> &children, ClientProperties &client_properties,
                            PyArrowObjectType type) {

	if (type == PyArrowObjectType::PyCapsuleInterface) {
		entry = entry.attr("__arrow_c_stream__")();
		type = PyArrowObjectType::PyCapsule;
	}

	auto stream_factory = make_uniq<PythonTableArrowArrayStreamFactory>(entry.ptr(), client_properties);
	auto stream_factory_produce = PythonTableArrowArrayStreamFactory::Produce;
	auto stream_factory_get_schema = PythonTableArrowArrayStreamFactory::GetSchema;

	children.push_back(make_uniq<ConstantExpression>(Value::POINTER(CastPointerToValue(stream_factory.get()))));
	children.push_back(make_uniq<ConstantExpression>(Value::POINTER(CastPointerToValue(stream_factory_produce))));
	children.push_back(make_uniq<ConstantExpression>(Value::POINTER(CastPointerToValue(stream_factory_get_schema))));

	if (type == PyArrowObjectType::PyCapsule) {
		// Disable projection+filter pushdown
		table_function.function = make_uniq<FunctionExpression>("arrow_scan_dumb", std::move(children));
	} else {
		table_function.function = make_uniq<FunctionExpression>("arrow_scan", std::move(children));
	}

	auto dependency = make_uniq<ExternalDependency>();
	auto dependency_item = PythonDependencyItem::Create(make_uniq<RegisteredArrow>(std::move(stream_factory), entry));
	dependency->AddDependency("replacement_cache", std::move(dependency_item));
	table_function.external_dependency = std::move(dependency);
}

static void ThrowScanFailureError(const py::object &entry, const string &name, const string &location = "") {