use crate::{
ColumnDef, Context, Dataset, Driver, DynQuery, Error, Executor, Expression, Query,
QueryBuilder, RawQuery, Result, Row, RowValues, RowsAffected, TableRef, future::Either,
stream::Stream, truncate_long, writer::SqlWriter,
};
use futures::{FutureExt, StreamExt};
use log::Level;
use std::{
future::{self, Future},
pin::pin,
sync::Arc,
};
pub trait AsEntity {
type Entity: Entity;
fn as_entity(&self) -> &Self::Entity;
}
pub trait Entity: AsEntity + Expression {
type PrimaryKey<'a>
where
Self: 'a;
fn table() -> &'static TableRef;
fn columns() -> &'static [ColumnDef];
fn primary_key_def() -> &'static [&'static ColumnDef];
fn primary_key(&self) -> Self::PrimaryKey<'_>;
fn primary_key_expr(&self) -> impl Expression;
fn unique_defs()
-> impl ExactSizeIterator<Item = impl ExactSizeIterator<Item = &'static ColumnDef>>;
fn row_values(&self) -> RowValues;
fn row(&self) -> Row {
Row {
labels: Self::columns()
.into_iter()
.map(|v| v.name().to_string())
.collect::<Arc<[String]>>(),
values: self.row_values(),
}
}
fn from_row(row: Row) -> Result<Self>
where
Self: Sized;
fn create_table(
executor: &mut impl Executor,
if_not_exists: bool,
create_schema: bool,
) -> impl Future<Output = Result<()>> + Send
where
Self: Sized,
{
async move {
let mut query = DynQuery::with_capacity(2048);
let writer = executor.driver().sql_writer();
if create_schema && !Self::table().schema.is_empty() {
writer.write_create_schema::<Self>(&mut query, true);
}
if !executor.accepts_multiple_statements() && !query.is_empty() {
let mut q = query.into_query(executor.driver());
executor.execute(&mut q).await?;
query = q.into();
query.clear();
}
writer.write_create_table::<Self>(&mut query, if_not_exists);
executor.execute(query).await.map(|_| ())
}
}
fn drop_table(
executor: &mut impl Executor,
if_exists: bool,
drop_schema: bool,
) -> impl Future<Output = Result<()>> + Send
where
Self: Sized,
{
async move {
let mut query = DynQuery::with_capacity(256);
let writer = executor.driver().sql_writer();
writer.write_drop_table::<Self>(&mut query, if_exists);
if drop_schema && !Self::table().schema.is_empty() {
if !executor.accepts_multiple_statements() {
let mut q = query.into_query(executor.driver());
executor.execute(&mut q).await?;
query = q.into();
query.clear();
}
writer.write_drop_schema::<Self>(&mut query, true);
}
executor.execute(query).await.map(|_| ())
}
}
fn insert_one(
executor: &mut impl Executor,
entity: impl AsEntity,
) -> impl Future<Output = Result<RowsAffected>> + Send {
let mut query = DynQuery::with_capacity(128);
executor
.driver()
.sql_writer()
.write_insert(&mut query, [entity], false);
executor.execute(query)
}
fn insert_many<It>(
executor: &mut impl Executor,
items: It,
) -> impl Future<Output = Result<RowsAffected>> + Send
where
Self: Sized,
It: IntoIterator + Send,
It::IntoIter: Send,
It::Item: AsEntity,
{
executor.append(items)
}
fn prepare_find<Exec: Executor>(
executor: &mut Exec,
condition: impl Expression,
limit: Option<u32>,
) -> impl Future<Output = Result<Query<Exec::Driver>>> + Send {
let builder = QueryBuilder::new()
.select(Self::columns())
.from(Self::table())
.where_expr(condition)
.limit(limit);
let writer = executor.driver().sql_writer();
let mut query = DynQuery::default();
writer.write_select(&mut query, &builder);
async {
if let DynQuery::Raw(RawQuery(sql)) = query {
executor.prepare(sql).await
} else {
Ok(query.into())
}
}
}
fn find_one(
executor: &mut impl Executor,
condition: impl Expression,
) -> impl Future<Output = Result<Option<Self>>> + Send
where
Self: Sized,
{
let stream = Self::find_many(executor, condition, Some(1));
async move { pin!(stream).into_future().map(|(v, _)| v).await.transpose() }
}
fn find_many(
executor: &mut impl Executor,
condition: impl Expression,
limit: Option<u32>,
) -> impl Stream<Item = Result<Self>> + Send
where
Self: Sized,
{
let builder = QueryBuilder::new()
.select(Self::columns())
.from(Self::table())
.where_expr(condition)
.limit(limit);
executor
.fetch(builder.build(&executor.driver()))
.map(|result| result.and_then(Self::from_row))
}
fn delete_many(
executor: &mut impl Executor,
condition: impl Expression,
) -> impl Future<Output = Result<RowsAffected>> + Send
where
Self: Sized,
{
let mut query = DynQuery::with_capacity(128);
executor
.driver()
.sql_writer()
.write_delete::<Self>(&mut query, condition);
executor.execute(query)
}
fn save(&self, executor: &mut impl Executor) -> impl Future<Output = Result<()>> + Send
where
Self: Sized,
for<'s> &'s Self: AsEntity,
{
if Self::primary_key_def().is_empty() {
let error = Error::msg(
"Cannot save an entity without a primary key, it would always result in an insert",
);
log::error!("{:#}", error);
return Either::Left(future::ready(Err(error)));
}
let mut query = DynQuery::with_capacity(512);
executor
.driver()
.sql_writer()
.write_insert(&mut query, [self], true);
let sql = query.as_str();
let context = format!("While saving using the query {}", truncate_long!(sql));
Either::Right(executor.execute(query).map(|mut v| {
if let Ok(result) = v
&& let Some(affected) = result.rows_affected
&& affected > 2
{
v = Err(Error::msg(format!(
"The driver returned affected rows: {affected} (expected <= 2)"
)));
}
match v {
Ok(_) => Ok(()),
Err(e) => {
let e = e.context(context);
Err(e)
}
}
}))
}
fn delete(&self, executor: &mut impl Executor) -> impl Future<Output = Result<()>> + Send
where
Self: Sized,
{
if Self::primary_key_def().is_empty() {
let error = Error::msg(
"Cannot delete an entity without a primary key, it would delete nothing",
);
log::error!("{:#}", error);
return Either::Left(future::ready(Err(error)));
}
Either::Right(
Self::delete_many(executor, self.primary_key_expr()).map(|v| {
v.and_then(|v| {
if let Some(affected) = v.rows_affected {
if affected != 1 {
let error = Error::msg(format!(
"The query deleted {affected} rows instead of the expected 1"
));
log::log!(
if affected == 0 {
Level::Info
} else {
Level::Error
},
"{error}",
);
return Err(error);
}
}
Ok(())
})
}),
)
}
}
impl<E: Entity> Dataset for E {
fn qualified_columns() -> bool
where
Self: Sized,
{
false
}
fn write_table_name(&self, writer: &dyn SqlWriter, context: &mut Context, out: &mut DynQuery) {
Self::table().write_table_name(writer, context, out);
}
fn table_ref(&self) -> TableRef {
Self::table().clone()
}
}