mod conversions;
mod drivers;
mod insert;
#[cfg(feature = "serde")]
mod json;
mod owned;
mod update;
pub use insert::*;
#[cfg(feature = "serde")]
pub use json::PostgresJsonType;
pub use owned::*;
pub use update::*;
use drizzle_core::{error::DrizzleError, sql::SQL, traits::SQLParam};
#[cfg(feature = "uuid")]
use uuid::Uuid;
#[cfg(feature = "chrono")]
use chrono::{DateTime, Duration, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
#[cfg(feature = "time")]
use time::{
Date as TimeDate, Duration as TimeDuration, OffsetDateTime, PrimitiveDateTime, Time as TimeTime,
};
#[cfg(feature = "jiff")]
use jiff::{
Timestamp as JiffTimestamp,
civil::{Date as JiffDate, DateTime as JiffDateTime, Time as JiffTime},
};
#[cfg(feature = "cidr")]
use cidr::{IpCidr, IpInet};
#[cfg(feature = "geo-types")]
use geo_types::{LineString, Point, Rect};
#[cfg(feature = "bit-vec")]
use bit_vec::BitVec;
#[cfg(feature = "rust-decimal")]
use rust_decimal::Decimal;
use crate::prelude::*;
use crate::traits::{FromPostgresValue, PostgresEnum};
#[derive(Debug, Clone, PartialEq, Default)]
pub enum PostgresValue<'a> {
Smallint(i16),
Integer(i32),
Bigint(i64),
Real(f32),
DoublePrecision(f64),
#[cfg(feature = "rust-decimal")]
Numeric(Decimal),
Text(Cow<'a, str>),
Bytea(Cow<'a, [u8]>),
Boolean(bool),
#[cfg(feature = "uuid")]
Uuid(Uuid),
#[cfg(feature = "serde")]
Json(serde_json::Value),
#[cfg(feature = "serde")]
Jsonb(serde_json::Value),
Enum(Box<dyn PostgresEnum>),
#[cfg(feature = "chrono")]
Date(NaiveDate),
#[cfg(feature = "chrono")]
Time(NaiveTime),
#[cfg(feature = "chrono")]
Timestamp(NaiveDateTime),
#[cfg(feature = "chrono")]
TimestampTz(DateTime<FixedOffset>),
#[cfg(feature = "chrono")]
Interval(Duration),
#[cfg(feature = "time")]
TimeDate(TimeDate),
#[cfg(feature = "time")]
TimeTime(TimeTime),
#[cfg(feature = "time")]
TimeTimestamp(PrimitiveDateTime),
#[cfg(feature = "time")]
TimeTimestampTz(OffsetDateTime),
#[cfg(feature = "time")]
TimeInterval(TimeDuration),
#[cfg(feature = "jiff")]
JiffDate(JiffDate),
#[cfg(feature = "jiff")]
JiffTime(JiffTime),
#[cfg(feature = "jiff")]
JiffDateTime(JiffDateTime),
#[cfg(feature = "jiff")]
JiffTimestamp(JiffTimestamp),
#[cfg(feature = "cidr")]
Inet(IpInet),
#[cfg(feature = "cidr")]
Cidr(IpCidr),
#[cfg(feature = "cidr")]
MacAddr([u8; 6]),
#[cfg(feature = "cidr")]
MacAddr8([u8; 8]),
#[cfg(feature = "geo-types")]
Point(Point<f64>),
#[cfg(feature = "geo-types")]
LineString(LineString<f64>),
#[cfg(feature = "geo-types")]
Rect(Rect<f64>),
#[cfg(feature = "bit-vec")]
BitVec(BitVec),
Array(Vec<Self>),
#[default]
Null,
}
impl core::fmt::Display for PostgresValue<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let value = match self {
PostgresValue::Smallint(i) => i.to_string(),
PostgresValue::Integer(i) => i.to_string(),
PostgresValue::Bigint(i) => i.to_string(),
PostgresValue::Real(r) => r.to_string(),
PostgresValue::DoublePrecision(r) => r.to_string(),
#[cfg(feature = "rust-decimal")]
PostgresValue::Numeric(d) => d.to_string(),
PostgresValue::Text(cow) => cow.to_string(),
PostgresValue::Bytea(cow) => {
use core::fmt::Write;
let mut s = String::with_capacity(2 + cow.len() * 2);
s.push_str("\\x");
for byte in cow.iter() {
write!(s, "{byte:02x}").expect("writing to String cannot fail");
}
s
}
PostgresValue::Boolean(b) => b.to_string(),
#[cfg(feature = "uuid")]
PostgresValue::Uuid(uuid) => uuid.to_string(),
#[cfg(feature = "serde")]
PostgresValue::Json(json) => json.to_string(),
#[cfg(feature = "serde")]
PostgresValue::Jsonb(json) => json.to_string(),
PostgresValue::Enum(enum_val) => enum_val.variant_name().to_string(),
#[cfg(feature = "chrono")]
PostgresValue::Date(date) => date.to_string(),
#[cfg(feature = "chrono")]
PostgresValue::Time(time) => time.to_string(),
#[cfg(feature = "chrono")]
PostgresValue::Timestamp(ts) => ts.to_string(),
#[cfg(feature = "chrono")]
PostgresValue::TimestampTz(ts) => ts.to_string(),
#[cfg(feature = "chrono")]
PostgresValue::Interval(dur) => format!("{} seconds", dur.num_seconds()),
#[cfg(feature = "time")]
PostgresValue::TimeDate(date) => date.to_string(),
#[cfg(feature = "time")]
PostgresValue::TimeTime(time) => time.to_string(),
#[cfg(feature = "time")]
PostgresValue::TimeTimestamp(ts) => ts.to_string(),
#[cfg(feature = "time")]
PostgresValue::TimeTimestampTz(ts) => ts.to_string(),
#[cfg(feature = "time")]
PostgresValue::TimeInterval(dur) => format!("{} seconds", dur.whole_seconds()),
#[cfg(feature = "jiff")]
PostgresValue::JiffDate(date) => date.to_string(),
#[cfg(feature = "jiff")]
PostgresValue::JiffTime(time) => time.to_string(),
#[cfg(feature = "jiff")]
PostgresValue::JiffDateTime(ts) => ts.to_string(),
#[cfg(feature = "jiff")]
PostgresValue::JiffTimestamp(ts) => ts.to_string(),
#[cfg(feature = "cidr")]
PostgresValue::Inet(net) => net.to_string(),
#[cfg(feature = "cidr")]
PostgresValue::Cidr(net) => net.to_string(),
#[cfg(feature = "cidr")]
PostgresValue::MacAddr(mac) => format!(
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
),
#[cfg(feature = "cidr")]
PostgresValue::MacAddr8(mac) => format!(
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
),
#[cfg(feature = "geo-types")]
PostgresValue::Point(point) => format!("({},{})", point.x(), point.y()),
#[cfg(feature = "geo-types")]
PostgresValue::LineString(line) => {
let coords: Vec<String> = line
.coords()
.map(|coord| format!("({},{})", coord.x, coord.y))
.collect();
format!("[{}]", coords.join(","))
}
#[cfg(feature = "geo-types")]
PostgresValue::Rect(rect) => {
format!(
"(({},{}),({},{}))",
rect.min().x,
rect.min().y,
rect.max().x,
rect.max().y
)
}
#[cfg(feature = "bit-vec")]
PostgresValue::BitVec(bv) => bv
.iter()
.map(|b| if b { '1' } else { '0' })
.collect::<String>(),
PostgresValue::Array(arr) => {
let elements: Vec<String> = arr.iter().map(ToString::to_string).collect();
format!("{{{}}}", elements.join(","))
}
PostgresValue::Null => String::new(),
};
write!(f, "{value}")
}
}
impl PostgresValue<'_> {
#[inline]
#[must_use]
pub const fn is_null(&self) -> bool {
matches!(self, PostgresValue::Null)
}
#[inline]
#[must_use]
pub const fn as_bool(&self) -> Option<bool> {
match self {
PostgresValue::Boolean(value) => Some(*value),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_i16(&self) -> Option<i16> {
match self {
PostgresValue::Smallint(value) => Some(*value),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_i32(&self) -> Option<i32> {
match self {
PostgresValue::Integer(value) => Some(*value),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_i64(&self) -> Option<i64> {
match self {
PostgresValue::Bigint(value) => Some(*value),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_f32(&self) -> Option<f32> {
match self {
PostgresValue::Real(value) => Some(*value),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_f64(&self) -> Option<f64> {
match self {
PostgresValue::DoublePrecision(value) => Some(*value),
_ => None,
}
}
#[inline]
#[cfg(feature = "rust-decimal")]
#[must_use]
pub const fn as_decimal(&self) -> Option<&Decimal> {
match self {
PostgresValue::Numeric(value) => Some(value),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match self {
PostgresValue::Text(value) => Some(value.as_ref()),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
PostgresValue::Bytea(value) => Some(value.as_ref()),
_ => None,
}
}
#[inline]
#[cfg(feature = "uuid")]
#[must_use]
pub const fn as_uuid(&self) -> Option<Uuid> {
match self {
PostgresValue::Uuid(value) => Some(*value),
_ => None,
}
}
#[inline]
#[cfg(feature = "serde")]
#[must_use]
pub const fn as_json(&self) -> Option<&serde_json::Value> {
match self {
PostgresValue::Json(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "serde")]
#[must_use]
pub const fn as_jsonb(&self) -> Option<&serde_json::Value> {
match self {
PostgresValue::Jsonb(value) => Some(value),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_enum(&self) -> Option<&dyn PostgresEnum> {
match self {
PostgresValue::Enum(value) => Some(value.as_ref()),
_ => None,
}
}
#[inline]
#[cfg(feature = "chrono")]
#[must_use]
pub const fn as_date(&self) -> Option<&NaiveDate> {
match self {
PostgresValue::Date(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "chrono")]
#[must_use]
pub const fn as_time(&self) -> Option<&NaiveTime> {
match self {
PostgresValue::Time(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "chrono")]
#[must_use]
pub const fn as_timestamp(&self) -> Option<&NaiveDateTime> {
match self {
PostgresValue::Timestamp(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "chrono")]
#[must_use]
pub const fn as_timestamp_tz(&self) -> Option<&DateTime<FixedOffset>> {
match self {
PostgresValue::TimestampTz(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "chrono")]
#[must_use]
pub const fn as_interval(&self) -> Option<&Duration> {
match self {
PostgresValue::Interval(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "cidr")]
#[must_use]
pub const fn as_inet(&self) -> Option<&IpInet> {
match self {
PostgresValue::Inet(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "cidr")]
#[must_use]
pub const fn as_cidr(&self) -> Option<&IpCidr> {
match self {
PostgresValue::Cidr(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "cidr")]
#[must_use]
pub const fn as_macaddr(&self) -> Option<[u8; 6]> {
match self {
PostgresValue::MacAddr(value) => Some(*value),
_ => None,
}
}
#[inline]
#[cfg(feature = "cidr")]
#[must_use]
pub const fn as_macaddr8(&self) -> Option<[u8; 8]> {
match self {
PostgresValue::MacAddr8(value) => Some(*value),
_ => None,
}
}
#[inline]
#[cfg(feature = "geo-types")]
#[must_use]
pub const fn as_point(&self) -> Option<&Point<f64>> {
match self {
PostgresValue::Point(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "geo-types")]
#[must_use]
pub const fn as_line_string(&self) -> Option<&LineString<f64>> {
match self {
PostgresValue::LineString(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "geo-types")]
#[must_use]
pub const fn as_rect(&self) -> Option<&Rect<f64>> {
match self {
PostgresValue::Rect(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "bit-vec")]
#[must_use]
pub const fn as_bitvec(&self) -> Option<&BitVec> {
match self {
PostgresValue::BitVec(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "time")]
#[must_use]
pub const fn as_time_date(&self) -> Option<&TimeDate> {
match self {
PostgresValue::TimeDate(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "time")]
#[must_use]
pub const fn as_time_time(&self) -> Option<&TimeTime> {
match self {
PostgresValue::TimeTime(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "time")]
#[must_use]
pub const fn as_time_timestamp(&self) -> Option<&PrimitiveDateTime> {
match self {
PostgresValue::TimeTimestamp(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "time")]
#[must_use]
pub const fn as_time_timestamp_tz(&self) -> Option<&OffsetDateTime> {
match self {
PostgresValue::TimeTimestampTz(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "time")]
#[must_use]
pub const fn as_time_interval(&self) -> Option<&TimeDuration> {
match self {
PostgresValue::TimeInterval(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "jiff")]
#[must_use]
pub const fn as_jiff_date(&self) -> Option<&JiffDate> {
match self {
PostgresValue::JiffDate(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "jiff")]
#[must_use]
pub const fn as_jiff_time(&self) -> Option<&JiffTime> {
match self {
PostgresValue::JiffTime(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "jiff")]
#[must_use]
pub const fn as_jiff_datetime(&self) -> Option<&JiffDateTime> {
match self {
PostgresValue::JiffDateTime(value) => Some(value),
_ => None,
}
}
#[inline]
#[cfg(feature = "jiff")]
#[must_use]
pub const fn as_jiff_timestamp(&self) -> Option<&JiffTimestamp> {
match self {
PostgresValue::JiffTimestamp(value) => Some(value),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_array(&self) -> Option<&[Self]> {
match self {
PostgresValue::Array(values) => Some(values),
_ => None,
}
}
#[inline]
#[must_use]
pub fn into_owned(self) -> OwnedPostgresValue {
self.into()
}
pub fn convert<T: FromPostgresValue>(self) -> Result<T, DrizzleError> {
match self {
PostgresValue::Boolean(value) => T::from_postgres_bool(value),
PostgresValue::Smallint(value) => T::from_postgres_i16(value),
PostgresValue::Integer(value) => T::from_postgres_i32(value),
PostgresValue::Bigint(value) => T::from_postgres_i64(value),
PostgresValue::Real(value) => T::from_postgres_f32(value),
PostgresValue::DoublePrecision(value) => T::from_postgres_f64(value),
#[cfg(feature = "rust-decimal")]
PostgresValue::Numeric(value) => {
let text = value.to_string();
T::from_postgres_text(&text)
}
PostgresValue::Text(value) => T::from_postgres_text(&value),
PostgresValue::Bytea(value) => T::from_postgres_bytes(&value),
#[cfg(feature = "uuid")]
PostgresValue::Uuid(value) => T::from_postgres_uuid(value),
#[cfg(feature = "serde")]
PostgresValue::Json(value) => T::from_postgres_json(value),
#[cfg(feature = "serde")]
PostgresValue::Jsonb(value) => T::from_postgres_jsonb(value),
PostgresValue::Enum(value) => T::from_postgres_text(value.variant_name()),
#[cfg(feature = "chrono")]
PostgresValue::Date(value) => T::from_postgres_date(value),
#[cfg(feature = "chrono")]
PostgresValue::Time(value) => T::from_postgres_time(value),
#[cfg(feature = "chrono")]
PostgresValue::Timestamp(value) => T::from_postgres_timestamp(value),
#[cfg(feature = "chrono")]
PostgresValue::TimestampTz(value) => T::from_postgres_timestamptz(value),
#[cfg(feature = "chrono")]
PostgresValue::Interval(value) => T::from_postgres_interval(value),
#[cfg(feature = "time")]
PostgresValue::TimeDate(value) => T::from_postgres_time_date(value),
#[cfg(feature = "time")]
PostgresValue::TimeTime(value) => T::from_postgres_time_time(value),
#[cfg(feature = "time")]
PostgresValue::TimeTimestamp(value) => T::from_postgres_time_timestamp(value),
#[cfg(feature = "time")]
PostgresValue::TimeTimestampTz(value) => T::from_postgres_time_timestamptz(value),
#[cfg(feature = "time")]
PostgresValue::TimeInterval(value) => T::from_postgres_time_interval(value),
#[cfg(feature = "jiff")]
PostgresValue::JiffDate(value) => T::from_postgres_jiff_date(value),
#[cfg(feature = "jiff")]
PostgresValue::JiffTime(value) => T::from_postgres_jiff_time(value),
#[cfg(feature = "jiff")]
PostgresValue::JiffDateTime(value) => T::from_postgres_jiff_datetime(value),
#[cfg(feature = "jiff")]
PostgresValue::JiffTimestamp(value) => T::from_postgres_jiff_timestamp(value),
#[cfg(feature = "cidr")]
PostgresValue::Inet(value) => T::from_postgres_inet(value),
#[cfg(feature = "cidr")]
PostgresValue::Cidr(value) => T::from_postgres_cidr(value),
#[cfg(feature = "cidr")]
PostgresValue::MacAddr(value) => T::from_postgres_macaddr(value),
#[cfg(feature = "cidr")]
PostgresValue::MacAddr8(value) => T::from_postgres_macaddr8(value),
#[cfg(feature = "geo-types")]
PostgresValue::Point(value) => T::from_postgres_point(value),
#[cfg(feature = "geo-types")]
PostgresValue::LineString(value) => T::from_postgres_linestring(value),
#[cfg(feature = "geo-types")]
PostgresValue::Rect(value) => T::from_postgres_rect(value),
#[cfg(feature = "bit-vec")]
PostgresValue::BitVec(value) => T::from_postgres_bitvec(value),
PostgresValue::Array(value) => T::from_postgres_array(value),
PostgresValue::Null => T::from_postgres_null(),
}
}
pub fn convert_ref<T: FromPostgresValue>(&self) -> Result<T, DrizzleError> {
match self {
PostgresValue::Boolean(value) => T::from_postgres_bool(*value),
PostgresValue::Smallint(value) => T::from_postgres_i16(*value),
PostgresValue::Integer(value) => T::from_postgres_i32(*value),
PostgresValue::Bigint(value) => T::from_postgres_i64(*value),
PostgresValue::Real(value) => T::from_postgres_f32(*value),
PostgresValue::DoublePrecision(value) => T::from_postgres_f64(*value),
#[cfg(feature = "rust-decimal")]
PostgresValue::Numeric(value) => {
let text = value.to_string();
T::from_postgres_text(&text)
}
PostgresValue::Text(value) => T::from_postgres_text(value),
PostgresValue::Bytea(value) => T::from_postgres_bytes(value),
#[cfg(feature = "uuid")]
PostgresValue::Uuid(value) => T::from_postgres_uuid(*value),
#[cfg(feature = "serde")]
PostgresValue::Json(value) => T::from_postgres_json(value.clone()),
#[cfg(feature = "serde")]
PostgresValue::Jsonb(value) => T::from_postgres_jsonb(value.clone()),
PostgresValue::Enum(value) => T::from_postgres_text(value.variant_name()),
#[cfg(feature = "chrono")]
PostgresValue::Date(value) => T::from_postgres_date(*value),
#[cfg(feature = "chrono")]
PostgresValue::Time(value) => T::from_postgres_time(*value),
#[cfg(feature = "chrono")]
PostgresValue::Timestamp(value) => T::from_postgres_timestamp(*value),
#[cfg(feature = "chrono")]
PostgresValue::TimestampTz(value) => T::from_postgres_timestamptz(*value),
#[cfg(feature = "chrono")]
PostgresValue::Interval(value) => T::from_postgres_interval(*value),
#[cfg(feature = "time")]
PostgresValue::TimeDate(value) => T::from_postgres_time_date(*value),
#[cfg(feature = "time")]
PostgresValue::TimeTime(value) => T::from_postgres_time_time(*value),
#[cfg(feature = "time")]
PostgresValue::TimeTimestamp(value) => T::from_postgres_time_timestamp(*value),
#[cfg(feature = "time")]
PostgresValue::TimeTimestampTz(value) => T::from_postgres_time_timestamptz(*value),
#[cfg(feature = "time")]
PostgresValue::TimeInterval(value) => T::from_postgres_time_interval(*value),
#[cfg(feature = "jiff")]
PostgresValue::JiffDate(value) => T::from_postgres_jiff_date(*value),
#[cfg(feature = "jiff")]
PostgresValue::JiffTime(value) => T::from_postgres_jiff_time(*value),
#[cfg(feature = "jiff")]
PostgresValue::JiffDateTime(value) => T::from_postgres_jiff_datetime(*value),
#[cfg(feature = "jiff")]
PostgresValue::JiffTimestamp(value) => T::from_postgres_jiff_timestamp(*value),
#[cfg(feature = "cidr")]
PostgresValue::Inet(value) => T::from_postgres_inet(*value),
#[cfg(feature = "cidr")]
PostgresValue::Cidr(value) => T::from_postgres_cidr(*value),
#[cfg(feature = "cidr")]
PostgresValue::MacAddr(value) => T::from_postgres_macaddr(*value),
#[cfg(feature = "cidr")]
PostgresValue::MacAddr8(value) => T::from_postgres_macaddr8(*value),
#[cfg(feature = "geo-types")]
PostgresValue::Point(value) => T::from_postgres_point(*value),
#[cfg(feature = "geo-types")]
PostgresValue::LineString(value) => T::from_postgres_linestring(value.clone()),
#[cfg(feature = "geo-types")]
PostgresValue::Rect(value) => T::from_postgres_rect(*value),
#[cfg(feature = "bit-vec")]
PostgresValue::BitVec(value) => T::from_postgres_bitvec(value.clone()),
PostgresValue::Array(value) => T::from_postgres_array(value.clone()),
PostgresValue::Null => T::from_postgres_null(),
}
}
}
impl SQLParam for PostgresValue<'_> {
const DIALECT: drizzle_core::dialect::Dialect = drizzle_core::dialect::Dialect::PostgreSQL;
type DialectMarker = drizzle_core::dialect::PostgresDialect;
#[inline]
fn pagination_param(value: usize) -> Option<Self> {
i64::try_from(value).ok().map(Self::Bigint)
}
fn write_literal(&self, buf: &mut String) -> bool {
let mut literal = String::new();
let written = write_postgres_literal(self, &mut literal).is_some();
if written {
buf.push_str(&literal);
}
written
}
}
fn write_quoted_literal(buf: &mut String, text: &str) -> Option<()> {
if text.contains('\0') {
return None;
}
buf.push('\'');
buf.push_str(&text.replace('\'', "''"));
buf.push('\'');
Some(())
}
fn write_cast_literal(buf: &mut String, text: &str, sql_type: &str) -> Option<()> {
write_quoted_literal(buf, text)?;
buf.push_str("::");
buf.push_str(sql_type);
Some(())
}
fn float_text(value: f64) -> String {
if value.is_nan() {
"NaN".to_string()
} else if value.is_infinite() {
if value.is_sign_positive() {
"Infinity"
} else {
"-Infinity"
}
.to_string()
} else {
format!("{value:?}")
}
}
fn write_postgres_literal(value: &PostgresValue<'_>, buf: &mut String) -> Option<()> {
use core::fmt::Write;
match value {
PostgresValue::Null => buf.push_str("NULL"),
PostgresValue::Smallint(v) => {
let _ = write!(buf, "{v}::smallint");
}
PostgresValue::Integer(v) => {
let _ = write!(buf, "{v}::integer");
}
PostgresValue::Bigint(v) => {
let _ = write!(buf, "{v}::bigint");
}
PostgresValue::Real(v) => write_cast_literal(buf, &float_text(f64::from(*v)), "real")?,
PostgresValue::DoublePrecision(v) => {
write_cast_literal(buf, &float_text(*v), "double precision")?;
}
#[cfg(feature = "rust-decimal")]
PostgresValue::Numeric(v) => write_cast_literal(buf, &v.to_string(), "numeric")?,
PostgresValue::Text(text) => write_quoted_literal(buf, text)?,
PostgresValue::Bytea(bytes) => {
let mut hex = String::with_capacity(2 + bytes.len() * 2);
hex.push_str("\\x");
for byte in bytes.iter() {
let _ = write!(hex, "{byte:02x}");
}
write_cast_literal(buf, &hex, "bytea")?;
}
PostgresValue::Boolean(v) => buf.push_str(if *v { "TRUE" } else { "FALSE" }),
#[cfg(feature = "uuid")]
PostgresValue::Uuid(v) => write_cast_literal(buf, &v.to_string(), "uuid")?,
#[cfg(feature = "serde")]
PostgresValue::Json(v) => write_cast_literal(buf, &v.to_string(), "json")?,
#[cfg(feature = "serde")]
PostgresValue::Jsonb(v) => write_cast_literal(buf, &v.to_string(), "jsonb")?,
PostgresValue::Enum(v) => write_quoted_literal(buf, v.variant_name())?,
#[cfg(feature = "chrono")]
PostgresValue::Date(v) => write_cast_literal(buf, &v.to_string(), "date")?,
#[cfg(feature = "chrono")]
PostgresValue::Time(v) => write_cast_literal(buf, &v.to_string(), "time")?,
#[cfg(feature = "chrono")]
PostgresValue::Timestamp(v) => write_cast_literal(buf, &v.to_string(), "timestamp")?,
#[cfg(feature = "chrono")]
PostgresValue::TimestampTz(v) => {
write_cast_literal(buf, &v.to_rfc3339(), "timestamptz")?;
}
#[cfg(feature = "chrono")]
PostgresValue::Interval(v) => {
let micros = v.num_microseconds()?;
write_cast_literal(buf, &format!("{micros} microseconds"), "interval")?;
}
#[cfg(feature = "time")]
PostgresValue::TimeDate(v) => write_cast_literal(buf, &v.to_string(), "date")?,
#[cfg(feature = "time")]
PostgresValue::TimeTime(v) => write_cast_literal(buf, &v.to_string(), "time")?,
#[cfg(feature = "time")]
PostgresValue::TimeTimestamp(v) => {
write_cast_literal(buf, &format!("{} {}", v.date(), v.time()), "timestamp")?;
}
#[cfg(feature = "time")]
PostgresValue::TimeTimestampTz(v) => {
let utc = v.to_offset(time::UtcOffset::UTC);
write_cast_literal(
buf,
&format!("{} {}+00", utc.date(), utc.time()),
"timestamptz",
)?;
}
#[cfg(feature = "time")]
PostgresValue::TimeInterval(v) => {
let micros = v.whole_microseconds();
write_cast_literal(buf, &format!("{micros} microseconds"), "interval")?;
}
#[cfg(feature = "jiff")]
PostgresValue::JiffDate(v) => write_cast_literal(buf, &v.to_string(), "date")?,
#[cfg(feature = "jiff")]
PostgresValue::JiffTime(v) => write_cast_literal(buf, &v.to_string(), "time")?,
#[cfg(feature = "jiff")]
PostgresValue::JiffDateTime(v) => write_cast_literal(buf, &v.to_string(), "timestamp")?,
#[cfg(feature = "jiff")]
PostgresValue::JiffTimestamp(v) => write_cast_literal(buf, &v.to_string(), "timestamptz")?,
#[cfg(feature = "cidr")]
PostgresValue::Inet(v) => write_cast_literal(buf, &v.to_string(), "inet")?,
#[cfg(feature = "cidr")]
PostgresValue::Cidr(v) => write_cast_literal(buf, &v.to_string(), "cidr")?,
#[cfg(feature = "cidr")]
PostgresValue::MacAddr(bytes) => {
let text = bytes
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<Vec<_>>()
.join(":");
write_cast_literal(buf, &text, "macaddr")?;
}
#[cfg(feature = "cidr")]
PostgresValue::MacAddr8(bytes) => {
let text = bytes
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<Vec<_>>()
.join(":");
write_cast_literal(buf, &text, "macaddr8")?;
}
#[cfg(feature = "geo-types")]
PostgresValue::Point(v) => {
let text = format!("({:?},{:?})", v.x(), v.y());
write_cast_literal(buf, &text, "point")?;
}
#[cfg(feature = "geo-types")]
PostgresValue::LineString(v) => {
let points = v
.coords()
.map(|coord| format!("({:?},{:?})", coord.x, coord.y))
.collect::<Vec<_>>()
.join(",");
write_cast_literal(buf, &format!("[{points}]"), "path")?;
}
#[cfg(feature = "geo-types")]
PostgresValue::Rect(v) => {
let (min, max) = (v.min(), v.max());
let text = format!("(({:?},{:?}),({:?},{:?}))", max.x, max.y, min.x, min.y);
write_cast_literal(buf, &text, "box")?;
}
#[cfg(feature = "bit-vec")]
PostgresValue::BitVec(v) => {
buf.push_str("B'");
for bit in v.iter() {
buf.push(if bit { '1' } else { '0' });
}
buf.push('\'');
}
PostgresValue::Array(values) if values.is_empty() => buf.push_str("'{}'"),
PostgresValue::Array(values) => {
buf.push_str("ARRAY[");
for (index, value) in values.iter().enumerate() {
if index > 0 {
buf.push_str(", ");
}
write_postgres_literal(value, buf)?;
}
buf.push(']');
}
}
Some(())
}
impl<'a> From<PostgresValue<'a>> for SQL<'a, PostgresValue<'a>> {
fn from(value: PostgresValue<'a>) -> Self {
SQL::param(value)
}
}
impl<'a> From<PostgresValue<'a>> for Cow<'a, PostgresValue<'a>> {
fn from(value: PostgresValue<'a>) -> Self {
Cow::Owned(value)
}
}
impl<'a> From<&'a PostgresValue<'a>> for Cow<'a, PostgresValue<'a>> {
fn from(value: &'a PostgresValue<'a>) -> Self {
Cow::Borrowed(value)
}
}