@@ -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