2222#include < limits>
2323#include < mutex>
2424#include < optional>
25+ #include < unordered_map>
2526#include < vector>
2627
2728#include < arrow/buffer.h>
3536#include " iceberg/arrow/arrow_io_internal.h"
3637#include " iceberg/arrow/arrow_io_util.h"
3738#include " iceberg/arrow/arrow_status_internal.h"
39+ #include " iceberg/arrow/s3/arrow_s3_internal.h"
40+ #include " iceberg/util/location_util.h"
3841#include " iceberg/util/macros.h"
3942
4043namespace iceberg ::arrow {
@@ -483,13 +486,53 @@ class ArrowOutputFile : public OutputFile {
483486
484487} // namespace
485488
486- Result<std::string> ArrowFileSystemFileIO::ResolvePath (const std::string& file_location) {
489+ const std::shared_ptr<::arrow::fs::FileSystem>& ArrowFileSystemFileIO::FileSystemForPath (
490+ std::string_view location) const {
491+ if (fs_by_prefix_.empty ()) {
492+ return arrow_fs_;
493+ }
494+ // Longest matching prefix wins; fall back to the default file system.
495+ const std::string canonical = LocationUtil::CanonicalizeS3Scheme (location);
496+ const std::shared_ptr<::arrow::fs::FileSystem>* best = &arrow_fs_;
497+ size_t best_len = 0 ;
498+ for (const auto & [prefix, fs] : fs_by_prefix_) {
499+ if (prefix.size () > best_len && LocationUtil::PathHasPrefix (canonical, prefix)) {
500+ best = &fs;
501+ best_len = prefix.size ();
502+ }
503+ }
504+ return *best;
505+ }
506+
507+ Status ArrowFileSystemFileIO::SetStorageCredentials (
508+ const std::vector<
509+ std::pair<std::string, std::unordered_map<std::string, std::string>>>&
510+ properties_by_prefix) {
511+ #if ICEBERG_S3_ENABLED
512+ std::vector<std::pair<std::string, std::shared_ptr<::arrow::fs::FileSystem>>>
513+ fs_by_prefix;
514+ fs_by_prefix.reserve (properties_by_prefix.size ());
515+ for (const auto & [prefix, properties] : properties_by_prefix) {
516+ ICEBERG_ASSIGN_OR_RAISE (auto fs, BuildArrowS3FileSystem (properties));
517+ fs_by_prefix.emplace_back (prefix, std::move (fs));
518+ }
519+ fs_by_prefix_ = std::move (fs_by_prefix);
520+ return {};
521+ #else
522+ (void )properties_by_prefix;
523+ return NotSupported (" S3 storage credentials require Arrow S3 support" );
524+ #endif
525+ }
526+
527+ Result<std::string> ArrowFileSystemFileIO::ResolvePath (
528+ const std::shared_ptr<::arrow::fs::FileSystem>& fs,
529+ const std::string& file_location) {
487530 const auto pos = file_location.find (" ://" );
488531 if (pos == std::string::npos) {
489532 return file_location;
490533 }
491534
492- auto path = arrow_fs_ ->PathFromUri (file_location);
535+ auto path = fs ->PathFromUri (file_location);
493536 if (path.ok ()) {
494537 return std::move (path).ValueOrDie ();
495538 }
@@ -512,14 +555,14 @@ Result<std::shared_ptr<::arrow::io::RandomAccessFile>> OpenArrowInputStream(
512555 ICEBERG_PRECHECK (io != nullptr , " FileIO cannot be null" );
513556
514557 if (auto arrow_io = std::dynamic_pointer_cast<ArrowFileSystemFileIO>(io)) {
515- ICEBERG_ASSIGN_OR_RAISE (auto resolved_path, arrow_io->ResolvePath (path));
558+ const auto & fs = arrow_io->FileSystemForPath (path);
559+ ICEBERG_ASSIGN_OR_RAISE (auto resolved_path, arrow_io->ResolvePath (fs, path));
516560 ::arrow::fs::FileInfo file_info (resolved_path, ::arrow::fs::FileType::File);
517561 if (length.has_value ()) {
518562 ICEBERG_ASSIGN_OR_RAISE (auto size, ToInt64Length (*length));
519563 file_info.set_size (size);
520564 }
521- ICEBERG_ARROW_ASSIGN_OR_RETURN (auto input,
522- arrow_io->arrow_fs_ ->OpenInputFile (file_info));
565+ ICEBERG_ARROW_ASSIGN_OR_RETURN (auto input, fs->OpenInputFile (file_info));
523566 return input;
524567 }
525568
@@ -543,16 +586,15 @@ Result<std::shared_ptr<::arrow::io::OutputStream>> OpenArrowOutputStream(
543586 ICEBERG_PRECHECK (io != nullptr , " FileIO cannot be null" );
544587
545588 if (auto arrow_io = std::dynamic_pointer_cast<ArrowFileSystemFileIO>(io)) {
546- ICEBERG_ASSIGN_OR_RAISE (auto resolved_path, arrow_io->ResolvePath (path));
589+ const auto & fs = arrow_io->FileSystemForPath (path);
590+ ICEBERG_ASSIGN_OR_RAISE (auto resolved_path, arrow_io->ResolvePath (fs, path));
547591 if (!overwrite) {
548- ICEBERG_ARROW_ASSIGN_OR_RETURN (auto info,
549- arrow_io->arrow_fs_ ->GetFileInfo (resolved_path));
592+ ICEBERG_ARROW_ASSIGN_OR_RETURN (auto info, fs->GetFileInfo (resolved_path));
550593 if (info.type () != ::arrow::fs::FileType::NotFound) {
551594 return AlreadyExists (" File already exists: {}" , path);
552595 }
553596 }
554- ICEBERG_ARROW_ASSIGN_OR_RETURN (auto output,
555- arrow_io->arrow_fs_ ->OpenOutputStream (resolved_path));
597+ ICEBERG_ARROW_ASSIGN_OR_RETURN (auto output, fs->OpenOutputStream (resolved_path));
556598 return output;
557599 }
558600
@@ -568,42 +610,60 @@ Result<std::shared_ptr<::arrow::io::OutputStream>> OpenArrowOutputStream(
568610
569611Result<std::unique_ptr<InputFile>> ArrowFileSystemFileIO::NewInputFile (
570612 std::string file_location) {
571- ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (file_location));
572- return std::make_unique<ArrowInputFile>(arrow_fs_, std::move (file_location),
573- std::move (path), std::nullopt );
613+ const auto & fs = FileSystemForPath (file_location);
614+ ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (fs, file_location));
615+ return std::make_unique<ArrowInputFile>(fs, std::move (file_location), std::move (path),
616+ std::nullopt );
574617}
575618
576619Result<std::unique_ptr<InputFile>> ArrowFileSystemFileIO::NewInputFile (
577620 std::string file_location, size_t length) {
578621 ICEBERG_ASSIGN_OR_RAISE (auto size, ToInt64Length (length));
579- ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (file_location));
580- return std::make_unique<ArrowInputFile>(arrow_fs_, std::move (file_location),
581- std::move (path), size);
622+ const auto & fs = FileSystemForPath (file_location);
623+ ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (fs, file_location));
624+ return std::make_unique<ArrowInputFile>(fs, std::move (file_location), std::move (path),
625+ size);
582626}
583627
584628Result<std::unique_ptr<OutputFile>> ArrowFileSystemFileIO::NewOutputFile (
585629 std::string file_location) {
586- ICEBERG_ASSIGN_OR_RAISE ( auto path, ResolvePath (file_location) );
587- return std::make_unique<ArrowOutputFile>(arrow_fs_, std::move ( file_location),
588- std::move (path));
630+ const auto & fs = FileSystemForPath (file_location);
631+ ICEBERG_ASSIGN_OR_RAISE ( auto path, ResolvePath (fs, file_location));
632+ return std::make_unique<ArrowOutputFile>(fs, std::move (file_location), std::move (path));
589633}
590634
591635// / \brief Delete a file at the given location.
592636Status ArrowFileSystemFileIO::DeleteFile (const std::string& file_location) {
593- ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (file_location));
594- ICEBERG_ARROW_RETURN_NOT_OK (arrow_fs_->DeleteFile (path));
637+ const auto & fs = FileSystemForPath (file_location);
638+ ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (fs, file_location));
639+ ICEBERG_ARROW_RETURN_NOT_OK (fs->DeleteFile (path));
595640 return {};
596641}
597642
598643Status ArrowFileSystemFileIO::DeleteFiles (
599644 const std::vector<std::string>& file_locations) {
600- std::vector<std::string> paths;
601- paths.reserve (file_locations.size ());
645+ if (fs_by_prefix_.empty ()) {
646+ // No per-prefix routing: one batched delete on the default file system.
647+ std::vector<std::string> paths;
648+ paths.reserve (file_locations.size ());
649+ for (const auto & file_location : file_locations) {
650+ ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (arrow_fs_, file_location));
651+ paths.push_back (std::move (path));
652+ }
653+ ICEBERG_ARROW_RETURN_NOT_OK (arrow_fs_->DeleteFiles (paths));
654+ return {};
655+ }
656+
657+ // Paths may route to different file systems: group by fs, then batch per fs.
658+ std::unordered_map<::arrow::fs::FileSystem*, std::vector<std::string>> paths_by_fs;
602659 for (const auto & file_location : file_locations) {
603- ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (file_location));
604- paths.push_back (std::move (path));
660+ const auto & fs = FileSystemForPath (file_location);
661+ ICEBERG_ASSIGN_OR_RAISE (auto path, ResolvePath (fs, file_location));
662+ paths_by_fs[fs.get ()].push_back (std::move (path));
663+ }
664+ for (auto & [fs, paths] : paths_by_fs) {
665+ ICEBERG_ARROW_RETURN_NOT_OK (fs->DeleteFiles (paths));
605666 }
606- ICEBERG_ARROW_RETURN_NOT_OK (arrow_fs_->DeleteFiles (paths));
607667 return {};
608668}
609669
0 commit comments