@@ -5894,13 +5894,12 @@ TEST(TestArrowReadWrite, WriteRecordBatchRespectsMaxRowGroupSize) {
58945894 auto pool = ::arrow::default_memory_pool ();
58955895 auto sink = CreateOutputStream ();
58965896 // Use a small byte size limit with the default (large) row count limit so
5897- // that only the byte size limit takes effect. Use a small data page size
5898- // so that buffered values are flushed to pages (and thus counted by the
5899- // size check) between batches .
5897+ // that only the byte size limit takes effect. The default data page size is
5898+ // kept, so this also covers values that are still buffered by the column
5899+ // encoders rather than flushed into pages .
59005900 auto writer_properties = WriterProperties::Builder ()
59015901 .max_row_group_size (4 * 1024 )
59025902 ->disable_dictionary ()
5903- ->data_pagesize (1024 )
59045903 ->build ();
59055904 auto arrow_writer_properties = default_arrow_writer_properties ();
59065905
@@ -5938,6 +5937,87 @@ TEST(TestArrowReadWrite, WriteRecordBatchRespectsMaxRowGroupSize) {
59385937 }
59395938}
59405939
5940+ TEST (TestArrowReadWrite, WriteTableRespectsMaxRowGroupSize) {
5941+ // When a byte size limit is set, WriteTable switches to buffered row groups
5942+ // and feeds the table in small batches, so a chunk_size covering the whole
5943+ // table is still split into row groups bounded by the limit.
5944+ constexpr int64_t kMaxRowGroupSize = 16 * 1024 ;
5945+ constexpr int kNumRows = 8000 ;
5946+
5947+ std::shared_ptr<Table> table;
5948+ ASSERT_NO_FATAL_FAILURE (
5949+ MakeDoubleTable (/* num_columns=*/ 1 , kNumRows , /* nchunks=*/ 1 , &table));
5950+
5951+ auto sink = CreateOutputStream ();
5952+ // The default data page size (1MB) is much larger than the limit here, so
5953+ // this only works if values still buffered by the column encoders count
5954+ // towards the row group size.
5955+ auto writer_properties = WriterProperties::Builder ()
5956+ .max_row_group_size (kMaxRowGroupSize )
5957+ ->write_batch_size (256 )
5958+ ->disable_dictionary ()
5959+ ->build ();
5960+ ASSERT_OK_NO_THROW (WriteTable (*table, ::arrow::default_memory_pool (), sink,
5961+ /* chunk_size=*/ kNumRows , writer_properties));
5962+ ASSERT_OK_AND_ASSIGN (auto buffer, sink->Finish ());
5963+
5964+ auto reader = ParquetFileReader::Open (std::make_shared<BufferReader>(buffer));
5965+ auto file_metadata = reader->metadata ();
5966+ // The limit must have split the single chunk into multiple row groups.
5967+ ASSERT_GT (file_metadata->num_row_groups (), 1 );
5968+
5969+ int64_t total_rows = 0 ;
5970+ for (int i = 0 ; i < file_metadata->num_row_groups (); ++i) {
5971+ auto row_group_metadata = file_metadata->RowGroup (i);
5972+ total_rows += row_group_metadata->num_rows ();
5973+ // The size estimate is conservative, so row groups stay under the limit.
5974+ EXPECT_LE (row_group_metadata->total_compressed_size (), kMaxRowGroupSize );
5975+ }
5976+ // All rows are written exactly once.
5977+ EXPECT_EQ (kNumRows , total_rows);
5978+ }
5979+
5980+ TEST (TestArrowReadWrite, WriteTableUnlimitedRowGroupSize) {
5981+ // Without an explicit byte size limit, chunk_size alone decides the row group
5982+ // boundaries and row groups are not buffered.
5983+ constexpr int kNumRows = 2000 ;
5984+ std::shared_ptr<Table> table;
5985+ ASSERT_NO_FATAL_FAILURE (
5986+ MakeDoubleTable (/* num_columns=*/ 1 , kNumRows , /* nchunks=*/ 1 , &table));
5987+
5988+ auto sink = CreateOutputStream ();
5989+ ASSERT_OK_NO_THROW (WriteTable (*table, ::arrow::default_memory_pool (), sink,
5990+ /* chunk_size=*/ 1000 , default_writer_properties ()));
5991+ ASSERT_OK_AND_ASSIGN (auto buffer, sink->Finish ());
5992+
5993+ auto reader = ParquetFileReader::Open (std::make_shared<BufferReader>(buffer));
5994+ ASSERT_EQ (2 , reader->metadata ()->num_row_groups ());
5995+ EXPECT_EQ (1000 , reader->metadata ()->RowGroup (0 )->num_rows ());
5996+ EXPECT_EQ (1000 , reader->metadata ()->RowGroup (1 )->num_rows ());
5997+ }
5998+
5999+ TEST (TestArrowReadWrite, WriteTableMaxRowGroupSizeRoundTrip) {
6000+ // The data must survive the switch to the buffered write path unchanged.
6001+ constexpr int kNumRows = 3000 ;
6002+ std::shared_ptr<Table> table;
6003+ ASSERT_NO_FATAL_FAILURE (
6004+ MakeDoubleTable (/* num_columns=*/ 3 , kNumRows , /* nchunks=*/ 1 , &table));
6005+
6006+ auto sink = CreateOutputStream ();
6007+ auto writer_properties =
6008+ WriterProperties::Builder ().max_row_group_size (16 * 1024 )->build ();
6009+ ASSERT_OK_NO_THROW (WriteTable (*table, ::arrow::default_memory_pool (), sink,
6010+ /* chunk_size=*/ kNumRows , writer_properties));
6011+ ASSERT_OK_AND_ASSIGN (auto buffer, sink->Finish ());
6012+
6013+ ASSERT_OK_AND_ASSIGN (auto reader, OpenFile (std::make_shared<BufferReader>(buffer),
6014+ ::arrow::default_memory_pool ()));
6015+ std::shared_ptr<Table> result;
6016+ ASSERT_OK_NO_THROW (reader->ReadTable (&result));
6017+ ASSERT_OK (result->ValidateFull ());
6018+ AssertTablesEqual (*table, *result, /* same_chunk_layout=*/ false );
6019+ }
6020+
59416021TEST (TestArrowReadWrite, MultithreadedWrite) {
59426022 const int num_columns = 20 ;
59436023 const int num_rows = 1000 ;
0 commit comments