rudb-opt 0.4.13

The rewrite passes, cardinality estimation, join ordering, predicate transfer and layout adaptation.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
//! Taking the domain back out of a decorrelated existence test.
//!
//! `domain.rs` and the rules beside it lower a correlated subquery by inventing a relation of the
//! distinct values the correlated columns take, running the subquery once against that relation,
//! and joining the answers back to the outer rows on those same columns. The domain is what makes
//! the inner side run once rather than once per outer row. It is built before anything knows which
//! outer rows there are going to be, so it is taken from whichever branch of the `FROM` list the
//! correlated columns come from, and it is a superset of the values the query ends up asking about.
//!
//! On TPC-H q21 that superset is the whole of lineitem. The outer query is supplier joined to
//! lineitem, orders and nation, and once its filters have run it has seventy eight thousand rows,
//! so each of the two existence tests in it is asked about seventy eight thousand pairs of order key
//! and supplier key. The domain has six million of them, because the branch the keys come from is
//! the lineitem scan and every join that cuts it down sits above the point the domain was taken at.
//! Both existence tests then build a hash table of six million rows to answer a question about
//! seventy eight thousand, and that is most of what q21 costs.
//!
//! What this pass does is notice that by the time the plan is this far along the outer side of the
//! join back is the exact relation the domain was standing in for, and write the whole shape as one
//! semi join against it. The domain goes, the grouping over it goes, the marker projection goes and
//! the join back goes, and what is left is the outer side joined to the subquery's own relation on
//! the conditions the subquery was correlated by. DuckDB calls the pass that does this the
//! deliminator and that is the name it goes by here.
//!
//! # Why it is the same query
//!
//! The shape it matches answers one question: which outer rows have a correlated key that the
//! subquery holds a match for. It answers it in four steps. The domain reduces the outer keys to the
//! distinct values they take, the join under the grouping keeps the domain values the subquery
//! matched, the marker projection writes a constant beside each of those, and the single join puts
//! the marker back beside every outer row whose key is one of them. The filter over the marker then
//! keeps the rows that got one, or the rows that did not.
//!
//! A semi join asks the same question in one step, and an anti join asks its negation. An outer row
//! reaches the output exactly when its key had a match, which is what both spellings say, and
//! neither can produce a row twice: a single join matches at most one row on the right by
//! definition, and a semi join produces each driving row at most once by definition.
//!
//! The nulls need no special handling and that is worth saying, because the join back is written
//! with `IS NOT DISTINCT FROM` rather than `=` for exactly that reason. The domain carries a row for
//! a null key and the null safe comparison finds it, so an outer row with a null key is asked about
//! rather than dropped. After the collapse the same outer row is handed to the same conditions the
//! domain row would have been handed to, with the same null in it, so the answer it gets is the one
//! it was getting before.
//!
//! # The same test with no domain in it
//!
//! Not every correlated existence test gets a domain. When the correlated column is compared for
//! equality against a column of the subquery's own relation, the values the outer query could ask
//! about are the values that relation holds, so the binder groups the relation itself rather than
//! inventing a domain to group. TPC-H q4 is that shape. `EXISTS (SELECT * FROM lineitem WHERE
//! l_orderkey = o_orderkey AND l_commitdate < l_receiptdate)` binds to the distinct order keys of
//! the filtered lineitem, a marker beside each of them, and the single join putting the marker back
//! beside the order it belongs to.
//!
//! The grouping is then the only thing between the single join and the subquery's relation, and it
//! is there to stop the join matching an outer row twice. A semi join stops at the first match, so
//! once the join is a semi join the grouping has nothing left to do and goes with the rest of it.
//! At SF1 that grouping reads 3,793,296 lineitem rows and produces 1,375,365 order keys, which is
//! most of what q4 costs, and q22 pays the same for the distinct customer keys of orders.
//!
//! The one thing the grouping was doing besides the duplicates is raising the error a single join
//! raises when it matches twice, so the collapse only happens where that error could not have
//! fired. That means one equality per grouped column, each naming a different one, which is the
//! condition under which a grouped relation holds at most one matching row per outer row. A
//! condition that leaves a grouped column unjoined is refused on those grounds, and so is one that
//! is not an equality at all.
//!
//! # What it refuses
//!
//! A marker that is not a constant, or a constant that is null. The filter tests the marker against
//! null to find out whether the single join matched, and that test only means what it is being read
//! to mean if a matched row always carries a value.
//!
//! A domain grouped on anything but bare columns of the outer side. A domain over an expression is
//! one whose values the outer side does not hold and so is not one the outer side can replace.
//!
//! A join back that is anything but one null safe equality per domain column. An extra condition is
//! a condition the collapse would drop, and a missing one is a key nothing lines up.
//!
//! A subquery relation that reads the outer side. Decorrelation is meant to have left it reading
//! nothing but the domain, and if something is still in there then moving it under a plain join
//! would leave it reading a side it cannot see.
//!
//! A plan where anything above reads a column of the marker projection. The collapse produces the
//! outer side's columns and nothing else, on the same grounds `semi.rs` refuses a mark join whose
//! gathered columns are read.
//!
//! # Rewriting in place
//!
//! The semi join is written over the filter's slot, the way `semi.rs` and `topn.rs` write theirs.
//! Both of the sides it points at are behind the single join, which is behind the filter, so the
//! arena's rule that a node may only point backwards still holds and nothing above has to be built
//! again.

use std::collections::HashMap;

use rudb_common::{Result, Value};
use rudb_plan::{
    BuildSide, ColumnBinding, CompareOp, Expr, ExprRef, JoinKind, Node, NodeRef, Plan, Slice,
};

use crate::domain::remap;
use crate::pass::{Context, Pass, top_down};
use crate::tables::{TableSet, produced};
use crate::walk;

/// Rewrites a decorrelated existence test into the semi join it is asking for.
#[derive(Debug, Clone, Copy)]
pub struct Deliminator;

impl Pass for Deliminator {
    /// DuckDB's name for the pass that removes a domain, which is already in [`crate::UPSTREAM`].
    fn name(&self) -> &'static str {
        "deliminator"
    }

    fn run(&self, plan: &mut Plan, _context: &Context) -> Result<()> {
        remove(plan);
        Ok(())
    }
}

/// Collapses every decorrelated existence test in `plan` into a semi or an anti join.
pub fn remove(plan: &mut Plan) {
    for node in top_down(plan) {
        let Some(found) = matched(plan, node).or_else(|| flattened(plan, node)) else {
            continue;
        };
        let held = plan.expr_list(found.conditions).to_vec();
        let moved: Vec<ExprRef> =
            held.into_iter().map(|condition| remap(plan, condition, &found.moved)).collect();
        let conditions = plan.add_expr_list(&moved);
        *plan.node_mut(node) = Node::Join {
            left: found.left,
            right: found.right,
            kind: found.kind,
            conditions,
            build: BuildSide::default(),
        };
    }
}

/// The parts of one collapsible existence test.
struct Found {
    /// The outer side, which becomes the driving side.
    left: NodeRef,
    /// The subquery's own relation, which becomes the gathered side.
    right: NodeRef,
    /// The conditions the subquery was correlated by, still written against the domain.
    conditions: Slice,
    /// Semi where the filter kept the rows that matched, anti where it kept the rest.
    kind: JoinKind,
    /// Which outer column each domain column was standing in for.
    moved: HashMap<ColumnBinding, ColumnBinding>,
}

/// Reads the shape out of the filter at `node`, or nothing if it is not one of these.
///
/// Every step is a check that the node underneath is the node decorrelation put there, and the
/// order they are written in is the order down the plan: the filter, the join back, the marker
/// projection, the grouping that made the answers distinct, the join to the subquery's relation and
/// the domain itself.
fn matched(plan: &Plan, node: NodeRef) -> Option<Found> {
    let Node::Filter { input, predicate } = *plan.node(node) else {
        return None;
    };
    let (kind, tested) = asked(plan, predicate)?;
    let Node::Join { left, right, kind: JoinKind::Single, conditions, .. } = *plan.node(input)
    else {
        return None;
    };
    let Node::Project { input: distinct, index: marker, exprs, .. } = *plan.node(right) else {
        return None;
    };
    if tested != ColumnBinding::new(marker, 0) {
        return None;
    }
    let projected = plan.expr_list(exprs).to_vec();
    let (&flag, carried) = projected.split_first()?;
    let Expr::Constant(value) = *plan.expr(flag) else {
        return None;
    };
    if matches!(plan.value(value), Value::Null) {
        return None;
    }

    let Node::Aggregate { input: answers, index: distinct_index, groups, aggregates } =
        *plan.node(distinct)
    else {
        return None;
    };
    if !plan.expr_list(aggregates).is_empty() || !columns(plan, carried, distinct_index) {
        return None;
    }
    let Node::Join { left: domain, right: inner, kind: inside, conditions: correlated, .. } =
        *plan.node(answers)
    else {
        return None;
    };
    if !matches!(inside, JoinKind::Inner | JoinKind::Semi) {
        return None;
    }
    let Node::Aggregate { index: domain_index, groups: keys, aggregates: none, .. } =
        *plan.node(domain)
    else {
        return None;
    };
    let grouped = plan.expr_list(groups).to_vec();
    if !plan.expr_list(none).is_empty() || !columns(plan, &grouped, domain_index) {
        return None;
    }

    let outer = produced(plan, left);
    let mut moved = HashMap::new();
    let mut keyed = Vec::new();
    for (position, &key) in plan.expr_list(keys).iter().enumerate() {
        let Expr::Column(binding) = *plan.expr(key) else {
            return None;
        };
        if !outer.contains(binding.table) {
            return None;
        }
        moved.insert(ColumnBinding::new(domain_index, at(position)?), binding);
        keyed.push(binding);
    }
    if keyed.len() != grouped.len() || keyed.len() != carried.len() {
        return None;
    }
    if !lines_up(plan, conditions, marker, &keyed) {
        return None;
    }
    if reads(plan, inner, &outer) || read_above(plan, marker, node, input) {
        return None;
    }
    Some(Found { left, right: inner, conditions: correlated, kind, moved })
}

/// Reads the same shape with no domain in it out of the filter at `node`.
///
/// The steps are [`matched`]'s down to the grouping, and then they stop: what is under the grouping
/// is the subquery's own relation rather than a join back to a domain, so there is no domain to
/// take out and the grouping is the whole of what goes. Which outer column each carried column
/// stands for is read off the group expressions instead of off a domain's keys, and it is one step
/// rather than two because the conditions name the marker projection's columns and the group
/// expressions name the relation's.
fn flattened(plan: &Plan, node: NodeRef) -> Option<Found> {
    let Node::Filter { input, predicate } = *plan.node(node) else {
        return None;
    };
    let (kind, tested) = asked(plan, predicate)?;
    let Node::Join { left, right, kind: JoinKind::Single, conditions, .. } = *plan.node(input)
    else {
        return None;
    };
    let Node::Project { input: distinct, index: marker, exprs, .. } = *plan.node(right) else {
        return None;
    };
    if tested != ColumnBinding::new(marker, 0) {
        return None;
    }
    let projected = plan.expr_list(exprs).to_vec();
    let (&flag, carried) = projected.split_first()?;
    let Expr::Constant(value) = *plan.expr(flag) else {
        return None;
    };
    if matches!(plan.value(value), Value::Null) {
        return None;
    }

    let Node::Aggregate { input: answers, index: distinct_index, groups, aggregates } =
        *plan.node(distinct)
    else {
        return None;
    };
    if !plan.expr_list(aggregates).is_empty() || !columns(plan, carried, distinct_index) {
        return None;
    }
    let grouped = plan.expr_list(groups).to_vec();
    if grouped.len() != carried.len() {
        return None;
    }

    let mut moved = HashMap::new();
    for (position, &group) in grouped.iter().enumerate() {
        let Expr::Column(binding) = *plan.expr(group) else {
            return None;
        };
        moved.insert(ColumnBinding::new(marker, at(position + 1)?), binding);
    }
    if !covers(plan, conditions, marker, carried.len())
        || reads(plan, answers, &produced(plan, left))
    {
        return None;
    }
    if read_above(plan, marker, node, input)
        || read_beside(plan, distinct_index, [node, input, right])
    {
        return None;
    }
    Some(Found { left, right: answers, conditions, kind, moved })
}

/// Which join a filter over a marker is asking for, and the marker it reads.
///
/// A bare test that the marker is not null is the existence test itself and is a semi join. The
/// same test with `not` in front of it is what a `NOT EXISTS` binds to and is an anti join. Nothing
/// else here is one of these, and in particular `not` over an anti join is not written, because
/// the rewrite that would produce one does not exist and a double negation folds away before this.
fn asked(plan: &Plan, predicate: ExprRef) -> Option<(JoinKind, ColumnBinding)> {
    if let Expr::Function { name, args } = *plan.expr(predicate) {
        if plan.string(name) != "not" {
            return None;
        }
        let [only] = *plan.expr_list(args) else {
            return None;
        };
        return match asked(plan, only)? {
            (JoinKind::Semi, binding) => Some((JoinKind::Anti, binding)),
            _ => None,
        };
    }
    let Expr::Compare { op: CompareOp::DistinctFrom, left, right } = *plan.expr(predicate) else {
        return None;
    };
    let Expr::Column(binding) = *plan.expr(left) else {
        return None;
    };
    let Expr::Constant(value) = *plan.expr(right) else {
        return None;
    };
    matches!(plan.value(value), Value::Null).then_some((JoinKind::Semi, binding))
}

/// Whether `exprs` reads columns nought upward of `index`, in that order and nothing else.
///
/// Both of the projections between the domain and the outer side carry their input's columns
/// straight through, and a pass that took them on trust would be reading the wrong column the day
/// one of them reorders or drops one.
fn columns(plan: &Plan, exprs: &[ExprRef], index: u32) -> bool {
    exprs.iter().enumerate().all(|(position, &expr)| {
        let Expr::Column(binding) = *plan.expr(expr) else {
            return false;
        };
        at(position).is_some_and(|column| binding == ColumnBinding::new(index, column))
    })
}

/// Whether the join back is one null safe equality per key, each naming a different domain column.
///
/// The count is checked as well as the names, because two conditions on one key and none on another
/// is a shape where the counts agree and a key goes unjoined.
fn lines_up(plan: &Plan, conditions: Slice, marker: u32, keyed: &[ColumnBinding]) -> bool {
    let held = plan.expr_list(conditions);
    if held.len() != keyed.len() {
        return false;
    }
    let mut seen = vec![false; keyed.len()];
    for &condition in held {
        let Expr::Compare { op: CompareOp::NotDistinctFrom, left, right } = *plan.expr(condition)
        else {
            return false;
        };
        let Expr::Column(here) = *plan.expr(left) else {
            return false;
        };
        let Expr::Column(there) = *plan.expr(right) else {
            return false;
        };
        // Whichever way round it was written, one side reads the marker projection and the other
        // reads the outer row the marker is being put back beside.
        let (outer, carried) = if there.table == marker { (here, there) } else { (there, here) };
        if carried.table != marker || carried.column == 0 {
            return false;
        }
        let Ok(position) = usize::try_from(carried.column - 1) else {
            return false;
        };
        if position >= keyed.len() || seen[position] || keyed[position] != outer {
            return false;
        }
        seen[position] = true;
    }
    seen.into_iter().all(|found| found)
}

/// Whether the join back is one equality per carried column, each naming a different one.
///
/// This is what says the grouping under the marker projection can go. A grouped relation holds one
/// row per combination of the grouped columns, so an outer row that fixes every one of them by an
/// equality has at most one row to match and the grouping was never stopping the join matching
/// twice. Leave a grouped column unjoined and it was, and taking the grouping away would turn a
/// query that raised an error into one that answers.
///
/// Either spelling of equality counts. The join back is written null safe where a domain was
/// involved, because the domain carries a row for a null key, and plain where the values came from
/// the subquery's own relation, and both of them match one row of a grouped relation at most.
fn covers(plan: &Plan, conditions: Slice, marker: u32, carried: usize) -> bool {
    let held = plan.expr_list(conditions);
    if held.len() != carried {
        return false;
    }
    let mut seen = vec![false; carried];
    for &condition in held {
        let Expr::Compare { op: CompareOp::Equal | CompareOp::NotDistinctFrom, left, right } =
            *plan.expr(condition)
        else {
            return false;
        };
        let Expr::Column(here) = *plan.expr(left) else {
            return false;
        };
        let Expr::Column(there) = *plan.expr(right) else {
            return false;
        };
        // One side reads the marker projection and the other reads the outer row. Both sides
        // reading it is a condition the outer row has no part in, and neither side reading it is a
        // condition on some grouped column this cannot see.
        let named = match (here.table == marker, there.table == marker) {
            (true, false) => here,
            (false, true) => there,
            _ => return false,
        };
        if named.column == 0 {
            return false;
        }
        let Ok(position) = usize::try_from(named.column - 1) else {
            return false;
        };
        if position >= carried || seen[position] {
            return false;
        }
        seen[position] = true;
    }
    seen.into_iter().all(|found| found)
}

/// Whether anything but the nodes in `going` reads a column of the grouping at `distinct`.
///
/// The marker projection reads it and is one of the three, along with the filter and the join,
/// because all three of them are what the collapse writes over. Anything else that reads it would
/// be left naming a table that the plan no longer produces.
fn read_beside(plan: &Plan, distinct: u32, going: [NodeRef; 3]) -> bool {
    let mut found = false;
    for at in top_down(plan) {
        if going.contains(&at) {
            continue;
        }
        walk::node_columns(plan, at, &mut |_, binding| found |= binding.table == distinct);
    }
    found
}

/// Whether anything in the subtree under `at` reads a column of a table in `outer`.
fn reads(plan: &Plan, at: NodeRef, outer: &TableSet) -> bool {
    let mut found = false;
    walk::node_columns(plan, at, &mut |_, binding| found |= outer.contains(binding.table));
    found || plan.node(at).children().into_iter().flatten().any(|child| reads(plan, child, outer))
}

/// Whether anything but the filter and the join under it reads a column of the marker projection.
///
/// The projection is the only place the marker's table index is produced, so nothing under it reads
/// one either and there is no subtree to skip. The filter is skipped because the whole point is that
/// it is going away, and the join because its conditions read the marker by definition.
fn read_above(plan: &Plan, marker: u32, filter: NodeRef, join: NodeRef) -> bool {
    let mut found = false;
    for at in top_down(plan) {
        if at == filter || at == join {
            continue;
        }
        walk::node_columns(plan, at, &mut |_, binding| found |= binding.table == marker);
    }
    found
}

/// A position in a column list as the column number it is.
fn at(position: usize) -> Option<u32> {
    u32::try_from(position).ok()
}

#[cfg(test)]
mod tests {
    use rudb_plan::Plan;

    use super::remove;

    /// What the plan a text prints looks like once the pass has run over it.
    fn removed(text: &str) -> String {
        let mut plan =
            Plan::parse(text).unwrap_or_else(|error| panic!("{text} did not parse: {error}"));
        remove(&mut plan);
        plan.validate().unwrap_or_else(|error| panic!("{text} did not stay valid: {error}"));
        plan.to_string()
    }

    /// The shape decorrelation leaves an `EXISTS` in, with `head` over the marker test.
    fn existence(head: &str) -> String {
        format!(
            concat!(
                "Filter {head}\n",
                "  Join SINGLE on=[(#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN]\n",
                "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
                "      Aggregate #4 groups=[#3.0::BIGINT] aggregates=[]\n",
                "        Join SEMI on=[(#1.0::BIGINT = #3.0::BIGINT)::BOOLEAN]\n",
                "          Aggregate #3 groups=[#0.0::BIGINT] aggregates=[]\n",
                "            Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "          Get memory.main.u AS u #1 [k::BIGINT]\n",
            ),
            head = head
        )
    }

    /// The marker test itself, which is what an `EXISTS` and a `NOT EXISTS` differ by.
    const TESTED: &str = "(#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN";

    #[test]
    fn an_existence_test_over_a_domain_becomes_a_semi_join_against_the_outer_side() {
        assert_eq!(
            removed(&existence(TESTED)),
            concat!(
                "Join SEMI on=[(#1.0::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
                "  Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "  Get memory.main.u AS u #1 [k::BIGINT]\n",
            )
        );
    }

    #[test]
    fn the_same_test_with_not_in_front_of_it_becomes_an_anti_join() {
        assert_eq!(
            removed(&existence(&format!("not({TESTED})::BOOLEAN"))),
            concat!(
                "Join ANTI on=[(#1.0::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
                "  Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "  Get memory.main.u AS u #1 [k::BIGINT]\n",
            )
        );
    }

    #[test]
    fn a_filter_on_something_other_than_the_marker_is_left_alone() {
        let text = existence("(#0.0::BIGINT > 3::BIGINT)::BOOLEAN");
        assert_eq!(removed(&text), text);
    }

    #[test]
    fn a_marker_column_read_above_the_filter_stops_the_collapse() {
        let text = concat!(
            "Project #6 [#5.1::BIGINT AS k]\n",
            "  Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "    Join SINGLE on=[(#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN]\n",
            "      Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "      Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "        Aggregate #4 groups=[#3.0::BIGINT] aggregates=[]\n",
            "          Join SEMI on=[(#1.0::BIGINT = #3.0::BIGINT)::BOOLEAN]\n",
            "            Aggregate #3 groups=[#0.0::BIGINT] aggregates=[]\n",
            "              Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "            Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_real_aggregate_under_the_marker_is_not_a_domain_and_is_left_alone() {
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "      Aggregate #4 groups=[#3.0::BIGINT] aggregates=[count_star()::BIGINT]\n",
            "        Join SEMI on=[(#1.0::BIGINT = #3.0::BIGINT)::BOOLEAN]\n",
            "          Aggregate #3 groups=[#0.0::BIGINT] aggregates=[]\n",
            "            Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "          Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_domain_grouped_on_an_expression_is_left_alone() {
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "      Aggregate #4 groups=[#3.0::BIGINT] aggregates=[]\n",
            "        Join SEMI on=[(#1.0::BIGINT = #3.0::BIGINT)::BOOLEAN]\n",
            "          Aggregate #3 groups=[\"+\"(#0.0::BIGINT, 1::BIGINT)::BIGINT] aggregates=[]\n",
            "            Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "          Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_subquery_relation_that_still_reads_the_outer_side_is_left_alone() {
        // Nothing decorrelation produces looks like this, and a join whose gathered side reads its
        // driving side is the one thing the collapse cannot write down.
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "      Aggregate #4 groups=[#3.0::BIGINT] aggregates=[]\n",
            "        Join SEMI on=[(#1.0::BIGINT = #3.0::BIGINT)::BOOLEAN]\n",
            "          Aggregate #3 groups=[#0.0::BIGINT] aggregates=[]\n",
            "            Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "          Filter (#1.0::BIGINT > #0.1::BIGINT)::BOOLEAN\n",
            "            Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_join_back_that_leaves_a_key_unjoined_is_left_alone() {
        // Two conditions on one of the two domain columns and none on the other. The counts agree
        // and the second key is joined on nothing, which is not the shape this reads it as.
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN, \
             (#0.0::BIGINT IS NOT DISTINCT FROM #5.1::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1, \
             #4.1::BIGINT AS __correlated_2]\n",
            "      Aggregate #4 groups=[#3.0::BIGINT, #3.1::BIGINT] aggregates=[]\n",
            "        Join SEMI on=[(#1.0::BIGINT = #3.0::BIGINT)::BOOLEAN]\n",
            "          Aggregate #3 groups=[#0.0::BIGINT, #0.1::BIGINT] aggregates=[]\n",
            "            Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "          Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    /// The shape decorrelation leaves an `EXISTS` in when the correlated column was already a
    /// column of the subquery's own relation, so there is no domain and only the grouping.
    fn grouped(head: &str) -> String {
        format!(
            concat!(
                "Filter {head}\n",
                "  Join SINGLE on=[(#5.1::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
                "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
                "      Aggregate #4 groups=[#1.0::BIGINT] aggregates=[]\n",
                "        Get memory.main.u AS u #1 [k::BIGINT]\n",
            ),
            head = head
        )
    }

    #[test]
    fn an_existence_test_over_a_grouping_becomes_a_semi_join_against_the_relation() {
        assert_eq!(
            removed(&grouped(TESTED)),
            concat!(
                "Join SEMI on=[(#1.0::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
                "  Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "  Get memory.main.u AS u #1 [k::BIGINT]\n",
            )
        );
    }

    #[test]
    fn the_same_grouping_with_not_in_front_of_the_test_becomes_an_anti_join() {
        assert_eq!(
            removed(&grouped(&format!("not({TESTED})::BOOLEAN"))),
            concat!(
                "Join ANTI on=[(#1.0::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
                "  Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
                "  Get memory.main.u AS u #1 [k::BIGINT]\n",
            )
        );
    }

    #[test]
    fn a_grouping_with_a_real_aggregate_in_it_is_left_alone() {
        // A `HAVING` inside the subquery, which produces rows the relation underneath does not.
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#5.1::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "      Aggregate #4 groups=[#1.0::BIGINT] aggregates=[count_star()::BIGINT]\n",
            "        Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_grouping_on_an_expression_is_left_alone() {
        // The relation holds no column of that shape, so there is nothing for the conditions to
        // read once the grouping producing it has gone.
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#5.1::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "      Aggregate #4 groups=[abs(#1.0::BIGINT)::BIGINT] aggregates=[]\n",
            "        Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_join_back_that_is_not_an_equality_is_left_alone() {
        // The grouping keeps one row per key and the outer row wants every key above its own, so
        // the single join raises its error here and taking the grouping away would hide it.
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#5.1::BIGINT > #0.0::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "      Aggregate #4 groups=[#1.0::BIGINT] aggregates=[]\n",
            "        Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_grouped_column_the_join_back_does_not_name_is_left_alone() {
        // Two conditions on the first of the two grouped columns and none on the second, which is
        // the same miscount the domain shape refuses and for the same reason.
        let text = concat!(
            "Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "  Join SINGLE on=[(#5.1::BIGINT = #0.0::BIGINT)::BOOLEAN, \
             (#5.1::BIGINT = #0.1::BIGINT)::BOOLEAN]\n",
            "    Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "    Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1, \
             #4.1::BIGINT AS __correlated_2]\n",
            "      Aggregate #4 groups=[#1.0::BIGINT, #1.1::BIGINT] aggregates=[]\n",
            "        Get memory.main.u AS u #1 [k::BIGINT, j::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }

    #[test]
    fn a_grouped_column_read_above_the_filter_stops_the_collapse() {
        let text = concat!(
            "Project #6 [#5.1::BIGINT AS k]\n",
            "  Filter (#5.0::BOOLEAN IS DISTINCT FROM NULL::BOOLEAN)::BOOLEAN\n",
            "    Join SINGLE on=[(#5.1::BIGINT = #0.0::BIGINT)::BOOLEAN]\n",
            "      Get memory.main.t AS t #0 [a::BIGINT, b::BIGINT]\n",
            "      Project #5 [TRUE::BOOLEAN AS exists, #4.0::BIGINT AS __correlated_1]\n",
            "        Aggregate #4 groups=[#1.0::BIGINT] aggregates=[]\n",
            "          Get memory.main.u AS u #1 [k::BIGINT]\n",
        );
        assert_eq!(removed(text), text);
    }
}