@@ -105,35 +105,9 @@ constexpr std::string_view kEndSnapshotId = "end-snapshot-id";
105105constexpr std::string_view kStatsFields = " stats-fields" ;
106106constexpr std::string_view kMinRowsRequested = " min-rows-requested" ;
107107constexpr std::string_view kPlanTask = " plan-task" ;
108- constexpr std::string_view kContent = " content" ;
109- constexpr std::string_view kContentData = " data" ;
110- constexpr std::string_view kContentPositionDeletes = " position-deletes" ;
111- constexpr std::string_view kContentEqualityDeletes = " equality-deletes" ;
112- constexpr std::string_view kFilePath = " file-path" ;
113- constexpr std::string_view kFileFormat = " file-format" ;
114- constexpr std::string_view kSpecId = " spec-id" ;
115- constexpr std::string_view kPartition = " partition" ;
116- constexpr std::string_view kRecordCount = " record-count" ;
117- constexpr std::string_view kFileSizeInBytes = " file-size-in-bytes" ;
118- constexpr std::string_view kColumnSizes = " column-sizes" ;
119- constexpr std::string_view kValueCounts = " value-counts" ;
120- constexpr std::string_view kNullValueCounts = " null-value-counts" ;
121- constexpr std::string_view kNanValueCounts = " nan-value-counts" ;
122- constexpr std::string_view kLowerBounds = " lower-bounds" ;
123- constexpr std::string_view kUpperBounds = " upper-bounds" ;
124- constexpr std::string_view kKeyMetadata = " key-metadata" ;
125- constexpr std::string_view kSplitOffsets = " split-offsets" ;
126- constexpr std::string_view kEqualityIds = " equality-ids" ;
127- constexpr std::string_view kSortOrderId = " sort-order-id" ;
128- constexpr std::string_view kFirstRowId = " first-row-id" ;
129- constexpr std::string_view kReferencedDataFile = " referenced-data-file" ;
130- constexpr std::string_view kContentOffset = " content-offset" ;
131- constexpr std::string_view kContentSizeInBytes = " content-size-in-bytes" ;
132108constexpr std::string_view kDataFile = " data-file" ;
133109constexpr std::string_view kDeleteFileReferences = " delete-file-references" ;
134110constexpr std::string_view kResidualFilter = " residual-filter" ;
135- constexpr std::string_view kMapKeys = " keys" ;
136- constexpr std::string_view kMapValues = " values" ;
137111
138112Result<nlohmann::json> StorageCredentialToJson (const StorageCredential& credential) {
139113 ICEBERG_RETURN_UNEXPECTED (credential.Validate ());
@@ -170,157 +144,8 @@ Result<std::vector<StorageCredential>> StorageCredentialsFromJson(
170144 }
171145 return credentials;
172146}
173-
174- template <typename Value>
175- Result<std::map<int32_t , Value>> KeyValueMapFromJson (const nlohmann::json& json,
176- std::string_view key) {
177- std::map<int32_t , Value> result;
178- if (!json.contains (key) || json.at (key).is_null ()) {
179- return result;
180- }
181-
182- ICEBERG_ASSIGN_OR_RAISE (auto map_json, GetJsonValue<nlohmann::json>(json, key));
183- ICEBERG_ASSIGN_OR_RAISE (auto keys,
184- GetJsonValue<std::vector<int32_t >>(map_json, kMapKeys ));
185- ICEBERG_ASSIGN_OR_RAISE (auto values,
186- GetJsonValue<std::vector<Value>>(map_json, kMapValues ));
187- if (keys.size () != values.size ()) {
188- return JsonParseError (" '{}' map keys and values have different lengths" , key);
189- }
190-
191- for (size_t i = 0 ; i < keys.size (); ++i) {
192- result[keys[i]] = std::move (values[i]);
193- }
194- return result;
195- }
196-
197- template <typename Value>
198- void SetKeyValueMap (nlohmann::json& json, std::string_view key,
199- const std::map<int32_t , Value>& map) {
200- if (map.empty ()) {
201- return ;
202- }
203-
204- std::vector<int32_t > keys;
205- std::vector<Value> values;
206- keys.reserve (map.size ());
207- values.reserve (map.size ());
208- for (const auto & [field_id, value] : map) {
209- keys.push_back (field_id);
210- values.push_back (value);
211- }
212- json[key] = {{kMapKeys , std::move (keys)}, {kMapValues , std::move (values)}};
213- }
214-
215147} // namespace
216148
217- Result<DataFile> DataFileFromJson (
218- const nlohmann::json& json,
219- const std::unordered_map<int32_t , std::shared_ptr<PartitionSpec>>&
220- partition_spec_by_id,
221- const Schema& schema) {
222- if (!json.is_object ()) {
223- return JsonParseError (" DataFile must be a JSON object: {}" , SafeDumpJson (json));
224- }
225- DataFile data_file;
226-
227- ICEBERG_ASSIGN_OR_RAISE (auto content_str, GetJsonValue<std::string>(json, kContent ));
228- if (content_str == kContentData ) {
229- data_file.content = DataFile::Content::kData ;
230- } else if (content_str == kContentPositionDeletes ) {
231- data_file.content = DataFile::Content::kPositionDeletes ;
232- } else if (content_str == kContentEqualityDeletes ) {
233- data_file.content = DataFile::Content::kEqualityDeletes ;
234- } else {
235- return JsonParseError (" Unknown data file content: {}" , content_str);
236- }
237-
238- ICEBERG_ASSIGN_OR_RAISE (data_file.file_path ,
239- GetJsonValue<std::string>(json, kFilePath ));
240- ICEBERG_ASSIGN_OR_RAISE (auto format_str, GetJsonValue<std::string>(json, kFileFormat ));
241- ICEBERG_ASSIGN_OR_RAISE (data_file.file_format , FileFormatTypeFromString (format_str));
242-
243- ICEBERG_ASSIGN_OR_RAISE (auto spec_id, GetJsonValue<int32_t >(json, kSpecId ));
244- data_file.partition_spec_id = spec_id;
245-
246- ICEBERG_ASSIGN_OR_RAISE (auto partition_vals,
247- GetJsonValue<nlohmann::json>(json, kPartition ));
248- if (!partition_vals.is_array ()) {
249- return JsonParseError (" PartitionValues must be a JSON array: {}" ,
250- SafeDumpJson (partition_vals));
251- }
252- std::vector<Literal> literals;
253- auto it = partition_spec_by_id.find (spec_id);
254- if (it == partition_spec_by_id.end ()) {
255- return JsonParseError (" Invalid partition spec id: {}" , spec_id);
256- }
257- ICEBERG_ASSIGN_OR_RAISE (auto struct_type, it->second ->PartitionType (schema));
258- auto fields = struct_type->fields ();
259- if (partition_vals.size () != fields.size ()) {
260- return JsonParseError (" Invalid partition data size: expected = {}, actual = {}" ,
261- fields.size (), partition_vals.size ());
262- }
263- for (size_t pos = 0 ; pos < fields.size (); ++pos) {
264- ICEBERG_ASSIGN_OR_RAISE (
265- auto literal, LiteralFromJson (partition_vals[pos], fields[pos].type ().get ()));
266- literals.push_back (std::move (literal));
267- }
268- data_file.partition = PartitionValues (std::move (literals));
269-
270- ICEBERG_ASSIGN_OR_RAISE (data_file.record_count ,
271- GetJsonValue<int64_t >(json, kRecordCount ));
272- ICEBERG_ASSIGN_OR_RAISE (data_file.file_size_in_bytes ,
273- GetJsonValue<int64_t >(json, kFileSizeInBytes ));
274-
275- ICEBERG_ASSIGN_OR_RAISE (data_file.column_sizes ,
276- KeyValueMapFromJson<int64_t >(json, kColumnSizes ));
277- ICEBERG_ASSIGN_OR_RAISE (data_file.value_counts ,
278- KeyValueMapFromJson<int64_t >(json, kValueCounts ));
279- ICEBERG_ASSIGN_OR_RAISE (data_file.null_value_counts ,
280- KeyValueMapFromJson<int64_t >(json, kNullValueCounts ));
281- ICEBERG_ASSIGN_OR_RAISE (data_file.nan_value_counts ,
282- KeyValueMapFromJson<int64_t >(json, kNanValueCounts ));
283- ICEBERG_ASSIGN_OR_RAISE (data_file.lower_bounds ,
284- KeyValueMapFromJson<std::vector<uint8_t >>(json, kLowerBounds ));
285- ICEBERG_ASSIGN_OR_RAISE (data_file.upper_bounds ,
286- KeyValueMapFromJson<std::vector<uint8_t >>(json, kUpperBounds ));
287-
288- if (json.contains (kKeyMetadata ) && !json.at (kKeyMetadata ).is_null ()) {
289- ICEBERG_ASSIGN_OR_RAISE (data_file.key_metadata ,
290- GetJsonValue<std::vector<uint8_t >>(json, kKeyMetadata ));
291- }
292- if (json.contains (kSplitOffsets ) && !json.at (kSplitOffsets ).is_null ()) {
293- ICEBERG_ASSIGN_OR_RAISE (data_file.split_offsets ,
294- GetJsonValue<std::vector<int64_t >>(json, kSplitOffsets ));
295- }
296- if (json.contains (kEqualityIds ) && !json.at (kEqualityIds ).is_null ()) {
297- ICEBERG_ASSIGN_OR_RAISE (data_file.equality_ids ,
298- GetJsonValue<std::vector<int32_t >>(json, kEqualityIds ));
299- }
300- if (json.contains (kSortOrderId ) && !json.at (kSortOrderId ).is_null ()) {
301- ICEBERG_ASSIGN_OR_RAISE (data_file.sort_order_id ,
302- GetJsonValue<int32_t >(json, kSortOrderId ));
303- }
304- if (json.contains (kFirstRowId ) && !json.at (kFirstRowId ).is_null ()) {
305- ICEBERG_ASSIGN_OR_RAISE (data_file.first_row_id ,
306- GetJsonValue<int64_t >(json, kFirstRowId ));
307- }
308- if (json.contains (kReferencedDataFile ) && !json.at (kReferencedDataFile ).is_null ()) {
309- ICEBERG_ASSIGN_OR_RAISE (data_file.referenced_data_file ,
310- GetJsonValue<std::string>(json, kReferencedDataFile ));
311- }
312- if (json.contains (kContentOffset ) && !json.at (kContentOffset ).is_null ()) {
313- ICEBERG_ASSIGN_OR_RAISE (data_file.content_offset ,
314- GetJsonValue<int64_t >(json, kContentOffset ));
315- }
316- if (json.contains (kContentSizeInBytes ) && !json.at (kContentSizeInBytes ).is_null ()) {
317- ICEBERG_ASSIGN_OR_RAISE (data_file.content_size_in_bytes ,
318- GetJsonValue<int64_t >(json, kContentSizeInBytes ));
319- }
320-
321- return data_file;
322- }
323-
324149Result<std::vector<std::shared_ptr<FileScanTask>>> FileScanTasksFromJson (
325150 const nlohmann::json& json,
326151 const std::vector<std::shared_ptr<DataFile>>& delete_files,
@@ -341,7 +166,8 @@ Result<std::vector<std::shared_ptr<FileScanTask>>> FileScanTasksFromJson(
341166 ICEBERG_ASSIGN_OR_RAISE (auto data_file_json,
342167 GetJsonValue<nlohmann::json>(task_json, kDataFile ));
343168 ICEBERG_ASSIGN_OR_RAISE (
344- auto data_file, DataFileFromJson (data_file_json, partition_spec_by_id, schema));
169+ auto data_file,
170+ iceberg::DataFileFromJson (data_file_json, partition_spec_by_id, schema));
345171 // FIXME: REST scan-task DataFile JSON currently carries first-row-id,
346172 // but not the manifest-entry data sequence number. Until the REST API exposes
347173 // it, REST-planned tasks cannot inherit _last_updated_sequence_number.
@@ -376,99 +202,6 @@ Result<std::vector<std::shared_ptr<FileScanTask>>> FileScanTasksFromJson(
376202 return file_scan_tasks;
377203}
378204
379- Result<nlohmann::json> DataFileToJsonUnchecked (const DataFile& data_file) {
380- nlohmann::json json;
381- switch (data_file.content ) {
382- case DataFile::Content::kData :
383- json[kContent ] = kContentData ;
384- break ;
385- case DataFile::Content::kPositionDeletes :
386- json[kContent ] = kContentPositionDeletes ;
387- break ;
388- case DataFile::Content::kEqualityDeletes :
389- json[kContent ] = kContentEqualityDeletes ;
390- break ;
391- }
392- json[kFilePath ] = data_file.file_path ;
393- json[kFileFormat ] = ToString (data_file.file_format );
394-
395- if (!data_file.partition_spec_id .has_value ()) {
396- return ValidationFailed (" Cannot serialize REST content file without 'spec-id'" );
397- }
398- json[kSpecId ] = data_file.partition_spec_id .value ();
399-
400- nlohmann::json partition_json = nlohmann::json::array ();
401- for (const auto & literal : data_file.partition .values ()) {
402- ICEBERG_ASSIGN_OR_RAISE (auto lit_json, iceberg::ToJson (literal));
403- partition_json.push_back (std::move (lit_json));
404- }
405- json[kPartition ] = std::move (partition_json);
406-
407- json[kRecordCount ] = data_file.record_count ;
408- json[kFileSizeInBytes ] = data_file.file_size_in_bytes ;
409-
410- SetKeyValueMap (json, kColumnSizes , data_file.column_sizes );
411- SetKeyValueMap (json, kValueCounts , data_file.value_counts );
412- SetKeyValueMap (json, kNullValueCounts , data_file.null_value_counts );
413- SetKeyValueMap (json, kNanValueCounts , data_file.nan_value_counts );
414- SetKeyValueMap (json, kLowerBounds , data_file.lower_bounds );
415- SetKeyValueMap (json, kUpperBounds , data_file.upper_bounds );
416-
417- if (!data_file.key_metadata .empty ()) {
418- json[kKeyMetadata ] = data_file.key_metadata ;
419- }
420- if (!data_file.split_offsets .empty ()) {
421- json[kSplitOffsets ] = data_file.split_offsets ;
422- }
423- if (!data_file.equality_ids .empty ()) {
424- json[kEqualityIds ] = data_file.equality_ids ;
425- }
426- if (data_file.sort_order_id .has_value ()) {
427- json[kSortOrderId ] = data_file.sort_order_id .value ();
428- }
429- if (data_file.first_row_id .has_value ()) {
430- json[kFirstRowId ] = data_file.first_row_id .value ();
431- }
432- if (data_file.referenced_data_file .has_value ()) {
433- json[kReferencedDataFile ] = data_file.referenced_data_file .value ();
434- }
435- if (data_file.content_offset .has_value ()) {
436- json[kContentOffset ] = data_file.content_offset .value ();
437- }
438- if (data_file.content_size_in_bytes .has_value ()) {
439- json[kContentSizeInBytes ] = data_file.content_size_in_bytes .value ();
440- }
441-
442- return json;
443- }
444-
445- Result<nlohmann::json> ToJson (
446- const DataFile& data_file,
447- const std::unordered_map<int32_t , std::shared_ptr<PartitionSpec>>&
448- partition_specs_by_id,
449- const Schema& schema) {
450- if (!data_file.partition_spec_id .has_value ()) {
451- return ValidationFailed (" Invalid partition spec id from content file: null" );
452- }
453- auto it = partition_specs_by_id.find (data_file.partition_spec_id .value ());
454- if (it == partition_specs_by_id.end () || !it->second ) {
455- return ValidationFailed (" Invalid partition spec: null" );
456- }
457- if (data_file.partition_spec_id .value () != it->second ->spec_id ()) {
458- return ValidationFailed (
459- " Invalid partition spec id from content file: expected = {}, actual = {}" ,
460- it->second ->spec_id (), data_file.partition_spec_id .value ());
461- }
462- ICEBERG_ASSIGN_OR_RAISE (auto partition_type, it->second ->PartitionType (schema));
463- if (data_file.partition .num_fields () != partition_type->fields ().size ()) {
464- return ValidationFailed (
465- " Invalid partition data from content file: expected = {}, actual = {}" ,
466- partition_type->fields ().empty () ? " unpartitioned" : " partitioned" ,
467- data_file.partition .num_fields () == 0 ? " unpartitioned" : " partitioned" );
468- }
469- return DataFileToJsonUnchecked (data_file);
470- }
471-
472205namespace {
473206
474207template <typename Response>
@@ -514,7 +247,7 @@ Result<nlohmann::json> ScanTaskFieldsToJson(
514247 if (task->data_file ()) {
515248 ICEBERG_ASSIGN_OR_RAISE (
516249 auto data_file_json,
517- ToJson (*task->data_file (), partition_specs_by_id, schema));
250+ iceberg:: ToJson (*task->data_file (), partition_specs_by_id, schema));
518251 task_json[kDataFile ] = std::move (data_file_json);
519252 }
520253 if (!task->delete_files ().empty ()) {
@@ -540,7 +273,8 @@ Result<nlohmann::json> ScanTaskFieldsToJson(
540273 }
541274 nlohmann::json delete_files_json = nlohmann::json::array ();
542275 for (const auto & file : delete_files) {
543- ICEBERG_ASSIGN_OR_RAISE (auto df_json, ToJson (*file, partition_specs_by_id, schema));
276+ ICEBERG_ASSIGN_OR_RAISE (auto df_json,
277+ iceberg::ToJson (*file, partition_specs_by_id, schema));
544278 delete_files_json.push_back (std::move (df_json));
545279 }
546280 if (!delete_files_json.empty ()) {
@@ -576,8 +310,9 @@ Status ScanTaskFieldsFromJson(
576310 SafeDumpJson (delete_files_json));
577311 }
578312 for (const auto & entry_json : delete_files_json) {
579- ICEBERG_ASSIGN_OR_RAISE (auto delete_file,
580- DataFileFromJson (entry_json, partition_specs_by_id, schema));
313+ ICEBERG_ASSIGN_OR_RAISE (
314+ auto delete_file,
315+ iceberg::DataFileFromJson (entry_json, partition_specs_by_id, schema));
581316 response.delete_files .push_back (std::make_shared<DataFile>(std::move (delete_file)));
582317 }
583318
0 commit comments