mod common;
use common::pgwire_harness::TestServer;
const FUTURE_MS: i64 = 99_999_999_999_999;
async fn create_bitemporal(srv: &TestServer, name: &str) {
srv.exec(&format!(
"CREATE COLLECTION {name} (id STRING PRIMARY KEY, value STRING) \
WITH (engine='document_schemaless', bitemporal=true)"
))
.await
.unwrap();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn bitemporal_insert_visible_via_as_of_system_time() {
let srv = TestServer::start().await;
create_bitemporal(&srv, "bt_ins").await;
srv.exec("INSERT INTO bt_ins (id, value) VALUES ('r1', 'v1')")
.await
.unwrap();
let plain = srv
.query_rows("SELECT id, value FROM bt_ins")
.await
.unwrap();
assert_eq!(
plain.len(),
1,
"plain SELECT must see the row, got {plain:?}"
);
let as_of = srv
.query_rows(&format!(
"SELECT id, value FROM bt_ins AS OF SYSTEM TIME {FUTURE_MS}"
))
.await
.unwrap();
assert_eq!(
as_of.len(),
1,
"AS OF SYSTEM TIME must see the INSERTed row (write/read namespace must agree), got {as_of:?}"
);
assert_eq!(
as_of[0][0], "r1",
"AS OF must project the `id` column, not the raw envelope, got {as_of:?}"
);
assert_eq!(
as_of[0][1], "v1",
"AS OF must project the `value` column, not the raw envelope, got {as_of:?}"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn bitemporal_batch_insert_visible_in_select_and_count() {
let srv = TestServer::start().await;
create_bitemporal(&srv, "bt_batch").await;
srv.exec(
"INSERT INTO bt_batch (id, value) VALUES \
('a', '1'), ('b', '2'), ('c', '3')",
)
.await
.unwrap();
let rows = srv.query_rows("SELECT id FROM bt_batch").await.unwrap();
assert_eq!(
rows.len(),
3,
"batch INSERT rows must all be visible, got {rows:?}"
);
let count = srv
.query_text("SELECT COUNT(*) FROM bt_batch")
.await
.unwrap();
assert_eq!(
count,
vec!["3".to_string()],
"COUNT(*) must agree with the scan"
);
let as_of = srv
.query_rows(&format!(
"SELECT id FROM bt_batch AS OF SYSTEM TIME {FUTURE_MS}"
))
.await
.unwrap();
assert_eq!(
as_of.len(),
3,
"AS OF must see all batch-inserted rows, got {as_of:?}"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn bitemporal_update_reflected_in_default_select() {
let srv = TestServer::start().await;
create_bitemporal(&srv, "bt_upd").await;
srv.exec("INSERT INTO bt_upd (id, value) VALUES ('r1', 'v1')")
.await
.unwrap();
srv.exec("UPDATE bt_upd SET value = 'v2' WHERE id = 'r1'")
.await
.unwrap();
let rows = srv
.query_rows("SELECT id, value FROM bt_upd WHERE id = 'r1'")
.await
.unwrap();
assert_eq!(rows.len(), 1, "exactly one current row, got {rows:?}");
assert_eq!(
rows[0][1], "v2",
"default SELECT must return the latest version, got {rows:?}"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn bitemporal_tx_insert_survives_commit() {
let srv = TestServer::start().await;
create_bitemporal(&srv, "bt_tx_ins").await;
srv.exec("BEGIN").await.unwrap();
srv.exec("INSERT INTO bt_tx_ins (id, value) VALUES ('tx1', 'v1')")
.await
.unwrap();
srv.exec("COMMIT").await.unwrap();
let rows = srv
.query_rows("SELECT id, value FROM bt_tx_ins WHERE id = 'tx1'")
.await
.unwrap();
assert_eq!(
rows.len(),
1,
"committed transactional INSERT into a bitemporal collection must be \
visible to a fresh SELECT, got {rows:?}"
);
assert_eq!(rows[0][0], "tx1");
assert_eq!(rows[0][1], "v1");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn bitemporal_tx_delete_survives_commit() {
let srv = TestServer::start().await;
create_bitemporal(&srv, "bt_tx_del").await;
srv.exec("INSERT INTO bt_tx_del (id, value) VALUES ('tx2', 'v1')")
.await
.unwrap();
srv.exec("BEGIN").await.unwrap();
srv.exec("DELETE FROM bt_tx_del WHERE id = 'tx2'")
.await
.unwrap();
srv.exec("COMMIT").await.unwrap();
let rows = srv
.query_rows("SELECT id FROM bt_tx_del WHERE id = 'tx2'")
.await
.unwrap();
assert!(
rows.is_empty(),
"committed transactional DELETE on a bitemporal collection must remove \
the row from a fresh SELECT, got {rows:?}"
);
}