use fsqlite::Connection;
use fsqlite_types::SqliteValue;
fn shape(conn: &Connection, sql: &str) -> (bool, bool) {
let ops: Vec<String> = conn
.query(&format!("EXPLAIN {sql}"))
.unwrap()
.iter()
.filter_map(|r| match r.values().get(1) {
Some(SqliteValue::Text(op)) => Some(op.to_string()),
_ => None,
})
.collect();
(
ops.iter().any(|o| o.starts_with("Seek")),
ops.iter().any(|o| o == "Rewind"),
)
}
#[test]
#[ignore = "probe; run with --ignored --nocapture"]
fn gap2() {
let c = Connection::open(":memory:").unwrap();
for ddl in [
"CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER, x INTEGER);",
"CREATE INDEX idx_ab ON t(a, b);",
"CREATE INDEX idx_a ON t(a);",
] {
c.execute(ddl).unwrap();
}
for i in 1..=300_i64 {
c.execute(&format!(
"INSERT INTO t VALUES ({i}, {}, {}, {});",
i % 15,
i % 30,
i % 50
))
.unwrap();
}
let cases = [
"SELECT MIN(b) FROM t WHERE a = 7 AND b > 2",
"SELECT MAX(b) FROM t WHERE a = 7 AND b > 2",
"SELECT COUNT(*) FROM t WHERE a = 7 AND b > 3",
"SELECT COUNT(*) FROM t WHERE a = 7 AND x = 5",
"SELECT EXISTS(SELECT 1 FROM t WHERE a = 7 AND b = 3)",
"SELECT a, COUNT(*) FROM t GROUP BY a",
"SELECT MIN(b) FROM t WHERE a = 7",
"SELECT AVG(b) FROM t WHERE a = 7 AND b > 3",
"SELECT COUNT(*) FROM t WHERE a = 7 AND b BETWEEN 3 AND 9",
"SELECT MIN(a) FROM t WHERE a > 5",
"SELECT COUNT(*) FROM t WHERE a < 5",
"SELECT COUNT(*) FROM t WHERE a >= 5 AND a <= 9",
"SELECT COUNT(DISTINCT a) FROM t",
"SELECT id FROM t WHERE a < 5",
"SELECT id FROM t WHERE a < 5 ORDER BY a",
"SELECT id, b FROM t WHERE a >= 5 AND a <= 9",
"SELECT id FROM t WHERE a >= 5 AND a <= 9 ORDER BY a",
];
eprintln!("\n########## gap2: SEEK vs SCAN ##########");
for sql in cases {
let (seek, rewind) = shape(&c, sql);
let tag = if seek { "SEEK " } else { "SCAN!" };
eprintln!(" [{tag}] seek={seek} rewind={rewind} {sql}");
}
eprintln!("########## end ##########\n");
}