1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use core::ops::Range;
use xitca_io::bytes::Bytes;
use xitca_unsafe_collection::bytes::BytesStr;
use super::types::{FromSql, Type, WasNull};
pub type FromSqlError = Box<dyn core::error::Error + Sync + Send>;
/// extension trait for [FromSql]
///
/// instead of working with explicit reference as `&[u8]` for parsing raw sql bytes this extension
/// offers cheap copy/slicing of [Bytes] type for reference counting based zero copy parsing.
///
/// # Examples
/// ```rust
/// # use xitca_postgres::row::Row;
/// use xitca_unsafe_collection::bytes::BytesStr; // a reference counted &str type has FromSqlExt impl
/// fn parse_row(row: Row<'_>) {
/// let s = row.get_zc::<BytesStr>(0); // parse index0 column with zero copy.
/// println!("{}", s.as_str());
/// }
/// ```
pub trait FromSqlExt<'a>: Sized {
/// byte parser of non null Postgres type
/// [Type] represents the Postgres type hint which Self must be matching.
/// [Bytes] represents the reference of raw bytes of row data Self belongs to.
/// [Range] represents the start and end indexing into the raw data for correctly parsing Self.
fn from_sql_ext(ty: &Type, col: (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError>;
/// Creates a new value of this type from a `NULL` SQL value.
///
/// The caller of this method is responsible for ensuring that this type
/// is compatible with the Postgres `Type`.
///
/// The default implementation returns `Err(Box::new(WasNull))`.
#[allow(unused_variables)]
#[inline]
fn from_sql_null_ext(ty: &Type) -> Result<Self, FromSqlError> {
Err(Box::new(WasNull))
}
/// A convenience function that delegates to `from_sql` and `from_sql_null`.
fn from_sql_nullable_ext(ty: &Type, col: (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
match col.0.is_empty() {
false => Self::from_sql_ext(ty, col),
true => Self::from_sql_null_ext(ty),
}
}
/// Determines if a value of this type can be created from the specified Postgres `Type`.
fn accepts_ext(ty: &Type) -> bool;
}
impl<'a> FromSqlExt<'a> for BytesStr {
fn from_sql_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
// copy/paste from postgres-protocol dependency.
fn adjust_start(name: &str, range_start: usize, buf: &Bytes) -> Result<usize, FromSqlError> {
if buf[range_start] == 1u8 {
Ok(range_start + 1)
} else {
Err(format!("only {name} version 1 supported").into())
}
}
let start = match ty.name() {
"ltree" => adjust_start("ltree", range.start, buf)?,
"lquery" => adjust_start("lquery", range.start, buf)?,
"ltxtquery" => adjust_start("ltxtquery", range.start, buf)?,
_ => range.start,
};
BytesStr::try_from(buf.slice(start..range.end)).map_err(Into::into)
}
#[inline]
fn accepts_ext(ty: &Type) -> bool {
<&str as FromSql>::accepts(ty)
}
}
impl<'a> FromSqlExt<'a> for Bytes {
#[inline]
fn from_sql_ext(_: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
Ok(buf.slice(range.start..range.end))
}
#[inline]
fn accepts_ext(ty: &Type) -> bool {
<&[u8] as FromSql>::accepts(ty)
}
}
impl<'a, T> FromSqlExt<'a> for Option<T>
where
T: FromSqlExt<'a>,
{
#[inline]
fn from_sql_ext(ty: &Type, raw: (&Range<usize>, &'a Bytes)) -> Result<Option<T>, FromSqlError> {
<T as FromSqlExt>::from_sql_ext(ty, raw).map(Some)
}
#[inline]
fn from_sql_null_ext(_: &Type) -> Result<Option<T>, FromSqlError> {
Ok(None)
}
#[inline]
fn accepts_ext(ty: &Type) -> bool {
<T as FromSqlExt>::accepts_ext(ty)
}
}