@@ -15,8 +15,30 @@ const string &IcebergTableInformation::BaseFilePath() const {
15
15
return load_table_result.metadata .location ;
16
16
}
17
17
18
- static void ParseConfigOptions (const case_insensitive_map_t <string> &config, case_insensitive_map_t <Value> &options) {
19
- // ! Set of recognized config parameters and the duckdb secret option that matches it.
18
+ static string DetectStorageType (const string &location) {
19
+ // Detect storage type from the location URL
20
+ if (StringUtil::StartsWith (location, " gs://" ) || StringUtil::Contains (location, " storage.googleapis.com" )) {
21
+ return " gcs" ;
22
+ } else if (StringUtil::StartsWith (location, " s3://" ) || StringUtil::StartsWith (location, " s3a://" )) {
23
+ return " s3" ;
24
+ } else if (StringUtil::StartsWith (location, " abfs://" ) || StringUtil::StartsWith (location, " az://" )) {
25
+ return " azure" ;
26
+ }
27
+ // Default to s3 for backward compatibility
28
+ return " s3" ;
29
+ }
30
+
31
+ static void ParseGCSConfigOptions (const case_insensitive_map_t <string> &config,
32
+ case_insensitive_map_t <Value> &options) {
33
+ // Parse GCS-specific configuration.
34
+ auto token_it = config.find (" gcs.oauth2.token" );
35
+ if (token_it != config.end ()) {
36
+ options[" bearer_token" ] = token_it->second ;
37
+ }
38
+ }
39
+
40
+ static void ParseS3ConfigOptions (const case_insensitive_map_t <string> &config, case_insensitive_map_t <Value> &options) {
41
+ // Set of recognized S3 config parameters and the duckdb secret option that matches it.
20
42
static const case_insensitive_map_t <string> config_to_option = {{" s3.access-key-id" , " key_id" },
21
43
{" s3.secret-access-key" , " secret" },
22
44
{" s3.session-token" , " session_token" },
@@ -25,15 +47,27 @@ static void ParseConfigOptions(const case_insensitive_map_t<string> &config, cas
25
47
{" client.region" , " region" },
26
48
{" s3.endpoint" , " endpoint" }};
27
49
28
- if (config.empty ()) {
29
- return ;
30
- }
31
50
for (auto &entry : config) {
32
51
auto it = config_to_option.find (entry.first );
33
52
if (it != config_to_option.end ()) {
34
53
options[it->second ] = entry.second ;
35
54
}
36
55
}
56
+ }
57
+
58
+ static void ParseConfigOptions (const case_insensitive_map_t <string> &config, case_insensitive_map_t <Value> &options,
59
+ const string &storage_type = " s3" ) {
60
+ if (config.empty ()) {
61
+ return ;
62
+ }
63
+
64
+ // Parse storage-specific config options
65
+ if (storage_type == " gcs" ) {
66
+ ParseGCSConfigOptions (config, options);
67
+ } else {
68
+ // Default to S3 parsing for backward compatibility
69
+ ParseS3ConfigOptions (config, options);
70
+ }
37
71
38
72
auto it = config.find (" s3.path-style-access" );
39
73
if (it != config.end ()) {
@@ -105,19 +139,20 @@ IRCAPITableCredentials IcebergTableInformation::GetVendedCredentials(ClientConte
105
139
}
106
140
}
107
141
108
- // Mapping from config key to a duckdb secret option
142
+ // Detect storage type from metadata location
143
+ const auto &metadata_location = load_table_result.metadata .location ;
144
+ string storage_type = DetectStorageType (metadata_location);
109
145
146
+ // Mapping from config key to a duckdb secret option
110
147
case_insensitive_map_t <Value> config_options;
111
148
// ! TODO: apply the 'defaults' retrieved from the /v1/config endpoint
112
149
config_options.insert (user_defaults.begin (), user_defaults.end ());
113
150
114
151
if (load_table_result.has_config ) {
115
152
auto &config = load_table_result.config ;
116
- ParseConfigOptions (config, config_options);
153
+ ParseConfigOptions (config, config_options, storage_type );
117
154
}
118
155
119
- const auto &metadata_location = load_table_result.metadata .location ;
120
-
121
156
if (load_table_result.has_storage_credentials ) {
122
157
auto &storage_credentials = load_table_result.storage_credentials ;
123
158
@@ -133,12 +168,12 @@ IRCAPITableCredentials IcebergTableInformation::GetVendedCredentials(ClientConte
133
168
create_secret_input.scope .push_back (ignore_credential_prefix ? metadata_location : credential.prefix );
134
169
create_secret_input.name = StringUtil::Format (" %s_%d_%s" , secret_base_name, index, credential.prefix );
135
170
136
- create_secret_input.type = " s3 " ;
171
+ create_secret_input.type = storage_type ;
137
172
create_secret_input.provider = " config" ;
138
173
create_secret_input.storage_type = " memory" ;
139
174
create_secret_input.options = config_options;
140
175
141
- ParseConfigOptions (credential.config , create_secret_input.options );
176
+ ParseConfigOptions (credential.config , create_secret_input.options , storage_type );
142
177
// ! TODO: apply the 'overrides' retrieved from the /v1/config endpoint
143
178
result.storage_credentials .push_back (create_secret_input);
144
179
}
@@ -154,7 +189,7 @@ IRCAPITableCredentials IcebergTableInformation::GetVendedCredentials(ClientConte
154
189
// ! TODO: apply the 'overrides' retrieved from the /v1/config endpoint
155
190
config.options = config_options;
156
191
config.name = secret_base_name;
157
- config.type = " s3 " ;
192
+ config.type = storage_type ;
158
193
config.provider = " config" ;
159
194
config.storage_type = " memory" ;
160
195
}
0 commit comments