Repository: duckdb/duckdb
File: src/include/duckdb/planner/operator/logical_get.hpp (C++)

Code before:
	//! A mapping of column index -> type/name for all virtual columns
	virtual_column_map_t virtual_columns;
	//! Columns that are used outside the scan
	vector<idx_t> projection_ids;
	//! Filters pushed down for table scan
	TableFilterSet table_filters;
	//! The set of input parameters for the table function
	vector<Value> parameters;
	//! The set of named input parameters for the table function
	named_parameter_map_t named_parameters;
	//! The set of named input table types for the table-in table-out function
	vector<LogicalType> input_table_types;
	//! The set of named input table names for the table-in table-out function
	vector<string> input_table_names;
	//! For a table-in-out function, the set of projected input columns
	vector<column_t> projected_input;
	//! Currently stores File Filters (as strings) applied by hive partitioning/complex filter pushdown and sample rate
	//! pushed down into the table scan
	//! Stored so the can be included in explain output
	ExtraOperatorInfo extra_info;
	//! Contains a reference to dynamically generated table filters (through e.g. a join up in the tree)
	shared_ptr<DynamicTableFilterSet> dynamic_filters;
	//! Information for WITH ORDINALITY
	optional_idx ordinality_idx;

	shared_ptr<RowGroupOrderOptions> row_group_order;

	string GetName() const override;
	InsertionOrderPreservingMap<string> ParamsToString() const override;
	//! Returns the underlying table that is being scanned, or nullptr if there is none
	optional_ptr<TableCatalogEntry> GetTable() const;
	//! Returns any column to query - preferably the cheapest column
	//! This is used when we are running e.g. a COUNT(*) and don't care about the contents of any columns in the table
	column_t GetAnyColumn() const;

	const LogicalType &GetColumnType(const ColumnIndex &column_index) const;
	const string &GetColumnName(const ColumnIndex &column_index) const;

public:
	void SetColumnIds(vector<ColumnIndex> &&column_ids);
	void AddColumnId(column_t column_id);
	void ClearColumnIds();
	const vector<ColumnIndex> &GetColumnIds() const;
	vector<ColumnIndex> &GetMutableColumnIds();
	vector<ColumnBinding> GetColumnBindings() override;
	idx_t EstimateCardinality(ClientContext &context) override;

	vector<idx_t> GetTableIndex() const override;
	//! Skips the serialization check in VerifyPlan
	bool SupportSerialization() const override {
		return function.verify_serialization;

Review comment (performance):

Instead of pushing this into the `LogicalGet` and propagating it throughout the stack - I think a cleaner approach would be to add a new callback to table functions that can be called by the optimizer, e.g. something like:

```cpp
function.set_scan_order(...);
```


This callback can then be implemented only by functions that support it (currently only the table scan). We then wouldn't have to touch any other places in the code I think.


Code after:
	//! A mapping of column index -> type/name for all virtual columns
	virtual_column_map_t virtual_columns;
	//! Columns that are used outside the scan
	vector<idx_t> projection_ids;
	//! Filters pushed down for table scan
	TableFilterSet table_filters;
	//! The set of input parameters for the table function
	vector<Value> parameters;
	//! The set of named input parameters for the table function
	named_parameter_map_t named_parameters;
	//! The set of named input table types for the table-in table-out function
	vector<LogicalType> input_table_types;
	//! The set of named input table names for the table-in table-out function
	vector<string> input_table_names;
	//! For a table-in-out function, the set of projected input columns
	vector<column_t> projected_input;
	//! Currently stores File Filters (as strings) applied by hive partitioning/complex filter pushdown and sample rate
	//! pushed down into the table scan
	//! Stored so the can be included in explain output
	ExtraOperatorInfo extra_info;
	//! Contains a reference to dynamically generated table filters (through e.g. a join up in the tree)
	shared_ptr<DynamicTableFilterSet> dynamic_filters;
	//! Information for WITH ORDINALITY
	optional_idx ordinality_idx;

	string GetName() const override;
	InsertionOrderPreservingMap<string> ParamsToString() const override;
	//! Returns the underlying table that is being scanned, or nullptr if there is none
	optional_ptr<TableCatalogEntry> GetTable() const;
	//! Returns any column to query - preferably the cheapest column
	//! This is used when we are running e.g. a COUNT(*) and don't care about the contents of any columns in the table
	column_t GetAnyColumn() const;

	const LogicalType &GetColumnType(const ColumnIndex &column_index) const;
	const string &GetColumnName(const ColumnIndex &column_index) const;

public:
	void SetColumnIds(vector<ColumnIndex> &&column_ids);
	void AddColumnId(column_t column_id);
	void ClearColumnIds();
	const vector<ColumnIndex> &GetColumnIds() const;
	vector<ColumnIndex> &GetMutableColumnIds();
	vector<ColumnBinding> GetColumnBindings() override;
	idx_t EstimateCardinality(ClientContext &context) override;

	vector<idx_t> GetTableIndex() const override;
	//! Skips the serialization check in VerifyPlan
	bool SupportSerialization() const override {
		return function.verify_serialization;
	}