From 275705f4a40213616ca078836f74f27073c0a79b Mon Sep 17 00:00:00 2001 From: andre sun Date: Tue, 15 Sep 2026 00:59:00 -0400 Subject: [PATCH] fix: relax validation for missing validity buffer when null_count is -1 Validation currently errors if the validity buffer is missing and null_count != 0. Arrow C++ only errors if null_count > 0, and arrays like this show up in the wild. Relax the check to match. Fixes #855 --- src/nanoarrow/common/array.c | 3 ++- src/nanoarrow/common/array_test.cc | 41 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/nanoarrow/common/array.c b/src/nanoarrow/common/array.c index 25b02c205..20daed5ce 100644 --- a/src/nanoarrow/common/array.c +++ b/src/nanoarrow/common/array.c @@ -1612,7 +1612,8 @@ static int ArrowArrayViewValidateMinimal(struct ArrowArrayView* array_view, switch (array_view->layout.buffer_type[i]) { case NANOARROW_BUFFER_TYPE_VALIDITY: - if (array_view->null_count == 0 && array_view->buffer_views[i].size_bytes == 0) { + // Allow a missing validity buffer unless null_count > 0 + if (array_view->null_count <= 0 && array_view->buffer_views[i].size_bytes == 0) { continue; } diff --git a/src/nanoarrow/common/array_test.cc b/src/nanoarrow/common/array_test.cc index 1b0d1290c..520f6df9d 100644 --- a/src/nanoarrow/common/array_test.cc +++ b/src/nanoarrow/common/array_test.cc @@ -2441,6 +2441,47 @@ TEST(ArrayTest, ArrayViewTestBasic) { ArrowArrayViewReset(&array_view); } +TEST(ArrayTest, ArrayViewTestMissingValidityUnknownNullCount) { + struct ArrowError error; + + // A missing validity buffer with null_count == -1 is valid + int32_t values[] = {11, 12, 13}; + const void* buffers[] = {nullptr, values}; + + nanoarrow::UniqueArray array; + array->length = 2; + array->offset = 1; + array->null_count = -1; + array->n_buffers = 2; + array->n_children = 0; + array->buffers = buffers; + array->children = nullptr; + array->dictionary = nullptr; + array->release = [](struct ArrowArray*) {}; + + nanoarrow::UniqueArrayView array_view; + ArrowArrayViewInitFromType(array_view.get(), NANOARROW_TYPE_INT32); + ASSERT_EQ(ArrowArrayViewSetArray(array_view.get(), array.get(), &error), NANOARROW_OK) + << error.message; + EXPECT_EQ( + ArrowArrayViewValidate(array_view.get(), NANOARROW_VALIDATION_LEVEL_FULL, &error), + NANOARROW_OK) + << error.message; + EXPECT_EQ(ArrowArrayViewGetBufferView(array_view.get(), 0).size_bytes, 0); + EXPECT_EQ(ArrowArrayViewComputeNullCount(array_view.get()), 0); + EXPECT_EQ(ArrowArrayViewIsNull(array_view.get(), 0), 0); + EXPECT_EQ(ArrowArrayViewIsNull(array_view.get(), 1), 0); + EXPECT_EQ(ArrowArrayViewGetIntUnsafe(array_view.get(), 0), 12); + EXPECT_EQ(ArrowArrayViewGetIntUnsafe(array_view.get(), 1), 13); + + // A missing validity buffer with null_count > 0 is still an error + array->null_count = 1; + EXPECT_EQ(ArrowArrayViewSetArray(array_view.get(), array.get(), &error), EINVAL); + EXPECT_STREQ(error.message, + "Expected int32 array buffer 0 to have size >= 1 bytes but found buffer " + "with 0 bytes"); +} + TEST(ArrayTest, ArrayViewCompareTestStructure) { struct ArrowError error; struct ArrowArrayView actual;