use super::*;
fn acct_engine() -> Engine {
let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let dir = std::env::temp_dir().join(format!("powdb_acct_{}_{}", std::process::id(), id));
let mut engine = Engine::new(&dir).unwrap();
engine
.execute_powql("type Acct { required unique id: str, balance: float, tag: int }")
.unwrap();
engine
.execute_powql(r#"insert Acct { id := "a", balance := 1.5, tag := 1 }"#)
.unwrap();
engine
}
fn acct_balance(engine: &mut Engine) -> Value {
match engine
.execute_powql(r#"Acct filter .id = "a" { .balance }"#)
.unwrap()
{
QueryResult::Rows { rows, .. } => rows[0][0].clone(),
other => panic!("expected rows, got {other:?}"),
}
}
#[test]
fn test_update_int_into_float_column_coerces_indexed_path() {
let mut engine = acct_engine();
engine
.execute_powql(r#"Acct filter .id = "a" update { balance := 10 }"#)
.unwrap();
assert_eq!(acct_balance(&mut engine), Value::Float(10.0));
}
#[test]
fn test_update_int_into_float_column_coerces_seqscan_path() {
let mut engine = acct_engine();
engine
.execute_powql("Acct filter .tag = 1 update { balance := 10 }")
.unwrap();
assert_eq!(acct_balance(&mut engine), Value::Float(10.0));
}
#[test]
fn test_update_str_into_float_column_errors_not_panic_indexed_path() {
let mut engine = acct_engine();
let result = engine.execute_powql(r#"Acct filter .id = "a" update { balance := "oops" }"#);
assert!(
result.is_err(),
"type-mismatched UPDATE must return Err, got {result:?}"
);
assert_eq!(acct_balance(&mut engine), Value::Float(1.5));
}
#[test]
fn test_update_str_into_float_column_errors_not_panic_seqscan_path() {
let mut engine = acct_engine();
let result = engine.execute_powql(r#"Acct filter .tag = 1 update { balance := "oops" }"#);
assert!(
result.is_err(),
"type-mismatched UPDATE via seqscan must return Err, got {result:?}"
);
assert_eq!(acct_balance(&mut engine), Value::Float(1.5));
}
#[test]
fn test_update_int_expr_into_float_column_coerces_plain_path() {
let mut engine = acct_engine();
engine
.execute_powql("Acct filter .id = \"a\" update { balance := .tag + 9 }")
.unwrap();
assert_eq!(acct_balance(&mut engine), Value::Float(10.0));
}
#[test]
fn test_update_int_expr_into_float_column_coerces_returning_path() {
let mut engine = acct_engine();
let result = engine
.execute_powql("Acct filter .id = \"a\" update { balance := .tag + 9 } returning")
.unwrap();
match result {
QueryResult::Rows { columns, rows } => {
let bidx = columns.iter().position(|c| c == "balance").unwrap();
assert_eq!(
rows[0][bidx],
Value::Float(10.0),
"RETURNING post-image must carry the coerced float, got {:?}",
rows[0][bidx]
);
}
other => panic!("expected rows, got {other:?}"),
}
assert_eq!(acct_balance(&mut engine), Value::Float(10.0));
}
#[test]
fn test_update_str_expr_into_float_column_errors_not_panic_plain_path() {
let mut engine = acct_engine();
let result = engine.execute_powql("Acct filter .id = \"a\" update { balance := .id }");
assert!(
result.is_err(),
"type-mismatched expr UPDATE must return Err, got {result:?}"
);
assert_eq!(acct_balance(&mut engine), Value::Float(1.5));
}
#[test]
fn test_update_str_expr_into_float_column_errors_not_panic_returning_path() {
let mut engine = acct_engine();
let result =
engine.execute_powql("Acct filter .id = \"a\" update { balance := .id } returning");
assert!(
result.is_err(),
"type-mismatched expr UPDATE (returning) must return Err, got {result:?}"
);
assert_eq!(acct_balance(&mut engine), Value::Float(1.5));
}
#[test]
fn test_upsert_conflict_int_into_float_column_coerces() {
let mut engine = acct_engine();
engine
.execute_powql(r#"upsert Acct on .id { id := "a", balance := 10 }"#)
.unwrap();
assert_eq!(acct_balance(&mut engine), Value::Float(10.0));
}
#[test]
fn test_upsert_conflict_str_into_float_column_errors_not_panic() {
let mut engine = acct_engine();
let result = engine.execute_powql(r#"upsert Acct on .id { id := "a", balance := "oops" }"#);
assert!(
result.is_err(),
"type-mismatched upsert must return Err, got {result:?}"
);
assert_eq!(acct_balance(&mut engine), Value::Float(1.5));
}
#[test]
fn test_engine_normal_sync_mode_persists_across_reopen() {
use super::WalSyncMode;
let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let dir =
std::env::temp_dir().join(format!("powdb_engine_normal_{}_{}", std::process::id(), id));
{
let mut engine = Engine::new(&dir).unwrap();
engine.set_wal_sync_mode(WalSyncMode::Normal);
engine
.execute_powql("type T { required id: int, required v: int }")
.unwrap();
engine
.execute_powql("insert T { id := 1, v := 100 }")
.unwrap();
engine
.execute_powql("insert T { id := 2, v := 200 }")
.unwrap();
} let mut engine = Engine::new(&dir).unwrap();
match engine.execute_powql("count(T)").unwrap() {
QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 2),
other => panic!("expected 2 rows after Normal-mode reopen, got {other:?}"),
}
}
#[test]
fn test_insert_returning_yields_inserted_row() {
let mut engine = test_engine();
match engine
.execute_powql(r#"insert User { name := "Dana", email := "d@x.com", age := 40 } returning"#)
.unwrap()
{
QueryResult::Rows { columns, rows } => {
assert_eq!(rows.len(), 1);
let name_idx = columns.iter().position(|c| c == "name").expect("name col");
let age_idx = columns.iter().position(|c| c == "age").expect("age col");
assert_eq!(rows[0][name_idx], Value::Str("Dana".into()));
assert_eq!(rows[0][age_idx], Value::Int(40));
}
other => panic!("expected Rows from insert ... returning, got {other:?}"),
}
match engine.execute_powql("count(User)").unwrap() {
QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 4),
other => panic!("{other:?}"),
}
}
#[test]
fn test_insert_multi_row_returning_yields_all_rows() {
let mut engine = test_engine();
match engine
.execute_powql(
r#"insert User { name := "A", email := "a2@x.com", age := 1 }, { name := "B", email := "b2@x.com", age := 2 } returning"#,
)
.unwrap()
{
QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 2),
other => panic!("expected 2 Rows, got {other:?}"),
}
}
#[test]
fn test_insert_without_returning_still_modified() {
let mut engine = test_engine();
match engine
.execute_powql(r#"insert User { name := "Eve", email := "e@x.com", age := 50 }"#)
.unwrap()
{
QueryResult::Modified(n) => assert_eq!(n, 1),
other => panic!("expected Modified, got {other:?}"),
}
}
#[test]
fn test_update_returning_yields_updated_rows() {
let mut engine = test_engine();
match engine
.execute_powql(r#"User filter .name = "Alice" update { age := 99 } returning"#)
.unwrap()
{
QueryResult::Rows { columns, rows } => {
assert_eq!(rows.len(), 1);
let name_idx = columns.iter().position(|c| c == "name").expect("name col");
let age_idx = columns.iter().position(|c| c == "age").expect("age col");
assert_eq!(rows[0][name_idx], Value::Str("Alice".into()));
assert_eq!(rows[0][age_idx], Value::Int(99));
}
other => panic!("expected Rows from update ... returning, got {other:?}"),
}
}
#[test]
fn test_update_returning_expression_path() {
let mut engine = test_engine();
match engine
.execute_powql(r#"User filter .name = "Bob" update { age := .age + 5 } returning"#)
.unwrap()
{
QueryResult::Rows { columns, rows } => {
assert_eq!(rows.len(), 1);
let age_idx = columns.iter().position(|c| c == "age").expect("age col");
assert_eq!(rows[0][age_idx], Value::Int(30)); }
other => panic!("expected Rows, got {other:?}"),
}
}
#[test]
fn test_update_returning_coerces_int_into_float() {
let mut engine = acct_engine();
match engine
.execute_powql(r#"Acct filter .id = "a" update { balance := 10 } returning"#)
.unwrap()
{
QueryResult::Rows { columns, rows } => {
let bal_idx = columns
.iter()
.position(|c| c == "balance")
.expect("balance col");
assert_eq!(rows[0][bal_idx], Value::Float(10.0));
}
other => panic!("expected Rows, got {other:?}"),
}
assert_eq!(acct_balance(&mut engine), Value::Float(10.0));
}
#[test]
fn test_update_without_returning_still_modified() {
let mut engine = test_engine();
match engine
.execute_powql(r#"User filter .name = "Alice" update { age := 99 }"#)
.unwrap()
{
QueryResult::Modified(n) => assert_eq!(n, 1),
other => panic!("expected Modified, got {other:?}"),
}
}
#[test]
fn test_delete_returning_yields_deleted_rows() {
let mut engine = test_engine();
match engine
.execute_powql(r#"User filter .name = "Alice" delete returning"#)
.unwrap()
{
QueryResult::Rows { columns, rows } => {
assert_eq!(rows.len(), 1);
let name_idx = columns.iter().position(|c| c == "name").expect("name col");
assert_eq!(rows[0][name_idx], Value::Str("Alice".into()));
}
other => panic!("expected Rows from delete ... returning, got {other:?}"),
}
match engine.execute_powql("count(User)").unwrap() {
QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 2),
other => panic!("{other:?}"),
}
}
#[test]
fn test_delete_returning_multi_row() {
let mut engine = test_engine();
match engine
.execute_powql("User filter .age > 28 delete returning")
.unwrap()
{
QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 2), other => panic!("expected 2 Rows, got {other:?}"),
}
match engine.execute_powql("count(User)").unwrap() {
QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 1),
other => panic!("{other:?}"),
}
}
#[test]
fn test_delete_without_returning_still_modified() {
let mut engine = test_engine();
match engine
.execute_powql(r#"User filter .name = "Alice" delete"#)
.unwrap()
{
QueryResult::Modified(n) => assert_eq!(n, 1),
other => panic!("expected Modified, got {other:?}"),
}
}