mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
feat(core): expand and document Row
This commit is contained in:
parent
ecb4bd0281
commit
2c66bcf19f
@ -31,10 +31,11 @@ pub mod encode;
|
||||
mod error;
|
||||
mod execute;
|
||||
mod executor;
|
||||
mod isolation_level;
|
||||
mod from_row;
|
||||
mod isolation_level;
|
||||
mod options;
|
||||
mod query_result;
|
||||
mod raw_value;
|
||||
pub mod row;
|
||||
mod runtime;
|
||||
mod r#type;
|
||||
@ -64,14 +65,15 @@ pub use connection::Connection;
|
||||
pub use database::Database;
|
||||
pub use decode::Decode;
|
||||
pub use encode::Encode;
|
||||
pub use from_row::FromRow;
|
||||
pub use error::{DatabaseError, Error, Result};
|
||||
pub use execute::Execute;
|
||||
pub use executor::Executor;
|
||||
pub use from_row::FromRow;
|
||||
pub use isolation_level::IsolationLevel;
|
||||
pub use options::ConnectOptions;
|
||||
pub use query_result::QueryResult;
|
||||
pub use r#type::{Type, TypeDecode, TypeEncode, TypeDecodeOwned};
|
||||
pub use r#type::{Type, TypeDecode, TypeDecodeOwned, TypeEncode};
|
||||
pub use raw_value::RawValue;
|
||||
pub use row::{ColumnIndex, Row};
|
||||
#[cfg(feature = "actix")]
|
||||
pub use runtime::Actix;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::any;
|
||||
|
||||
use crate::database::HasRawValue;
|
||||
use crate::{Database, Decode};
|
||||
use crate::{decode, Database, Error, RawValue, Result, TypeDecode, TypeInfo};
|
||||
|
||||
/// A single row from a result set generated from the database.
|
||||
pub trait Row: 'static + Send + Sync {
|
||||
@ -20,82 +22,182 @@ pub trait Row: 'static + Send + Sync {
|
||||
fn columns(&self) -> &[<Self::Database as Database>::Column];
|
||||
|
||||
/// Returns the column at the index, if available.
|
||||
fn column<I: ColumnIndex<Self>>(&self, index: I) -> &<Self::Database as Database>::Column;
|
||||
fn column<I: ColumnIndex<Self>>(&self, index: I) -> &<Self::Database as Database>::Column {
|
||||
self.try_column(index).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the column at the index, if available.
|
||||
fn try_column<I: ColumnIndex<Self>>(
|
||||
&self,
|
||||
index: I,
|
||||
) -> crate::Result<&<Self::Database as Database>::Column>;
|
||||
) -> Result<&<Self::Database as Database>::Column>;
|
||||
|
||||
/// Returns the column name, given the index of the column.
|
||||
fn column_name_of(&self, index: usize) -> &str;
|
||||
|
||||
/// Returns the column name, given the index of the column.
|
||||
fn try_column_name_of(&self, index: usize) -> crate::Result<&str>;
|
||||
fn column_name(&self, index: usize) -> Option<&str>;
|
||||
|
||||
/// Returns the column index, given the name of the column.
|
||||
fn index_of(&self, name: &str) -> usize;
|
||||
fn column_index(&self, name: &str) -> Option<usize>;
|
||||
|
||||
/// Returns the column index, given the name of the column.
|
||||
fn try_index_of(&self, name: &str) -> crate::Result<usize>;
|
||||
|
||||
/// Returns the decoded value at the index.
|
||||
/// Returns the value for a column.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic for any errors documented in [`try_get`].
|
||||
///
|
||||
fn get<'r, T, I>(&'r self, index: I) -> T
|
||||
where
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>;
|
||||
T: TypeDecode<'r, Self::Database>,
|
||||
{
|
||||
self.try_get(index).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the decoded value at the index.
|
||||
fn try_get<'r, T, I>(&'r self, index: I) -> crate::Result<T>
|
||||
/// Returns the _unchecked_ value for a column.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic for any errors documented in [`try_get_unchecked`].
|
||||
///
|
||||
fn get_unchecked<'r, T, I>(&'r self, index: I) -> T
|
||||
where
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>;
|
||||
T: TypeDecode<'r, Self::Database>,
|
||||
{
|
||||
self.try_get_unchecked(index).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the raw representation of the value at the index.
|
||||
/// Returns the value for a column.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - Will return `Error::ColumnNotFound` or `Error::ColumnIndexOutOfBounds` if the
|
||||
/// there is no column with the given name or index.
|
||||
///
|
||||
/// - Will return `Error::ColumnDecode` if there was an issue decoding
|
||||
/// the value. Common reasons include:
|
||||
///
|
||||
/// - The SQL value is `NULL` and `T` is not `Option<T>`
|
||||
///
|
||||
/// - The SQL value cannot be represented as a `T`. Truncation or
|
||||
/// loss of precision are considered errors.
|
||||
///
|
||||
/// - The SQL type is not [`compatible`][Type::compatible] with
|
||||
/// the Rust type.
|
||||
///
|
||||
///
|
||||
fn try_get<'r, T, I>(&'r self, index: I) -> Result<T>
|
||||
where
|
||||
I: ColumnIndex<Self>,
|
||||
T: TypeDecode<'r, Self::Database>,
|
||||
{
|
||||
let value = self.try_get_raw(&index)?;
|
||||
|
||||
let res = if !T::compatible(value.type_info()) {
|
||||
Err(decode::Error::TypeNotCompatible {
|
||||
rust_type_name: any::type_name::<T>(),
|
||||
sql_type_name: value.type_info().name(),
|
||||
})
|
||||
} else {
|
||||
T::decode(value)
|
||||
};
|
||||
|
||||
res.map_err(|err| Error::column_decode(self.column(&index), err))
|
||||
}
|
||||
|
||||
/// Returns the _unchecked_ value for a column.
|
||||
///
|
||||
/// In this case, _unchecked_ does not mean `unsafe`. Unlike [`try_get`],
|
||||
/// this method will not check that the source SQL value is compatible
|
||||
/// with the target Rust type. This may result in *weird* behavior (reading
|
||||
/// a `bool` from bytes of a `TEXT` column) but it is not `unsafe` and
|
||||
/// cannot cause undefined behavior.
|
||||
///
|
||||
/// The type-compatible checks in SQLx will likely never be perfect. This
|
||||
/// method exists to work-around them in a controlled scenario.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - Will return `Error::ColumnNotFound` or `Error::ColumnIndexOutOfBounds` if the
|
||||
/// there is no column with the given name or index.
|
||||
///
|
||||
/// - Will return `Error::ColumnDecode` if there was an issue decoding
|
||||
/// the value. Common reasons include:
|
||||
///
|
||||
/// - The SQL value is `NULL` and `T` is not `Option<T>`
|
||||
///
|
||||
/// - The SQL value cannot be represented as a `T`. Truncation or
|
||||
/// loss of precision are considered errors.
|
||||
///
|
||||
fn try_get_unchecked<'r, T, I>(&'r self, index: I) -> Result<T>
|
||||
where
|
||||
I: ColumnIndex<Self>,
|
||||
T: TypeDecode<'r, Self::Database>,
|
||||
{
|
||||
let value = self.try_get_raw(&index)?;
|
||||
|
||||
T::decode(value).map_err(|err| Error::column_decode(self.column(&index), err))
|
||||
}
|
||||
|
||||
/// Returns the raw representation of the value for a column.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic for any errors documented in [`try_get_raw`].
|
||||
///
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn get_raw<'r, I: ColumnIndex<Self>>(
|
||||
&'r self,
|
||||
index: I,
|
||||
) -> <Self::Database as HasRawValue<'r>>::RawValue;
|
||||
) -> <Self::Database as HasRawValue<'r>>::RawValue {
|
||||
self.try_get_raw(index).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the raw representation of the value at the index.
|
||||
/// Returns the raw representation of the value for a column.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - Will return `Error::ColumnNotFound` or `Error::ColumnIndexOutOfBounds` if the
|
||||
/// there is no column with the given name or index.
|
||||
///
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn try_get_raw<'r, I: ColumnIndex<Self>>(
|
||||
&'r self,
|
||||
index: I,
|
||||
) -> crate::Result<<Self::Database as HasRawValue<'r>>::RawValue>;
|
||||
) -> Result<<Self::Database as HasRawValue<'r>>::RawValue>;
|
||||
}
|
||||
|
||||
/// A helper trait used for indexing into a [`Row`].
|
||||
pub trait ColumnIndex<R: Row + ?Sized> {
|
||||
/// Returns the index of the column at this index, if present.
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn get<'r>(&self, row: &'r R) -> crate::Result<usize>;
|
||||
fn get<'r>(&self, row: &'r R) -> Result<usize>;
|
||||
}
|
||||
|
||||
// access by index
|
||||
impl<R: Row> ColumnIndex<R> for usize {
|
||||
impl<R: ?Sized + Row> ColumnIndex<R> for usize {
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn get<'r>(&self, _row: &'r R) -> crate::Result<usize> {
|
||||
// note: the "index out of bounds" error will be surfaced
|
||||
// by [try_get]
|
||||
fn get<'r>(&self, row: &'r R) -> Result<usize> {
|
||||
if *self >= row.len() {
|
||||
return Err(Error::ColumnIndexOutOfBounds { len: row.len(), index: *self });
|
||||
}
|
||||
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
|
||||
// access by name
|
||||
impl<R: Row> ColumnIndex<R> for &'_ str {
|
||||
impl<R: ?Sized + Row> ColumnIndex<R> for &'_ str {
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn get<'r>(&self, row: &'r R) -> crate::Result<usize> {
|
||||
row.try_index_of(self)
|
||||
fn get<'r>(&self, row: &'r R) -> Result<usize> {
|
||||
row.column_index(self)
|
||||
.ok_or_else(|| Error::ColumnNotFound { name: self.to_string().into_boxed_str() })
|
||||
}
|
||||
}
|
||||
|
||||
// access by reference
|
||||
impl<R: Row, I: ColumnIndex<R>> ColumnIndex<R> for &'_ I {
|
||||
impl<R: ?Sized + Row, I: ColumnIndex<R>> ColumnIndex<R> for &'_ I {
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn get<'r>(&self, row: &'r R) -> crate::Result<usize> {
|
||||
fn get<'r>(&self, row: &'r R) -> Result<usize> {
|
||||
(*self).get(row)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user