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
//! v7.37.16 — subquery-semantics PG18 differential corpus (13th sweep).
//!
//! Every assertion below is the live PostgreSQL 18.4 answer captured on the
//! mini bench container (`psql -tA`, NULL rendered `<NULL>` via
//! `coalesce(...::text,'<NULL>')`, error cases run to observe whether PG
//! raises vs returns). This sweep targets the expression/predicate-position
//! subquery surface that prior sweeps had not directly covered:
//!
//! * scalar-subquery cardinality (>1 row → error, 0 rows → NULL) in the
//! SELECT list, in WHERE, and inside a larger expression,
//! * correlated scalar subqueries (count / avg / bare column, empty → NULL,
//! >1 row → error, 2-level nesting),
//! * EXISTS / NOT EXISTS (SELECT NULL, SELECT *, empty inner),
//! * IN / NOT IN with a NULL in the list (the classic 3VL NOT-IN trap),
//! * `op ANY / ALL / SOME (subquery)` including the empty-subquery corners.
//!
//! SPG matched PG18 on every case **except one**, now fixed: a NULL LHS
//! against an empty ANY/ALL set (`NULL op ALL(empty)` must be TRUE,
//! `NULL op ANY(empty)` must be FALSE — emptiness decides, the NULL is
//! never compared). SPG previously returned NULL, excluding the row. See
//! `null_lhs_empty_any_all_pg_semantics` below; fix in
//! `eval.rs` AnyAll branch (short-circuit empty before seeding saw_null).
use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
/// Render one scalar cell the way `psql -tA` prints it (NULL → `<NULL>`).
fn cell(v: &Value) -> String {
match v {
Value::Null => "<NULL>".to_string(),
Value::Bool(b) => if *b { "t" } else { "f" }.to_string(),
Value::SmallInt(n) => n.to_string(),
Value::Int(n) => n.to_string(),
Value::BigInt(n) => n.to_string(),
Value::Float(f) => f.to_string(),
Value::Text(s) => s.to_string(),
other => format!("{other:?}"),
}
}
/// Single-row single-column scalar result.
fn s1(e: &mut Engine, sql: &str) -> String {
let r = e
.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
let QueryResult::Rows { rows, .. } = r else {
panic!("{sql}: expected Rows");
};
assert_eq!(rows.len(), 1, "{sql}: expected exactly one row");
cell(&rows[0].values[0])
}
/// Every row rendered as pipe-joined columns, rows joined by comma —
/// mirrors the PG `-tA` capture format used to record ground truth.
fn grid(e: &mut Engine, sql: &str) -> String {
let r = e
.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
let QueryResult::Rows { rows, .. } = r else {
panic!("{sql}: expected Rows");
};
rows.iter()
.map(|row| row.values.iter().map(cell).collect::<Vec<_>>().join("|"))
.collect::<Vec<_>>()
.join(",")
}
/// Assert the statement is rejected (PG raises an error).
fn err(e: &mut Engine, sql: &str) {
assert!(
e.execute(sql).is_err(),
"{sql}: expected an error (PG18 raises here), got Ok"
);
}
fn setup(e: &mut Engine) {
e.execute("CREATE TABLE a (id int, k int, x int)").unwrap();
e.execute("INSERT INTO a VALUES (1,10,100),(2,20,200),(3,20,NULL),(4,30,400)")
.unwrap();
e.execute("CREATE TABLE b (id int, k int, y int)").unwrap();
e.execute("INSERT INTO b VALUES (1,10,5),(2,10,7),(3,20,NULL),(4,40,9)")
.unwrap();
e.execute("CREATE TABLE one (id int, v int)").unwrap();
e.execute("INSERT INTO one VALUES (1,42)").unwrap();
e.execute("CREATE TABLE emp (id int, k int)").unwrap();
e.execute("CREATE TABLE nn (v int)").unwrap();
e.execute("INSERT INTO nn VALUES (1),(2),(NULL)").unwrap();
e.execute("CREATE TABLE nomatch (v int)").unwrap();
e.execute("INSERT INTO nomatch VALUES (5),(6)").unwrap();
}
// ---- scalar subquery cardinality -----------------------------------
#[test]
fn scalar_subquery_cardinality() {
let mut e = Engine::new();
setup(&mut e);
// WHERE id=1 → exactly one row → that value.
assert_eq!(s1(&mut e, "SELECT (SELECT x FROM a WHERE id=1)"), "100");
// 0 rows → NULL (not an error).
assert_eq!(s1(&mut e, "SELECT (SELECT x FROM a WHERE false)"), "<NULL>");
// >1 row → PG raises "more than one row returned by a subquery ...".
err(&mut e, "SELECT (SELECT x FROM a)");
// ORDER BY … LIMIT 1 collapses to one row deterministically.
assert_eq!(
s1(&mut e, "SELECT (SELECT x FROM a ORDER BY id LIMIT 1)"),
"100"
);
}
#[test]
fn scalar_subquery_in_where_and_expr() {
let mut e = Engine::new();
setup(&mut e);
// Scalar subquery on the RHS of a WHERE comparison.
assert_eq!(
s1(
&mut e,
"SELECT count(*) FROM a WHERE x = (SELECT x FROM a WHERE id=1)"
),
"1"
);
// Scalar subquery embedded inside an arithmetic expression.
assert_eq!(s1(&mut e, "SELECT ((SELECT x FROM a WHERE id=1)+1)"), "101");
// >1 row still errors when the scalar sits in WHERE.
err(&mut e, "SELECT count(*) FROM a WHERE x = (SELECT x FROM a)");
}
// ---- correlated scalar subqueries ----------------------------------
#[test]
fn correlated_scalar_subquery() {
let mut e = Engine::new();
setup(&mut e);
// count per correlated key; k=30 has no match in b → 0 (COUNT over empty).
assert_eq!(
grid(
&mut e,
"SELECT id, (SELECT count(*) FROM b WHERE b.k=a.k) FROM a ORDER BY id"
),
"1|2,2|1,3|1,4|0"
);
// correlated avg in WHERE: only id=1 (x=100 > avg(5,7)=6).
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE x > (SELECT avg(y) FROM b WHERE b.k=a.k) ORDER BY id"
),
"1"
);
// correlated aggregate over an empty partition → NULL (max/no COUNT).
assert_eq!(
grid(
&mut e,
"SELECT id, (SELECT max(y) FROM b WHERE b.k=a.k AND false) FROM a ORDER BY id"
),
"1|<NULL>,2|<NULL>,3|<NULL>,4|<NULL>"
);
// correlated bare column returning 0 rows → NULL.
assert_eq!(
grid(
&mut e,
"SELECT id, (SELECT y FROM b WHERE b.k=a.k AND b.id=99) FROM a ORDER BY id"
),
"1|<NULL>,2|<NULL>,3|<NULL>,4|<NULL>"
);
// correlated bare column returning >1 row (k=10 matches two b rows) → error.
err(
&mut e,
"SELECT id, (SELECT y FROM b WHERE b.k=a.k) FROM a ORDER BY id",
);
}
#[test]
fn nested_two_level_correlation() {
let mut e = Engine::new();
setup(&mut e);
// a.k=10 → b rows y∈{5,7}; one(v=42) > y holds → id 1 kept. Others drop.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE EXISTS (SELECT 1 FROM b WHERE b.k=a.k AND \
EXISTS (SELECT 1 FROM one WHERE one.v > b.y)) ORDER BY id"
),
"1"
);
}
// ---- EXISTS / NOT EXISTS -------------------------------------------
#[test]
fn exists_and_not_exists() {
let mut e = Engine::new();
setup(&mut e);
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE EXISTS (SELECT 1 FROM b WHERE b.k=a.k) ORDER BY id"
),
"1,2,3"
);
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE b.k=a.k) ORDER BY id"
),
"4"
);
// EXISTS only cares about row presence, not projected values.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE EXISTS (SELECT NULL FROM b WHERE b.k=a.k) ORDER BY id"
),
"1,2,3"
);
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE EXISTS (SELECT * FROM b WHERE b.k=a.k) ORDER BY id"
),
"1,2,3"
);
// Empty inner → EXISTS false for every outer row.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE EXISTS (SELECT 1 FROM b WHERE false) ORDER BY id"
),
""
);
}
// ---- IN / NOT IN with NULL (3VL traps) -----------------------------
#[test]
fn in_and_not_in_null_traps() {
let mut e = Engine::new();
setup(&mut e);
// plain IN over a subquery.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k IN (SELECT k FROM b) ORDER BY id"
),
"1,2,3"
);
// NOT IN where the inner set contains a NULL and no value matches →
// result is NULL for every row → all excluded (the classic trap).
assert_eq!(
grid(
&mut e,
"SELECT v FROM nomatch WHERE v NOT IN (SELECT v FROM nn) ORDER BY v"
),
""
);
// NOT IN over b.y (contains a NULL) → all outer rows excluded.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k NOT IN (SELECT y FROM b) ORDER BY id"
),
""
);
// IN over a NULL-bearing set with no match → excluded (NULL, not true).
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE x IN (SELECT y FROM b) ORDER BY id"
),
""
);
// Empty inner: NOT IN → all kept, IN → none.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k NOT IN (SELECT k FROM emp) ORDER BY id"
),
"1,2,3,4"
);
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k IN (SELECT k FROM emp) ORDER BY id"
),
""
);
}
// ---- ANY / ALL / SOME over a subquery ------------------------------
#[test]
fn any_all_over_subquery() {
let mut e = Engine::new();
setup(&mut e);
// = ANY is IN.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k = ANY(SELECT k FROM b) ORDER BY id"
),
"1,2,3"
);
// = ALL only holds if every inner value equals k — here none (mixed set).
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k = ALL(SELECT k FROM b) ORDER BY id"
),
""
);
// > ALL over the non-null y set {5,7,9}: 100/200/400 all pass, NULL drops.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE x > ALL(SELECT y FROM b WHERE y IS NOT NULL) ORDER BY id"
),
"1,2,4"
);
// < ANY over y: no x is below any y → empty.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE x < ANY(SELECT y FROM b) ORDER BY id"
),
""
);
// > ALL over a NULL-bearing set → NULL for the comparison → all excluded.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE x > ALL(SELECT y FROM b) ORDER BY id"
),
""
);
// <> ALL is NOT IN → NULL in set → all excluded.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k <> ALL(SELECT y FROM b) ORDER BY id"
),
""
);
}
#[test]
fn any_all_over_empty_subquery() {
let mut e = Engine::new();
setup(&mut e);
// = ALL(empty) is vacuously true → every row kept.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k = ALL(SELECT k FROM emp) ORDER BY id"
),
"1,2,3,4"
);
// = ANY(empty) is false → none.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE k = ANY(SELECT k FROM emp) ORDER BY id"
),
""
);
// > ALL(empty) is true even for the NULL-x row (id=3). This was the
// one divergence found in this sweep — SPG used to drop id=3.
assert_eq!(
grid(
&mut e,
"SELECT id FROM a WHERE x > ALL(SELECT k FROM emp) ORDER BY id"
),
"1,2,3,4"
);
}
/// The corrected divergence, at scalar granularity: a NULL LHS against an
/// empty ANY/ALL set is decided purely by emptiness, never by the NULL.
/// PG18: `NULL op ALL(empty)` → t, `NULL op ANY(empty)` → f.
#[test]
fn null_lhs_empty_any_all_pg_semantics() {
let mut e = Engine::new();
setup(&mut e);
assert_eq!(
s1(&mut e, "SELECT (NULL::int > ALL(SELECT k FROM emp))"),
"t"
);
assert_eq!(
s1(&mut e, "SELECT (NULL::int = ALL(SELECT k FROM emp))"),
"t"
);
assert_eq!(
s1(&mut e, "SELECT (NULL::int <> ALL(SELECT k FROM emp))"),
"t"
);
assert_eq!(
s1(&mut e, "SELECT (NULL::int = ANY(SELECT k FROM emp))"),
"f"
);
assert_eq!(
s1(&mut e, "SELECT (NULL::int > ANY(SELECT k FROM emp))"),
"f"
);
// Non-null LHS over empty already matched PG; pin it too.
assert_eq!(s1(&mut e, "SELECT (5 > ALL(SELECT k FROM emp))"), "t");
assert_eq!(s1(&mut e, "SELECT (5 = ANY(SELECT k FROM emp))"), "f");
// Non-empty NULL LHS stays NULL (must not be broken by the empty fix).
assert_eq!(
s1(&mut e, "SELECT (NULL::int = ANY(SELECT k FROM b))"),
"<NULL>"
);
assert_eq!(
s1(&mut e, "SELECT (NULL::int = ALL(SELECT k FROM b))"),
"<NULL>"
);
}
// ---- scalar aggregate subqueries -----------------------------------
#[test]
fn scalar_aggregate_subquery() {
let mut e = Engine::new();
setup(&mut e);
assert_eq!(s1(&mut e, "SELECT (SELECT count(*) FROM a)"), "4");
// COUNT over empty → 0, other aggregates over empty → NULL.
assert_eq!(s1(&mut e, "SELECT (SELECT count(*) FROM emp)"), "0");
assert_eq!(s1(&mut e, "SELECT (SELECT sum(k) FROM emp)"), "<NULL>");
}