use bytes::{BufMut, BytesMut};
use crate::error::Result;
use crate::types::Oid;
use crate::types::ToSql;
impl ToSql for bool {
fn oid(&self) -> Oid {
Oid::BOOL
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_u8(u8::from(*self));
Ok(())
}
}
impl ToSql for i16 {
fn oid(&self) -> Oid {
Oid::INT2
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_i16(*self);
Ok(())
}
}
impl ToSql for i32 {
fn oid(&self) -> Oid {
Oid::INT4
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_i32(*self);
Ok(())
}
}
impl ToSql for i64 {
fn oid(&self) -> Oid {
Oid::INT8
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_i64(*self);
Ok(())
}
}
impl ToSql for f32 {
fn oid(&self) -> Oid {
Oid::FLOAT4
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_f32(*self);
Ok(())
}
}
impl ToSql for f64 {
fn oid(&self) -> Oid {
Oid::FLOAT8
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_f64(*self);
Ok(())
}
}
impl ToSql for &str {
fn oid(&self) -> Oid {
Oid::TEXT
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_slice(self.as_bytes());
Ok(())
}
}
impl ToSql for String {
fn oid(&self) -> Oid {
Oid::TEXT
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_slice(self.as_bytes());
Ok(())
}
}
impl ToSql for &[u8] {
fn oid(&self) -> Oid {
Oid::BYTEA
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_slice(self);
Ok(())
}
}
impl ToSql for Vec<u8> {
fn oid(&self) -> Oid {
Oid::BYTEA
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_slice(self);
Ok(())
}
}
const PG_EPOCH_OFFSET_US: i64 = 946_684_800_000_000;
impl ToSql for chrono::NaiveDateTime {
fn oid(&self) -> Oid {
Oid::TIMESTAMP
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
if *self == chrono::NaiveDateTime::MAX {
buf.put_i64(i64::MAX);
} else if *self == chrono::NaiveDateTime::MIN {
buf.put_i64(i64::MIN);
} else {
let us = self.and_utc().timestamp_micros() - PG_EPOCH_OFFSET_US;
buf.put_i64(us);
}
Ok(())
}
}
impl ToSql for chrono::DateTime<chrono::Utc> {
fn oid(&self) -> Oid {
Oid::TIMESTAMPTZ
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
if self.naive_utc() == chrono::NaiveDateTime::MAX {
buf.put_i64(i64::MAX);
} else if self.naive_utc() == chrono::NaiveDateTime::MIN {
buf.put_i64(i64::MIN);
} else {
let us = self.timestamp_micros() - PG_EPOCH_OFFSET_US;
buf.put_i64(us);
}
Ok(())
}
}
impl ToSql for chrono::NaiveDate {
fn oid(&self) -> Oid {
Oid::DATE
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
if *self == chrono::NaiveDate::MAX {
buf.put_i32(i32::MAX);
} else if *self == chrono::NaiveDate::MIN {
buf.put_i32(i32::MIN);
} else {
#[allow(clippy::expect_used)]
let epoch = chrono::NaiveDate::from_ymd_opt(2000, 1, 1).expect("PG epoch is valid");
let days = (*self - epoch).num_days() as i32;
buf.put_i32(days);
}
Ok(())
}
}
impl ToSql for chrono::NaiveTime {
fn oid(&self) -> Oid {
Oid::TIME
}
#[allow(clippy::expect_used)]
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
let midnight = chrono::NaiveTime::from_hms_opt(0, 0, 0).expect("midnight is valid");
let us = self
.signed_duration_since(midnight)
.num_microseconds()
.unwrap_or(0);
buf.put_i64(us);
Ok(())
}
}
impl ToSql for uuid::Uuid {
fn oid(&self) -> Oid {
Oid::UUID
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
buf.put_slice(self.as_bytes());
Ok(())
}
}
fn encode_array<T: ToSql>(vec: &[T], element_oid: Oid, buf: &mut BytesMut) -> Result<()> {
if vec.is_empty() {
buf.put_i32(0); buf.put_i32(0); buf.put_u32(element_oid.0);
return Ok(());
}
buf.put_i32(1); buf.put_i32(0); buf.put_u32(element_oid.0);
buf.put_i32(vec.len() as i32); buf.put_i32(1);
for elem in vec {
let len_pos = buf.len();
buf.put_i32(0); let data_start = buf.len();
elem.to_sql(buf)?;
let data_len = (buf.len() - data_start) as i32;
buf[len_pos..len_pos + 4].copy_from_slice(&data_len.to_be_bytes());
}
Ok(())
}
macro_rules! impl_array_to_sql {
($elem_ty:ty, $array_oid:expr, $elem_oid:expr) => {
impl ToSql for Vec<$elem_ty> {
fn oid(&self) -> Oid {
$array_oid
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
encode_array(self, $elem_oid, buf)
}
}
};
}
impl_array_to_sql!(bool, Oid::BOOL_ARRAY, Oid::BOOL);
impl_array_to_sql!(i16, Oid::INT2_ARRAY, Oid::INT2);
impl_array_to_sql!(i32, Oid::INT4_ARRAY, Oid::INT4);
impl_array_to_sql!(i64, Oid::INT8_ARRAY, Oid::INT8);
impl_array_to_sql!(f32, Oid::FLOAT4_ARRAY, Oid::FLOAT4);
impl_array_to_sql!(f64, Oid::FLOAT8_ARRAY, Oid::FLOAT8);
impl_array_to_sql!(String, Oid::TEXT_ARRAY, Oid::TEXT);
impl_array_to_sql!(uuid::Uuid, Oid::UUID_ARRAY, Oid::UUID);
impl_array_to_sql!(
crate::types::interval::PgInterval,
Oid::INTERVAL_ARRAY,
Oid::INTERVAL
);
impl_array_to_sql!(crate::types::money::PgMoney, Oid::MONEY_ARRAY, Oid::MONEY);
impl_array_to_sql!(crate::types::xml::PgXml, Oid::XML_ARRAY, Oid::XML);
impl_array_to_sql!(crate::types::lsn::PgLsn, Oid::PG_LSN_ARRAY, Oid::PG_LSN);
impl_array_to_sql!(crate::types::network::PgInet, Oid::INET_ARRAY, Oid::INET);
impl_array_to_sql!(crate::types::network::PgCidr, Oid::CIDR_ARRAY, Oid::CIDR);
impl_array_to_sql!(
crate::types::network::PgMacAddr,
Oid::MACADDR_ARRAY,
Oid::MACADDR
);
#[cfg(feature = "with-rust-decimal")]
impl_array_to_sql!(rust_decimal::Decimal, Oid::NUMERIC_ARRAY, Oid::NUMERIC);
impl_array_to_sql!(crate::types::bit::PgBit, Oid::VARBIT_ARRAY, Oid::VARBIT);
impl_array_to_sql!(chrono::NaiveDateTime, Oid::TIMESTAMP_ARRAY, Oid::TIMESTAMP);
impl_array_to_sql!(
chrono::DateTime<chrono::Utc>,
Oid::TIMESTAMPTZ_ARRAY,
Oid::TIMESTAMPTZ
);
impl_array_to_sql!(chrono::NaiveDate, Oid::DATE_ARRAY, Oid::DATE);
impl_array_to_sql!(chrono::NaiveTime, Oid::TIME_ARRAY, Oid::TIME);
impl_array_to_sql!(
crate::types::geometric::PgPoint,
Oid::POINT_ARRAY,
Oid::POINT
);
impl_array_to_sql!(
crate::types::geometric::PgCircle,
Oid::CIRCLE_ARRAY,
Oid::CIRCLE
);
impl_array_to_sql!(
crate::types::timetz::PgTimeTz,
Oid::TIMETZ_ARRAY,
Oid::TIMETZ
);
impl_array_to_sql!(
crate::types::network::PgMacAddr8,
Oid::MACADDR8_ARRAY,
Oid::MACADDR8
);
impl ToSql for Vec<Vec<u8>> {
fn oid(&self) -> Oid {
Oid::BYTEA_ARRAY
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
encode_array(self, Oid::BYTEA, buf)
}
}
impl ToSql for Vec<&str> {
fn oid(&self) -> Oid {
Oid::TEXT_ARRAY
}
fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
encode_array(self, Oid::TEXT, buf)
}
}