This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 8c903819 fix: use i64 for index file size (#586)
8c903819 is described below

commit 8c9038195ae20fa4c0d5f8e812170a7497a83420
Author: XiaoHongbo <[email protected]>
AuthorDate: Wed Jul 22 22:47:13 2026 +0800

    fix: use i64 for index file size (#586)
---
 bindings/c/src/tests.rs                            |  2 +-
 .../datafusion/src/system_tables/table_indexes.rs  |  2 +-
 .../integrations/datafusion/tests/system_tables.rs |  5 +---
 .../src/spec/avro/index_manifest_entry_decode.rs   |  4 +--
 crates/paimon/src/spec/index_file_meta.rs          |  2 +-
 crates/paimon/src/spec/index_manifest.rs           | 33 +++++++++++++++++++---
 .../src/table/btree_global_index_build_builder.rs  |  6 ++--
 crates/paimon/src/table/bucket_assigner_dynamic.rs |  4 +--
 crates/paimon/src/table/data_evolution_writer.rs   |  2 +-
 crates/paimon/src/table/global_index_scanner.rs    |  2 +-
 .../paimon/src/table/lumina_index_build_builder.rs | 18 ++++++++----
 crates/paimon/src/table/referenced_files.rs        |  2 +-
 .../paimon/src/table/vindex_index_build_builder.rs |  9 +++++-
 crates/paimon/tests/pk_vector_baseline_test.rs     |  4 +--
 crates/paimon/tests/pk_vector_batch_test.rs        |  2 +-
 15 files changed, 66 insertions(+), 31 deletions(-)

diff --git a/bindings/c/src/tests.rs b/bindings/c/src/tests.rs
index 3897189c..58fb27ab 100644
--- a/bindings/c/src/tests.rs
+++ b/bindings/c/src/tests.rs
@@ -1764,7 +1764,7 @@ fn build_pk_vector_table(path: &str, vectors: &[[f32; 
PK_DIM]]) -> Table {
         let index_file = IndexFileMeta {
             index_type: INDEX_TYPE.to_string(),
             file_name: index_file_name,
-            file_size: i32::try_from(index_file_size).unwrap(),
+            file_size: i64::try_from(index_file_size).unwrap(),
             row_count: i32::try_from(row_count).unwrap(),
             deletion_vectors_ranges: None,
             global_index_meta: Some(GlobalIndexMeta {
diff --git a/crates/integrations/datafusion/src/system_tables/table_indexes.rs 
b/crates/integrations/datafusion/src/system_tables/table_indexes.rs
index b48a8a1c..c1c900ee 100644
--- a/crates/integrations/datafusion/src/system_tables/table_indexes.rs
+++ b/crates/integrations/datafusion/src/system_tables/table_indexes.rs
@@ -131,7 +131,7 @@ impl TableProvider for TableIndexesTable {
             buckets.push(entry.bucket);
             index_types.push(index_file.index_type.as_str());
             file_names.push(index_file.file_name.as_str());
-            file_sizes.push(i64::from(index_file.file_size));
+            file_sizes.push(index_file.file_size);
             row_counts.push(i64::from(index_file.row_count));
             append_dv_ranges(
                 &mut dv_ranges,
diff --git a/crates/integrations/datafusion/tests/system_tables.rs 
b/crates/integrations/datafusion/tests/system_tables.rs
index be1d13ec..ca32da12 100644
--- a/crates/integrations/datafusion/tests/system_tables.rs
+++ b/crates/integrations/datafusion/tests/system_tables.rs
@@ -286,10 +286,7 @@ async fn test_table_indexes_system_table() {
         assert_eq!(buckets.value(row), expected.bucket);
         assert_eq!(index_types.value(row), expected.index_file.index_type);
         assert_eq!(file_names.value(row), expected.index_file.file_name);
-        assert_eq!(
-            file_sizes.value(row),
-            i64::from(expected.index_file.file_size)
-        );
+        assert_eq!(file_sizes.value(row), expected.index_file.file_size);
         assert_eq!(
             row_counts.value(row),
             i64::from(expected.index_file.row_count)
diff --git a/crates/paimon/src/spec/avro/index_manifest_entry_decode.rs 
b/crates/paimon/src/spec/avro/index_manifest_entry_decode.rs
index 986b27b9..efdd960f 100644
--- a/crates/paimon/src/spec/avro/index_manifest_entry_decode.rs
+++ b/crates/paimon/src/spec/avro/index_manifest_entry_decode.rs
@@ -35,7 +35,7 @@ impl AvroRecordDecode for IndexManifestEntry {
         let mut bucket: Option<i32> = None;
         let mut index_type: Option<String> = None;
         let mut file_name: Option<String> = None;
-        let mut file_size: Option<i32> = None;
+        let mut file_size: Option<i64> = None;
         let mut row_count: Option<i32> = None;
         let mut deletion_vectors_ranges: Option<IndexMap<String, 
DeletionVectorMeta>> = None;
         let mut global_index_meta: Option<GlobalIndexMeta> = None;
@@ -60,7 +60,7 @@ impl AvroRecordDecode for IndexManifestEntry {
                 "_BUCKET" => bucket = Some(read_int_field(cursor, 
field.nullable)?),
                 "_INDEX_TYPE" => index_type = Some(read_string_field(cursor, 
field.nullable)?),
                 "_FILE_NAME" => file_name = Some(read_string_field(cursor, 
field.nullable)?),
-                "_FILE_SIZE" => file_size = Some(read_long_field(cursor, 
field.nullable)? as i32),
+                "_FILE_SIZE" => file_size = Some(read_long_field(cursor, 
field.nullable)?),
                 "_ROW_COUNT" => row_count = Some(read_long_field(cursor, 
field.nullable)? as i32),
                 "_DELETIONS_VECTORS_RANGES" | "_DELETION_VECTORS_RANGES" => {
                     deletion_vectors_ranges = 
decode_nullable_dv_ranges(cursor, field.nullable)?;
diff --git a/crates/paimon/src/spec/index_file_meta.rs 
b/crates/paimon/src/spec/index_file_meta.rs
index ff1e63c0..d6e6e80e 100644
--- a/crates/paimon/src/spec/index_file_meta.rs
+++ b/crates/paimon/src/spec/index_file_meta.rs
@@ -63,7 +63,7 @@ pub struct IndexFileMeta {
     pub file_name: String,
 
     #[serde(rename = "_FILE_SIZE")]
-    pub file_size: i32,
+    pub file_size: i64,
 
     #[serde(rename = "_ROW_COUNT")]
     pub row_count: i32,
diff --git a/crates/paimon/src/spec/index_manifest.rs 
b/crates/paimon/src/spec/index_manifest.rs
index a0430b2f..5160b61e 100644
--- a/crates/paimon/src/spec/index_manifest.rs
+++ b/crates/paimon/src/spec/index_manifest.rs
@@ -27,10 +27,9 @@ use crate::Result;
 ///
 /// Must match the serde layout of `IndexManifestEntry`.
 ///
-/// Note: `_FILE_SIZE` and `_ROW_COUNT` are declared as Avro `long` to match
-/// Java Paimon's schema, while the Rust `IndexFileMeta` fields are `i32`.
-/// `serde_avro_fast` transparently coerces between integer widths during
-/// serialization/deserialization, so the mismatch is intentional.
+/// Note: `_FILE_SIZE` is an Avro `long` and Rust `i64`, matching Java Paimon's
+/// schema. `_ROW_COUNT` remains an Avro `long` for Java compatibility while
+/// the Rust `IndexFileMeta` field is still `i32`.
 pub const INDEX_MANIFEST_ENTRY_SCHEMA: &str = r#"{
     "type": "record",
     "name": "org.apache.paimon.avro.generated.record",
@@ -345,6 +344,32 @@ mod tests {
         );
     }
 
+    #[test]
+    fn file_size_above_i32_max_round_trips_through_index_manifest() {
+        let file_size = i64::from(i32::MAX) + 1;
+        let entry: IndexManifestEntry = 
serde_json::from_value(serde_json::json!({
+            "_VERSION": 1,
+            "_KIND": 0,
+            "_PARTITION": [0, 0, 0, 0],
+            "_BUCKET": 0,
+            "_INDEX_TYPE": "TEST",
+            "_FILE_NAME": "index",
+            "_FILE_SIZE": file_size,
+            "_ROW_COUNT": 1
+        }))
+        .unwrap();
+
+        let bytes = crate::spec::to_avro_bytes_with_compression(
+            INDEX_MANIFEST_ENTRY_SCHEMA,
+            std::slice::from_ref(&entry),
+            crate::spec::DEFAULT_AVRO_COMPRESSION,
+        )
+        .unwrap();
+
+        let decoded = IndexManifest::read_from_bytes(&bytes).unwrap();
+        assert_eq!(decoded, vec![entry]);
+    }
+
     #[test]
     fn legacy_five_field_global_index_decodes_without_source_meta() {
         // 5-field _GLOBAL_INDEX schema (pre-#8549): no _SOURCE_META. 
Identical to
diff --git a/crates/paimon/src/table/btree_global_index_build_builder.rs 
b/crates/paimon/src/table/btree_global_index_build_builder.rs
index 90b9891a..07bbe166 100644
--- a/crates/paimon/src/table/btree_global_index_build_builder.rs
+++ b/crates/paimon/src/table/btree_global_index_build_builder.rs
@@ -275,7 +275,7 @@ impl<'a> BTreeGlobalIndexBuildBuilder<'a> {
         Ok(IndexFileMeta {
             index_type: index_type.to_string(),
             file_name,
-            file_size: checked_i32(
+            file_size: checked_i64(
                 status.size,
                 "Index file is too large for Rust IndexFileMeta",
             )?,
@@ -733,8 +733,8 @@ fn sort_index_rows(rows: &mut [BTreeKeyRow], cmp: &dyn 
Fn(&[u8], &[u8]) -> Order
     });
 }
 
-fn checked_i32(value: u64, context: &str) -> Result<i32> {
-    i32::try_from(value).map_err(|_| Error::DataInvalid {
+fn checked_i64(value: u64, context: &str) -> Result<i64> {
+    i64::try_from(value).map_err(|_| Error::DataInvalid {
         message: format!("{context}: {value}"),
         source: None,
     })
diff --git a/crates/paimon/src/table/bucket_assigner_dynamic.rs 
b/crates/paimon/src/table/bucket_assigner_dynamic.rs
index 9db4db9c..030385d9 100644
--- a/crates/paimon/src/table/bucket_assigner_dynamic.rs
+++ b/crates/paimon/src/table/bucket_assigner_dynamic.rs
@@ -80,10 +80,10 @@ impl HashIndexFile {
             buf.extend_from_slice(&h.to_be_bytes());
         }
 
-        let file_size: i32 = buf
+        let file_size: i64 = buf
             .len()
             .try_into()
-            .expect("hash index file size exceeds i32::MAX");
+            .expect("hash index file size exceeds i64::MAX");
         let output = file_io.new_output(&path)?;
         output.write(bytes::Bytes::from(buf)).await?;
 
diff --git a/crates/paimon/src/table/data_evolution_writer.rs 
b/crates/paimon/src/table/data_evolution_writer.rs
index 49057e9d..47752e3f 100644
--- a/crates/paimon/src/table/data_evolution_writer.rs
+++ b/crates/paimon/src/table/data_evolution_writer.rs
@@ -689,7 +689,7 @@ impl DataEvolutionDeleteWriter {
             bytes.extend_from_slice(&serialized);
         }
 
-        let file_size = i32::try_from(bytes.len()).map_err(|_| 
crate::Error::DataInvalid {
+        let file_size = i64::try_from(bytes.len()).map_err(|_| 
crate::Error::DataInvalid {
             message: "Deletion-vector index file is too large".to_string(),
             source: None,
         })?;
diff --git a/crates/paimon/src/table/global_index_scanner.rs 
b/crates/paimon/src/table/global_index_scanner.rs
index c535db7f..b6f50e88 100644
--- a/crates/paimon/src/table/global_index_scanner.rs
+++ b/crates/paimon/src/table/global_index_scanner.rs
@@ -211,7 +211,7 @@ impl GlobalIndexScanner {
                     BITMAP_GLOBAL_INDEX_TYPE => GlobalIndexFileKind::Bitmap,
                     _ => unreachable!("normalized sorted global index type"),
                 },
-                file_size: i64::from(entry.index_file.file_size),
+                file_size: entry.index_file.file_size,
                 row_range_start: global_meta.row_range_start,
                 meta: sorted_meta,
             };
diff --git a/crates/paimon/src/table/lumina_index_build_builder.rs 
b/crates/paimon/src/table/lumina_index_build_builder.rs
index cbff6e7e..5e45abf8 100644
--- a/crates/paimon/src/table/lumina_index_build_builder.rs
+++ b/crates/paimon/src/table/lumina_index_build_builder.rs
@@ -230,7 +230,7 @@ impl<'a> LuminaIndexBuildBuilder<'a> {
         Ok(IndexFileMeta {
             index_type: LUMINA_IDENTIFIER.to_string(),
             file_name,
-            file_size: checked_i32(
+            file_size: checked_i64(
                 status.size,
                 "Index file is too large for Rust IndexFileMeta",
             )?,
@@ -741,8 +741,8 @@ fn extract_vectors_from_batches(
     Ok(vectors)
 }
 
-fn checked_i32(value: u64, context: &str) -> Result<i32> {
-    i32::try_from(value).map_err(|_| Error::DataInvalid {
+fn checked_i64(value: u64, context: &str) -> Result<i64> {
+    i64::try_from(value).map_err(|_| Error::DataInvalid {
         message: format!("{context}: {value}"),
         source: None,
     })
@@ -1517,9 +1517,15 @@ mod tests {
     }
 
     #[test]
-    fn test_checked_metadata_conversion_rejects_large_file_size() {
-        let err = checked_i32(i32::MAX as u64 + 1, "Index file is too large")
-            .expect_err("large file size should fail");
+    fn test_checked_metadata_conversion_supports_i64_file_size() {
+        let above_i32_max = i32::MAX as u64 + 1;
+        assert_eq!(
+            checked_i64(above_i32_max, "Index file is too large").unwrap(),
+            i64::from(i32::MAX) + 1
+        );
+
+        let err = checked_i64(i64::MAX as u64 + 1, "Index file is too large")
+            .expect_err("file size above i64::MAX should fail");
         assert!(matches!(err, Error::DataInvalid { message, .. } if 
message.contains("too large")));
     }
 
diff --git a/crates/paimon/src/table/referenced_files.rs 
b/crates/paimon/src/table/referenced_files.rs
index 8d1f1cac..10fe2437 100644
--- a/crates/paimon/src/table/referenced_files.rs
+++ b/crates/paimon/src/table/referenced_files.rs
@@ -499,7 +499,7 @@ async fn collect_snapshot_files(
             file_set
                 .index_files
                 .entry(entry.index_file.file_name.clone())
-                .or_insert(entry.index_file.file_size as i64);
+                .or_insert(entry.index_file.file_size);
         }
     }
 
diff --git a/crates/paimon/src/table/vindex_index_build_builder.rs 
b/crates/paimon/src/table/vindex_index_build_builder.rs
index 0f0d8c25..11579c6e 100644
--- a/crates/paimon/src/table/vindex_index_build_builder.rs
+++ b/crates/paimon/src/table/vindex_index_build_builder.rs
@@ -255,7 +255,7 @@ impl<'a> VindexIndexBuildBuilder<'a> {
         Ok(IndexFileMeta {
             index_type: self.index_type.clone(),
             file_name,
-            file_size: checked_i32(
+            file_size: checked_i64(
                 status.size,
                 "Index file is too large for Rust IndexFileMeta",
             )?,
@@ -728,6 +728,13 @@ fn checked_i32(value: u64, context: &str) -> Result<i32> {
     })
 }
 
+fn checked_i64(value: u64, context: &str) -> Result<i64> {
+    i64::try_from(value).map_err(|_| Error::DataInvalid {
+        message: format!("{context}: {value}"),
+        source: None,
+    })
+}
+
 fn checked_row_count(row_range_start: i64, row_range_end: i64) -> Result<i32> {
     if row_range_end < row_range_start {
         return Err(Error::DataInvalid {
diff --git a/crates/paimon/tests/pk_vector_baseline_test.rs 
b/crates/paimon/tests/pk_vector_baseline_test.rs
index 97d9e18c..1328cddd 100644
--- a/crates/paimon/tests/pk_vector_baseline_test.rs
+++ b/crates/paimon/tests/pk_vector_baseline_test.rs
@@ -423,7 +423,7 @@ async fn build_table_with_first_row_id(
     let index_file = IndexFileMeta {
         index_type: INDEX_TYPE.to_string(),
         file_name: index_file_name,
-        file_size: i32::try_from(index_file_size).unwrap(),
+        file_size: i64::try_from(index_file_size).unwrap(),
         row_count: i32::try_from(row_count).unwrap(),
         deletion_vectors_ranges: None,
         global_index_meta: Some(GlobalIndexMeta {
@@ -1270,7 +1270,7 @@ async fn 
pk_vector_refine_factor_matches_exact_ground_truth() {
     let index_file = IndexFileMeta {
         index_type: INDEX_TYPE.to_string(),
         file_name: index_file_name,
-        file_size: i32::try_from(index_file_size).unwrap(),
+        file_size: i64::try_from(index_file_size).unwrap(),
         row_count: i32::try_from(row_count).unwrap(),
         deletion_vectors_ranges: None,
         global_index_meta: Some(GlobalIndexMeta {
diff --git a/crates/paimon/tests/pk_vector_batch_test.rs 
b/crates/paimon/tests/pk_vector_batch_test.rs
index 84790e77..235d82d8 100644
--- a/crates/paimon/tests/pk_vector_batch_test.rs
+++ b/crates/paimon/tests/pk_vector_batch_test.rs
@@ -284,7 +284,7 @@ async fn build_table(vectors: &[[f32; DIM]]) -> 
(tempfile::TempDir, Table) {
     let index_file = IndexFileMeta {
         index_type: INDEX_TYPE.to_string(),
         file_name: index_file_name,
-        file_size: i32::try_from(index_file_size).unwrap(),
+        file_size: i64::try_from(index_file_size).unwrap(),
         row_count: i32::try_from(row_count).unwrap(),
         deletion_vectors_ranges: None,
         global_index_meta: Some(GlobalIndexMeta {

Reply via email to