PgLsn type.

This commit is contained in:
Jeff Davis 2020-12-14 11:56:21 -08:00
parent 77aa702e6c
commit f3cbc8ce04
5 changed files with 117 additions and 2 deletions

View File

@ -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`.

View File

@ -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<i64, StdBox<dyn Error + Sync + Se
Ok(v)
}
/// Serializes a `PG_LSN` value.
#[inline]
pub fn lsn_to_sql(v: Lsn, buf: &mut BytesMut) {
buf.put_u64(v);
}
/// Deserializes a `PG_LSN` value.
#[inline]
pub fn lsn_from_sql(mut buf: &[u8]) -> Result<Lsn, StdBox<dyn Error + Sync + Send>> {
let v = buf.read_u64::<BigEndian>()?;
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) {

View File

@ -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;

View File

@ -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<u64> for PgLsn {
fn from(lsn_u64: u64) -> Self {
PgLsn(lsn_u64)
}
}
impl From<PgLsn> for u64 {
fn from(lsn: PgLsn) -> u64 {
lsn.0
}
}
impl FromStr for PgLsn {
type Err = ParseLsnError;
fn from_str(lsn_str: &str) -> Result<Self, Self::Err> {
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<Self, Box<dyn Error + Sync + Send>> {
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<IsNull, Box<dyn Error + Sync + Send>> {
types::lsn_to_sql((*self).into(), out);
Ok(IsNull::No)
}
accepts!(PG_LSN);
to_sql_checked!();
}

View File

@ -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(