use spg_engine::{Engine, QueryResult};
fn one(e: &mut Engine, sql: &str) -> String {
let r = e
.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
let QueryResult::Rows { rows, .. } = r else {
panic!("expected Rows from {sql}");
};
rows.iter()
.map(|row| {
row.values
.iter()
.map(|v| match v {
spg_storage::Value::Null => String::new(),
other => spg_engine::eval::value_to_text(other),
})
.collect::<Vec<_>>()
.join("|")
})
.collect::<Vec<_>>()
.join(";")
}
fn err(e: &mut Engine, sql: &str) -> String {
match e.execute(sql) {
Ok(v) => panic!("{sql}: expected an error, got {v:?}"),
Err(x) => format!("{x}").replace("unsupported: ", ""),
}
}
#[test]
fn a_round_trip_through_bytea() {
let mut e = Engine::new();
assert_eq!(
one(&mut e, "SELECT lo_from_bytea(0, '\\x48656c6c6f'::bytea)"),
"500000"
);
assert_eq!(one(&mut e, "SELECT lo_get(500000)"), "\\x48656c6c6f");
assert_eq!(
one(&mut e, "SELECT encode(lo_get(500000), 'escape')"),
"Hello"
);
assert_eq!(one(&mut e, "SELECT length(lo_get(500000))"), "5");
}
#[test]
fn the_offsets_are_zero_based() {
let mut e = Engine::new();
e.execute("SELECT lo_from_bytea(0, '\\x48656c6c6f'::bytea)")
.unwrap();
assert_eq!(one(&mut e, "SELECT lo_get(500000, 1, 3)"), "\\x656c6c");
e.execute("SELECT lo_put(500000, 1, '\\x41'::bytea)")
.unwrap();
assert_eq!(
one(&mut e, "SELECT encode(lo_get(500000),'escape')"),
"HAllo"
);
}
#[test]
fn lo_put_returns_void() {
let mut e = Engine::new();
e.execute("SELECT lo_from_bytea(0, '\\x4142'::bytea)")
.unwrap();
assert_eq!(one(&mut e, "SELECT lo_put(500000, 0, '\\x5a'::bytea)"), "");
}
#[test]
fn unlink_removes_it_and_the_wording_is_pgs() {
let mut e = Engine::new();
e.execute("SELECT lo_from_bytea(0, '\\x41'::bytea)")
.unwrap();
assert_eq!(one(&mut e, "SELECT lo_unlink(500000)"), "1");
assert_eq!(
err(&mut e, "SELECT lo_get(500000)"),
"large object 500000 does not exist",
);
assert_eq!(
err(&mut e, "SELECT lo_unlink(9999999)"),
"large object 9999999 does not exist",
);
}
#[test]
fn the_page_split_matches_pgs_2kb() {
let mut e = Engine::new();
e.execute("SELECT lo_from_bytea(0, repeat('a',5000)::bytea)")
.unwrap();
assert_eq!(
one(
&mut e,
"SELECT loid, pageno, length(data) FROM pg_largeobject WHERE loid = 500000 ORDER BY pageno",
),
"500000|0|2048;500000|1|2048;500000|2|904",
);
}
#[test]
fn an_empty_object_has_metadata_but_no_pages() {
let mut e = Engine::new();
assert_eq!(one(&mut e, "SELECT lo_from_bytea(0, ''::bytea)"), "500000");
assert_eq!(
one(
&mut e,
"SELECT count(*) FROM pg_largeobject WHERE loid = 500000"
),
"0",
);
assert_eq!(
one(
&mut e,
"SELECT oid, lomowner, lomacl FROM pg_largeobject_metadata"
),
"500000|10|",
);
}
#[test]
fn lo_creat_and_lo_create_both_allocate() {
let mut e = Engine::new();
assert_eq!(one(&mut e, "SELECT lo_creat(-1)"), "500000");
assert_eq!(one(&mut e, "SELECT lo_create(0)"), "500001");
assert_eq!(
one(&mut e, "SELECT count(*) FROM pg_largeobject_metadata"),
"2"
);
}
#[test]
fn the_objects_survive_a_catalog_round_trip() {
let mut e = Engine::new();
e.execute("SELECT lo_from_bytea(0, '\\x48656c6c6f'::bytea)")
.unwrap();
let bytes = e.catalog().serialize();
let mut restored = Engine::restore_envelope(&bytes).expect("reload");
assert_eq!(
one(&mut restored, "SELECT encode(lo_get(500000),'escape')"),
"Hello",
);
assert_eq!(
one(
&mut restored,
"SELECT count(*) FROM pg_largeobject_metadata"
),
"1",
);
}