sungwy commented on code in PR #1043: URL: https://github.com/apache/iceberg-python/pull/1043#discussion_r1716137061
########## pyiceberg/io/pyarrow.py: ########## @@ -1308,6 +1309,138 @@ def _read_all_delete_files(fs: FileSystem, tasks: Iterable[FileScanTask]) -> Dic return deletes_per_file +def _fs_from_file_path(file_path: str, io: FileIO) -> FileSystem: + scheme, netloc, _ = _parse_location(file_path) + if isinstance(io, PyArrowFileIO): + return io.fs_by_scheme(scheme, netloc) + else: + try: + from pyiceberg.io.fsspec import FsspecFileIO + + if isinstance(io, FsspecFileIO): + from pyarrow.fs import PyFileSystem + + return PyFileSystem(FSSpecHandler(io.get_fs(scheme))) + else: + raise ValueError(f"Expected PyArrowFileIO or FsspecFileIO, got: {io}") + except ModuleNotFoundError as e: + # When FsSpec is not installed + raise ValueError(f"Expected PyArrowFileIO or FsspecFileIO, got: {io}") from e + + +class PyArrowProjector: + _table_metadata: TableMetadata + _io: FileIO + _fs: FileSystem + _projected_schema: Schema + _bound_row_filter: BooleanExpression + _case_sensitive: bool + _limit: Optional[int] + + def __init__( + self, + table_metadata: TableMetadata, + io: FileIO, + projected_schema: Schema, + row_filter: BooleanExpression, + case_sensitive: bool = True, + limit: Optional[int] = None, + ) -> None: + self._table_metadata = table_metadata + self._io = io + self._fs = _fs_from_file_path(table_metadata.location, io) # TODO: use different FileSystem per file + self._projected_schema = projected_schema + self._bound_row_filter = bind(table_metadata.schema(), row_filter, case_sensitive=case_sensitive) + self._case_sensitive = case_sensitive + self._limit = limit + + @property + def _use_large_types(self) -> bool: + return property_as_bool(self._io.properties, PYARROW_USE_LARGE_TYPES_ON_READ, True) + + @property + def _projected_field_ids(self) -> Set[int]: + return { + id + for id in self._projected_schema.field_ids + if not isinstance(self._projected_schema.find_type(id), (MapType, ListType)) + }.union(extract_field_ids(self._bound_row_filter)) + + def project_table(self, tasks: Iterable[FileScanTask]) -> pa.Table: + deletes_per_file = _read_all_delete_files(self._fs, tasks) + executor = ExecutorFactory.get_or_create() + + def _project_table_from_scan_task(task: FileScanTask) -> pa.Table: + batches = list(self._project_batches_from_scan_tasks_and_deletes([task], deletes_per_file)) + if len(batches) > 0: + return pa.Table.from_batches(batches) + else: + return None + + futures = [ + executor.submit( + _project_table_from_scan_task, + task, Review Comment: So my proposal is that we don't have to pass limit here ourselves, since each `_project_table_from_scan_task` invokes `_project_batches_from_scan_tasks_and_deletes` separately, they all respect the limit individually when fetching the record batches and materializing them as `pa.Table`: https://github.com/apache/iceberg-python/pull/1043/files#diff-8d5e63f2a87ead8cebe2fd8ac5dcf2198d229f01e16bb9e06e21f7277c328abdR1422-R1441 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org For additional commands, e-mail: issues-h...@iceberg.apache.org