use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
fn rows(r: QueryResult) -> Vec<Vec<Value<'static>>> {
match r {
QueryResult::Rows { rows, .. } => rows.into_iter().map(|r| r.values).collect(),
_ => panic!("expected rows"),
}
}
fn setup(e: &mut Engine) {
e.execute("CREATE TABLE t (id INT NOT NULL, name TEXT NOT NULL)")
.unwrap();
e.execute("INSERT INTO t VALUES (1, 'a'), (2, 'b'), (3, 'c')")
.unwrap();
}
#[test]
fn for_update_basic() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(
e.execute("SELECT id, name FROM t WHERE id = 2 FOR UPDATE")
.unwrap(),
);
assert_eq!(r.len(), 1);
assert_eq!(r[0][0], Value::Int(2));
assert_eq!(r[0][1], Value::text("b"));
}
#[test]
fn for_share_basic() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(
e.execute("SELECT id FROM t WHERE id = 1 FOR SHARE")
.unwrap(),
);
assert_eq!(r.len(), 1);
assert_eq!(r[0][0], Value::Int(1));
}
#[test]
fn for_no_key_update_pg_form() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(e.execute("SELECT id FROM t FOR NO KEY UPDATE").unwrap());
assert_eq!(r.len(), 3);
}
#[test]
fn for_key_share_pg_form() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(e.execute("SELECT id FROM t FOR KEY SHARE").unwrap());
assert_eq!(r.len(), 3);
}
#[test]
fn for_update_nowait() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(
e.execute("SELECT id FROM t WHERE id = 3 FOR UPDATE NOWAIT")
.unwrap(),
);
assert_eq!(r.len(), 1);
}
#[test]
fn for_update_skip_locked() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(
e.execute("SELECT id FROM t FOR UPDATE SKIP LOCKED")
.unwrap(),
);
assert_eq!(r.len(), 3);
}
#[test]
fn for_update_of_specific_table() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(e.execute("SELECT id FROM t FOR UPDATE OF t").unwrap());
assert_eq!(r.len(), 3);
}
#[test]
fn for_update_of_multiple_tables() {
let mut e = Engine::new();
setup(&mut e);
e.execute("CREATE TABLE u (id INT NOT NULL)").unwrap();
e.execute("INSERT INTO u VALUES (1)").unwrap();
let r = rows(
e.execute("SELECT t.id FROM t JOIN u ON t.id = u.id FOR UPDATE OF t, u")
.unwrap(),
);
assert_eq!(r.len(), 1);
}
#[test]
fn stacked_for_clauses() {
let mut e = Engine::new();
setup(&mut e);
e.execute("CREATE TABLE u (id INT NOT NULL)").unwrap();
e.execute("INSERT INTO u VALUES (1), (2)").unwrap();
let r = rows(
e.execute(
"SELECT t.id FROM t JOIN u ON t.id = u.id \
FOR UPDATE OF t FOR SHARE OF u",
)
.unwrap(),
);
assert!(!r.is_empty());
}
#[test]
fn for_update_after_limit_offset() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(
e.execute("SELECT id FROM t ORDER BY id LIMIT 2 OFFSET 1 FOR UPDATE")
.unwrap(),
);
assert_eq!(r.len(), 2);
assert_eq!(r[0][0], Value::Int(2));
assert_eq!(r[1][0], Value::Int(3));
}
#[test]
fn for_update_skip_locked_with_of_and_limit() {
let mut e = Engine::new();
setup(&mut e);
let r = rows(
e.execute(
"SELECT id FROM t ORDER BY id LIMIT 1 \
FOR UPDATE OF t SKIP LOCKED",
)
.unwrap(),
);
assert_eq!(r.len(), 1);
assert_eq!(r[0][0], Value::Int(1));
}
#[test]
fn returns_same_rows_with_and_without_lock_clause() {
let mut e = Engine::new();
setup(&mut e);
let plain = rows(e.execute("SELECT id, name FROM t ORDER BY id").unwrap());
let locked = rows(
e.execute("SELECT id, name FROM t ORDER BY id FOR UPDATE")
.unwrap(),
);
assert_eq!(plain, locked, "FOR UPDATE is a parser-noop in v7.17");
}