@@ -60,6 +60,43 @@ TEST(SizeStatistics, UpdateLevelHistogram) {
6060 UpdateLevelHistogram (std::vector<int16_t >{}, histogram);
6161 EXPECT_THAT (histogram, ::testing::ElementsAre (3 , 3 , 2 ));
6262 }
63+ {
64+ // Empty span should be a no-op.
65+ std::vector<int64_t > histogram (2 , 0 );
66+ UpdateLevelHistogram (std::span<const int16_t >{}, histogram);
67+ EXPECT_THAT (histogram, ::testing::ElementsAre (0 , 0 ));
68+ }
69+ }
70+
71+ // Regression test for GH-49928: WriteBatch(0, nullptr, ...) on a nullable column
72+ // must not crash or DCHECK-fail, even though max_definition_level > 0.
73+ TEST (SizeStatistics, NullLevelsInColumnWriter) {
74+ auto node = schema::Int32 (" a" , Repetition::OPTIONAL );
75+ auto schema_node = schema::GroupNode::Make (" schema" , Repetition::REQUIRED , {node});
76+
77+ auto props = WriterProperties::Builder ()
78+ .enable_write_page_index ()
79+ ->enable_statistics ()
80+ ->set_size_statistics_level (SizeStatisticsLevel::PageAndColumnChunk)
81+ ->build ();
82+
83+ auto sink = CreateOutputStream ();
84+ auto writer = ParquetFileWriter::Open (sink, std::dynamic_pointer_cast<schema::GroupNode>(schema_node), props);
85+ auto rg = writer->AppendRowGroup ();
86+ auto col = static_cast <Int32Writer*>(rg->NextColumn ());
87+
88+ // Empty write: num_values=0 with nullptr levels — must not crash.
89+ col->WriteBatch (/* num_values=*/ 0 , /* def_levels=*/ nullptr ,
90+ /* rep_levels=*/ nullptr , /* values=*/ nullptr );
91+
92+ // Follow up with a real write so the file is valid.
93+ std::vector<int32_t > values = {42 };
94+ std::vector<int16_t > def_levels = {1 };
95+ col->WriteBatch (1 , def_levels.data (), nullptr , values.data ());
96+
97+ col->Close ();
98+ rg->Close ();
99+ writer->Close ();
63100}
64101
65102TEST (SizeStatistics, ThriftSerDe) {
0 commit comments