Skip to content

Commit 4c8425f

Browse files
Add Filter/Split/Project planner and add Datafusion usage of this (#10706)
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
1 parent cb6e041 commit 4c8425f

35 files changed

Lines changed: 1128 additions & 798 deletions

File tree

‎benchmarks/datafusion-bench/src/lib.rs‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,15 @@ pub fn make_object_store(
9898
}
9999
}
100100

101-
pub fn format_to_df_format(format: Format) -> anyhow::Result<Arc<dyn FileFormat>> {
101+
pub fn format_to_df_format(format: Format, scan_v2: bool) -> anyhow::Result<Arc<dyn FileFormat>> {
102+
let mut options = vortex_table_options();
103+
options.scan_v2 = scan_v2;
102104
Ok(match format {
103105
Format::Csv => Arc::new(CsvFormat::default()) as _,
104106
Format::Parquet => Arc::new(ParquetFormat::new()),
105-
Format::OnDiskVortex | Format::VortexCompact | Format::VortexSpatialNative => Arc::new(
106-
VortexFormat::new_with_options(SESSION.clone(), vortex_table_options()),
107-
),
107+
Format::OnDiskVortex | Format::VortexCompact | Format::VortexSpatialNative => {
108+
Arc::new(VortexFormat::new_with_options(SESSION.clone(), options))
109+
}
108110
Format::ArrowIpc | Format::OnDiskDuckDB | Format::Lance | Format::LitData => {
109111
anyhow::bail!("Format {format} cannot be turned into a DataFusion `FileFormat`")
110112
}

‎benchmarks/datafusion-bench/src/main.rs‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ struct Args {
120120
#[arg(long, value_delimiter = ',', value_parser = value_parser!(Format))]
121121
formats: Vec<Format>,
122122

123+
/// Use the planner-driven V2 Vortex scan (defaults to true for TPC-H).
124+
#[arg(long, action = clap::ArgAction::Set)]
125+
scan_v2: Option<bool>,
126+
123127
#[arg(long = "opt", value_delimiter = ',', value_parser = value_parser!(Opt))]
124128
options: Vec<Opt>,
125129
}
@@ -132,6 +136,10 @@ async fn main() -> anyhow::Result<()> {
132136
vortex_bench::configure_allocator();
133137
let args = Args::parse();
134138
let opts = Opts::from(args.options);
139+
let scan_v2 = args.scan_v2.unwrap_or_else(|| {
140+
matches!(args.benchmark, BenchmarkArg::TpcH)
141+
&& !std::env::var("VORTEX_SCAN_V2").is_ok_and(|v| v == "0")
142+
});
135143

136144
set_join_set_tracer(get_static_tracer())?;
137145
setup_logging_and_tracing(args.verbose, args.tracing)?;
@@ -191,7 +199,7 @@ async fn main() -> anyhow::Result<()> {
191199
session.sql(&sql).await?.collect().await?;
192200
}
193201
datafusion_bench::make_object_store(&session, benchmark.data_url())?;
194-
register_benchmark_tables(&session, benchmark, format).await?;
202+
register_benchmark_tables(&session, benchmark, format, scan_v2).await?;
195203
Ok((session, format))
196204
}
197205
},
@@ -255,12 +263,14 @@ async fn register_benchmark_tables<B: Benchmark + ?Sized>(
255263
session: &SessionContext,
256264
benchmark: &B,
257265
format: Format,
266+
scan_v2: bool,
258267
) -> anyhow::Result<()> {
259-
if use_scan_api() && matches!(format, Format::OnDiskVortex | Format::VortexCompact) {
268+
if !scan_v2 && use_scan_api() && matches!(format, Format::OnDiskVortex | Format::VortexCompact)
269+
{
260270
register_v2_tables(session, benchmark, format).await
261271
} else {
262272
let benchmark_base = benchmark.data_url().join(&format!("{}/", format.name()))?;
263-
let file_format = format_to_df_format(format)?;
273+
let file_format = format_to_df_format(format, scan_v2)?;
264274

265275
for table in benchmark.table_specs().iter() {
266276
let pattern = benchmark.pattern(table.name, format);

‎vortex-datafusion/src/persistent/format.rs‎

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ extensions_options! {
197197
/// [`ParquetFormatFactory::new_with_options`]: https://docs.rs/datafusion/latest/datafusion/datasource/file_format/parquet/struct.ParquetFormatFactory.html#method.new_with_options
198198
/// [`JsonFormatFactory::new_with_options`]: https://docs.rs/datafusion/latest/datafusion/datasource/file_format/json/struct.JsonFormatFactory.html#method.new_with_options
199199
pub struct VortexTableOptions {
200+
/// Use the planner-driven V2 scan executor for supported layouts.
201+
///
202+
/// Disabled by default. Select V2 before creating a table with
203+
/// `SET vortex.scan_v2 = true` (with the `vortex` options extension registered),
204+
/// or for one table with `OPTIONS(scan_v2 'true')`. In Rust, set
205+
/// `VortexTableOptions { scan_v2: true, ..Default::default() }` on the factory.
206+
pub scan_v2: bool, default = false
200207
/// The number of bytes to read when parsing a file footer.
201208
///
202209
/// Values smaller than `MAX_POSTSCRIPT_SIZE + EOF_SIZE` will be clamped to that minimum
@@ -1033,17 +1040,18 @@ mod tests {
10331040
Ok(())
10341041
}
10351042

1043+
#[rstest]
10361044
#[tokio::test]
1037-
async fn configure_format_source() -> anyhow::Result<()> {
1045+
async fn configure_format_source(#[values(false, true)] scan_v2: bool) -> anyhow::Result<()> {
10381046
let ctx = TestSessionContext::default();
10391047

10401048
ctx.session
10411049
.sql(
1042-
"CREATE EXTERNAL TABLE my_tbl \
1050+
&format!("CREATE EXTERNAL TABLE my_tbl \
10431051
(c1 VARCHAR NOT NULL, c2 INT NOT NULL) \
10441052
STORED AS vortex \
10451053
LOCATION 'table/' \
1046-
OPTIONS( footer_initial_read_size_bytes '12345', predicate_pushdown 'false', scan_concurrency '3' );",
1054+
OPTIONS( footer_initial_read_size_bytes '12345', predicate_pushdown 'false', scan_concurrency '3', scan_v2 '{scan_v2}' );"),
10471055
)
10481056
.await?
10491057
.collect()
@@ -1061,6 +1069,35 @@ mod tests {
10611069
assert_eq!(format.options().footer_initial_read_size_bytes, 12345);
10621070
assert!(!format.options().predicate_pushdown);
10631071
assert_eq!(format.options().scan_concurrency, Some(3));
1072+
assert_eq!(format.options().scan_v2, scan_v2);
1073+
Ok(())
1074+
}
1075+
1076+
#[rstest]
1077+
#[tokio::test]
1078+
async fn session_scan_version(#[values(false, true)] scan_v2: bool) -> anyhow::Result<()> {
1079+
let factory = Arc::new(VortexFormatFactory::new());
1080+
let ctx = TestSessionContext::new_with_factory(Arc::clone(&factory));
1081+
ctx.session
1082+
.state_ref()
1083+
.write()
1084+
.config_mut()
1085+
.options_mut()
1086+
.extensions
1087+
.insert(VortexTableOptions::default());
1088+
1089+
for enabled in [scan_v2, !scan_v2] {
1090+
ctx.session
1091+
.sql(&format!("SET vortex.scan_v2 = {enabled}"))
1092+
.await?
1093+
.collect()
1094+
.await?;
1095+
let format = factory.create(&ctx.session.state(), &Default::default())?;
1096+
let format = format
1097+
.downcast_ref::<VortexFormat>()
1098+
.ok_or_else(|| anyhow::anyhow!("expected VortexFormat"))?;
1099+
assert_eq!(format.options().scan_v2, enabled);
1100+
}
10641101
Ok(())
10651102
}
10661103

@@ -1076,6 +1113,7 @@ mod tests {
10761113
#[test]
10771114
fn format_plumbs_source_options() -> anyhow::Result<()> {
10781115
let opts = VortexTableOptions {
1116+
scan_v2: true,
10791117
projection_pushdown: true,
10801118
predicate_pushdown: false,
10811119
scan_concurrency: Some(3),
@@ -1095,6 +1133,7 @@ mod tests {
10951133
);
10961134
assert_eq!(source.options().predicate_pushdown, opts.predicate_pushdown);
10971135
assert_eq!(source.options().scan_concurrency, opts.scan_concurrency);
1136+
assert_eq!(source.options().scan_v2, opts.scan_v2);
10981137
Ok(())
10991138
}
11001139

0 commit comments

Comments
 (0)