From f3cbc8ce0431efb1196727a7e6016a5928dcbd55 Mon Sep 17 00:00:00 2001 From: Jeff Davis Date: Mon, 14 Dec 2020 11:56:21 -0800 Subject: [PATCH] PgLsn type. --- postgres-protocol/src/lib.rs | 3 + postgres-protocol/src/types/mod.rs | 18 +++++- postgres-types/src/lib.rs | 4 ++ postgres-types/src/pg_lsn.rs | 79 ++++++++++++++++++++++++++ tokio-postgres/tests/test/types/mod.rs | 15 ++++- 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 postgres-types/src/pg_lsn.rs diff --git a/postgres-protocol/src/lib.rs b/postgres-protocol/src/lib.rs index b4374afa..13de51e7 100644 --- a/postgres-protocol/src/lib.rs +++ b/postgres-protocol/src/lib.rs @@ -24,6 +24,9 @@ pub mod types; /// A Postgres OID. pub type Oid = u32; +/// A Postgres Log Sequence Number (LSN). +pub type Lsn = u64; + /// An enum indicating if a value is `NULL` or not. pub enum IsNull { /// The value is `NULL`. diff --git a/postgres-protocol/src/types/mod.rs b/postgres-protocol/src/types/mod.rs index 621c01cc..436132c2 100644 --- a/postgres-protocol/src/types/mod.rs +++ b/postgres-protocol/src/types/mod.rs @@ -8,7 +8,7 @@ use std::io::Read; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::str; -use crate::{write_nullable, FromUsize, IsNull, Oid}; +use crate::{write_nullable, FromUsize, IsNull, Lsn, Oid}; #[cfg(test)] mod test; @@ -142,6 +142,22 @@ pub fn int8_from_sql(mut buf: &[u8]) -> Result Result> { + let v = buf.read_u64::()?; + if !buf.is_empty() { + return Err("invalid buffer size".into()); + } + Ok(v) +} + /// Serializes a `FLOAT4` value. #[inline] pub fn float4_to_sql(v: f32, buf: &mut BytesMut) { diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 2909b81e..e7cc781c 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -130,6 +130,9 @@ use crate::type_gen::{Inner, Other}; #[doc(inline)] pub use postgres_protocol::Oid; +#[doc(inline)] +pub use pg_lsn::PgLsn; + pub use crate::special::{Date, Timestamp}; use bytes::BytesMut; @@ -204,6 +207,7 @@ mod uuid_08; #[cfg(feature = "with-time-0_2")] extern crate time_02 as time; +mod pg_lsn; #[doc(hidden)] pub mod private; mod special; diff --git a/postgres-types/src/pg_lsn.rs b/postgres-types/src/pg_lsn.rs new file mode 100644 index 00000000..f0bbf402 --- /dev/null +++ b/postgres-types/src/pg_lsn.rs @@ -0,0 +1,79 @@ +//! Log Sequence Number (LSN) type for PostgreSQL Write-Ahead Log +//! (WAL), also known as the transaction log. + +use bytes::BytesMut; +use postgres_protocol::types; +use std::error::Error; +use std::fmt; +use std::str::FromStr; + +use crate::{FromSql, IsNull, ToSql, Type}; + +/// Postgres `PG_LSN` type. +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +pub struct PgLsn(u64); + +/// Error parsing LSN. +#[derive(Debug)] +pub struct ParseLsnError(()); + +impl From for PgLsn { + fn from(lsn_u64: u64) -> Self { + PgLsn(lsn_u64) + } +} + +impl From for u64 { + fn from(lsn: PgLsn) -> u64 { + lsn.0 + } +} + +impl FromStr for PgLsn { + type Err = ParseLsnError; + + fn from_str(lsn_str: &str) -> Result { + let split: Vec<&str> = lsn_str.split('/').collect(); + if split.len() == 2 { + let (hi, lo) = ( + u64::from_str_radix(split[0], 16).map_err(|_| ParseLsnError(()))?, + u64::from_str_radix(split[1], 16).map_err(|_| ParseLsnError(()))?, + ); + Ok(PgLsn((hi << 32) | lo)) + } else { + Err(ParseLsnError(())) + } + } +} + +impl fmt::Display for PgLsn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:X}/{:X}", self.0 >> 32, self.0 & 0x00000000ffffffff) + } +} + +impl fmt::Debug for PgLsn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("{}", self)) + } +} + +impl<'a> FromSql<'a> for PgLsn { + fn from_sql(_: &Type, raw: &'a [u8]) -> Result> { + let v = types::lsn_from_sql(raw)?; + Ok(v.into()) + } + + accepts!(PG_LSN); +} + +impl ToSql for PgLsn { + fn to_sql(&self, _: &Type, out: &mut BytesMut) -> Result> { + types::lsn_to_sql((*self).into(), out); + Ok(IsNull::No) + } + + accepts!(PG_LSN); + + to_sql_checked!(); +} diff --git a/tokio-postgres/tests/test/types/mod.rs b/tokio-postgres/tests/test/types/mod.rs index 168ca3a4..c1480bf8 100644 --- a/tokio-postgres/tests/test/types/mod.rs +++ b/tokio-postgres/tests/test/types/mod.rs @@ -6,8 +6,9 @@ use std::f64; use std::fmt; use std::net::IpAddr; use std::result; +use std::str::FromStr; use std::time::{Duration, UNIX_EPOCH}; -use tokio_postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, ToSql, Type, WrongType}; +use tokio_postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, PgLsn, ToSql, Type, WrongType}; use crate::connect; use bytes::BytesMut; @@ -135,6 +136,18 @@ async fn test_i64_params() { .await; } +#[tokio::test] +async fn test_lsn_params() { + test_type( + "PG_LSN", + &[ + (Some(PgLsn::from_str("2B/1757980").unwrap()), "'2B/1757980'"), + (None, "NULL"), + ], + ) + .await +} + #[tokio::test] async fn test_f32_params() { test_type(