mod common;
use common::TestConnection;
use hyperdb_api::Result;
#[test]
fn query_after_bad_query_returns_correct_result() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE t (v INT)")?;
tc.execute_command("INSERT INTO t VALUES (1), (2), (3)")?;
let err = tc
.connection
.execute_scalar_query::<i64>("SELECT no_such_column FROM t");
assert!(err.is_err(), "SELECT of unknown column must error");
let count = tc.execute_scalar_i64("SELECT COUNT(*) FROM t")?;
assert_eq!(count, 3, "wire is desynced — expected count=3 got {count}");
Ok(())
}
#[test]
fn command_after_bad_command_works() -> Result<()> {
let tc = TestConnection::new()?;
let err = tc
.execute_command("CREATE TABLE (") .err();
assert!(err.is_some(), "syntax error must be reported");
tc.execute_command("CREATE TABLE good (id INT)")?;
tc.execute_command("INSERT INTO good VALUES (42)")?;
let v = tc.execute_scalar_i64("SELECT v FROM (SELECT id as v FROM good)")?;
assert_eq!(v, 42);
Ok(())
}
#[test]
fn transaction_after_failed_statement_in_tx() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE acc (v INT NOT NULL)")?;
tc.connection.begin_transaction()?;
tc.execute_command("INSERT INTO acc VALUES (1)")?;
let err = tc.execute_command("INSERT INTO acc VALUES (NULL)"); assert!(err.is_err());
tc.connection.rollback()?;
let count = tc.execute_scalar_i64("SELECT COUNT(*) FROM acc")?;
assert_eq!(count, 0, "rollback should leave zero rows");
Ok(())
}
#[test]
fn query_after_abandoned_result_stream() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE many (i INT)")?;
for i in 0..100 {
tc.execute_command(&format!("INSERT INTO many VALUES ({i})"))?;
}
{
let mut rs = tc
.connection
.execute_query("SELECT * FROM many ORDER BY i")?;
let _first = rs.next_chunk()?; }
let count = tc.execute_scalar_i64("SELECT COUNT(*) FROM many")?;
assert_eq!(count, 100);
Ok(())
}
#[test]
fn query_after_failed_copy() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE stream (id INT, name TEXT)")?;
let err =
tc.execute_command("COPY stream FROM '/definitely/not/a/real/path.csv' WITH (FORMAT csv)");
assert!(err.is_err(), "expected I/O error from COPY");
let count = tc.execute_scalar_i64("SELECT COUNT(*) FROM stream")?;
assert_eq!(count, 0);
Ok(())
}
#[test]
fn query_after_abandoning_large_stream() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE big (v INT)")?;
let values: Vec<String> = (0..500).map(|i| format!("({i})")).collect();
tc.execute_command(&format!("INSERT INTO big VALUES {}", values.join(",")))?;
{
let mut rs = tc
.connection
.execute_query("SELECT * FROM big ORDER BY v")?;
let _ = rs.next_chunk()?;
}
let count = tc.execute_scalar_i64("SELECT COUNT(*) FROM big")?;
assert_eq!(count, 500);
Ok(())
}
#[test]
fn cancel_fires_when_streaming_result_is_abandoned() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE huge (v INT)")?;
let values: Vec<String> = (0..3000).map(|i| format!("({i})")).collect();
tc.execute_command(&format!("INSERT INTO huge VALUES {}", values.join(",")))?;
let seeded = tc.execute_scalar_i64("SELECT COUNT(*) FROM huge")?;
assert_eq!(seeded, 3000, "test setup: INSERT didn't seed all rows");
let start = std::time::Instant::now();
{
let mut rs = tc
.connection
.execute_query("SELECT * FROM huge ORDER BY v")?;
let _first = rs.next_chunk()?;
}
let drop_elapsed = start.elapsed();
let count = tc.execute_scalar_i64("SELECT COUNT(*) FROM huge")?;
assert_eq!(count, 3000);
const DROP_DEADLINE: std::time::Duration = std::time::Duration::from_secs(3);
assert!(
drop_elapsed < DROP_DEADLINE,
"stream Drop took {drop_elapsed:?} (deadline {DROP_DEADLINE:?}), \
suggesting cancel did not fire",
);
Ok(())
}
#[test]
fn begin_transaction_after_error_is_not_poisoned() -> Result<()> {
let tc = TestConnection::new()?;
tc.execute_command("CREATE TABLE t (v INT)")?;
let err = tc
.connection
.execute_scalar_query::<i64>("SELECT made_up_column FROM t");
assert!(err.is_err());
tc.connection.begin_transaction()?;
tc.execute_command("INSERT INTO t VALUES (7)")?;
tc.connection.commit()?;
let v = tc.execute_scalar_i64("SELECT v FROM t")?;
assert_eq!(v, 7);
Ok(())
}