@@ -32,24 +32,179 @@ use datafusion::error::Result;
3232use datafusion:: execution:: context:: SessionContext ;
3333use datafusion:: prelude:: JoinType ;
3434use datafusion:: prelude:: { CsvReadOptions , ParquetReadOptions } ;
35+ use datafusion:: test_util:: parquet_test_data;
3536use datafusion:: { assert_batches_eq, assert_batches_sorted_eq} ;
37+ use datafusion_common:: ScalarValue ;
3638use datafusion_expr:: expr:: { GroupingSet , Sort } ;
37- use datafusion_expr:: { avg, col, count, lit, max, sum, Expr , ExprSchemable } ;
39+ use datafusion_expr:: utils:: COUNT_STAR_EXPANSION ;
40+ use datafusion_expr:: Expr :: { ScalarSubquery , Wildcard } ;
41+ use datafusion_expr:: {
42+ avg, col, count, expr, lit, max, sum, AggregateFunction , Expr , ExprSchemable ,
43+ Subquery , WindowFrame , WindowFrameBound , WindowFrameUnits , WindowFunction ,
44+ } ;
3845
3946#[ tokio:: test]
40- async fn count_wildcard ( ) -> Result < ( ) > {
41- let ctx = SessionContext :: new ( ) ;
42- let testdata = datafusion:: test_util:: parquet_test_data ( ) ;
47+ async fn test_count_wildcard_on_sort ( ) -> Result < ( ) > {
48+ let ctx = create_join_context ( ) ?;
4349
44- ctx. register_parquet (
45- "alltypes_tiny_pages" ,
46- & format ! ( "{testdata}/alltypes_tiny_pages.parquet" ) ,
47- ParquetReadOptions :: default ( ) ,
48- )
49- . await ?;
50+ let sql_results = ctx
51+ . sql ( "select b,count(*) from t1 group by b order by count(*)" )
52+ . await ?
53+ . explain ( false , false ) ?
54+ . collect ( )
55+ . await ?;
56+
57+ let df_results = ctx
58+ . table ( "t1" )
59+ . await ?
60+ . aggregate ( vec ! [ col( "b" ) ] , vec ! [ count( Wildcard ) ] ) ?
61+ . sort ( vec ! [ count( Wildcard ) . sort( true , false ) ] ) ?
62+ . explain ( false , false ) ?
63+ . collect ( )
64+ . await ?;
65+ //make sure sql plan same with df plan
66+ assert_eq ! (
67+ pretty_format_batches( & sql_results) ?. to_string( ) ,
68+ pretty_format_batches( & df_results) ?. to_string( )
69+ ) ;
70+ Ok ( ( ) )
71+ }
72+
73+ #[ tokio:: test]
74+ async fn test_count_wildcard_on_where_in ( ) -> Result < ( ) > {
75+ let ctx = create_join_context ( ) ?;
76+ let sql_results = ctx
77+ . sql ( "SELECT a,b FROM t1 WHERE a in (SELECT count(*) FROM t2)" )
78+ . await ?
79+ . explain ( false , false ) ?
80+ . collect ( )
81+ . await ?;
82+
83+ // In the same SessionContext, AliasGenerator will increase subquery_alias id by 1
84+ // https://github.com/apache/arrow-datafusion/blame/cf45eb9020092943b96653d70fafb143cc362e19/datafusion/optimizer/src/alias.rs#L40-L43
85+ // for compare difference betwwen sql and df logical plan, we need to create a new SessionContext here
86+ let ctx = create_join_context ( ) ?;
87+ let df_results = ctx
88+ . table ( "t1" )
89+ . await ?
90+ . filter ( Expr :: InSubquery {
91+ expr : Box :: new ( col ( "a" ) ) ,
92+ subquery : Subquery {
93+ subquery : Arc :: new (
94+ ctx. table ( "t2" )
95+ . await ?
96+ . aggregate ( vec ! [ ] , vec ! [ count( Expr :: Wildcard ) ] ) ?
97+ . select ( vec ! [ count( Expr :: Wildcard ) ] ) ?
98+ . into_unoptimized_plan ( ) ,
99+ // Usually, into_optimized_plan() should be used here, but due to
100+ // https://github.com/apache/arrow-datafusion/issues/5771,
101+ // subqueries in SQL cannot be optimized, resulting in differences in logical_plan. Therefore, into_unoptimized_plan() is temporarily used here.
102+ ) ,
103+ outer_ref_columns : vec ! [ ] ,
104+ } ,
105+ negated : false ,
106+ } ) ?
107+ . select ( vec ! [ col( "a" ) , col( "b" ) ] ) ?
108+ . explain ( false , false ) ?
109+ . collect ( )
110+ . await ?;
111+
112+ // make sure sql plan same with df plan
113+ assert_eq ! (
114+ pretty_format_batches( & sql_results) ?. to_string( ) ,
115+ pretty_format_batches( & df_results) ?. to_string( )
116+ ) ;
117+
118+ Ok ( ( ) )
119+ }
120+
121+ #[ tokio:: test]
122+ async fn test_count_wildcard_on_where_exist ( ) -> Result < ( ) > {
123+ let ctx = create_join_context ( ) ?;
124+ let sql_results = ctx
125+ . sql ( "SELECT a, b FROM t1 WHERE EXISTS (SELECT count(*) FROM t2)" )
126+ . await ?
127+ . explain ( false , false ) ?
128+ . collect ( )
129+ . await ?;
130+ let df_results = ctx
131+ . table ( "t1" )
132+ . await ?
133+ . filter ( Expr :: Exists {
134+ subquery : Subquery {
135+ subquery : Arc :: new (
136+ ctx. table ( "t2" )
137+ . await ?
138+ . aggregate ( vec ! [ ] , vec ! [ count( Expr :: Wildcard ) ] ) ?
139+ . select ( vec ! [ count( Expr :: Wildcard ) ] ) ?
140+ . into_unoptimized_plan ( ) ,
141+ // Usually, into_optimized_plan() should be used here, but due to
142+ // https://github.com/apache/arrow-datafusion/issues/5771,
143+ // subqueries in SQL cannot be optimized, resulting in differences in logical_plan. Therefore, into_unoptimized_plan() is temporarily used here.
144+ ) ,
145+ outer_ref_columns : vec ! [ ] ,
146+ } ,
147+ negated : false ,
148+ } ) ?
149+ . select ( vec ! [ col( "a" ) , col( "b" ) ] ) ?
150+ . explain ( false , false ) ?
151+ . collect ( )
152+ . await ?;
153+
154+ //make sure sql plan same with df plan
155+ assert_eq ! (
156+ pretty_format_batches( & sql_results) ?. to_string( ) ,
157+ pretty_format_batches( & df_results) ?. to_string( )
158+ ) ;
159+
160+ Ok ( ( ) )
161+ }
162+
163+ #[ tokio:: test]
164+ async fn test_count_wildcard_on_window ( ) -> Result < ( ) > {
165+ let ctx = create_join_context ( ) ?;
166+
167+ let sql_results = ctx
168+ . sql ( "select COUNT(*) OVER(ORDER BY a DESC RANGE BETWEEN 6 PRECEDING AND 2 FOLLOWING) from t1" )
169+ . await ?
170+ . explain ( false , false ) ?
171+ . collect ( )
172+ . await ?;
173+
174+ let df_results = ctx
175+ . table ( "t1" )
176+ . await ?
177+ . select ( vec ! [ Expr :: WindowFunction ( expr:: WindowFunction :: new(
178+ WindowFunction :: AggregateFunction ( AggregateFunction :: Count ) ,
179+ vec![ Expr :: Wildcard ] ,
180+ vec![ ] ,
181+ vec![ Expr :: Sort ( Sort :: new( Box :: new( col( "a" ) ) , false , true ) ) ] ,
182+ WindowFrame {
183+ units: WindowFrameUnits :: Range ,
184+ start_bound: WindowFrameBound :: Preceding ( ScalarValue :: UInt32 ( Some ( 6 ) ) ) ,
185+ end_bound: WindowFrameBound :: Following ( ScalarValue :: UInt32 ( Some ( 2 ) ) ) ,
186+ } ,
187+ ) ) ] ) ?
188+ . explain ( false , false ) ?
189+ . collect ( )
190+ . await ?;
191+
192+ //make sure sql plan same with df plan
193+ assert_eq ! (
194+ pretty_format_batches( & df_results) ?. to_string( ) ,
195+ pretty_format_batches( & sql_results) ?. to_string( )
196+ ) ;
197+
198+ Ok ( ( ) )
199+ }
200+
201+ #[ tokio:: test]
202+ async fn test_count_wildcard_on_aggregate ( ) -> Result < ( ) > {
203+ let ctx = create_join_context ( ) ?;
204+ register_alltypes_tiny_pages_parquet ( & ctx) . await ?;
50205
51206 let sql_results = ctx
52- . sql ( "select count(*) from alltypes_tiny_pages " )
207+ . sql ( "select count(*) from t1 " )
53208 . await ?
54209 . select ( vec ! [ count( Expr :: Wildcard ) ] ) ?
55210 . explain ( false , false ) ?
@@ -58,7 +213,7 @@ async fn count_wildcard() -> Result<()> {
58213
59214 // add `.select(vec![count(Expr::Wildcard)])?` to make sure we can analyze all node instead of just top node.
60215 let df_results = ctx
61- . table ( "alltypes_tiny_pages " )
216+ . table ( "t1 " )
62217 . await ?
63218 . aggregate ( vec ! [ ] , vec ! [ count( Expr :: Wildcard ) ] ) ?
64219 . select ( vec ! [ count( Expr :: Wildcard ) ] ) ?
@@ -72,24 +227,54 @@ async fn count_wildcard() -> Result<()> {
72227 pretty_format_batches( & df_results) ?. to_string( )
73228 ) ;
74229
75- let results = ctx
76- . table ( "alltypes_tiny_pages" )
230+ Ok ( ( ) )
231+ }
232+ #[ tokio:: test]
233+ async fn test_count_wildcard_on_where_scalar_subquery ( ) -> Result < ( ) > {
234+ let ctx = create_join_context ( ) ?;
235+
236+ let sql_results = ctx
237+ . sql ( "select a,b from t1 where (select count(*) from t2 where t1.a = t2.a)>0;" )
77238 . await ?
78- . aggregate ( vec ! [ ] , vec ! [ count ( Expr :: Wildcard ) ] ) ?
239+ . explain ( false , false ) ?
79240 . collect ( )
80241 . await ?;
81242
82- let expected = vec ! [
83- "+-----------------+" ,
84- "| COUNT(UInt8(1)) |" ,
85- "+-----------------+" ,
86- "| 7300 |" ,
87- "+-----------------+" ,
88- ] ;
89- assert_batches_sorted_eq ! ( expected, & results) ;
243+ // In the same SessionContext, AliasGenerator will increase subquery_alias id by 1
244+ // https://github.com/apache/arrow-datafusion/blame/cf45eb9020092943b96653d70fafb143cc362e19/datafusion/optimizer/src/alias.rs#L40-L43
245+ // for compare difference betwwen sql and df logical plan, we need to create a new SessionContext here
246+ let ctx = create_join_context ( ) ?;
247+ let df_results = ctx
248+ . table ( "t1" )
249+ . await ?
250+ . filter (
251+ ScalarSubquery ( datafusion_expr:: Subquery {
252+ subquery : Arc :: new (
253+ ctx. table ( "t2" )
254+ . await ?
255+ . filter ( col ( "t1.a" ) . eq ( col ( "t2.a" ) ) ) ?
256+ . aggregate ( vec ! [ ] , vec ! [ count( lit( COUNT_STAR_EXPANSION ) ) ] ) ?
257+ . select ( vec ! [ count( lit( COUNT_STAR_EXPANSION ) ) ] ) ?
258+ . into_unoptimized_plan ( ) ,
259+ ) ,
260+ outer_ref_columns : vec ! [ ] ,
261+ } )
262+ . gt ( lit ( ScalarValue :: UInt8 ( Some ( 0 ) ) ) ) ,
263+ ) ?
264+ . select ( vec ! [ col( "t1.a" ) , col( "t1.b" ) ] ) ?
265+ . explain ( false , false ) ?
266+ . collect ( )
267+ . await ?;
268+
269+ //make sure sql plan same with df plan
270+ assert_eq ! (
271+ pretty_format_batches( & sql_results) ?. to_string( ) ,
272+ pretty_format_batches( & df_results) ?. to_string( )
273+ ) ;
90274
91275 Ok ( ( ) )
92276}
277+
93278#[ tokio:: test]
94279async fn describe ( ) -> Result < ( ) > {
95280 let ctx = SessionContext :: new ( ) ;
@@ -1047,3 +1232,14 @@ async fn table_with_nested_types(n: usize) -> Result<DataFrame> {
10471232 ctx. register_batch ( "shapes" , batch) ?;
10481233 ctx. table ( "shapes" ) . await
10491234}
1235+
1236+ pub async fn register_alltypes_tiny_pages_parquet ( ctx : & SessionContext ) -> Result < ( ) > {
1237+ let testdata = parquet_test_data ( ) ;
1238+ ctx. register_parquet (
1239+ "alltypes_tiny_pages" ,
1240+ & format ! ( "{testdata}/alltypes_tiny_pages.parquet" ) ,
1241+ ParquetReadOptions :: default ( ) ,
1242+ )
1243+ . await ?;
1244+ Ok ( ( ) )
1245+ }
0 commit comments