@@ -50,10 +50,10 @@ use vortex_layout::layouts::compressed::CompressingStrategy;
5050use vortex_layout:: layouts:: compressed:: CompressorPlugin ;
5151use vortex_layout:: layouts:: dict:: writer:: DictStrategy ;
5252use vortex_layout:: layouts:: flat:: writer:: FlatLayoutStrategy ;
53- use vortex_layout:: layouts:: list:: writer:: ListLayoutStrategy ;
5453use vortex_layout:: layouts:: repartition:: RepartitionStrategy ;
5554use vortex_layout:: layouts:: repartition:: RepartitionWriterOptions ;
5655use vortex_layout:: layouts:: table:: TableStrategy ;
56+ use vortex_layout:: layouts:: table:: use_experimental_list_layout;
5757use vortex_layout:: layouts:: zoned:: writer:: ZonedLayoutOptions ;
5858use vortex_layout:: layouts:: zoned:: writer:: ZonedStrategy ;
5959#[ cfg( feature = "unstable_encodings" ) ]
@@ -158,6 +158,10 @@ pub struct WriteStrategyBuilder {
158158 allow_encodings : Option < HashSet < ArrayId > > ,
159159 flat_strategy : Option < Arc < dyn LayoutStrategy > > ,
160160 probe_compressor : Option < Arc < dyn CompressorPlugin > > ,
161+ /// Force list-column decomposition on, overriding the
162+ /// [`use_experimental_list_layout`](vortex_layout::layouts::table::use_experimental_list_layout)
163+ /// env-var default of off.
164+ list_layout : bool ,
161165}
162166
163167impl Default for WriteStrategyBuilder {
@@ -171,6 +175,7 @@ impl Default for WriteStrategyBuilder {
171175 allow_encodings : Some ( ALLOWED_ENCODINGS . clone ( ) ) ,
172176 flat_strategy : None ,
173177 probe_compressor : None ,
178+ list_layout : false ,
174179 }
175180 }
176181}
@@ -185,6 +190,14 @@ impl WriteStrategyBuilder {
185190 self
186191 }
187192
193+ /// Enable list-column decomposition, overriding the env-var default of off. When enabled, list
194+ /// columns are written as a `ListLayout` (independently compressed/chunked
195+ /// elements/offsets/validity) rather than as flat arrays.
196+ pub fn with_list_layout ( mut self ) -> Self {
197+ self . list_layout = true ;
198+ self
199+ }
200+
188201 /// Override the write layout for a specific field somewhere in the nested schema tree.
189202 ///
190203 /// The field path is matched after the root struct is split into columns. This is useful when a
@@ -251,20 +264,10 @@ impl WriteStrategyBuilder {
251264 Arc :: new ( FlatLayoutStrategy :: default ( ) )
252265 } ;
253266
254- // 7. for each chunk create a layout. List-typed chunks route through
255- // `ListLayoutStrategy` (separately-addressable elements/offsets/validity sub-layouts;
256- // non-list chunks fall through its built-in fallback to `flat`).
257- let leaf: Arc < dyn LayoutStrategy > = Arc :: new (
258- // Thread the configured `flat` (which carries `allow_encodings` / any custom flat
259- // override) through every child.
260- ListLayoutStrategy :: default ( )
261- . with_elements ( Arc :: clone ( & flat) )
262- . with_offsets ( Arc :: clone ( & flat) )
263- . with_validity ( Arc :: clone ( & flat) )
264- . with_fallback ( Arc :: clone ( & flat) ) ,
265- ) ;
266-
267- let chunked = ChunkedLayoutStrategy :: new ( leaf) ;
267+ // 7. for each chunk create a flat layout. List columns are decomposed above this point by
268+ // the `TableStrategy` dispatcher (into independently-compressed elements/offsets/validity
269+ // sub-columns), so the per-chunk leaf only ever sees flat, non-list chunks.
270+ let chunked = ChunkedLayoutStrategy :: new ( Arc :: clone ( & flat) ) ;
268271 // 6. buffer chunks so they end up with closer segment ids physically
269272 let buffered = BufferedStrategy :: new ( chunked, 2 * ONE_MEG ) ; // 2MB
270273
@@ -348,8 +351,14 @@ impl WriteStrategyBuilder {
348351 let validity_strategy = CollectStrategy :: new ( compress_then_flat) ;
349352
350353 // Take any field overrides from the builder and apply them to the final strategy.
351- let table_strategy = TableStrategy :: new ( Arc :: new ( validity_strategy) , Arc :: new ( repartition) )
352- . with_field_writers ( self . field_writers ) ;
354+ let mut table_strategy =
355+ TableStrategy :: new ( Arc :: new ( validity_strategy) , Arc :: new ( repartition) )
356+ . with_field_writers ( self . field_writers ) ;
357+ // List decomposition is experimental: enabled by an explicit builder opt-in or the
358+ // `VORTEX_EXPERIMENTAL_LIST_LAYOUT` env var; off otherwise.
359+ if self . list_layout || use_experimental_list_layout ( ) {
360+ table_strategy = table_strategy. with_list_layout ( ) ;
361+ }
353362
354363 Arc :: new ( table_strategy)
355364 }
0 commit comments