Validates that the query's physical plan uses a NestedLoopJoin (NLJ), then executes the query and collects execution times. TODO: ensure the optimizer won't change the join order (it's not at v48.0.0).
(
&self,
sql: &str,
query_name: &str,
ctx: &SessionContext,
)
| 242 | /// TODO: ensure the optimizer won't change the join order (it's not at |
| 243 | /// v48.0.0). |
| 244 | async fn benchmark_query( |
| 245 | &self, |
| 246 | sql: &str, |
| 247 | query_name: &str, |
| 248 | ctx: &SessionContext, |
| 249 | ) -> Result<Vec<QueryResult>> { |
| 250 | let mut query_results = vec![]; |
| 251 | |
| 252 | // Validate that the query plan includes a Nested Loop Join |
| 253 | let df = ctx.sql(sql).await?; |
| 254 | let physical_plan = df.create_physical_plan().await?; |
| 255 | let plan_string = format!("{physical_plan:#?}"); |
| 256 | |
| 257 | if !plan_string.contains("NestedLoopJoinExec") { |
| 258 | return Err(exec_datafusion_err!( |
| 259 | "Query {query_name} does not use Nested Loop Join. Physical plan: {plan_string}" |
| 260 | )); |
| 261 | } |
| 262 | |
| 263 | for i in 0..self.common.iterations { |
| 264 | let start = Instant::now(); |
| 265 | |
| 266 | let row_count = Self::execute_sql_without_result_buffering(sql, ctx).await?; |
| 267 | |
| 268 | let elapsed = start.elapsed(); |
| 269 | |
| 270 | println!( |
| 271 | "Query {query_name} iteration {i} returned {row_count} rows in {elapsed:?}" |
| 272 | ); |
| 273 | |
| 274 | query_results.push(QueryResult { elapsed, row_count }); |
| 275 | } |
| 276 | |
| 277 | Ok(query_results) |
| 278 | } |
| 279 | |
| 280 | /// Executes the SQL query and drops each result batch after evaluation, to |
| 281 | /// minimizes memory usage by not buffering results. |
no test coverage detected