Creates [SqlTransform::Intersect] from [Transform::Join]
(
pipeline: Vec<SqlTransform>,
ctx: &mut Context,
)
| 397 | |
| 398 | /// Creates [SqlTransform::Intersect] from [Transform::Join] |
| 399 | pub(in crate::sql) fn intersect( |
| 400 | pipeline: Vec<SqlTransform>, |
| 401 | ctx: &mut Context, |
| 402 | ) -> Result<Vec<SqlTransform>> { |
| 403 | use SqlTransform::*; |
| 404 | |
| 405 | let output = ctx.anchor.determine_select_columns(&pipeline); |
| 406 | let output: HashSet<CId, RandomState> = HashSet::from_iter(output); |
| 407 | |
| 408 | let mut res = Vec::with_capacity(pipeline.len()); |
| 409 | let mut pipeline = pipeline.into_iter().peekable(); |
| 410 | while let Some(t) = pipeline.next() { |
| 411 | res.push(t); |
| 412 | |
| 413 | if res.is_empty() { |
| 414 | continue; |
| 415 | } |
| 416 | let Join { |
| 417 | side: JoinSide::Inner, |
| 418 | filter: join_cond, |
| 419 | with, |
| 420 | } = &res[res.len() - 1] |
| 421 | else { |
| 422 | continue; |
| 423 | }; |
| 424 | let with = ctx.anchor.relation_instances.get_mut(with).unwrap(); |
| 425 | |
| 426 | let bottom = with.table_ref.columns.iter().map(|(_, c)| *c).collect_vec(); |
| 427 | let top = ctx.anchor.determine_select_columns(&res[0..res.len() - 1]); |
| 428 | |
| 429 | // join_cond must be a join over all columns |
| 430 | // (this could be loosened to check only the relation key) |
| 431 | let (left, right) = collect_equals(join_cond)?; |
| 432 | if !(all_in(&top, left) && all_in(&bottom, right)) { |
| 433 | continue; |
| 434 | } |
| 435 | |
| 436 | // select must not contain things from bottom |
| 437 | if bottom.iter().any(|c| output.contains(c)) { |
| 438 | continue; |
| 439 | } |
| 440 | // select must contain at least one thing from top |
| 441 | if top.iter().all(|c| !output.contains(c)) { |
| 442 | continue; |
| 443 | } |
| 444 | |
| 445 | // determine DISTINCT |
| 446 | let mut distinct = false; |
| 447 | // INTERSECT ALL can become except INTERSECT DISTINCT |
| 448 | // - if top is DISTINCT or |
| 449 | // - if output is DISTINCT |
| 450 | if res.len() > 1 { |
| 451 | if let Distinct = &res[res.len() - 2] { |
| 452 | distinct = true; |
| 453 | } |
| 454 | } |
| 455 | if let Some(SqlTransform::Distinct) = pipeline.peek() { |
| 456 | distinct = true; |
no test coverage detected