use super::*; use indoc::indoc; use lazy_static::lazy_static; use pretty_assertions::assert_eq; use regex::Regex; use std::str::from_utf8; fn setup() -> Result<(Database, Rc>>)> { let conn = Database::open(":memory:")?; let out = Rc::new(RefCell::new(vec![])); init(&conn, out.clone())?; conn.execute( "CREATE VIRTUAL TABLE temp.log USING vtablog(schema='CREATE TABLE x(a,b,c)', rows=3)", (), )?; Ok((conn, out)) } #[cfg(modern_sqlite)] lazy_static! { static ref IGNORED_LINES: Regex = Regex::new("(?m)^ String { let input = IGNORED_LINES.replace_all(&input, ""); INCLUDED_LINES.replace_all(&input, "$1").to_string() } #[test] fn read() -> Result<()> { let (conn, out) = setup()?; let ret: Vec> = conn .prepare("SELECT * FROM log WHERE a IN ('a1', 'a2')")? .query(())? .map(|row| { Ok(vec![ row[0].get_str()?.to_owned(), row[1].get_str()?.to_owned(), row[2].get_str()?.to_owned(), ]) }) .collect()?; drop(conn); assert_eq!( ret, (1..3) .map(|i| vec![format!("a{}", i), format!("b{}", i), format!("c{}", i)]) .collect::>>() ); let out = from_utf8(&out.borrow()).unwrap().to_owned(); let expected = patch_output(indoc! {r#" create(tab=100, args=["vtablog", "temp", "log", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) begin(tab=100, transaction=101) sync(tab=100, transaction=101) commit(tab=100, transaction=101) drop_transaction(tab=100, transaction=101) false column(tab=100, cursor=101, idx=0) -> Ok("a0") next(tab=100, cursor=101) rowid 0 -> 1 eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a1") column(tab=100, cursor=101, idx=0) -> Ok("a1") column(tab=100, cursor=101, idx=1) -> Ok("b1") column(tab=100, cursor=101, idx=2) -> Ok("c1") next(tab=100, cursor=101) rowid 1 -> 2 eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a2") next(tab=100, cursor=101) rowid 2 -> 3 eof(tab=100, cursor=101) -> true filter(tab=100, cursor=101, args=[Text(Ok("a2"))]) eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a0") next(tab=100, cursor=101) rowid 0 -> 1 eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a1") next(tab=100, cursor=101) rowid 1 -> 2 eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a2") column(tab=100, cursor=101, idx=0) -> Ok("a2") column(tab=100, cursor=101, idx=1) -> Ok("b2") column(tab=100, cursor=101, idx=2) -> Ok("c2") next(tab=100, cursor=101) rowid 2 -> 3 eof(tab=100, cursor=101) -> true drop(tab=100, cursor=101) disconnect(tab=100) drop(tab=100) "#}.to_owned()); assert_eq!(out, expected); Ok(()) } #[test] fn insert() -> Result<()> { let (conn, out) = setup()?; conn.execute("INSERT INTO log VALUES ( 1, 2, 3 ), (4, 5, 6)", ())?; drop(conn); let out = from_utf8(&out.borrow()).unwrap().to_owned(); let expected = indoc! {r#" create(tab=100, args=["vtablog", "temp", "log", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) begin(tab=100, transaction=101) sync(tab=100, transaction=101) commit(tab=100, transaction=101) drop_transaction(tab=100, transaction=101) begin(tab=100, transaction=102) update(tab=100, args=ChangeInfo { change_type: Insert, rowid: Null, args: [Null, Integer(1), Integer(2), Integer(3)], conflict_mode: Abort }) update(tab=100, args=ChangeInfo { change_type: Insert, rowid: Null, args: [Null, Integer(4), Integer(5), Integer(6)], conflict_mode: Abort }) sync(tab=100, transaction=102) commit(tab=100, transaction=102) drop_transaction(tab=100, transaction=102) disconnect(tab=100) drop(tab=100) "#}; assert_eq!(out, expected); Ok(()) } #[test] fn update() -> Result<()> { let (conn, out) = setup()?; conn.execute("UPDATE log SET a = b WHERE rowid = 1", ())?; drop(conn); let out = from_utf8(&out.borrow()).unwrap().to_owned(); let expected = patch_output(indoc! {r#" create(tab=100, args=["vtablog", "temp", "log", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) begin(tab=100, transaction=101) sync(tab=100, transaction=101) commit(tab=100, transaction=101) drop_transaction(tab=100, transaction=101) false rowid(tab=100, cursor=101) -> 0 next(tab=100, cursor=101) rowid 0 -> 1 eof(tab=100, cursor=101) -> false rowid(tab=100, cursor=101) -> 1 column(tab=100, cursor=101, idx=1) -> Ok("b1") Ok("b1") Ok("c1") =M column(tab=100, cursor=101, idx=1) -> Err(NoChange) =M column(tab=100, cursor=101, idx=2) -> Err(NoChange) rowid(tab=100, cursor=101) -> 1 rowid(tab=100, cursor=101) -> 1 next(tab=100, cursor=101) rowid 1 -> 2 eof(tab=100, cursor=101) -> false rowid(tab=100, cursor=101) -> 2 next(tab=100, cursor=101) rowid 2 -> 3 eof(tab=100, cursor=101) -> true Result<()> { let (conn, out) = setup()?; conn.execute("DELETE FROM log WHERE a = 'a1'", ())?; drop(conn); let out = from_utf8(&out.borrow()).unwrap().to_owned(); let expected = patch_output(indoc! {r#" create(tab=100, args=["vtablog", "temp", "log", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) begin(tab=100, transaction=101) sync(tab=100, transaction=101) commit(tab=100, transaction=101) drop_transaction(tab=100, transaction=101) false column(tab=100, cursor=101, idx=0) -> Ok("a0") next(tab=100, cursor=101) rowid 0 -> 1 eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a1") rowid(tab=100, cursor=101) -> 1 next(tab=100, cursor=101) rowid 1 -> 2 eof(tab=100, cursor=101) -> false column(tab=100, cursor=101, idx=0) -> Ok("a2") next(tab=100, cursor=101) rowid 2 -> 3 eof(tab=100, cursor=101) -> true update(tab=100, args=ChangeInfo { change_type: Delete, rowid: Integer(1), args: [], conflict_mode: Abort }) drop(tab=100, cursor=101) sync(tab=100, transaction=102) commit(tab=100, transaction=102) drop_transaction(tab=100, transaction=102) disconnect(tab=100) drop(tab=100) "#}.to_owned()); assert_eq!(out, expected); Ok(()) } #[test] fn rename() -> Result<()> { let (conn, out) = setup()?; conn.execute("ALTER TABLE log RENAME to newname", ())?; conn.execute("DROP TABLE newname", ())?; drop(conn); let out = from_utf8(&out.borrow()).unwrap().to_owned(); let expected = indoc! {r#" create(tab=100, args=["vtablog", "temp", "log", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) begin(tab=100, transaction=101) sync(tab=100, transaction=101) commit(tab=100, transaction=101) drop_transaction(tab=100, transaction=101) rename(tab=100, name="newname") disconnect(tab=100) drop(tab=100) connect(tab=200, args=["vtablog", "temp", "newname", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) destroy(tab=200) drop(tab=200) "#}; assert_eq!(out, expected); Ok(()) } #[test] #[cfg(modern_sqlite)] fn shadow_name() -> Result<()> { let (conn, out) = setup()?; conn.db_config_defensive(true)?; match conn.execute("CREATE TABLE log_shadow (a, b, c)", ()) { Err(_) => (), _ => panic!("expected error, got ok"), } drop(conn); let out = from_utf8(&out.borrow()).unwrap().to_owned(); let expected = indoc! {r#" create(tab=100, args=["vtablog", "temp", "log", "schema='CREATE TABLE x(a,b,c)'", "rows=3"]) begin(tab=100, transaction=101) sync(tab=100, transaction=101) commit(tab=100, transaction=101) drop_transaction(tab=100, transaction=101) disconnect(tab=100) drop(tab=100) "#}; assert_eq!(out, expected); Ok(()) }