#![cfg_attr(
not(any(feature = "mariadb", feature = "postgres", feature = "sqlite")),
allow(unused_imports, unused_variables)
)]
#[cfg(any(feature = "mariadb", feature = "postgres", feature = "sqlite"))]
use crate::ColumnType;
#[cfg(any(feature = "mariadb", feature = "postgres", feature = "sqlite"))]
use crate::Value;
use crate::{
Column, ColumnIndex, Date, DateTime, DateTimeTz, Error, FromColumnRef, FromRow, FromRowRef,
Result, Row, RowStream, Time,
};
pub struct Rows<'a> {
columns: Vec<Column>,
inner: RowsInner<'a>,
}
impl RowStream for Rows<'_> {
fn columns(&self) -> &[Column] {
self.columns()
}
async fn next(&mut self) -> Result<Option<RowRef<'_>>> {
Rows::next(self).await
}
}
enum RowsInner<'a> {
#[cfg(feature = "mariadb")]
Mysql(quex_driver::mysql::ResultSet),
#[cfg(feature = "postgres")]
Postgres(quex_driver::postgres::ResultSet),
#[cfg(feature = "sqlite")]
Sqlite(quex_driver::sqlite::ResultSet),
_Marker(std::marker::PhantomData<&'a ()>),
}
impl<'a> Rows<'a> {
pub(crate) fn into_lifetime<'b>(self) -> Rows<'b> {
let inner = match self.inner {
#[cfg(feature = "mariadb")]
RowsInner::Mysql(rows) => RowsInner::Mysql(rows),
#[cfg(feature = "postgres")]
RowsInner::Postgres(rows) => RowsInner::Postgres(rows),
#[cfg(feature = "sqlite")]
RowsInner::Sqlite(rows) => RowsInner::Sqlite(rows),
RowsInner::_Marker(_) => RowsInner::_Marker(std::marker::PhantomData),
};
Rows {
columns: self.columns,
inner,
}
}
#[cfg(feature = "mariadb")]
pub(crate) fn mysql(rows: quex_driver::mysql::ResultSet) -> Self {
let columns = rows
.columns()
.iter()
.map(|column| Column {
name: column.name.clone(),
nullable: column.nullable,
kind: ColumnType::Mysql(column.column_type),
})
.collect();
Self {
columns,
inner: RowsInner::Mysql(rows),
}
}
#[cfg(feature = "postgres")]
pub(crate) fn postgres(rows: quex_driver::postgres::ResultSet) -> Self {
let columns = rows
.columns()
.iter()
.map(|column| Column {
name: column.name.clone(),
nullable: column.nullable,
kind: ColumnType::Postgres(column.type_oid),
})
.collect();
Self {
columns,
inner: RowsInner::Postgres(rows),
}
}
#[cfg(feature = "sqlite")]
pub(crate) fn sqlite(rows: quex_driver::sqlite::ResultSet) -> Self {
let columns = rows
.columns()
.iter()
.map(|column| Column {
name: column.name.clone(),
nullable: column.nullable,
kind: ColumnType::Sqlite(column.declared_type.clone()),
})
.collect();
Self {
columns,
inner: RowsInner::Sqlite(rows),
}
}
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub async fn next(&mut self) -> Result<Option<RowRef<'_>>> {
match &mut self.inner {
#[cfg(feature = "mariadb")]
RowsInner::Mysql(rows) => Ok(rows.next().await?.map(|row| RowRef {
columns: &self.columns,
inner: RowRefInner::Mysql(row),
})),
#[cfg(feature = "postgres")]
RowsInner::Postgres(rows) => Ok(rows.next().await?.map(|row| RowRef {
columns: &self.columns,
inner: RowRefInner::Postgres(row),
})),
#[cfg(feature = "sqlite")]
RowsInner::Sqlite(rows) => Ok(rows.next().await?.map(|row| RowRef {
columns: &self.columns,
inner: RowRefInner::Sqlite(row),
})),
RowsInner::_Marker(_) => Ok(None),
}
}
pub async fn collect_owned(mut self) -> Result<Vec<Row>> {
let mut rows = Vec::new();
while let Some(row) = self.next().await? {
rows.push(row.to_owned()?);
}
Ok(rows)
}
pub async fn next_decoded<T>(&mut self) -> Result<Option<T>>
where
T: FromRow,
{
match self.next().await? {
Some(row) => Ok(Some(row.decode()?)),
None => Ok(None),
}
}
pub async fn collect_decoded<T>(mut self) -> Result<Vec<T>>
where
T: FromRow,
{
let mut rows = Vec::new();
while let Some(row) = self.next().await? {
rows.push(row.decode()?);
}
Ok(rows)
}
}
pub struct RowRef<'a> {
columns: &'a [Column],
inner: RowRefInner<'a>,
}
enum RowRefInner<'a> {
#[cfg(feature = "mariadb")]
Mysql(quex_driver::mysql::RowRef<'a>),
#[cfg(feature = "postgres")]
Postgres(quex_driver::postgres::RowRef<'a>),
#[cfg(feature = "sqlite")]
Sqlite(quex_driver::sqlite::RowRef<'a>),
_Marker(std::marker::PhantomData<&'a ()>),
}
impl<'a> RowRef<'a> {
pub fn get<T>(&'a self, index: impl ColumnIndex) -> Result<T>
where
T: FromColumnRef<'a>,
{
let idx = index.index(self.columns)?;
T::from_column_ref(self, idx)
}
pub fn columns(&self) -> &[Column] {
self.columns
}
pub fn decode<T>(&self) -> Result<T>
where
T: FromRow,
{
T::from_row_ref(self)
}
pub fn decode_ref<T>(&'a self) -> Result<T>
where
T: FromRowRef<'a>,
{
T::from_row_ref(self)
}
pub fn is_null(&self, index: impl ColumnIndex) -> Result<bool> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row.is_null(idx).map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.is_null(idx).map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => match sqlite_owned_value(row, idx)? {
Value::Null => Ok(true),
_ => Ok(false),
},
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub fn get_i64(&self, index: impl ColumnIndex) -> Result<i64> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row.get_i64(idx).map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.get_i64(idx).map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => row.get_i64(idx).map_err(Into::into),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub fn get_f64(&self, index: impl ColumnIndex) -> Result<f64> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row.get_f64(idx).map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.get_f64(idx).map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => row.get_f64(idx).map_err(Into::into),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub fn get_u64(&self, index: impl ColumnIndex) -> Result<u64> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row.get_u64(idx).map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.get_u64(idx).map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => match row.get_i64(idx).map_err(Error::from)? {
value if value >= 0 => Ok(value as u64),
_ => Err(Error::Unsupported(
"negative SQLite integer cannot be represented as u64".into(),
)),
},
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub fn get_str(&self, index: impl ColumnIndex) -> Result<&str> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row.get_str(idx).map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.get_str(idx).map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => row.get_str(idx).map_err(Into::into),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub fn get_bytes(&self, index: impl ColumnIndex) -> Result<&[u8]> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row.get_bytes(idx).map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.get_bytes(idx).map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => row.get_bytes(idx).map_err(Into::into),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub(crate) fn get_date(&self, index: impl ColumnIndex) -> Result<Date> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row
.get_date(idx)
.map(|value| Date {
year: value.year,
month: value.month,
day: value.day,
})
.map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row
.get_date(idx)
.map(|value| Date {
year: value.year,
month: value.month,
day: value.day,
})
.map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => super::data::parse_date(row.get_str(idx)?)
.ok_or_else(|| Error::Unsupported("column is not date".into())),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub(crate) fn get_time_value(&self, index: impl ColumnIndex) -> Result<Time> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row
.get_time_value(idx)
.map(|value| Time {
hour: value.hour,
minute: value.minute,
second: value.second,
microsecond: value.microsecond,
})
.map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row
.get_time_value(idx)
.map(|value| Time {
hour: value.hour,
minute: value.minute,
second: value.second,
microsecond: value.microsecond,
})
.map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => super::data::parse_time(row.get_str(idx)?)
.ok_or_else(|| Error::Unsupported("column is not time".into())),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub(crate) fn get_datetime(&self, index: impl ColumnIndex) -> Result<DateTime> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => row
.get_datetime(idx)
.map(|value| DateTime {
date: Date {
year: value.date.year,
month: value.date.month,
day: value.date.day,
},
time: Time {
hour: value.time.hour,
minute: value.time.minute,
second: value.time.second,
microsecond: value.time.microsecond,
},
})
.map_err(Into::into),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row
.get_datetime(idx)
.map(|value| DateTime {
date: Date {
year: value.date.year,
month: value.date.month,
day: value.date.day,
},
time: Time {
hour: value.time.hour,
minute: value.time.minute,
second: value.time.second,
microsecond: value.time.microsecond,
},
})
.map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => super::data::parse_datetime(row.get_str(idx)?)
.ok_or_else(|| Error::Unsupported("column is not datetime".into())),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub(crate) fn get_datetimetz(&self, index: impl ColumnIndex) -> Result<DateTimeTz> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => {
super::data::parse_datetime_tz(row.get_str(idx).map_err(Error::from)?)
.ok_or_else(|| Error::Unsupported("column is not datetime with offset".into()))
}
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row
.get_datetimetz(idx)
.map(|value| DateTimeTz {
datetime: DateTime {
date: Date {
year: value.datetime.date.year,
month: value.datetime.date.month,
day: value.datetime.date.day,
},
time: Time {
hour: value.datetime.time.hour,
minute: value.datetime.time.minute,
second: value.datetime.time.second,
microsecond: value.datetime.time.microsecond,
},
},
offset_seconds: value.offset_seconds,
})
.map_err(Into::into),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => super::data::parse_datetime_tz(row.get_str(idx)?)
.ok_or_else(|| Error::Unsupported("column is not datetime with offset".into())),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
#[cfg(feature = "uuid")]
pub(crate) fn get_uuid_bytes(&self, index: impl ColumnIndex) -> Result<[u8; 16]> {
let idx = index.index(self.columns)?;
match &self.inner {
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => row.get_uuid(idx).map_err(Into::into),
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => {
let bytes = row.get_bytes(idx).map_err(Error::from)?;
bytes
.try_into()
.map_err(|_| Error::Unsupported("column is not a 16-byte uuid".into()))
}
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => {
let bytes = row.get_bytes(idx).map_err(Error::from)?;
bytes
.try_into()
.map_err(|_| Error::Unsupported("column is not a 16-byte uuid".into()))
}
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
pub fn to_owned(&self) -> Result<Row> {
match &self.inner {
#[cfg(feature = "mariadb")]
RowRefInner::Mysql(row) => Ok(mysql_owned_row(row)?),
#[cfg(feature = "postgres")]
RowRefInner::Postgres(row) => Ok(postgres_owned_row(row)?),
#[cfg(feature = "sqlite")]
RowRefInner::Sqlite(row) => Ok(sqlite_owned_row(row)?),
RowRefInner::_Marker(_) => unreachable!("disabled backend placeholder"),
}
}
}
pub(crate) async fn decode_one_rows<R, T>(rows: &mut R) -> Result<T>
where
R: RowStream + ?Sized,
T: FromRow,
{
rows.next()
.await?
.map(|row| row.decode())
.transpose()?
.ok_or_else(|| Error::Unsupported("expected at least one row, got zero".into()))
}
pub(crate) async fn decode_optional_rows<R, T>(rows: &mut R) -> Result<Option<T>>
where
R: RowStream + ?Sized,
T: FromRow,
{
let first = rows.next().await?.map(|row| row.decode()).transpose()?;
if first.is_none() {
return Ok(None);
}
Ok(first)
}
pub(crate) async fn collect_decoded_rows<R, T>(mut rows: R) -> Result<Vec<T>>
where
R: RowStream,
T: FromRow,
{
let mut decoded = Vec::new();
while let Some(row) = rows.next().await? {
decoded.push(row.decode()?);
}
Ok(decoded)
}
#[cfg(feature = "mariadb")]
fn mysql_owned_row(row: &quex_driver::mysql::RowRef<'_>) -> Result<Row> {
let owned = row.to_owned()?;
Ok(Row {
columns: owned
.columns
.into_iter()
.map(|column| Column {
name: column.name,
nullable: column.nullable,
kind: ColumnType::Mysql(column.column_type),
})
.collect(),
values: owned.values.into_iter().map(mysql_value).collect(),
})
}
#[cfg(feature = "postgres")]
fn postgres_owned_row(row: &quex_driver::postgres::RowRef<'_>) -> Result<Row> {
let owned = row.to_owned()?;
Ok(Row {
columns: owned
.columns
.into_iter()
.map(|column| Column {
name: column.name,
nullable: column.nullable,
kind: ColumnType::Postgres(column.type_oid),
})
.collect(),
values: owned.values.into_iter().map(postgres_value).collect(),
})
}
#[cfg(feature = "sqlite")]
fn sqlite_owned_row(row: &quex_driver::sqlite::RowRef<'_>) -> Result<Row> {
let owned = row.to_owned();
Ok(Row {
columns: owned
.columns
.into_iter()
.map(|column| Column {
name: column.name,
nullable: column.nullable,
kind: ColumnType::Sqlite(column.declared_type),
})
.collect(),
values: owned.values.into_iter().map(sqlite_value).collect(),
})
}
#[cfg(feature = "sqlite")]
fn sqlite_owned_value(row: &quex_driver::sqlite::RowRef<'_>, index: usize) -> Result<Value> {
Ok(sqlite_value(row.to_owned().values[index].clone()))
}
#[cfg(feature = "mariadb")]
fn mysql_value(value: quex_driver::mysql::Value) -> Value {
match value {
quex_driver::mysql::Value::Null => Value::Null,
quex_driver::mysql::Value::I64(v) => Value::I64(v),
quex_driver::mysql::Value::U64(v) => Value::U64(v),
quex_driver::mysql::Value::F64(v) => Value::F64(v),
quex_driver::mysql::Value::Date(v) => Value::Date(crate::Date {
year: v.year,
month: v.month,
day: v.day,
}),
quex_driver::mysql::Value::Time(v) => Value::Time(crate::Time {
hour: v.hour,
minute: v.minute,
second: v.second,
microsecond: v.microsecond,
}),
quex_driver::mysql::Value::DateTime(v) => Value::DateTime(crate::DateTime {
date: crate::Date {
year: v.date.year,
month: v.date.month,
day: v.date.day,
},
time: crate::Time {
hour: v.time.hour,
minute: v.time.minute,
second: v.time.second,
microsecond: v.time.microsecond,
},
}),
quex_driver::mysql::Value::DateTimeTz(v) => Value::DateTimeTz(crate::DateTimeTz {
datetime: crate::DateTime {
date: crate::Date {
year: v.datetime.date.year,
month: v.datetime.date.month,
day: v.datetime.date.day,
},
time: crate::Time {
hour: v.datetime.time.hour,
minute: v.datetime.time.minute,
second: v.datetime.time.second,
microsecond: v.datetime.time.microsecond,
},
},
offset_seconds: v.offset_seconds,
}),
quex_driver::mysql::Value::Uuid(v) => Value::Uuid(v),
quex_driver::mysql::Value::Bytes(v) => Value::Bytes(v),
quex_driver::mysql::Value::String(v) => Value::String(v),
}
}
#[cfg(feature = "postgres")]
fn postgres_value(value: quex_driver::postgres::Value) -> Value {
match value {
quex_driver::postgres::Value::Null => Value::Null,
quex_driver::postgres::Value::I64(v) => Value::I64(v),
quex_driver::postgres::Value::U64(v) => Value::U64(v),
quex_driver::postgres::Value::F64(v) => Value::F64(v),
quex_driver::postgres::Value::Date(v) => Value::Date(crate::Date {
year: v.year,
month: v.month,
day: v.day,
}),
quex_driver::postgres::Value::Time(v) => Value::Time(crate::Time {
hour: v.hour,
minute: v.minute,
second: v.second,
microsecond: v.microsecond,
}),
quex_driver::postgres::Value::DateTime(v) => Value::DateTime(crate::DateTime {
date: crate::Date {
year: v.date.year,
month: v.date.month,
day: v.date.day,
},
time: crate::Time {
hour: v.time.hour,
minute: v.time.minute,
second: v.time.second,
microsecond: v.time.microsecond,
},
}),
quex_driver::postgres::Value::DateTimeTz(v) => Value::DateTimeTz(crate::DateTimeTz {
datetime: crate::DateTime {
date: crate::Date {
year: v.datetime.date.year,
month: v.datetime.date.month,
day: v.datetime.date.day,
},
time: crate::Time {
hour: v.datetime.time.hour,
minute: v.datetime.time.minute,
second: v.datetime.time.second,
microsecond: v.datetime.time.microsecond,
},
},
offset_seconds: v.offset_seconds,
}),
quex_driver::postgres::Value::Uuid(v) => Value::Uuid(v),
quex_driver::postgres::Value::Bytes(v) => Value::Bytes(v),
quex_driver::postgres::Value::String(v) => Value::String(v),
}
}
#[cfg(feature = "sqlite")]
fn sqlite_value(value: quex_driver::sqlite::Value) -> Value {
match value {
quex_driver::sqlite::Value::Null => Value::Null,
quex_driver::sqlite::Value::I64(v) => Value::I64(v),
quex_driver::sqlite::Value::F64(v) => Value::F64(v),
quex_driver::sqlite::Value::Bytes(v) => Value::Bytes(v),
quex_driver::sqlite::Value::String(v) => Value::String(v),
}
}