@@ -52,6 +52,7 @@ use vortex_array::expr::select;
5252use vortex_array:: extension:: datetime:: TimeUnit ;
5353use vortex_array:: extension:: datetime:: Timestamp ;
5454use vortex_array:: extension:: datetime:: TimestampOptions ;
55+ use vortex_array:: field_path;
5556use vortex_array:: scalar:: Scalar ;
5657use vortex_array:: scalar_fn:: ScalarFnVTableExt ;
5758use vortex_array:: scalar_fn:: fns:: pack:: Pack ;
@@ -67,18 +68,25 @@ use vortex_buffer::Buffer;
6768use vortex_buffer:: ByteBuffer ;
6869use vortex_buffer:: ByteBufferMut ;
6970use vortex_buffer:: buffer;
71+ use vortex_edition:: EditionSession ;
7072use vortex_error:: VortexExpect ;
7173use vortex_error:: VortexResult ;
7274use vortex_flatbuffers:: footer as fb;
7375use vortex_io:: session:: RuntimeSession ;
7476use vortex_layout:: DynLayout ;
77+ use vortex_layout:: LayoutStrategy ;
78+ use vortex_layout:: layouts:: buffered:: BufferedStrategy ;
79+ use vortex_layout:: layouts:: chunked:: writer:: ChunkedLayoutStrategy ;
7580use vortex_layout:: layouts:: flat:: writer:: FlatLayoutStrategy ;
81+ use vortex_layout:: layouts:: struct_:: StructStrategy ;
82+ use vortex_layout:: layouts:: table:: TableStrategy ;
7683use vortex_layout:: layouts:: zoned:: LegacyStats ;
7784use vortex_layout:: layouts:: zoned:: Zoned ;
7885use vortex_layout:: scan:: scan_builder:: ScanBuilder ;
7986use vortex_layout:: scan:: split_by:: SplitBy ;
8087use vortex_layout:: session:: LayoutSession ;
8188use vortex_session:: VortexSession ;
89+ use vortex_zigzag:: ZigZag ;
8290
8391use crate :: MAX_POSTSCRIPT_SIZE ;
8492use crate :: OpenOptionsSessionExt ;
@@ -1377,7 +1385,7 @@ async fn test_into_tokio_array_stream() -> VortexResult<()> {
13771385 ] )
13781386 . into_array ( ) ;
13791387
1380- let st = StructArray :: from_fields ( & [ ( "strings" , strings) , ( "numbers" , numbers) ] ) . unwrap ( ) ;
1388+ let st = StructArray :: from_fields ( & [ ( "strings" , strings) , ( "numbers" , numbers) ] ) ? ;
13811389 let mut buf = ByteBufferMut :: empty ( ) ;
13821390 SESSION
13831391 . write_options ( )
@@ -1542,6 +1550,143 @@ async fn test_writer_bytes_written() -> VortexResult<()> {
15421550 Ok ( ( ) )
15431551}
15441552
1553+ #[ rstest]
1554+ #[ case:: table_one_leaf( true , 1 , false , 32 ) ]
1555+ #[ case:: table_two_shared_leaves( true , 2 , false , 64 ) ]
1556+ #[ case:: table_field_override( true , 2 , true , 64 ) ]
1557+ #[ case:: struct_default( false , 1 , false , 32 ) ]
1558+ #[ tokio:: test]
1559+ async fn test_writer_buffered_bytes (
1560+ #[ case] use_table_strategy : bool ,
1561+ #[ case] leaf_count : usize ,
1562+ #[ case] field_override : bool ,
1563+ #[ case] expected_buffered_bytes : u64 ,
1564+ ) -> VortexResult < ( ) > {
1565+ const BUFFER_SIZE : u64 = 16 ;
1566+
1567+ let fields = [
1568+ ( "a" , buffer ! [ 1u32 , 2 , 3 , 4 ] . into_array ( ) ) ,
1569+ ( "b" , buffer ! [ 5u32 , 6 , 7 , 8 ] . into_array ( ) ) ,
1570+ ] ;
1571+ let array = StructArray :: from_fields ( & fields[ ..leaf_count] ) ?. into_array ( ) ;
1572+
1573+ let new_leaf = || -> Arc < dyn LayoutStrategy > {
1574+ Arc :: new ( BufferedStrategy :: new (
1575+ ChunkedLayoutStrategy :: new ( FlatLayoutStrategy :: default ( ) ) ,
1576+ BUFFER_SIZE ,
1577+ ) )
1578+ } ;
1579+ let validity: Arc < dyn LayoutStrategy > = Arc :: new ( FlatLayoutStrategy :: default ( ) ) ;
1580+ let strategy: Arc < dyn LayoutStrategy > = if use_table_strategy {
1581+ let mut table = TableStrategy :: new ( validity, new_leaf ( ) ) ;
1582+ if field_override {
1583+ table = table. with_field_writer ( field_path ! ( b) , new_leaf ( ) ) ;
1584+ }
1585+ Arc :: new ( table)
1586+ } else {
1587+ Arc :: new ( StructStrategy :: new ( validity, new_leaf ( ) ) )
1588+ } ;
1589+
1590+ let mut buf = ByteBufferMut :: empty ( ) ;
1591+ let options = SESSION . write_options ( ) . with_strategy ( Arc :: clone ( & strategy) ) ;
1592+ let buffered_bytes = options. buffered_bytes_tracker ( ) ;
1593+ let mut writer = options. writer ( & mut buf, array. dtype ( ) . clone ( ) ) ;
1594+
1595+ assert_eq ! ( writer. buffered_bytes( ) , 0 ) ;
1596+
1597+ // The third push forces two chunks through the capacity-one input channel while keeping the
1598+ // writer open. Each physical leaf retains two BUFFER_SIZE chunks while peeking for more input.
1599+ writer. push ( array. clone ( ) ) . await ?;
1600+ writer. push ( array. clone ( ) ) . await ?;
1601+ writer. push ( array) . await ?;
1602+
1603+ assert_eq ! ( writer. buffered_bytes( ) , expected_buffered_bytes) ;
1604+
1605+ let summary = writer. finish ( ) . await ?;
1606+ assert_eq ! ( summary. row_count( ) , 12 ) ;
1607+ assert_eq ! ( buffered_bytes. buffered_bytes( ) , 0 ) ;
1608+
1609+ Ok ( ( ) )
1610+ }
1611+
1612+ #[ tokio:: test]
1613+ async fn test_buffered_bytes_are_writer_scoped ( ) -> VortexResult < ( ) > {
1614+ const BUFFER_SIZE : u64 = 16 ;
1615+
1616+ let array =
1617+ StructArray :: from_fields ( & [ ( "a" , buffer ! [ 1u32 , 2 , 3 , 4 ] . into_array ( ) ) ] ) ?. into_array ( ) ;
1618+ let leaf = Arc :: new ( BufferedStrategy :: new (
1619+ ChunkedLayoutStrategy :: new ( FlatLayoutStrategy :: default ( ) ) ,
1620+ BUFFER_SIZE ,
1621+ ) ) ;
1622+ let strategy: Arc < dyn LayoutStrategy > = Arc :: new ( TableStrategy :: new (
1623+ Arc :: new ( FlatLayoutStrategy :: default ( ) ) ,
1624+ leaf,
1625+ ) ) ;
1626+
1627+ let mut first_buf = ByteBufferMut :: empty ( ) ;
1628+ let mut first = SESSION
1629+ . write_options ( )
1630+ . with_strategy ( Arc :: clone ( & strategy) )
1631+ . writer ( & mut first_buf, array. dtype ( ) . clone ( ) ) ;
1632+ let mut second_buf = ByteBufferMut :: empty ( ) ;
1633+ let mut second = SESSION
1634+ . write_options ( )
1635+ . with_strategy ( strategy)
1636+ . writer ( & mut second_buf, array. dtype ( ) . clone ( ) ) ;
1637+
1638+ first. push ( array. clone ( ) ) . await ?;
1639+ first. push ( array. clone ( ) ) . await ?;
1640+ first. push ( array. clone ( ) ) . await ?;
1641+ second. push ( array. clone ( ) ) . await ?;
1642+ second. push ( array. clone ( ) ) . await ?;
1643+ second. push ( array) . await ?;
1644+
1645+ assert_eq ! ( first. buffered_bytes( ) , 2 * BUFFER_SIZE ) ;
1646+ assert_eq ! ( second. buffered_bytes( ) , 2 * BUFFER_SIZE ) ;
1647+
1648+ first. finish ( ) . await ?;
1649+ second. finish ( ) . await ?;
1650+
1651+ Ok ( ( ) )
1652+ }
1653+
1654+ #[ tokio:: test]
1655+ async fn test_encoding_registered_after_write_options ( ) -> VortexResult < ( ) > {
1656+ // A session that does not know about ZigZag yet.
1657+ let session = array_session ( )
1658+ . with :: < EditionSession > ( )
1659+ . with :: < LayoutSession > ( )
1660+ . with :: < RuntimeSession > ( ) ;
1661+
1662+ // Configure the options before the encoding is registered; `write` is what snapshots the
1663+ // session's encodings, so registering in between must still be honoured.
1664+ let options = session
1665+ . write_options ( )
1666+ . with_strategy ( Arc :: new ( FlatLayoutStrategy :: default ( ) ) ) ;
1667+ vortex_zigzag:: initialize ( & session) ;
1668+ crate :: enable_all_registered_array_encodings ( & session) ;
1669+
1670+ let array = ZigZag :: try_new ( buffer ! [ 1u32 , 2 , 3 , 4 ] . into_array ( ) ) ?. into_array ( ) ;
1671+ let dtype = array. dtype ( ) . clone ( ) ;
1672+
1673+ let mut buf = ByteBufferMut :: empty ( ) ;
1674+ options. write ( & mut buf, array. to_array_stream ( ) ) . await ?;
1675+
1676+ let chunks: Vec < _ > = session
1677+ . open_options ( )
1678+ . open_buffer ( buf) ?
1679+ . scan ( ) ?
1680+ . into_array_stream ( ) ?
1681+ . try_collect ( )
1682+ . await ?;
1683+ let read = ChunkedArray :: try_new ( chunks, dtype) ?. into_array ( ) ;
1684+ let mut ctx = session. create_execution_ctx ( ) ;
1685+ assert_arrays_eq ! ( read, buffer![ -1i32 , 1 , -2 , 2 ] . into_array( ) , & mut ctx) ;
1686+
1687+ Ok ( ( ) )
1688+ }
1689+
15451690#[ tokio:: test]
15461691async fn test_writer_empty_chunks ( ) -> VortexResult < ( ) > {
15471692 let mut ctx = SESSION . create_execution_ctx ( ) ;
@@ -2029,7 +2174,7 @@ async fn timestamp_unit_mismatch_errors_with_constant_children()
20292174 "Expected error from timestamp unit mismatch (ms vs s), but got {} results. \
20302175 This indicates the scanner silently applied the filter incorrectly when \
20312176 DateTimePartsArray children use ConstantArray encoding.",
2032- results. unwrap ( ) . len( )
2177+ results? . len( )
20332178 ) ;
20342179
20352180 Ok ( ( ) )
@@ -2223,7 +2368,7 @@ async fn test_segment_ordering_dict_codes_before_values() -> VortexResult<()> {
22232368 let strings = VarBinArray :: from ( values) . into_array ( ) ;
22242369 let numbers = PrimitiveArray :: from_iter ( 0 ..n as i32 ) . into_array ( ) ;
22252370
2226- let st = StructArray :: from_fields ( & [ ( "strings" , strings) , ( "numbers" , numbers) ] ) . unwrap ( ) ;
2371+ let st = StructArray :: from_fields ( & [ ( "strings" , strings) , ( "numbers" , numbers) ] ) ? ;
22272372
22282373 let mut buf = ByteBufferMut :: empty ( ) ;
22292374 let summary = SESSION
@@ -2345,8 +2490,7 @@ async fn test_segment_ordering_zonemaps_after_data() -> VortexResult<()> {
23452490 ( "strings" , strings) ,
23462491 ( "numbers" , numbers) ,
23472492 ( "floats" , floats) ,
2348- ] )
2349- . unwrap ( ) ;
2493+ ] ) ?;
23502494
23512495 let mut buf = ByteBufferMut :: empty ( ) ;
23522496 let summary = SESSION
0 commit comments