Skip to content

Commit c4540d3

Browse files
committed
refactor: bind scan expressions once
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
1 parent b3b0043 commit c4540d3

15 files changed

Lines changed: 308 additions & 152 deletions

File tree

benchmarks/compress-bench/src/vortex.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use vortex::expr::root;
1818
use vortex::expr::select;
1919
use vortex::file::OpenOptionsSessionExt;
2020
use vortex::file::WriteOptionsSessionExt;
21+
use vortex::layout::scan::scan_builder::optimize_and_bind;
2122
use vortex_arrow::ToArrowType;
2223
use vortex_bench::Format;
2324
use vortex_bench::SESSION;
@@ -64,14 +65,15 @@ impl Compressor for VortexCompressor {
6465
let start = Instant::now();
6566
let data = Bytes::from(buf);
6667
let mut scan = SESSION.open_options().open_buffer(data)?.scan()?;
67-
let root_columns = scan
68-
.dtype()?
68+
let source_dtype = scan.dtype()?;
69+
let root_columns = source_dtype
6970
.as_struct_fields_opt()
7071
.map_or(0, |fields| fields.nfields());
7172
if let Some(cols) = read_projection(root_columns) {
7273
// Columns are named "0".."num_columns-1"; project the given subset.
7374
let names: FieldNames = cols.iter().map(|i| i.to_string()).collect();
74-
scan = scan.with_projection(select(names, root()));
75+
let projection = optimize_and_bind(select(names, root()), &source_dtype)?;
76+
scan = scan.with_projection(projection);
7577
}
7678
let schema = Arc::new(scan.dtype()?.to_arrow_schema()?);
7779

docs/developer-guide/internals/session.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ session.write_options()
7878
.await?;
7979

8080
// Scanning a layout
81+
let filter = optimize_and_bind(expr, layout_reader.dtype())?;
8182
ScanBuilder::new(session.clone(), layout_reader)
82-
.with_filter(expr)
83+
.with_filter(filter)
8384
.into_array_stream()?;
8485
```
8586

fuzz/fuzz_targets/file_io.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use itertools::Itertools;
77
use libfuzzer_sys::Corpus;
88
use libfuzzer_sys::fuzz_target;
9+
use vortex::layout::scan::scan_builder::optimize_and_bind;
910
use vortex_array::Canonical;
1011
use vortex_array::IntoArray;
1112
use vortex_array::VortexSessionExecute;
@@ -78,14 +79,21 @@ fuzz_target!(|fuzz: FuzzFileAction| -> Corpus {
7879
.write(&mut full_buff, array_data.to_array_iterator())
7980
.vortex_expect("file write should succeed in fuzz test");
8081

81-
let mut output = SESSION
82+
let file = SESSION
8283
.open_options()
8384
.open_buffer(full_buff)
84-
.vortex_expect("open_buffer should succeed in fuzz test")
85+
.vortex_expect("open_buffer should succeed in fuzz test");
86+
let projection = optimize_and_bind(projection_expr.unwrap_or_else(root), file.dtype())
87+
.vortex_expect("projection should bind in fuzz test");
88+
let filter = filter_expr
89+
.map(|filter| optimize_and_bind(filter, file.dtype()))
90+
.transpose()
91+
.vortex_expect("filter should bind in fuzz test");
92+
let mut output = file
8593
.scan()
8694
.vortex_expect("scan should succeed in fuzz test")
87-
.with_projection(projection_expr.unwrap_or_else(root))
88-
.with_some_filter(filter_expr)
95+
.with_projection(projection)
96+
.with_some_filter(filter)
8997
.into_array_iter(&*RUNTIME)
9098
.vortex_expect("into_array_iter should succeed in fuzz test")
9199
.try_collect::<_, Vec<_>, _>()

vortex-bench/src/datasets/tpch_l_comment.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use vortex::dtype::Nullability::NonNullable;
1616
use vortex::expr::col;
1717
use vortex::expr::pack;
1818
use vortex::file::OpenOptionsSessionExt;
19+
use vortex::layout::scan::scan_builder::optimize_and_bind;
1920

2021
use crate::Format;
2122
use crate::IdempotentPath;
@@ -66,9 +67,13 @@ impl Dataset for TPCHLCommentChunked {
6667

6768
let path = data_dir.join("lineitem.vortex");
6869
let file = SESSION.open_options().open_path(path).await?;
70+
let projection = optimize_and_bind(
71+
pack(vec![("l_comment", col("l_comment"))], NonNullable),
72+
file.dtype(),
73+
)?;
6974
let chunks: Vec<_> = file
7075
.scan()?
71-
.with_projection(pack(vec![("l_comment", col("l_comment"))], NonNullable))
76+
.with_projection(projection)
7277
.map({
7378
let ctx = ctx.clone();
7479
move |a| {

vortex-datafusion/src/persistent/opener.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use vortex::file::OpenOptionsSessionExt;
4848
use vortex::io::InstrumentedReadAt;
4949
use vortex::layout::LayoutReader;
5050
use vortex::layout::scan::scan_builder::ScanBuilder;
51+
use vortex::layout::scan::scan_builder::optimize_and_bind;
5152
use vortex::layout::scan::split_by::SplitBy;
5253
use vortex::metrics::Label;
5354
use vortex::metrics::MetricsRegistry;
@@ -302,9 +303,11 @@ impl FileOpener for VortexOpener {
302303

303304
// The schema of the stream returned from the vortex scan.
304305
// We use a reference schema for types that don't roundtrip (Dictionary, Utf8, etc.).
305-
let scan_dtype = scan_projection.return_dtype(vxf.dtype()).map_err(|_e| {
306-
exec_datafusion_err!("Couldn't get the dtype for the underlying Vortex scan")
307-
})?;
306+
let scan_projection =
307+
optimize_and_bind(scan_projection, vxf.dtype()).map_err(|_e| {
308+
exec_datafusion_err!("Couldn't get the dtype for the underlying Vortex scan")
309+
})?;
310+
let scan_dtype = scan_projection.dtype().clone();
308311

309312
// When projection pushdown is enabled, the scan outputs the projected columns.
310313
// When disabled, the scan outputs raw columns and the projection is applied after.
@@ -419,6 +422,10 @@ impl FileOpener for VortexOpener {
419422
make_vortex_predicate(expr_convertor.as_ref(), &pushed).transpose()
420423
})
421424
.transpose()?;
425+
let filter = filter
426+
.map(|filter| optimize_and_bind(filter, vxf.dtype()))
427+
.transpose()
428+
.map_err(|e| exec_datafusion_err!("Couldn't bind Vortex scan filter: {e}"))?;
422429

423430
if let Some(limit) = limit
424431
&& filter.is_none()

0 commit comments

Comments
 (0)