use fsqlite_error::FrankenError;
use crate::Connection;
pub trait BatchExt {
fn execute_batch(&self, sql: &str) -> Result<(), FrankenError>;
}
impl BatchExt for Connection {
#[allow(clippy::use_self)]
fn execute_batch(&self, sql: &str) -> Result<(), FrankenError> {
Connection::execute_batch(self, sql)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compat::RowExt;
#[test]
fn execute_batch_creates_tables() {
let conn = Connection::open(":memory:").unwrap();
conn.execute_batch(
"
CREATE TABLE a (id INTEGER PRIMARY KEY);
CREATE TABLE b (id INTEGER PRIMARY KEY);
INSERT INTO a (id) VALUES (1);
INSERT INTO b (id) VALUES (2);
",
)
.unwrap();
let a: i64 = conn
.query_row("SELECT id FROM a")
.map(|row| row.get_typed::<i64>(0).unwrap())
.unwrap();
assert_eq!(a, 1);
let b: i64 = conn
.query_row("SELECT id FROM b")
.map(|row| row.get_typed::<i64>(0).unwrap())
.unwrap();
assert_eq!(b, 2);
}
#[test]
fn execute_batch_empty_string() {
let conn = Connection::open(":memory:").unwrap();
conn.execute_batch("").unwrap();
conn.execute_batch(" ; ; ").unwrap();
conn.execute_batch(" -- nothing here\n/* still empty */ ; ")
.unwrap();
}
#[test]
fn execute_batch_semicolons_in_string_literals() {
let conn = Connection::open(":memory:").unwrap();
conn.execute_batch(
"
CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);
INSERT INTO t (val) VALUES ('hello;world');
INSERT INTO t (val) VALUES ('a''b;c''d');
",
)
.unwrap();
let rows = conn.query("SELECT val FROM t ORDER BY id").unwrap();
assert_eq!(rows.len(), 2);
assert_eq!(rows[0].get_typed::<String>(0).unwrap(), "hello;world");
assert_eq!(rows[1].get_typed::<String>(0).unwrap(), "a'b;c'd");
}
#[test]
fn execute_batch_allows_triggers_with_internal_semicolons() {
let conn = Connection::open(":memory:").unwrap();
conn.execute_batch(
"
CREATE TABLE items (id INTEGER PRIMARY KEY, value TEXT);
CREATE TABLE item_audit (item_id INTEGER, seen_value TEXT);
CREATE TRIGGER audit_items
AFTER INSERT ON items
BEGIN
INSERT INTO item_audit (item_id, seen_value) VALUES (NEW.id, NEW.value);
END;
INSERT INTO items (id, value) VALUES (1, 'alpha');
",
)
.unwrap();
let audit_rows = conn
.query("SELECT item_id, seen_value FROM item_audit")
.unwrap();
assert_eq!(audit_rows.len(), 1);
assert_eq!(audit_rows[0].get_typed::<i64>(0).unwrap(), 1);
assert_eq!(audit_rows[0].get_typed::<String>(1).unwrap(), "alpha");
}
#[test]
fn batch_ext_trait_delegates_to_core_execute_batch() {
let conn = Connection::open(":memory:").unwrap();
<Connection as BatchExt>::execute_batch(&conn, "").unwrap();
<Connection as BatchExt>::execute_batch(&conn, "-- comment only\n/* and block */ ;")
.unwrap();
}
#[test]
fn execute_batch_rejects_unterminated_block_comment() {
let conn = Connection::open(":memory:").unwrap();
let error = conn
.execute_batch("/* unterminated")
.expect_err("unterminated block comments should not be treated as empty batches");
assert!(matches!(error, FrankenError::ParseError { .. }));
}
}