From 38d9d707ef7ad3acb4aa7374e09753c99a49f306 Mon Sep 17 00:00:00 2001 From: Gatefixer <312823363+lance-gatefixer[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 13:26:40 +0000 Subject: [PATCH] fix: allow empty V1 writes with non-nullable columns --- rust/lance-file/src/versions/v1/writer/mod.rs | 31 +++++++++++++++++++ .../src/versions/v1/writer/statistics.rs | 6 ++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/rust/lance-file/src/versions/v1/writer/mod.rs b/rust/lance-file/src/versions/v1/writer/mod.rs index 911065d2c2c..e3188e0d6a7 100644 --- a/rust/lance-file/src/versions/v1/writer/mod.rs +++ b/rust/lance-file/src/versions/v1/writer/mod.rs @@ -982,6 +982,37 @@ mod tests { assert_eq!(actual, batch); } + #[tokio::test] + async fn test_write_empty_non_nullable_string() { + let arrow_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new( + "value", + DataType::Utf8, + false, + )])); + let schema = Schema::try_from(arrow_schema.as_ref()).unwrap(); + let store = ObjectStore::memory(); + let path = Path::from("/empty"); + let mut file_writer = FileWriter::::try_new( + &store, + &path, + schema.clone(), + &Default::default(), + ) + .await + .unwrap(); + + file_writer + .write(&[RecordBatch::new_empty(arrow_schema)]) + .await + .unwrap(); + let summary = file_writer.finish().await.unwrap(); + assert_eq!(summary.num_rows, 0); + + let reader = FileReader::try_new(&store, &path, schema).await.unwrap(); + let actual = reader.read_batch(0, .., reader.schema()).await.unwrap(); + assert_eq!(actual.num_rows(), 0); + } + #[tokio::test] async fn test_collect_stats() { // Validate: diff --git a/rust/lance-file/src/versions/v1/writer/statistics.rs b/rust/lance-file/src/versions/v1/writer/statistics.rs index 1ccc38ca43d..7eeb3db12bc 100644 --- a/rust/lance-file/src/versions/v1/writer/statistics.rs +++ b/rust/lance-file/src/versions/v1/writer/statistics.rs @@ -642,8 +642,10 @@ impl StatisticsCollector { let max_value = Arc::new(builder.max_value.finish()); let struct_fields = vec![ ArrowField::new("null_count", DataType::Int64, false), - ArrowField::new("min_value", field.data_type(), field.nullable), - ArrowField::new("max_value", field.data_type(), field.nullable), + // Bounds can be absent for empty pages regardless of the data field's + // nullability. + ArrowField::new("min_value", field.data_type(), true), + ArrowField::new("max_value", field.data_type(), true), ]; let stats = StructArray::new(