#![cfg(any(feature = "postgres-sync", feature = "tokio-postgres"))]
use crate::common::schema::postgres::*;
use drizzle::core::expr::*;
use drizzle::postgres::prelude::*;
use drizzle_postgres::common::PostgresTransactionType;
#[derive(Debug, PostgresFromRow, PartialEq)]
struct TxSimpleResult {
id: i32,
name: String,
}
#[drizzle::test]
fn transaction_commit(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([InsertSimple::new("Alice")])
.execute();
db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("Bob")])
.execute()
)?;
Ok(())
});
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
assert_eq!(2, results.len());
}
#[drizzle::test]
fn transaction_rollback(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([InsertSimple::new("Alice")])
.execute();
let result: Result<(), drizzle::error::DrizzleError> =
result!(db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("Bob")])
.execute()
)?;
Err(drizzle::error::DrizzleError::Other("rollback".into()))
}));
let _ = result;
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
assert_eq!(1, results.len());
assert_eq!("Alice", results[0].name.as_str());
}
#[drizzle::test]
fn transaction_update_and_select(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([InsertSimple::new("Alice"), InsertSimple::new("Bob")])
.execute();
db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.update(simple)
.set(UpdateSimple::default().with_name("Charlie"))
.r#where(eq(simple.name, "Bob"))
.execute()
)?;
let results: Vec<TxSimpleResult> =
result!(tx.select((simple.id, simple.name)).from(simple).all())?;
let names: Vec<&str> = results.iter().map(|r| r.name.as_str()).collect();
assert!(names.contains(&"Alice"));
assert!(names.contains(&"Charlie"));
assert!(!names.contains(&"Bob"));
Ok(())
});
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
let names: Vec<&str> = results.iter().map(|r| r.name.as_str()).collect();
assert!(names.contains(&"Charlie"));
assert!(!names.contains(&"Bob"));
}
#[drizzle::test]
fn transaction_delete(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([
InsertSimple::new("Alice"),
InsertSimple::new("Bob"),
InsertSimple::new("Charlie"),
])
.execute();
db.transaction(PostgresTransactionType::default(), |tx| {
result!(tx.delete(simple).r#where(eq(simple.name, "Bob")).execute())?;
Ok(())
});
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
assert_eq!(2, results.len());
let names: Vec<&str> = results.iter().map(|r| r.name.as_str()).collect();
assert!(!names.contains(&"Bob"));
}
#[drizzle::test]
fn savepoint_commit(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("outer")])
.execute()
)?;
result!(tx.savepoint(|tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("inner")])
.execute()
)?;
Ok(())
}))?;
Ok(())
});
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
assert_eq!(2, results.len());
let names: Vec<&str> = results.iter().map(|r| r.name.as_str()).collect();
assert!(names.contains(&"outer"));
assert!(names.contains(&"inner"));
}
#[drizzle::test]
fn savepoint_rollback_preserves_outer(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("outer")])
.execute()
)?;
let sp_result: Result<(), _> = result!(tx.savepoint(|tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("inner_rollback")])
.execute()
)?;
Err(drizzle::error::DrizzleError::Other("rollback inner".into()))
}));
assert!(sp_result.is_err());
result!(
tx.insert(simple)
.values([InsertSimple::new("after_sp")])
.execute()
)?;
Ok(())
});
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
assert_eq!(2, results.len());
let names: Vec<&str> = results.iter().map(|r| r.name.as_str()).collect();
assert!(names.contains(&"outer"));
assert!(names.contains(&"after_sp"));
assert!(!names.contains(&"inner_rollback"));
}
#[drizzle::test]
fn savepoint_nested_two_levels(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("level0")])
.execute()
)?;
result!(tx.savepoint(|tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("level1")])
.execute()
)?;
result!(tx.savepoint(|tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("level2")])
.execute()
)?;
Ok(())
}))?;
Ok(())
}))?;
Ok(())
});
let results: Vec<TxSimpleResult> = db.select((simple.id, simple.name)).from(simple).all();
assert_eq!(3, results.len());
let names: Vec<&str> = results.iter().map(|r| r.name.as_str()).collect();
assert!(names.contains(&"level0"));
assert!(names.contains(&"level1"));
assert!(names.contains(&"level2"));
}
#[drizzle::test]
fn prepared_outside_transaction(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([InsertSimple::new("Alice"), InsertSimple::new("Bob")])
.execute();
let find_alice = db
.select((simple.id, simple.name))
.from(simple)
.r#where(eq(simple.name, "Alice"))
.prepare()
.into_owned();
let find_bob = db
.select((simple.id, simple.name))
.from(simple)
.r#where(eq(simple.name, "Bob"))
.prepare()
.into_owned();
db.transaction(PostgresTransactionType::default(), |tx| {
let alice: Vec<TxSimpleResult> = result!(tx.all(&find_alice))?;
assert_eq!(alice.len(), 1);
assert_eq!(alice[0].name, "Alice");
let bob: Vec<TxSimpleResult> = result!(tx.all(&find_bob))?;
assert_eq!(bob.len(), 1);
assert_eq!(bob[0].name, "Bob");
Ok(())
});
}
#[drizzle::test]
fn prepared_in_savepoint(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([InsertSimple::new("Alice")])
.execute();
let select_all = db
.select((simple.id, simple.name))
.from(simple)
.prepare()
.into_owned();
db.transaction(PostgresTransactionType::default(), |tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("Bob")])
.execute()
)?;
result!(tx.savepoint(|tx| {
let rows: Vec<TxSimpleResult> = result!(tx.all(&select_all))?;
assert_eq!(rows.len(), 2);
Ok(())
}))?;
Ok(())
});
}
#[drizzle::test]
fn prepared_survives_savepoint_rollback(db: &mut TestDb<SimpleSchema>) {
let SimpleSchema { simple } = schema;
db.insert(simple)
.values([InsertSimple::new("Alice")])
.execute();
let select_all = db
.select((simple.id, simple.name))
.from(simple)
.prepare()
.into_owned();
db.transaction(PostgresTransactionType::default(), |tx| {
let sp_result: Result<(), _> = result!(tx.savepoint(|tx| {
result!(
tx.insert(simple)
.values([InsertSimple::new("Ghost")])
.execute()
)?;
let rows: Vec<TxSimpleResult> = result!(tx.all(&select_all))?;
assert_eq!(rows.len(), 2);
Err(drizzle::error::DrizzleError::Other("rollback".into()))
}));
assert!(sp_result.is_err());
let rows: Vec<TxSimpleResult> = result!(tx.all(&select_all))?;
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].name, "Alice");
Ok(())
});
}
#[cfg(feature = "tokio-postgres")]
#[test]
fn test_pg_owned_prepared_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<drizzle_postgres::builder::prepared::OwnedPreparedStatement>();
}