Repository: duckdb/duckdb
File: src/optimizer/remove_unused_columns.cpp (C++)

Code before:
			}
			if (column_ids.empty()) {
				// this generally means we are only interested in whether or not anything exists in the table (e.g.
				// EXISTS(SELECT * FROM tbl)) in this case, we just scan the row identifier column as it means we do not
				// need to read any of the columns
				column_ids.emplace_back(get.GetAnyColumn());
			}
			get.SetColumnIds(std::move(column_ids));

			if (!get.function.filter_prune) {
				break;
			}
			// Now set the projection cols by matching the "selection vector" that excludes filter columns
			// with the "selection vector" that includes filter columns
			idx_t col_idx = 0;
			get.projection_ids.clear();
			for (auto proj_sel_idx : proj_sel) {
				for (; col_idx < col_sel.size(); col_idx++) {
					if (proj_sel_idx == col_sel[col_idx]) {
						get.projection_ids.push_back(col_idx);
						break;
					}
				}
			}
		} while (false);
		if (get.function.in_out_function && !get.children.empty()) {
			// Recurse into the child of the table in out function.
			RemoveUnusedColumns remove(binder, context, true);
			remove.VisitOperator(*op.children[0]);
		}
		return;
	}
	case LogicalOperatorType::LOGICAL_DISTINCT: {
		auto &distinct = op.Cast<LogicalDistinct>();
		if (distinct.distinct_type == DistinctType::DISTINCT_ON) {
			// distinct type references columns that need to be distinct on, so no
			// need to implicity reference everything.
			break;
		}
		// distinct, all projected columns are used for the DISTINCT computation
		// mark all columns as used and continue to the children
		// FIXME: DISTINCT with expression list does not implicitly reference everything
		everything_referenced = true;
		break;
	}
	case LogicalOperatorType::LOGICAL_RECURSIVE_CTE:
	case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE:
	case LogicalOperatorType::LOGICAL_CTE_REF:
	case LogicalOperatorType::LOGICAL_COPY_TO_FILE:
	case LogicalOperatorType::LOGICAL_PIVOT: {
		everything_referenced = true;

Review comment (suggestion):
I don't think we even need to check for `in_out_function` If the get children are not empty, we can recurse.

Code after:
		return;
	}

	auto final_column_ids = get.GetColumnIds();

	// Create "selection vector" of all column ids
	vector<idx_t> proj_sel;
	for (idx_t col_idx = 0; col_idx < final_column_ids.size(); col_idx++) {
		proj_sel.push_back(col_idx);
	}
	// Create a copy that we can use to match ids later
	auto col_sel = proj_sel;
	// Clear unused ids, exclude filter columns that are projected out immediately
	ClearUnusedExpressions(proj_sel, get.table_index, false);

	vector<unique_ptr<Expression>> filter_expressions;
	// for every table filter, push a column binding into the column references map to prevent the column from
	// being projected out
	for (auto &filter : get.table_filters.filters) {
		optional_idx index;
		for (idx_t i = 0; i < final_column_ids.size(); i++) {
			if (final_column_ids[i].GetPrimaryIndex() == filter.first) {
				index = i;
				break;
			}
		}
		if (!index.IsValid()) {
			throw InternalException("Could not find column index for table filter");
		}

		auto column_type = get.GetColumnType(ColumnIndex(filter.first));

		ColumnBinding filter_binding(get.table_index, index.GetIndex());
		auto column_ref = make_uniq<BoundColumnRefExpression>(std::move(column_type), filter_binding);
		auto filter_expr = filter.second->ToExpression(*column_ref);
		if (filter_expr->IsScalar()) {
			filter_expr = std::move(column_ref);
		}
		VisitExpression(&filter_expr);
		filter_expressions.push_back(std::move(filter_expr));
	}

	// Clear unused ids, include filter columns that are projected out immediately
	ClearUnusedExpressions(col_sel, get.table_index);

	// Now set the column ids in the LogicalGet using the "selection vector"
	vector<ColumnIndex> column_ids;
	column_ids.reserve(col_sel.size());
	for (auto col_sel_idx : col_sel) {
		auto entry = column_references.find(ColumnBinding(get.table_index, col_sel_idx));
		if (entry == column_references.end()) {