use crate::{RawStmt, raw};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[must_use]
pub enum AdvisoryLockKey {
BigInt(i64),
Pair(i32, i32),
}
impl From<i64> for AdvisoryLockKey {
#[inline]
fn from(key: i64) -> Self {
Self::BigInt(key)
}
}
impl From<(i32, i32)> for AdvisoryLockKey {
#[inline]
fn from((left, right): (i32, i32)) -> Self {
Self::Pair(left, right)
}
}
pub fn advisory_xact_lock(key: impl Into<AdvisoryLockKey>) -> RawStmt {
advisory_lock_stmt("pg_advisory_xact_lock", key.into())
}
pub fn try_advisory_xact_lock(key: impl Into<AdvisoryLockKey>) -> RawStmt {
advisory_lock_stmt("pg_try_advisory_xact_lock", key.into())
}
pub fn advisory_xact_lock_named(name: impl AsRef<str>) -> RawStmt {
advisory_xact_lock(advisory_name_key(name.as_ref()))
}
pub fn try_advisory_xact_lock_named(name: impl AsRef<str>) -> RawStmt {
try_advisory_xact_lock(advisory_name_key(name.as_ref()))
}
fn advisory_lock_stmt(function: &'static str, key: AdvisoryLockKey) -> RawStmt {
match key {
AdvisoryLockKey::BigInt(key) => raw(format!("SELECT {function}(?)")).bind(key),
AdvisoryLockKey::Pair(left, right) => raw(format!("SELECT {function}(?, ?)"))
.bind(left)
.bind(right),
}
}
fn advisory_name_key(name: &str) -> i64 {
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
let mut hash = FNV_OFFSET;
for byte in name.as_bytes() {
hash ^= u64::from(*byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
hash as i64
}
#[cfg(test)]
mod tests {
use super::{
advisory_name_key, advisory_xact_lock, advisory_xact_lock_named, try_advisory_xact_lock,
try_advisory_xact_lock_named,
};
#[test]
fn advisory_xact_lock_helpers_render_bigint_and_pair_keys() {
let bigint = advisory_xact_lock(42_i64).build().unwrap();
assert_eq!(bigint.sql, "SELECT pg_advisory_xact_lock($1)");
assert_eq!(bigint.params.len(), 1);
let pair = try_advisory_xact_lock((10_i32, 42_i32)).build().unwrap();
assert_eq!(pair.sql, "SELECT pg_try_advisory_xact_lock($1, $2)");
assert_eq!(pair.params.len(), 2);
}
#[test]
fn named_advisory_lock_helpers_use_stable_fnv1a_key() {
assert_eq!(
advisory_name_key("billing:invoice:123"),
6695150957860309103
);
let lock = advisory_xact_lock_named("billing:invoice:123")
.build()
.unwrap();
assert_eq!(lock.sql, "SELECT pg_advisory_xact_lock($1)");
assert_eq!(lock.params.len(), 1);
let try_lock = try_advisory_xact_lock_named("billing:invoice:123")
.build()
.unwrap();
assert_eq!(try_lock.sql, "SELECT pg_try_advisory_xact_lock($1)");
assert_eq!(try_lock.params.len(), 1);
}
}