@@ -189,6 +189,46 @@ def test_from_arrow_column_cparams(tmp_path):
189189 assert reopened ._cols ["x" ].cparams .filters [:2 ] == [blosc2 .Filter .TRUNC_PREC , blosc2 .Filter .SHUFFLE ]
190190
191191
192+ def test_from_arrow_column_cparams_nested_struct (tmp_path ):
193+ # Regression: column_cparams with TRUNC_PREC must be applied to leaf columns
194+ # produced by struct flattening, not silently dropped.
195+ struct_type = pa .struct (
196+ [
197+ pa .field ("lon" , pa .float64 ()),
198+ pa .field ("lat" , pa .float64 ()),
199+ ]
200+ )
201+ at = pa .table (
202+ {
203+ "pos" : pa .array (
204+ [{"lon" : - 87.6 , "lat" : 41.8 }, {"lon" : - 87.7 , "lat" : 41.9 }],
205+ type = struct_type ,
206+ ),
207+ "fare" : pa .array ([10.0 , 20.0 ], type = pa .float32 ()),
208+ }
209+ )
210+ trunc_cparams = {
211+ "codec" : blosc2 .Codec .ZSTD .value ,
212+ "clevel" : 5 ,
213+ "typesize" : 8 ,
214+ "filters" : [blosc2 .Filter .TRUNC_PREC .value , blosc2 .Filter .SHUFFLE .value ],
215+ "filters_meta" : [22 , 0 ],
216+ }
217+ t = CTable .from_arrow (
218+ at .schema ,
219+ at .to_batches (),
220+ urlpath = str (tmp_path / "trunc_nested.b2d" ),
221+ column_cparams = {"pos.lon" : trunc_cparams , "pos.lat" : trunc_cparams },
222+ )
223+ assert t .col_names == ["pos.lon" , "pos.lat" , "fare" ]
224+ assert t ._cols ["pos.lon" ].cparams .filters [:2 ] == [blosc2 .Filter .TRUNC_PREC , blosc2 .Filter .SHUFFLE ]
225+ assert t ._cols ["pos.lon" ].cparams .filters_meta [:2 ] == [22 , 0 ]
226+ assert t ._cols ["pos.lat" ].cparams .filters [:2 ] == [blosc2 .Filter .TRUNC_PREC , blosc2 .Filter .SHUFFLE ]
227+ # fare is float32, no TRUNC_PREC requested
228+ assert blosc2 .Filter .TRUNC_PREC not in t ._cols ["fare" ].cparams .filters
229+ t .close ()
230+
231+
192232def test_from_arrow_string_values ():
193233 # Without string_max_length, scalar strings become vlstring columns.
194234 # Accessing [:] on a vlstring column returns a Python list, not an ndarray.
0 commit comments