#![cfg(feature = "std")]
use graphitesql::{Connection, Value};
use std::process::Command;
fn sqlite3(script: &str) -> String {
let o = Command::new("sqlite3")
.arg(":memory:")
.arg(script)
.output()
.unwrap();
String::from_utf8_lossy(&o.stdout).trim_end().to_string()
}
fn have_sqlite3() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn fmt(v: &Value) -> String {
match v {
Value::Null => String::new(),
Value::Integer(i) => i.to_string(),
Value::Real(r) => {
if *r == (*r as i64) as f64 {
format!("{:.1}", r)
} else {
format!("{r}")
}
}
Value::Text(s) => s.clone(),
Value::Blob(b) => String::from_utf8_lossy(b).into_owned(),
}
}
fn graphite_rows(c: &Connection, sql: &str) -> String {
c.query(sql)
.unwrap()
.rows
.iter()
.map(|row| row.iter().map(fmt).collect::<Vec<_>>().join("|"))
.collect::<Vec<_>>()
.join("\n")
}
fn graphite(setup: &str, use_vdbe: bool) -> Connection {
let mut c = Connection::open_memory().unwrap();
c.set_use_vdbe(use_vdbe);
for stmt in setup.split(';') {
if !stmt.trim().is_empty() {
c.execute(stmt).unwrap();
}
}
c
}
fn assert_rows(setup: &str, sql: &str) {
let want = sqlite3(&format!("{setup}\n{sql};"));
for &vdbe in &[true, false] {
let c = graphite(setup, vdbe);
let got = graphite_rows(&c, sql);
assert_eq!(got, want, "rows diverged (use_vdbe={vdbe}) for `{sql}`");
}
}
const IDX_SETUP: &str = "\
CREATE TABLE t(a,b);\
CREATE INDEX i ON t(a);\
CREATE TABLE u(a,c);\
INSERT INTO t VALUES(1,'x'),(2,'y'),(1,'z');\
INSERT INTO u VALUES(1,'m'),(2,'n');\
";
#[test]
fn index_inner_swap_qualified_rowid() {
if !have_sqlite3() {
return;
}
assert_rows(IDX_SETUP, "SELECT t.rowid,u.c FROM t JOIN u ON t.a=u.a");
assert_eq!(
sqlite3(&format!(
"{IDX_SETUP}\nSELECT t.rowid,u.c FROM t JOIN u ON t.a=u.a;"
)),
"1|m\n3|m\n2|n",
"sqlite's expected order changed; the repro assumption is stale"
);
assert_rows(
IDX_SETUP,
"SELECT t.rowid,u.rowid,u.c FROM t JOIN u ON t.a=u.a",
);
assert_rows(
IDX_SETUP,
"SELECT t.rowid,u.c FROM t JOIN u ON t.a=u.a WHERE t.rowid>0",
);
assert_rows(IDX_SETUP, "SELECT t._rowid_,u.c FROM t JOIN u ON t.a=u.a");
assert_rows(IDX_SETUP, "SELECT t.oid,u.c FROM t JOIN u ON t.a=u.a");
}
#[test]
fn index_inner_swap_qualified_rowid_order_by_unchanged() {
if !have_sqlite3() {
return;
}
assert_rows(
IDX_SETUP,
"SELECT t.rowid,u.c FROM t JOIN u ON t.a=u.a ORDER BY t.rowid,u.c",
);
assert_rows(
IDX_SETUP,
"SELECT t.rowid,u.c FROM t JOIN u ON t.a=u.a ORDER BY u.c,t.rowid DESC",
);
}
#[test]
fn index_inner_swap_nonrowid_unchanged() {
if !have_sqlite3() {
return;
}
assert_rows(IDX_SETUP, "SELECT t.b,u.c FROM t JOIN u ON t.a=u.a");
assert_rows(IDX_SETUP, "SELECT * FROM t JOIN u ON t.a=u.a");
}
const ROWID_SETUP: &str = "\
CREATE TABLE u(x INTEGER PRIMARY KEY, y);\
CREATE TABLE v(p, q);\
CREATE INDEX iv ON v(p);\
INSERT INTO u VALUES(3,30),(1,10),(2,20);\
INSERT INTO v VALUES(2,200),(1,100),(3,300);\
";
#[test]
fn rowid_inner_swap_qualified_rowid() {
if !have_sqlite3() {
return;
}
assert_rows(ROWID_SETUP, "SELECT u.rowid,v.q FROM u JOIN v ON u.x=v.p");
assert_rows(
ROWID_SETUP,
"SELECT u.x,v.q,v.rowid FROM u JOIN v ON u.x=v.p",
);
assert_rows(
ROWID_SETUP,
"SELECT u.rowid,v.q FROM u JOIN v ON u.x=v.p WHERE u.rowid>0",
);
}
#[test]
fn rowid_inner_swap_qualified_rowid_order_by_unchanged() {
if !have_sqlite3() {
return;
}
assert_rows(
ROWID_SETUP,
"SELECT u.rowid,v.q FROM u JOIN v ON u.x=v.p ORDER BY u.rowid",
);
assert_rows(
ROWID_SETUP,
"SELECT u.rowid,v.q FROM u JOIN v ON u.x=v.p ORDER BY v.q DESC",
);
}
#[test]
fn rowid_inner_swap_nonrowid_unchanged() {
if !have_sqlite3() {
return;
}
assert_rows(ROWID_SETUP, "SELECT u.x,v.q FROM u JOIN v ON u.x=v.p");
assert_rows(ROWID_SETUP, "SELECT * FROM u JOIN v ON u.x=v.p");
}
const N_SETUP: &str = "\
CREATE TABLE p(id INTEGER PRIMARY KEY, pv);\
CREATE TABLE q(qv, pid);\
CREATE TABLE r(rv, pid);\
INSERT INTO p VALUES(1,'p1'),(2,'p2');\
INSERT INTO q VALUES('q1',1),('q2',2);\
INSERT INTO r VALUES('r1',1),('r2',2),('r3',1);\
";
#[test]
fn three_table_reorder_qualified_rowid() {
if !have_sqlite3() {
return;
}
assert_rows(
N_SETUP,
"SELECT p.rowid,q.qv,r.rv FROM q JOIN p ON q.pid=p.id JOIN r ON r.pid=p.id",
);
assert_rows(
N_SETUP,
"SELECT p.rowid,q.rowid,r.rowid,q.qv,r.rv FROM q JOIN p ON q.pid=p.id JOIN r ON r.pid=p.id",
);
assert_rows(
N_SETUP,
"SELECT p.pv,q.qv,r.rv FROM q JOIN p ON q.pid=p.id JOIN r ON r.pid=p.id WHERE p.rowid>0",
);
}
#[test]
fn three_table_reorder_qualified_rowid_order_by_unchanged() {
if !have_sqlite3() {
return;
}
assert_rows(
N_SETUP,
"SELECT p.rowid,q.qv,r.rv FROM q JOIN p ON q.pid=p.id JOIN r ON r.pid=p.id \
ORDER BY p.rowid,q.qv,r.rv",
);
}
#[test]
fn three_table_reorder_nonrowid_unchanged() {
if !have_sqlite3() {
return;
}
assert_rows(
N_SETUP,
"SELECT p.pv,q.qv,r.rv FROM q JOIN p ON q.pid=p.id JOIN r ON r.pid=p.id",
);
assert_rows(
N_SETUP,
"SELECT * FROM q JOIN p ON q.pid=p.id JOIN r ON r.pid=p.id",
);
}