1818 */
1919
2020#include < cstdlib>
21- #include < mutex >
21+ #include < optional >
2222#include < string>
23+ #include < string_view>
2324
2425#include < arrow/filesystem/filesystem.h>
25- #ifdef ICEBERG_S3_ENABLED
26+ #if ICEBERG_S3_ENABLED
2627# include < arrow/filesystem/s3fs.h>
27- # define ICEBERG_ARROW_HAS_S3 1
28- #else
29- # define ICEBERG_ARROW_HAS_S3 0
3028#endif
3129
3230#include " iceberg/arrow/arrow_file_io.h"
@@ -40,55 +38,77 @@ namespace iceberg::arrow {
4038
4139namespace {
4240
43- #if ICEBERG_ARROW_HAS_S3
41+ #if ICEBERG_S3_ENABLED
42+ const std::string* FindProperty (
43+ const std::unordered_map<std::string, std::string>& properties,
44+ std::string_view key) {
45+ auto it = properties.find (std::string (key));
46+ return it == properties.end () ? nullptr : &it->second ;
47+ }
48+
49+ Result<std::optional<bool >> ParseOptionalBool (
50+ const std::unordered_map<std::string, std::string>& properties,
51+ std::string_view key) {
52+ const auto * value = FindProperty (properties, key);
53+ if (value == nullptr ) {
54+ return std::nullopt ;
55+ }
56+ if (*value == " true" ) {
57+ return true ;
58+ }
59+ if (*value == " false" ) {
60+ return false ;
61+ }
62+ return InvalidArgument (R"( "{}" must be "true" or "false")" , key);
63+ }
64+
4465Status EnsureS3Initialized () {
45- static std::once_flag init_flag;
46- static ::arrow::Status init_status = ::arrow::Status::OK ();
47- std::call_once (init_flag, []() {
66+ static const ::arrow::Status init_status = []() {
4867 auto options = ::arrow::fs::S3GlobalOptions::Defaults ();
49- init_status = ::arrow::fs::InitializeS3 (options);
50- });
68+ return ::arrow::fs::InitializeS3 (options);
69+ }( );
5170 if (!init_status.ok ()) {
5271 return std::unexpected (Error{.kind = ::iceberg::arrow::ToErrorKind (init_status),
5372 .message = init_status.ToString ()});
5473 }
5574 return {};
5675}
76+
5777// / \brief Configure S3Options from a properties map.
5878// /
5979// / \param properties The configuration properties map.
6080// / \return Configured S3Options.
6181Result<::arrow::fs::S3Options> ConfigureS3Options (
6282 const std::unordered_map<std::string, std::string>& properties) {
63- ::arrow::fs::S3Options options ;
83+ auto options = ::arrow::fs::S3Options::Defaults () ;
6484
6585 // Configure credentials
66- auto access_key_it = properties.find (std::string (S3Properties::kAccessKeyId ));
67- auto secret_key_it = properties.find (std::string (S3Properties::kSecretAccessKey ));
68- auto session_token_it = properties.find (std::string (S3Properties::kSessionToken ));
69-
70- if (access_key_it != properties.end () && secret_key_it != properties.end ()) {
71- if (session_token_it != properties.end ()) {
72- options.ConfigureAccessKey (access_key_it->second , secret_key_it->second ,
73- session_token_it->second );
86+ const auto * access_key = FindProperty (properties, S3Properties::kAccessKeyId );
87+ const auto * secret_key = FindProperty (properties, S3Properties::kSecretAccessKey );
88+ const auto * session_token = FindProperty (properties, S3Properties::kSessionToken );
89+
90+ if ((access_key == nullptr ) != (secret_key == nullptr )) {
91+ return InvalidArgument (
92+ " S3 client access key ID and secret access key must be set at the same time" );
93+ }
94+ if (access_key != nullptr ) {
95+ if (session_token != nullptr ) {
96+ options.ConfigureAccessKey (*access_key, *secret_key, *session_token);
7497 } else {
75- options.ConfigureAccessKey (access_key_it-> second , secret_key_it-> second );
98+ options.ConfigureAccessKey (*access_key, *secret_key );
7699 }
77- } else {
78- // Use default credential chain (environment, instance profile, etc.)
79- options.ConfigureDefaultCredentials ();
80100 }
81101
82102 // Configure region
83- auto region_it = properties. find ( std::string ( S3Properties::kRegion ) );
84- if (region_it != properties. end () ) {
85- options.region = region_it-> second ;
103+ if ( const auto * region = FindProperty (properties, S3Properties::kRegion );
104+ region != nullptr ) {
105+ options.region = *region ;
86106 }
87107
88108 // Configure endpoint (for MinIO, LocalStack, etc.)
89- auto endpoint_it = properties. find ( std::string ( S3Properties::kEndpoint ) );
90- if (endpoint_it != properties. end () ) {
91- options.endpoint_override = endpoint_it-> second ;
109+ if ( const auto * endpoint = FindProperty (properties, S3Properties::kEndpoint );
110+ endpoint != nullptr ) {
111+ options.endpoint_override = *endpoint ;
92112 } else {
93113 // Fall back to AWS standard environment variables for endpoint override
94114 const char * s3_endpoint_env = std::getenv (" AWS_ENDPOINT_URL_S3" );
@@ -102,14 +122,16 @@ Result<::arrow::fs::S3Options> ConfigureS3Options(
102122 }
103123 }
104124
105- auto path_style_it = properties.find (std::string (S3Properties::kPathStyleAccess ));
106- if (path_style_it != properties.end () && path_style_it->second == " true" ) {
107- options.force_virtual_addressing = false ;
125+ ICEBERG_ASSIGN_OR_RAISE (const auto path_style_access,
126+ ParseOptionalBool (properties, S3Properties::kPathStyleAccess ));
127+ if (path_style_access.has_value ()) {
128+ options.force_virtual_addressing = !*path_style_access;
108129 }
109130
110131 // Configure SSL
111- auto ssl_it = properties.find (std::string (S3Properties::kSslEnabled ));
112- if (ssl_it != properties.end () && ssl_it->second == " false" ) {
132+ ICEBERG_ASSIGN_OR_RAISE (const auto ssl_enabled,
133+ ParseOptionalBool (properties, S3Properties::kSslEnabled ));
134+ if (ssl_enabled.has_value () && !*ssl_enabled) {
113135 options.scheme = " http" ;
114136 }
115137
@@ -136,21 +158,21 @@ Result<::arrow::fs::S3Options> ConfigureS3Options(
136158
137159Result<std::unique_ptr<FileIO>> MakeS3FileIO (
138160 const std::unordered_map<std::string, std::string>& properties) {
139- #if !ICEBERG_ARROW_HAS_S3
140- return NotSupported (" Arrow S3 support is not enabled" );
141- #else
161+ #if ICEBERG_S3_ENABLED
142162 ICEBERG_RETURN_UNEXPECTED (EnsureS3Initialized ());
143163
144164 // Configure S3 options from properties (uses default credentials if empty)
145165 ICEBERG_ASSIGN_OR_RAISE (auto options, ConfigureS3Options (properties));
146166 ICEBERG_ARROW_ASSIGN_OR_RETURN (auto fs, ::arrow::fs::S3FileSystem::Make (options));
147167
148168 return std::make_unique<ArrowFileSystemFileIO>(std::move (fs));
169+ #else
170+ return NotSupported (" Arrow S3 support is not enabled" );
149171#endif
150172}
151173
152174Status FinalizeS3 () {
153- #if ICEBERG_ARROW_HAS_S3
175+ #if ICEBERG_S3_ENABLED
154176 auto status = ::arrow::fs::FinalizeS3 ();
155177 ICEBERG_ARROW_RETURN_NOT_OK (status);
156178 return {};
0 commit comments