#![cfg(feature = "std")]
use graphitesql::{Connection, Value};
fn setup() -> Connection {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(1,'a'),(2,'b'),(3,'c')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R')")
.unwrap();
c
}
#[test]
fn nested_loop_join_runs_on_vdbe() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x, b.q FROM a JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Text("P".into())],
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
]
);
}
#[test]
fn nested_loop_join_where_limit_offset() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x, b.q FROM a, b WHERE a.x = b.p LIMIT 2 OFFSET 1")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
]
);
}
#[test]
fn nested_loop_join_star_and_computed() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x * 10 + b.p AS s FROM a JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(11)],
vec![Value::Integer(22)],
vec![Value::Integer(22)],
]
);
}
#[test]
fn three_table_nested_loop_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1),(2)").unwrap();
c.execute("CREATE TABLE b(y)").unwrap();
c.execute("INSERT INTO b VALUES(10),(20)").unwrap();
c.execute("CREATE TABLE cc(z)").unwrap();
c.execute("INSERT INTO cc VALUES(100)").unwrap();
let r = c
.query_vdbe("SELECT a.x, b.y, cc.z FROM a, b, cc WHERE a.x = 2")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(2), Value::Integer(10), Value::Integer(100)],
vec![Value::Integer(2), Value::Integer(20), Value::Integer(100)],
]
);
}
#[test]
fn left_join_runs_on_vdbe_with_null_padding() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x, b.q FROM a LEFT JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Text("P".into())],
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
vec![Value::Integer(3), Value::Null],
]
);
}
#[test]
fn left_join_where_filters_after_null_padding() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x FROM a LEFT JOIN b ON a.x = b.p WHERE b.q IS NULL")
.unwrap();
assert_eq!(r.rows, vec![vec![Value::Integer(3)]]);
}
#[test]
fn left_join_empty_right_null_pads_every_left_row() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1),(2)").unwrap();
c.execute("CREATE TABLE b(y)").unwrap();
let r = c
.query_vdbe("SELECT a.x, b.y FROM a LEFT JOIN b ON a.x = b.y")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Null],
vec![Value::Integer(2), Value::Null],
]
);
}
#[test]
fn right_join_runs_on_vdbe_with_null_padding() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x, b.q FROM a RIGHT JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Text("P".into())],
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
]
);
}
#[test]
fn right_join_null_pads_unmatched_right_row() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1)").unwrap();
c.execute("CREATE TABLE b(p)").unwrap();
c.execute("INSERT INTO b VALUES(1),(2)").unwrap();
let r = c
.query_vdbe("SELECT a.x, b.p FROM a RIGHT JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Integer(1)],
vec![Value::Null, Value::Integer(2)],
]
);
}
#[test]
fn full_join_runs_on_vdbe_with_both_sided_null_padding() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1),(2),(3)").unwrap();
c.execute("CREATE TABLE b(p)").unwrap();
c.execute("INSERT INTO b VALUES(2),(3),(4)").unwrap();
let r = c
.query_vdbe("SELECT a.x, b.p FROM a FULL JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Null],
vec![Value::Integer(2), Value::Integer(2)],
vec![Value::Integer(3), Value::Integer(3)],
vec![Value::Null, Value::Integer(4)],
]
);
let r2 = c
.query_vdbe("SELECT b.p FROM a FULL JOIN b ON a.x = b.p WHERE a.x IS NULL")
.unwrap();
assert_eq!(r2.rows, vec![vec![Value::Integer(4)]]);
}
#[test]
fn distinct_over_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1),(1),(2)").unwrap();
c.execute("CREATE TABLE b(p)").unwrap();
c.execute("INSERT INTO b VALUES(1),(1),(2)").unwrap();
let r = c
.query_vdbe("SELECT DISTINCT a.x FROM a JOIN b ON a.x = b.p ORDER BY a.x")
.unwrap();
assert_eq!(
r.rows,
vec![vec![Value::Integer(1)], vec![Value::Integer(2)]]
);
}
#[test]
fn distinct_over_outer_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(1,'a'),(1,'a2'),(2,'b'),(3,'c')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R')")
.unwrap();
let r = c
.query_vdbe("SELECT DISTINCT a.x FROM a LEFT JOIN b ON a.x = b.p ORDER BY a.x DESC")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(3)],
vec![Value::Integer(2)],
vec![Value::Integer(1)],
]
);
let r2 = c
.query_vdbe("SELECT DISTINCT a.x, b.q FROM a LEFT JOIN b ON a.x = b.p ORDER BY a.x, b.q")
.unwrap();
assert_eq!(
r2.rows,
vec![
vec![Value::Integer(1), Value::Text("P".into())],
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
vec![Value::Integer(3), Value::Null],
]
);
}
#[test]
fn distinct_over_full_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1),(2),(2)").unwrap();
c.execute("CREATE TABLE b(p)").unwrap();
c.execute("INSERT INTO b VALUES(2),(5),(5)").unwrap();
let r = c
.query_vdbe("SELECT DISTINCT a.x, b.p FROM a FULL JOIN b ON a.x = b.p ORDER BY a.x, b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Null, Value::Integer(5)],
vec![Value::Integer(1), Value::Null],
vec![Value::Integer(2), Value::Integer(2)],
]
);
}
#[test]
fn order_by_over_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(3,'c'),(1,'a'),(2,'b')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R')")
.unwrap();
let r = c
.query_vdbe("SELECT a.x, b.q FROM a JOIN b ON a.x = b.p ORDER BY a.x DESC, b.q")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
vec![Value::Integer(1), Value::Text("P".into())],
]
);
let r2 = c
.query_vdbe("SELECT a.x FROM a JOIN b ON a.x = b.p ORDER BY a.x LIMIT 1 OFFSET 1")
.unwrap();
assert_eq!(r2.rows, vec![vec![Value::Integer(2)]]);
}
#[test]
fn order_by_over_outer_join_runs_on_vdbe() {
let c = setup();
let r = c
.query_vdbe("SELECT a.x, b.q FROM a LEFT JOIN b ON a.x = b.p ORDER BY a.x DESC, b.q")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(3), Value::Null],
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
vec![Value::Integer(1), Value::Text("P".into())],
]
);
let r2 = c
.query_vdbe("SELECT a.x, b.q FROM a LEFT JOIN b ON a.x = b.p ORDER BY b.q")
.unwrap();
assert_eq!(
r2.rows,
vec![
vec![Value::Integer(3), Value::Null],
vec![Value::Integer(1), Value::Text("P".into())],
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
]
);
let r3 = c
.query_vdbe(
"SELECT a.x, b.q FROM a LEFT JOIN b ON a.x = b.p ORDER BY a.x, b.q LIMIT 2 OFFSET 1",
)
.unwrap();
assert_eq!(
r3.rows,
vec![
vec![Value::Integer(2), Value::Text("Q".into())],
vec![Value::Integer(2), Value::Text("R".into())],
]
);
let r4 = c
.query_vdbe("SELECT a.x, b.p FROM a RIGHT JOIN b ON a.x = b.p ORDER BY b.p DESC, a.x")
.unwrap();
assert_eq!(
r4.rows,
vec![
vec![Value::Integer(2), Value::Integer(2)],
vec![Value::Integer(2), Value::Integer(2)],
vec![Value::Integer(1), Value::Integer(1)],
]
);
}
#[test]
fn order_by_over_full_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(1,'a'),(2,'b'),(3,'c')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R'),(5,'Z')")
.unwrap();
let r = c
.query_vdbe("SELECT a.x, b.p FROM a FULL JOIN b ON a.x = b.p ORDER BY a.x DESC, b.p")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(3), Value::Null],
vec![Value::Integer(2), Value::Integer(2)],
vec![Value::Integer(2), Value::Integer(2)],
vec![Value::Integer(1), Value::Integer(1)],
vec![Value::Null, Value::Integer(5)],
]
);
let r2 = c
.query_vdbe(
"SELECT a.x, b.p FROM a FULL JOIN b ON a.x = b.p ORDER BY b.p, a.x LIMIT 3 OFFSET 1",
)
.unwrap();
assert_eq!(
r2.rows,
vec![
vec![Value::Integer(1), Value::Integer(1)],
vec![Value::Integer(2), Value::Integer(2)],
vec![Value::Integer(2), Value::Integer(2)],
]
);
}
#[test]
fn bare_aggregate_over_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(3,'c'),(1,'a'),(2,'b')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R')")
.unwrap();
let r = c
.query_vdbe("SELECT count(*), sum(a.x), min(b.q), max(b.q) FROM a JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(
r.rows,
vec![vec![
Value::Integer(3),
Value::Integer(5), Value::Text("P".into()),
Value::Text("R".into()),
]]
);
let g = c
.query_vdbe("SELECT group_concat(b.q) FROM a JOIN b ON a.x = b.p")
.unwrap();
assert_eq!(g.rows, vec![vec![Value::Text("P,Q,R".into())]]);
let e = c
.query_vdbe("SELECT count(*), sum(a.x) FROM a JOIN b ON a.x = b.p AND a.x = 99")
.unwrap();
assert_eq!(e.rows, vec![vec![Value::Integer(0), Value::Null]]);
}
#[test]
fn group_by_over_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(1,'a'),(2,'b'),(2,'c')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R')")
.unwrap();
let r = c
.query_vdbe("SELECT a.x, count(*) FROM a JOIN b ON a.x = b.p GROUP BY a.x")
.unwrap();
assert_eq!(
r.rows,
vec![
vec![Value::Integer(1), Value::Integer(1)],
vec![Value::Integer(2), Value::Integer(4)],
]
);
let g = c
.query_vdbe("SELECT a.x, group_concat(b.q) FROM a JOIN b ON a.x = b.p GROUP BY a.x")
.unwrap();
assert_eq!(
g.rows,
vec![
vec![Value::Integer(1), Value::Text("P".into())],
vec![Value::Integer(2), Value::Text("Q,R,Q,R".into())],
]
);
let e = c
.query_vdbe("SELECT a.x, count(*) FROM a JOIN b ON a.x = b.p AND a.x = 99 GROUP BY a.x")
.unwrap();
assert!(e.rows.is_empty());
}
#[test]
fn general_group_by_over_join_runs_on_vdbe() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x, y)").unwrap();
c.execute("INSERT INTO a VALUES(1,'a'),(2,'b'),(2,'c')")
.unwrap();
c.execute("CREATE TABLE b(p, q)").unwrap();
c.execute("INSERT INTO b VALUES(1,'P'),(2,'Q'),(2,'R')")
.unwrap();
let h = c
.query_vdbe(
"SELECT a.x, count(*) FROM a JOIN b ON a.x = b.p \
GROUP BY a.x HAVING count(*) > 1 ORDER BY a.x",
)
.unwrap();
assert_eq!(h.rows, vec![vec![Value::Integer(2), Value::Integer(4)]]);
let o = c
.query_vdbe(
"SELECT a.x, count(*) FROM a JOIN b ON a.x = b.p GROUP BY a.x ORDER BY count(*) DESC",
)
.unwrap();
assert_eq!(
o.rows,
vec![
vec![Value::Integer(2), Value::Integer(4)],
vec![Value::Integer(1), Value::Integer(1)],
]
);
let l = c
.query_vdbe("SELECT a.x, count(*) FROM a JOIN b ON a.x = b.p GROUP BY a.x ORDER BY a.x LIMIT 1 OFFSET 1")
.unwrap();
assert_eq!(l.rows, vec![vec![Value::Integer(2), Value::Integer(4)]]);
let g = c
.query_vdbe(
"SELECT a.x, group_concat(b.q) FROM a JOIN b ON a.x = b.p \
GROUP BY a.x HAVING count(*) > 1",
)
.unwrap();
assert_eq!(
g.rows,
vec![vec![Value::Integer(2), Value::Text("Q,R,Q,R".into())]]
);
}
#[test]
fn nested_loop_join_empty_side_yields_no_rows() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x)").unwrap();
c.execute("INSERT INTO a VALUES(1),(2)").unwrap();
c.execute("CREATE TABLE b(y)").unwrap();
assert!(c
.query_vdbe("SELECT a.x, b.y FROM a JOIN b ON 1=1")
.unwrap()
.rows
.is_empty());
}