use super::common::common_helpers;
use crate::abstract_layer::DbType;
use crate::model::{DbBackendTypeMapper, Model, Row, Value};
use crate::query::builder::{
FourTableSelect, GroupedSelect, InnerJoinedSelect, LeftJoinedSelect, MappedSelect,
MultiTableSelect, RelatedSelect, RightJoinedSelect, Select, WhereExpr,
};
use crate::query::filter::FilterExpr;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;
use crate::impl_backend_executor_methods;
use crate::impl_backend_join_executor_methods;
use crate::impl_backend_related_executor_methods;
pub struct SqliteTypeMapper;
impl DbBackendTypeMapper for SqliteTypeMapper {
fn sql_type(
rust_type: &str,
is_primary: bool,
is_auto_increment: bool,
is_nullable: bool,
enum_variants: Option<&[&str]>,
) -> String {
if enum_variants.is_some() {
let base_type = "TEXT";
let mut sql_type = base_type.to_string();
if !is_nullable && !is_primary {
sql_type.push_str(" NOT NULL");
}
return sql_type;
}
if is_primary {
if is_auto_increment {
return "INTEGER PRIMARY KEY AUTOINCREMENT".to_string();
} else {
return "INTEGER PRIMARY KEY".to_string();
}
}
let base_type = match rust_type {
"i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" => "INTEGER",
"f32" | "f64" => "REAL",
"String" => "TEXT",
"bool" => "INTEGER",
"Vec<u8>" | "&[u8]" => "BLOB",
"DateTime" | "chrono::DateTime" | "NaiveDateTime" | "chrono::NaiveDateTime" => "TEXT",
"NaiveDate" | "chrono::NaiveDate" => "TEXT",
"NaiveTime" | "chrono::NaiveTime" => "TEXT",
"JsonValue" | "serde_json::Value" => "TEXT",
_ => "TEXT",
};
let mut sql_type = base_type.to_string();
if !is_nullable {
sql_type.push_str(" NOT NULL");
}
sql_type
}
}
pub struct Database {
conn: Arc<turso::Connection>,
}
unsafe impl Send for Database {}
unsafe impl Sync for Database {}
#[allow(dead_code)]
struct SendableConnection(turso::Connection);
unsafe impl Send for SendableConnection {}
pub struct CreateTableExecutor<'a, T: Model> {
db: &'a Database,
table_name: Option<String>,
_marker: std::marker::PhantomData<T>,
}
impl<'a, T: Model> CreateTableExecutor<'a, T> {
pub async fn execute(self) -> anyhow::Result<()> {
let create_sql = crate::generate_create_table_sql_with_name::<T>(
crate::abstract_layer::DbType::Sqlite,
self.table_name.as_deref(),
)?;
self.db.conn.execute(&create_sql, ()).await?;
Ok(())
}
}
pub struct DropTableExecutor<'a, T: Model> {
db: &'a Database,
_marker: std::marker::PhantomData<T>,
}
impl<'a, T: Model> DropTableExecutor<'a, T> {
pub async fn execute(self) -> anyhow::Result<()> {
let sql = format!("DROP TABLE IF EXISTS {}", T::TABLE_NAME);
self.db.conn.execute(&sql, ()).await?;
Ok(())
}
}
pub struct InsertExecutor<'a, I: crate::model::Insertable> {
db: &'a Database,
models: I,
_marker: std::marker::PhantomData<I::Model>,
}
impl<'a, I: crate::model::Insertable> InsertExecutor<'a, I> {
pub async fn execute(self) -> anyhow::Result<()> {
let refs = self.models.as_refs();
self.db.insert_impl::<I::Model>(&refs).await
}
}
pub struct InsertOrUpdateExecutor<'a, I: crate::model::Insertable> {
db: &'a Database,
models: I,
_marker: std::marker::PhantomData<I::Model>,
}
impl<'a, I: crate::model::Insertable> InsertOrUpdateExecutor<'a, I> {
pub async fn execute(self) -> anyhow::Result<()> {
let refs = self.models.as_refs();
self.db.insert_or_update_batch::<I::Model>(&refs).await
}
}
pub struct InsertOrIgnoreExecutor<'a, I: crate::model::Insertable> {
db: &'a Database,
models: I,
_marker: std::marker::PhantomData<I::Model>,
}
impl<'a, I: crate::model::Insertable> InsertOrIgnoreExecutor<'a, I> {
pub async fn execute(self) -> anyhow::Result<()> {
let refs = self.models.as_refs();
self.db.insert_or_ignore_batch::<I::Model>(&refs).await
}
}
impl Database {
pub async fn connect(_db_type: super::DbType, path: &str) -> anyhow::Result<Self> {
let db = turso::Builder::new_local(path).build().await?;
let conn = Arc::new(db.connect()?);
Ok(Self { conn })
}
pub fn create_table<T: Model>(&self) -> CreateTableExecutor<'_, T> {
CreateTableExecutor {
db: self,
table_name: None,
_marker: std::marker::PhantomData,
}
}
pub async fn validate_table<T: Model>(&self) -> anyhow::Result<()> {
let table_exists = self.check_table_exists::<T>().await?;
if !table_exists {
return Err(anyhow::anyhow!(
"Schema mismatch: table {} does not exist",
T::TABLE_NAME
));
}
self.validate_table_schema::<T>().await
}
async fn check_table_exists<T: Model>(&self) -> anyhow::Result<bool> {
let sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?";
let mut rows = self.conn.query(sql, [T::TABLE_NAME]).await?;
if let Some(row) = rows.next().await? {
let count = row.get_value(0)?;
match count {
turso::Value::Integer(c) => Ok(c > 0),
_ => Ok(false),
}
} else {
Ok(false)
}
}
async fn validate_table_schema<T: Model>(&self) -> anyhow::Result<()> {
let sql = format!("PRAGMA table_info({})", T::TABLE_NAME);
let mut rows = self.conn.query(&sql, ()).await?;
let mut actual_columns: Vec<(String, String, bool, bool)> = Vec::new();
while let Some(row) = rows.next().await? {
let name = row.get_value(1)?;
let col_type = row.get_value(2)?;
let notnull = row.get_value(3)?;
let pk = row.get_value(5)?;
if let (
turso::Value::Text(name),
turso::Value::Text(col_type),
turso::Value::Integer(notnull),
turso::Value::Integer(pk),
) = (name, col_type, notnull, pk)
{
actual_columns.push((name, col_type, notnull != 0, pk != 0));
}
}
if actual_columns.len() != T::COLUMNS.len() {
return Err(anyhow::anyhow!(
"Schema mismatch: table {}, reason: Column count mismatch: expected {}, but actual is {}",
T::TABLE_NAME,
T::COLUMNS.len(),
actual_columns.len()
));
}
for (i, expected_col) in T::COLUMN_SCHEMA.iter().enumerate() {
if i >= actual_columns.len() {
return Err(anyhow::anyhow!(
"Schema mismatch: table {}, reason: Missing column: {}",
T::TABLE_NAME,
expected_col.name
));
}
let (actual_name, actual_type, actual_notnull, actual_pk) = &actual_columns[i];
if actual_name != expected_col.name {
return Err(anyhow::anyhow!(
"Schema mismatch: table {}, reason: Column name mismatch at position {}: expected '{}', but actual is '{}'",
T::TABLE_NAME,
i,
expected_col.name,
actual_name
));
}
if expected_col.is_primary != *actual_pk {
return Err(anyhow::anyhow!(
"Schema mismatch: table {}, reason: Primary key mismatch for '{}': expected {}primary key, but actual is {}primary key",
T::TABLE_NAME,
expected_col.name,
if expected_col.is_primary { "" } else { "not " },
if *actual_pk { "" } else { "not " }
));
}
let expected_type = crate::abstract_layer::DbType::Sqlite.sql_type(
expected_col.rust_type,
expected_col.is_primary,
expected_col.is_auto_increment,
expected_col.is_nullable,
expected_col.enum_variants,
);
let type_to_compare = if expected_col.is_primary {
match expected_col.rust_type {
"i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" => {
"INTEGER".to_string()
}
"f32" | "f64" => "REAL".to_string(),
"String" => "TEXT".to_string(),
"bool" => "INTEGER".to_string(),
"Vec<u8>" | "&[u8]" => "BLOB".to_string(),
_ => "TEXT".to_string(),
}
} else {
let full_type = crate::abstract_layer::DbType::Sqlite.sql_type(
expected_col.rust_type,
false,
expected_col.is_auto_increment,
expected_col.is_nullable,
expected_col.enum_variants,
);
full_type.replace(" NOT NULL", "")
};
if !self.types_compatible(actual_type, &type_to_compare) {
return Err(anyhow::anyhow!(
"Schema mismatch: table {}, reason: Column type mismatch for '{}': expected '{expected_type}', but actual is '{actual_type}'",
T::TABLE_NAME,
expected_col.name
));
}
if !expected_col.is_primary {
let expected_notnull = !expected_col.is_nullable;
if *actual_notnull != expected_notnull {
return Err(anyhow::anyhow!(
"Schema mismatch: table {}, reason: Column nullability mismatch for '{}': expected {}NULL, but actual is {}NULL",
T::TABLE_NAME,
expected_col.name,
if expected_notnull { "NOT " } else { "" },
if *actual_notnull { "NOT " } else { "" }
));
}
}
}
Ok(())
}
fn types_compatible(&self, actual: &str, expected: &str) -> bool {
fn normalize(s: &str) -> String {
match s.to_uppercase().as_str() {
"INT" | "INTEGER" | "MEDIUMINT" | "BIGINT" | "INT64" => "INTEGER".to_string(),
"VARCHAR" | "CHARACTER" | "NCHAR" | "NVARCHAR" | "TEXT" | "CLOB" => {
"TEXT".to_string()
}
"BLOB" => "BLOB".to_string(),
"REAL" | "FLOAT" | "DOUBLE" | "DECIMAL" | "NUMERIC" => "REAL".to_string(),
_ => s.to_string(),
}
}
normalize(actual) == normalize(expected)
}
pub fn insert<I: crate::model::Insertable>(&self, models: I) -> InsertExecutor<'_, I> {
InsertExecutor {
db: self,
models,
_marker: std::marker::PhantomData,
}
}
pub fn insert_or_update<I: crate::model::Insertable>(
&self,
models: I,
) -> InsertOrUpdateExecutor<'_, I> {
InsertOrUpdateExecutor {
db: self,
models,
_marker: std::marker::PhantomData,
}
}
pub fn insert_or_ignore<I: crate::model::Insertable>(
&self,
models: I,
) -> InsertOrIgnoreExecutor<'_, I> {
InsertOrIgnoreExecutor {
db: self,
models,
_marker: std::marker::PhantomData,
}
}
pub(crate) async fn insert_impl<T: Model>(&self, models: &[&T]) -> anyhow::Result<()> {
if models.is_empty() {
return Ok(());
}
let columns = T::insert_columns();
let (sql, _) = super::common::common_helpers::build_batch_insert_sql_with_columns(
T::TABLE_NAME,
&columns,
models.len(),
);
let all_values =
super::common::common_helpers::collect_batch_insert_values_with_auto_increment::<T>(
models,
);
let all_params = values_to_params(&all_values)?;
self.conn.execute(&sql, all_params).await?;
Ok(())
}
pub async fn insert_or_update_batch<T: Model>(&self, models: &[&T]) -> anyhow::Result<()> {
if models.is_empty() {
return Ok(());
}
let columns = T::insert_columns();
let col_count = columns.len();
let primary_key_columns = T::primary_key_columns();
let primary_key = primary_key_columns.join(", ");
let mut sql = format!(
"INSERT INTO {} ({}) VALUES ",
T::TABLE_NAME,
columns.join(", ")
);
let mut all_params = Vec::new();
for (idx, model) in models.iter().enumerate() {
if idx > 0 {
sql.push_str(", ");
}
let placeholders: Vec<String> = (1..=col_count).map(|_| "?".to_string()).collect();
sql.push_str(&format!("({})", placeholders.join(", ")));
let values = model.insert_values();
let params = values_to_params(&values)?;
all_params.extend(params);
}
sql.push_str(&format!(" ON CONFLICT ({}) DO UPDATE SET ", primary_key));
let mut first = true;
for col_name in columns.iter() {
if primary_key_columns.contains(col_name) {
continue; }
if !first {
sql.push_str(", ");
}
sql.push_str(&format!("{col_name} = excluded.{col_name}"));
first = false;
}
self.conn.execute(&sql, all_params).await?;
Ok(())
}
pub async fn insert_or_ignore_batch<T: Model>(&self, models: &[&T]) -> anyhow::Result<()> {
if models.is_empty() {
return Ok(());
}
let columns = T::insert_columns();
let col_count = columns.len();
let primary_key_columns = T::primary_key_columns();
let primary_key = primary_key_columns.join(", ");
let mut sql = format!(
"INSERT INTO {} ({}) VALUES ",
T::TABLE_NAME,
columns.join(", ")
);
let mut all_params = Vec::new();
for (idx, model) in models.iter().enumerate() {
if idx > 0 {
sql.push_str(", ");
}
let placeholders: Vec<String> = (1..=col_count).map(|_| "?".to_string()).collect();
sql.push_str(&format!("({})", placeholders.join(", ")));
let values = model.insert_values();
let params = values_to_params(&values)?;
all_params.extend(params);
}
sql.push_str(&format!(" ON CONFLICT ({}) DO NOTHING", primary_key));
self.conn.execute(&sql, all_params).await?;
Ok(())
}
pub fn select<T: Model>(&self) -> SelectExecutor<'_, T> {
SelectExecutor {
select: Select::<T>::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn select_column<T: Model, V>(&self) -> GroupedSelectExecutor<'_, T, V> {
GroupedSelectExecutor {
select: GroupedSelect::<T, V>::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn delete<T: Model>(&self) -> DeleteExecutor<T> {
DeleteExecutor {
filters: Vec::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn update<T: Model>(&self) -> UpdateExecutor<T> {
UpdateExecutor {
sets: Vec::new(),
filters: Vec::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn related<T: Model + 'static, R: Model>(&self) -> RelatedSelectExecutor<T, R> {
RelatedSelectExecutor {
select: Select::<T>::new().from::<T, R>(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub async fn begin(&self) -> anyhow::Result<Transaction> {
self.conn.execute("BEGIN", ()).await?;
Ok(Transaction {
conn: self.conn.clone(),
committed: false,
rolled_back: false,
})
}
pub fn drop_table<T: Model>(&self) -> DropTableExecutor<'_, T> {
DropTableExecutor {
db: self,
_marker: std::marker::PhantomData,
}
}
pub async fn execute<T: Model>(&self, sql: &str) -> anyhow::Result<Vec<T>> {
let mut rows = self.conn.query(sql, ()).await?;
let mut results = Vec::new();
while let Some(row) = rows.next().await? {
let mut data = HashMap::new();
for (i, col_name) in T::COLUMNS.iter().enumerate() {
let value = row.get_value(i)?;
let ormer_value = convert_turso_value(&value)?;
data.insert(col_name.to_string(), ormer_value);
}
let ormer_row = Row::new(data);
let model = T::from_row(&ormer_row)?;
results.push(model);
}
Ok(results)
}
#[deprecated(since = "0.1.0", note = "请使用 execute 方法")]
pub async fn exec_table<T: Model>(&self, sql: &str) -> anyhow::Result<Vec<T>> {
self.execute::<T>(sql).await
}
pub async fn exec_non_query(&self, sql: &str) -> anyhow::Result<u64> {
let result = self.conn.execute(sql, ()).await?;
Ok(result)
}
pub async fn is_valid(&self) -> bool {
self.conn.execute("SELECT 1", ()).await.is_ok()
}
}
pub struct Transaction {
conn: Arc<turso::Connection>,
committed: bool,
rolled_back: bool,
}
pub struct TransactionInsertExecutor<'a, I: crate::model::Insertable> {
txn: &'a mut Transaction,
models: I,
_marker: std::marker::PhantomData<I::Model>,
}
impl<'a, I: crate::model::Insertable> TransactionInsertExecutor<'a, I> {
pub async fn execute(self) -> anyhow::Result<()> {
let refs = self.models.as_refs();
self.txn.insert_impl::<I::Model>(&refs).await
}
}
pub struct TransactionInsertOrUpdateExecutor<'a, I: crate::model::Insertable> {
txn: &'a mut Transaction,
models: I,
_marker: std::marker::PhantomData<I::Model>,
}
impl<'a, I: crate::model::Insertable> TransactionInsertOrUpdateExecutor<'a, I> {
pub async fn execute(self) -> anyhow::Result<()> {
let refs = self.models.as_refs();
self.txn.insert_or_update_impl::<I::Model>(&refs).await
}
}
pub struct TransactionInsertOrIgnoreExecutor<'a, I: crate::model::Insertable> {
txn: &'a mut Transaction,
models: I,
_marker: std::marker::PhantomData<I::Model>,
}
impl<'a, I: crate::model::Insertable> TransactionInsertOrIgnoreExecutor<'a, I> {
pub async fn execute(self) -> anyhow::Result<()> {
let refs = self.models.as_refs();
self.txn.insert_or_ignore_impl::<I::Model>(&refs).await
}
}
impl Transaction {
pub async fn commit(mut self) -> anyhow::Result<()> {
if self.committed || self.rolled_back {
return Err(anyhow::anyhow!(
"Transaction already committed or rolled back"
));
}
self.conn.execute("COMMIT", ()).await?;
self.committed = true;
Ok(())
}
pub async fn rollback(mut self) -> anyhow::Result<()> {
if self.committed || self.rolled_back {
return Err(anyhow::anyhow!(
"Transaction already committed or rolled back"
));
}
self.conn.execute("ROLLBACK", ()).await?;
self.rolled_back = true;
Ok(())
}
pub fn select<T: Model>(&self) -> SelectExecutor<'_, T> {
SelectExecutor {
select: Select::<T>::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn select_column<T: Model, V>(&self) -> GroupedSelectExecutor<'_, T, V> {
GroupedSelectExecutor {
select: GroupedSelect::<T, V>::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn delete<T: Model>(&self) -> DeleteExecutor<T> {
DeleteExecutor {
filters: Vec::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn update<T: Model>(&self) -> UpdateExecutor<T> {
UpdateExecutor {
sets: Vec::new(),
filters: Vec::new(),
conn: self.conn.clone(),
_marker: PhantomData,
}
}
pub fn insert<I: crate::model::Insertable>(
&mut self,
models: I,
) -> TransactionInsertExecutor<'_, I> {
TransactionInsertExecutor {
txn: self,
models,
_marker: std::marker::PhantomData,
}
}
pub fn insert_or_update<I: crate::model::Insertable>(
&mut self,
models: I,
) -> TransactionInsertOrUpdateExecutor<'_, I> {
TransactionInsertOrUpdateExecutor {
txn: self,
models,
_marker: std::marker::PhantomData,
}
}
pub fn insert_or_ignore<I: crate::model::Insertable>(
&mut self,
models: I,
) -> TransactionInsertOrIgnoreExecutor<'_, I> {
TransactionInsertOrIgnoreExecutor {
txn: self,
models,
_marker: std::marker::PhantomData,
}
}
async fn insert_impl<T: Model>(&mut self, models: &[&T]) -> anyhow::Result<()> {
if models.is_empty() {
return Ok(());
}
let columns = T::insert_columns();
let (sql, _) = super::common::common_helpers::build_batch_insert_sql_with_columns(
T::TABLE_NAME,
&columns,
models.len(),
);
let all_values =
super::common::common_helpers::collect_batch_insert_values_with_auto_increment::<T>(
models,
);
let all_params = values_to_params(&all_values)?;
self.conn.execute(&sql, all_params).await?;
Ok(())
}
async fn insert_or_update_impl<T: Model>(&mut self, models: &[&T]) -> anyhow::Result<()> {
if models.is_empty() {
return Ok(());
}
let columns = T::insert_columns();
let col_count = columns.len();
let primary_key_columns = T::primary_key_columns();
let primary_key = primary_key_columns.join(", ");
let mut sql = format!(
"INSERT INTO {} ({}) VALUES ",
T::TABLE_NAME,
columns.join(", ")
);
let mut all_params = Vec::new();
for (idx, model) in models.iter().enumerate() {
if idx > 0 {
sql.push_str(", ");
}
let placeholders: Vec<String> = (1..=col_count).map(|_| "?".to_string()).collect();
sql.push_str(&format!("({})", placeholders.join(", ")));
let values = model.insert_values();
let params = values_to_params(&values)?;
all_params.extend(params);
}
sql.push_str(&format!(" ON CONFLICT ({}) DO UPDATE SET ", primary_key));
let mut first = true;
for col_name in columns.iter() {
if primary_key_columns.contains(col_name) {
continue; }
if !first {
sql.push_str(", ");
}
sql.push_str(&format!("{col_name} = excluded.{col_name}"));
first = false;
}
self.conn.execute(&sql, all_params).await?;
Ok(())
}
async fn insert_or_ignore_impl<T: Model>(&mut self, models: &[&T]) -> anyhow::Result<()> {
if models.is_empty() {
return Ok(());
}
let columns = T::insert_columns();
let col_count = columns.len();
let primary_key_columns = T::primary_key_columns();
let primary_key = primary_key_columns.join(", ");
let mut sql = format!(
"INSERT INTO {} ({}) VALUES ",
T::TABLE_NAME,
columns.join(", ")
);
let mut all_params = Vec::new();
for (idx, model) in models.iter().enumerate() {
if idx > 0 {
sql.push_str(", ");
}
let placeholders: Vec<String> = (1..=col_count).map(|_| "?".to_string()).collect();
sql.push_str(&format!("({})", placeholders.join(", ")));
let values = model.insert_values();
let params = values_to_params(&values)?;
all_params.extend(params);
}
sql.push_str(&format!(" ON CONFLICT ({}) DO NOTHING", primary_key));
self.conn.execute(&sql, all_params).await?;
Ok(())
}
}
pub struct SelectExecutor<'a, T: Model> {
select: Select<T>,
conn: Arc<turso::Connection>,
_marker: std::marker::PhantomData<&'a T>,
}
impl<'a, T: Model> Clone for SelectExecutor<'a, T> {
fn clone(&self) -> Self {
Self {
select: self.select.clone(),
conn: Arc::clone(&self.conn),
_marker: PhantomData,
}
}
}
pub struct LeftJoinedSelectExecutor<T: Model, J: Model> {
select: LeftJoinedSelect<T, J>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, J)>,
}
impl<T: Model, J: Model> Clone for LeftJoinedSelectExecutor<T, J> {
fn clone(&self) -> Self {
Self {
select: self.select.clone(),
conn: Arc::clone(&self.conn),
_marker: PhantomData,
}
}
}
pub struct InnerJoinedSelectExecutor<T: Model, J: Model> {
select: InnerJoinedSelect<T, J>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, J)>,
}
impl<T: Model, J: Model> Clone for InnerJoinedSelectExecutor<T, J> {
fn clone(&self) -> Self {
Self {
select: self.select.clone(),
conn: Arc::clone(&self.conn),
_marker: PhantomData,
}
}
}
pub struct RightJoinedSelectExecutor<T: Model, J: Model> {
select: RightJoinedSelect<T, J>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, J)>,
}
impl<T: Model, J: Model> Clone for RightJoinedSelectExecutor<T, J> {
fn clone(&self) -> Self {
Self {
select: self.select.clone(),
conn: Arc::clone(&self.conn),
_marker: PhantomData,
}
}
}
pub struct RelatedSelectExecutor<T: Model, R: Model> {
select: RelatedSelect<T, R>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, R)>,
}
#[allow(dead_code)]
pub struct MultiTableSelectExecutor<T: Model, R1: Model, R2: Model> {
select: MultiTableSelect<T, R1, R2>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, R1, R2)>,
}
#[allow(dead_code)]
pub struct FourTableSelectExecutor<T: Model, R1: Model, R2: Model, R3: Model> {
select: FourTableSelect<T, R1, R2, R3>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, R1, R2, R3)>,
}
pub struct MappedSelectExecutor<'a, T: Model, V> {
select: MappedSelect<T, V>,
conn: Arc<turso::Connection>,
_marker: PhantomData<&'a (T, V)>,
}
pub struct GroupedSelectExecutor<'a, T: Model, V> {
select: GroupedSelect<T, V>,
conn: Arc<turso::Connection>,
_marker: PhantomData<&'a (T, V)>,
}
impl<'a, T: Model, V> Clone for MappedSelectExecutor<'a, T, V> {
fn clone(&self) -> Self {
Self {
select: self.select.clone(),
conn: Arc::clone(&self.conn),
_marker: PhantomData,
}
}
}
impl<'a, T: Model, V> Clone for GroupedSelectExecutor<'a, T, V> {
fn clone(&self) -> Self {
Self {
select: self.select.clone(),
conn: Arc::clone(&self.conn),
_marker: PhantomData,
}
}
}
impl<'a, T: Model> SelectExecutor<'a, T> {
pub fn left_join<J: Model>(
self,
f: impl FnOnce(T::Where, J::Where) -> WhereExpr,
) -> LeftJoinedSelectExecutor<T, J> {
LeftJoinedSelectExecutor {
select: self.select.left_join::<J>(f),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn inner_join<J: Model>(
self,
f: impl FnOnce(T::Where, J::Where) -> WhereExpr,
) -> InnerJoinedSelectExecutor<T, J> {
InnerJoinedSelectExecutor {
select: self.select.inner_join::<J>(f),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn right_join<J: Model>(
self,
f: impl FnOnce(T::Where, J::Where) -> WhereExpr,
) -> RightJoinedSelectExecutor<T, J> {
RightJoinedSelectExecutor {
select: self.select.right_join::<J>(f),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn map_to<F, M>(self, f: F) -> MappedSelectExecutor<'a, T, M::Output>
where
F: FnOnce(<T as Model>::Where) -> M,
M: crate::query::builder::MapToResult,
{
let mapped_select = self.select.map_to(f);
MappedSelectExecutor {
select: mapped_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn select_column<F, V>(self, f: F) -> GroupedSelectExecutor<'a, T, V>
where
F: FnOnce(<T as Model>::Where) -> V,
V: crate::query::builder::SelectColumnResult,
{
let grouped_select = self.select.select_column(f);
GroupedSelectExecutor {
select: grouped_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn collect<C: FromIterator<T> + 'static>(self) -> CollectFuture<'a, T, C> {
CollectFuture {
executor: self,
_marker: PhantomData,
}
}
pub fn exec(self) -> CollectFuture<'a, T, Vec<T>>
where
T: 'static,
{
self.collect::<Vec<T>>()
}
pub fn execute(self) -> CollectFuture<'a, T, Vec<T>>
where
T: 'static,
{
self.collect::<Vec<T>>()
}
pub fn count<F, C>(self, f: F) -> AggregateFuture<T, usize>
where
F: FnOnce(<T as Model>::Where) -> crate::query::builder::TypedColumn<C>,
{
let aggregate_select = self.select.count(f);
AggregateFuture {
aggregate_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn sum<F, C>(self, f: F) -> AggregateFuture<T, C::Output>
where
F: FnOnce(<T as Model>::Where) -> crate::query::builder::TypedColumn<C>,
C: crate::query::builder::AggregateResultType + 'static,
{
let aggregate_select = self.select.sum(f);
AggregateFuture {
aggregate_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn avg<F, C>(self, f: F) -> AggregateFuture<T, Option<f64>>
where
F: FnOnce(<T as Model>::Where) -> crate::query::builder::TypedColumn<C>,
C: crate::query::builder::AggregateResultType + 'static,
{
let aggregate_select = self.select.avg(f);
AggregateFuture {
aggregate_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn max<F, C>(self, f: F) -> AggregateFuture<T, C::Output>
where
F: FnOnce(<T as Model>::Where) -> crate::query::builder::TypedColumn<C>,
C: crate::query::builder::AggregateResultType + 'static,
{
let aggregate_select = self.select.max(f);
AggregateFuture {
aggregate_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn min<F, C>(self, f: F) -> AggregateFuture<T, C::Output>
where
F: FnOnce(<T as Model>::Where) -> crate::query::builder::TypedColumn<C>,
C: crate::query::builder::AggregateResultType + 'static,
{
let aggregate_select = self.select.min(f);
AggregateFuture {
aggregate_select,
conn: self.conn,
_marker: PhantomData,
}
}
pub fn from<T2, R: Model>(self) -> RelatedSelectExecutor<T, R>
where
T2: Model + 'static,
{
RelatedSelectExecutor {
select: self.select.from::<T2, R>(),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn from3<T2, R1: Model, R2: Model>(self) -> MultiTableSelectExecutor<T, R1, R2>
where
T2: Model + 'static,
{
MultiTableSelectExecutor {
select: self.select.from3::<T2, R1, R2>(),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn from4<T2, R1: Model, R2: Model, R3: Model>(
self,
) -> FourTableSelectExecutor<T, R1, R2, R3>
where
T2: Model + 'static,
{
FourTableSelectExecutor {
select: self.select.from4::<T2, R1, R2, R3>(),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn stream(self) -> SelectStream<'a, T> {
SelectStream {
select: self.select,
conn: super::common::StreamConnection::Sqlite(self.conn),
_marker: std::marker::PhantomData,
}
}
}
impl_backend_executor_methods!(SelectExecutor, conn, Arc<turso::Connection>, Select);
impl_backend_join_executor_methods!(
LeftJoinedSelectExecutor,
conn,
Arc<turso::Connection>,
LeftJoinedSelect
);
impl<T: Model, J: Model> LeftJoinedSelectExecutor<T, J> {
pub fn to_sql(&self) -> String {
self.select.to_sql_with_params(DbType::Sqlite).0
}
pub fn collect<C: FromIterator<(T, Option<J>)> + 'static>(
&self,
) -> LeftJoinCollectFuture<T, J> {
LeftJoinCollectFuture {
executor: self.clone(),
}
}
pub fn exec(self) -> LeftJoinCollectFuture<T, J>
where
T: 'static,
J: 'static,
{
self.collect::<Vec<(T, Option<J>)>>()
}
pub fn execute(self) -> LeftJoinCollectFuture<T, J>
where
T: 'static,
J: 'static,
{
self.collect::<Vec<(T, Option<J>)>>()
}
async fn collect_inner<C: FromIterator<(T, Option<J>)>>(self) -> anyhow::Result<C> {
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
let t_col_count = T::COLUMNS.len();
while let Some(row) = rows.next().await? {
let mut t_data = HashMap::new();
for (i, col_name) in T::COLUMNS.iter().enumerate() {
let value = row.get_value(i)?;
t_data.insert(col_name.to_string(), convert_turso_value(&value)?);
}
let t_model = T::from_row(&Row::new(t_data))?;
let mut j_data = HashMap::new();
let mut j_is_null = true;
for (i, col_name) in J::COLUMNS.iter().enumerate() {
let idx = t_col_count + i;
if let Ok(value) = row.get_value(idx) {
let ormer_value = convert_turso_value(&value)?;
if !matches!(ormer_value, Value::Null) {
j_is_null = false;
}
j_data.insert(col_name.to_string(), ormer_value);
}
}
let j_model = if j_is_null {
None
} else {
Some(J::from_row(&Row::new(j_data))?)
};
results.push((t_model, j_model));
}
Ok(results.into_iter().collect())
}
}
impl_backend_join_executor_methods!(
InnerJoinedSelectExecutor,
conn,
Arc<turso::Connection>,
InnerJoinedSelect
);
impl<T: Model, J: Model> InnerJoinedSelectExecutor<T, J> {
pub fn exec(self) -> InnerJoinCollectFuture<T, J>
where
T: 'static,
J: 'static,
{
InnerJoinCollectFuture { executor: self }
}
pub fn collect<C: FromIterator<(T, J)> + 'static>(&self) -> InnerJoinCollectFuture<T, J>
where
T: 'static,
J: 'static,
{
InnerJoinCollectFuture {
executor: self.clone(),
}
}
async fn collect_inner<C: FromIterator<(T, J)>>(self) -> anyhow::Result<C> {
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
let t_col_count = T::COLUMNS.len();
while let Some(row) = rows.next().await? {
let mut t_data = HashMap::new();
for (i, col_name) in T::COLUMNS.iter().enumerate() {
let value = row.get_value(i)?;
t_data.insert(col_name.to_string(), convert_turso_value(&value)?);
}
let t_model = T::from_row(&Row::new(t_data))?;
let mut j_data = HashMap::new();
for (i, col_name) in J::COLUMNS.iter().enumerate() {
let idx = t_col_count + i;
let value = row.get_value(idx)?;
j_data.insert(col_name.to_string(), convert_turso_value(&value)?);
}
let j_model = J::from_row(&Row::new(j_data))?;
results.push((t_model, j_model));
}
Ok(results.into_iter().collect())
}
}
impl_backend_join_executor_methods!(
RightJoinedSelectExecutor,
conn,
Arc<turso::Connection>,
RightJoinedSelect
);
impl<T: Model, J: Model> RightJoinedSelectExecutor<T, J> {
pub fn exec(self) -> RightJoinCollectFuture<T, J>
where
T: 'static,
J: 'static,
{
RightJoinCollectFuture { executor: self }
}
pub fn collect<C: FromIterator<(Option<T>, J)> + 'static>(&self) -> RightJoinCollectFuture<T, J>
where
T: 'static,
J: 'static,
{
RightJoinCollectFuture {
executor: self.clone(),
}
}
async fn collect_inner<C: FromIterator<(Option<T>, J)>>(self) -> anyhow::Result<C> {
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
let t_col_count = T::COLUMNS.len();
while let Some(row) = rows.next().await? {
let mut t_data = HashMap::new();
let mut t_is_null = true;
for (i, col_name) in T::COLUMNS.iter().enumerate() {
if let Ok(value) = row.get_value(i) {
t_data.insert(col_name.to_string(), convert_turso_value(&value)?);
t_is_null = false;
}
}
let t_model = if t_is_null {
None
} else {
Some(T::from_row(&Row::new(t_data))?)
};
let mut j_data = HashMap::new();
for (i, col_name) in J::COLUMNS.iter().enumerate() {
let idx = t_col_count + i;
let value = row.get_value(idx)?;
j_data.insert(col_name.to_string(), convert_turso_value(&value)?);
}
let j_model = J::from_row(&Row::new(j_data))?;
results.push((t_model, j_model));
}
Ok(results.into_iter().collect())
}
}
pub struct CollectFuture<'a, T: Model, C: FromIterator<T>> {
executor: SelectExecutor<'a, T>,
_marker: std::marker::PhantomData<C>,
}
unsafe impl<'a, T: Model + Send, C: FromIterator<T> + Send> Send for CollectFuture<'a, T, C> {}
pub struct AggregateFuture<T: Model, R> {
aggregate_select: crate::query::builder::AggregateSelect<T, R>,
conn: Arc<turso::Connection>,
_marker: PhantomData<(T, R)>,
}
impl<
T: Model + 'static + std::marker::Send,
R: crate::model::FromValue + 'static + std::marker::Send,
> std::future::IntoFuture for AggregateFuture<T, R>
{
type Output = anyhow::Result<R>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let (sql, params) = self.aggregate_select.to_sql_with_params(DbType::Sqlite);
let values: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64),
crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if values.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, values).await?
};
if let Some(row) = rows.next().await? {
let value = row.get_value(0)?;
let ormer_value = match value {
turso::Value::Integer(i) => crate::model::Value::Integer(i),
turso::Value::Real(r) => crate::model::Value::Real(r),
turso::Value::Text(t) => crate::model::Value::Text(t),
turso::Value::Blob(b) => {
crate::model::Value::Text(String::from_utf8_lossy(&b).to_string())
}
turso::Value::Null => crate::model::Value::Null,
};
R::from_value(&ormer_value)
} else {
R::from_value(&crate::model::Value::Null)
}
})
}
}
pub struct LeftJoinCollectFuture<T: Model, J: Model> {
executor: LeftJoinedSelectExecutor<T, J>,
}
unsafe impl<T: Model + Send, J: Model + Send> Send for LeftJoinCollectFuture<T, J> {}
pub struct InnerJoinCollectFuture<T: Model, J: Model> {
executor: InnerJoinedSelectExecutor<T, J>,
}
unsafe impl<T: Model + Send, J: Model + Send> Send for InnerJoinCollectFuture<T, J> {}
pub struct RightJoinCollectFuture<T: Model, J: Model> {
executor: RightJoinedSelectExecutor<T, J>,
}
unsafe impl<T: Model + Send, J: Model + Send> Send for RightJoinCollectFuture<T, J> {}
pub struct GroupedCollectFuture<'a, T: Model, V, C> {
executor: GroupedSelectExecutor<'a, T, V>,
_marker: PhantomData<(T, V, C)>,
}
unsafe impl<'a, T: Model + Send, V: Send, C: Send> Send for GroupedCollectFuture<'a, T, V, C> {}
impl<'a, T: Model + 'static + std::marker::Send + std::marker::Sync, C: FromIterator<T> + 'static>
std::future::IntoFuture for CollectFuture<'a, T, C>
{
type Output = anyhow::Result<C>;
type IntoFuture =
std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'a>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.executor.collect_inner().await })
}
}
impl<T: Model + 'static + std::marker::Send, J: Model + 'static + std::marker::Send>
std::future::IntoFuture for LeftJoinCollectFuture<T, J>
{
type Output = anyhow::Result<Vec<(T, Option<J>)>>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.executor.collect_inner().await })
}
}
impl<T: Model + 'static + std::marker::Send, J: Model + 'static + std::marker::Send>
std::future::IntoFuture for InnerJoinCollectFuture<T, J>
{
type Output = anyhow::Result<Vec<(T, J)>>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.executor.collect_inner().await })
}
}
impl<T: Model + 'static + std::marker::Send, J: Model + 'static + std::marker::Send>
std::future::IntoFuture for RightJoinCollectFuture<T, J>
{
type Output = anyhow::Result<Vec<(Option<T>, J)>>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.executor.collect_inner().await })
}
}
impl_backend_related_executor_methods!(
RelatedSelectExecutor,
conn,
Arc<turso::Connection>,
RelatedSelect
);
impl<T: Model, R: Model> RelatedSelectExecutor<T, R> {
pub fn collect<C: FromIterator<T> + 'static>(self) -> RelatedCollectFuture<T, R> {
RelatedCollectFuture { executor: self }
}
pub fn exec(self) -> RelatedCollectFuture<T, R>
where
T: 'static,
R: 'static,
{
self.collect::<Vec<T>>()
}
pub fn execute(self) -> RelatedCollectFuture<T, R>
where
T: 'static,
R: 'static,
{
self.collect::<Vec<T>>()
}
async fn collect_inner<C: FromIterator<T>>(self) -> anyhow::Result<C> {
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
while let Some(row) = rows.next().await? {
let mut data = HashMap::new();
for (i, col_name) in T::COLUMNS.iter().enumerate() {
let value = row.get_value(i)?;
let ormer_value = convert_turso_value(&value)?;
data.insert(col_name.to_string(), ormer_value);
}
let ormer_row = Row::new(data);
let model = T::from_row(&ormer_row)?;
results.push(model);
}
Ok(results.into_iter().collect())
}
}
pub struct RelatedCollectFuture<T: Model, R: Model> {
executor: RelatedSelectExecutor<T, R>,
}
unsafe impl<T: Model + Send, R: Model + Send> Send for RelatedCollectFuture<T, R> {}
impl<T: Model + 'static + std::marker::Send, R: Model + 'static + std::marker::Send>
std::future::IntoFuture for RelatedCollectFuture<T, R>
{
type Output = anyhow::Result<Vec<T>>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.executor.collect_inner().await })
}
}
impl<'a, T: Model> SelectExecutor<'a, T> {
async fn collect_inner<C: FromIterator<T>>(self) -> anyhow::Result<C> {
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
while let Some(row) = rows.next().await? {
let mut data = HashMap::new();
for (i, col_name) in T::COLUMNS.iter().enumerate() {
let value = row.get_value(i)?;
let ormer_value = convert_turso_value(&value)?;
data.insert(col_name.to_string(), ormer_value);
}
let ormer_row = Row::new(data);
let model = T::from_row(&ormer_row)?;
results.push(model);
}
Ok(results.into_iter().collect())
}
}
pub struct DeleteExecutor<T: Model> {
filters: Vec<FilterExpr>,
conn: Arc<turso::Connection>,
_marker: PhantomData<T>,
}
impl<T: Model> DeleteExecutor<T> {
pub fn filter<F>(mut self, f: F) -> Self
where
F: FnOnce(T::Where) -> WhereExpr,
{
let where_obj = T::Where::default();
let expr = f(where_obj);
self.filters.push(expr.into());
self
}
pub async fn execute(self) -> anyhow::Result<u64> {
let (sql, params) = self.build_sql();
let result = self.conn.execute(&sql, params).await?;
Ok(result)
}
pub async fn exec(self) -> anyhow::Result<u64> {
self.execute().await
}
fn build_sql(&self) -> (String, Vec<turso::Value>) {
let mut sql = format!("DELETE FROM {}", T::TABLE_NAME);
let mut ormer_params = Vec::new();
if !self.filters.is_empty() {
sql.push_str(" WHERE ");
let mut param_idx = 1;
for (i, filter) in self.filters.iter().enumerate() {
if i > 0 {
sql.push_str(" AND ");
}
let _ = common_helpers::format_filter_with_params(
filter,
&mut sql,
&mut param_idx,
&mut ormer_params,
DbType::Sqlite,
);
}
}
let turso_params = values_to_params(&ormer_params).unwrap_or_default();
(sql, turso_params)
}
}
impl<T: Model + 'static + std::marker::Send> std::future::IntoFuture for DeleteExecutor<T> {
type Output = anyhow::Result<u64>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.execute().await })
}
}
pub struct UpdateExecutor<T: Model> {
sets: Vec<(String, Value)>,
filters: Vec<FilterExpr>,
conn: Arc<turso::Connection>,
_marker: PhantomData<T>,
}
impl<T: Model> UpdateExecutor<T> {
pub fn filter<F>(mut self, f: F) -> Self
where
F: FnOnce(T::Where) -> WhereExpr,
{
let where_obj = T::Where::default();
let expr = f(where_obj);
self.filters.push(expr.into());
self
}
pub fn set<F, V, C>(mut self, field_fn: F, value: V) -> Self
where
F: FnOnce(T::Where) -> crate::query::builder::TypedColumn<C>,
V: Into<Value>,
{
let where_obj = T::Where::default();
let column = field_fn(where_obj);
let column_name = column.column_name().to_string();
self.sets.push((column_name, value.into()));
self
}
pub async fn execute(self) -> anyhow::Result<u64> {
let (sql, params) = self.build_sql()?;
let result = self.conn.execute(&sql, params).await?;
Ok(result)
}
pub async fn exec(self) -> anyhow::Result<u64> {
self.execute().await
}
fn build_sql(&self) -> anyhow::Result<(String, Vec<turso::Value>)> {
let mut sql = format!("UPDATE {} SET ", T::TABLE_NAME);
let mut ormer_params = Vec::new();
let mut first = true;
for (col_name, value) in &self.sets {
if !first {
sql.push_str(", ");
}
sql.push_str(&format!("{col_name} = ?"));
ormer_params.push(value.clone());
first = false;
}
if !self.filters.is_empty() {
sql.push_str(" WHERE ");
let mut param_idx = ormer_params.len() + 1;
for (i, filter) in self.filters.iter().enumerate() {
if i > 0 {
sql.push_str(" AND ");
}
let _ = common_helpers::format_filter_with_params(
filter,
&mut sql,
&mut param_idx,
&mut ormer_params,
DbType::Sqlite,
);
}
}
let turso_params = values_to_params(&ormer_params)?;
Ok((sql, turso_params))
}
}
impl<T: Model + 'static + std::marker::Send> std::future::IntoFuture for UpdateExecutor<T> {
type Output = anyhow::Result<u64>;
type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.execute().await })
}
}
fn values_to_params(values: &[Value]) -> anyhow::Result<Vec<turso::Value>> {
let mut params = Vec::new();
for value in values {
let param = match value {
Value::Integer(v) => turso::Value::Integer(*v),
Value::Text(v) => turso::Value::Text(v.clone()),
Value::Real(v) => turso::Value::Real(*v),
Value::Boolean(v) => turso::Value::Integer(if *v { 1 } else { 0 }),
Value::Bytes(v) => turso::Value::Blob(v.clone()),
Value::DateTime(v) => turso::Value::Text(v.to_rfc3339()),
Value::Json(v) => turso::Value::Text(v.to_string()),
Value::Uuid(v) => turso::Value::Text(v.to_string()),
Value::BigInt(v) => turso::Value::Integer(*v as i64),
Value::Null => turso::Value::Null,
};
params.push(param);
}
Ok(params)
}
fn convert_turso_value(value: &turso::Value) -> anyhow::Result<Value> {
match value {
turso::Value::Integer(v) => Ok(Value::Integer(*v)),
turso::Value::Text(v) => {
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(v) {
return Ok(Value::DateTime(dt.with_timezone(&chrono::Utc)));
}
Ok(Value::Text(v.clone()))
}
turso::Value::Real(v) => Ok(Value::Real(*v)),
turso::Value::Null => Ok(Value::Null),
_ => Err(anyhow::anyhow!("Unsupported turso value type: {:?}", value)),
}
}
pub struct MappedCollectFuture<'a, T: Model + 'static, V: 'static, C: FromIterator<V> + 'static> {
executor: MappedSelectExecutor<'a, T, V>,
_marker: PhantomData<C>,
}
unsafe impl<'a, T: Model + Send, V: Send, C: FromIterator<V> + Send> Send
for MappedCollectFuture<'a, T, V, C>
{
}
impl<
'a,
T: Model + 'static + std::marker::Send + std::marker::Sync,
V: crate::model::FromRowValues + 'static + std::marker::Send + std::marker::Sync,
C: FromIterator<V> + 'static,
> std::future::IntoFuture for MappedCollectFuture<'a, T, V, C>
{
type Output = anyhow::Result<C>;
type IntoFuture =
std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'a>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.executor.collect_inner().await })
}
}
pub struct ModelCollectWithFuture<'a, T: Model, V, C, M, F> {
executor: MappedSelectExecutor<'a, T, V>,
transform: F,
_marker: PhantomData<(C, M)>,
}
unsafe impl<'a, T: Model + Send, V: Send, C: Send, M: Send, F: Send> Send
for ModelCollectWithFuture<'a, T, V, C, M, F>
{
}
impl<'a, T, V, C, M, F> std::future::IntoFuture for ModelCollectWithFuture<'a, T, V, C, M, F>
where
T: Model + 'static + std::marker::Send + std::marker::Sync,
V: crate::model::FromRowValues + 'static + std::marker::Send + std::marker::Sync,
C: FromIterator<M> + 'static,
M: 'static + std::marker::Send,
F: Fn(V) -> M + Clone + Send + 'static,
{
type Output = anyhow::Result<C>;
type IntoFuture =
std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'a>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let results: Vec<V> = self.executor.collect_inner().await?;
Ok(results.into_iter().map(|v| (self.transform)(v)).collect())
})
}
}
impl<'a, T: Model, V> MappedSelectExecutor<'a, T, V> {
pub fn to_subquery_sql(&self) -> (String, Vec<crate::model::Value>) {
self.select.to_sql_with_params(DbType::Sqlite)
}
pub fn collect<C: FromIterator<V> + 'static>(self) -> MappedCollectFuture<'a, T, V, C> {
MappedCollectFuture {
executor: self,
_marker: PhantomData,
}
}
pub fn collect_with<C, F, M>(self, f: F) -> ModelCollectWithFuture<'a, T, V, C, M, F>
where
C: FromIterator<M> + 'static,
F: Fn(V) -> M + Clone + 'static,
M: 'static,
{
ModelCollectWithFuture {
executor: self.clone(),
transform: f,
_marker: PhantomData,
}
}
async fn collect_inner<C: FromIterator<V>>(self) -> anyhow::Result<C>
where
V: crate::model::FromRowValues,
{
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
while let Some(row) = rows.next().await? {
let column_count = self.select.column_names().len();
let mut values = Vec::with_capacity(column_count);
for i in 0..column_count {
let value = row.get_value(i)?;
let ormer_value = convert_turso_value(&value)?;
values.push(ormer_value);
}
let typed_value = V::from_row_values(&values)?;
results.push(typed_value);
}
Ok(results.into_iter().collect())
}
}
impl<'a, T: Model, V> GroupedSelectExecutor<'a, T, V> {
pub fn collect<C: FromIterator<V> + 'static>(&self) -> GroupedCollectFuture<'a, T, V, C>
where
T: 'static,
V: crate::model::FromRowValues + 'static,
{
GroupedCollectFuture {
executor: self.clone(),
_marker: PhantomData,
}
}
pub fn group_by<F, G>(self, f: F) -> Self
where
F: FnOnce(<T as Model>::Where) -> G,
G: crate::query::builder::GroupByColumns,
{
Self {
select: self.select.group_by(f),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn having<F>(self, f: F) -> Self
where
F: FnOnce(<T as Model>::Where) -> crate::query::builder::WhereExpr,
{
Self {
select: self.select.having(f),
conn: self.conn,
_marker: PhantomData,
}
}
pub fn filter<F>(self, f: F) -> Self
where
F: FnOnce(T::Where) -> crate::query::builder::WhereExpr,
{
Self {
select: self.select.filter(f),
conn: self.conn,
_marker: PhantomData,
}
}
}
impl<
'a,
T: Model + 'static + std::marker::Send + std::marker::Sync,
V: crate::model::FromRowValues + 'static + std::marker::Send + std::marker::Sync,
C: FromIterator<V> + 'static,
> std::future::IntoFuture for GroupedCollectFuture<'a, T, V, C>
{
type Output = anyhow::Result<C>;
type IntoFuture =
std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'a>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let results: Vec<V> = self.executor.collect_inner().await?;
Ok(results.into_iter().collect())
})
}
}
impl<'a, T: Model, V> GroupedSelectExecutor<'a, T, V> {
async fn collect_inner<C: FromIterator<V>>(self) -> anyhow::Result<C>
where
V: crate::model::FromRowValues,
{
let (sql, params) = self.select.build_sql(DbType::Sqlite);
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let mut rows = if turso_params.is_empty() {
self.conn.query(&sql, ()).await?
} else {
self.conn.query(&sql, turso_params).await?
};
let mut results = Vec::new();
while let Some(row) = rows.next().await? {
let column_count = self.select.column_count();
let mut values = Vec::with_capacity(column_count);
for i in 0..column_count {
let value = row.get_value(i)?;
let ormer_value = convert_turso_value(&value)?;
values.push(ormer_value);
}
let typed_value = V::from_row_values(&values)?;
results.push(typed_value);
}
Ok(results.into_iter().collect())
}
}
pub struct SelectStream<'a, T: Model> {
select: Select<T>,
conn: super::common::StreamConnection<'a>,
_marker: std::marker::PhantomData<&'a T>,
}
impl<'a, T: Model + 'static> SelectStream<'a, T> {
pub async fn into_iter(self) -> anyhow::Result<SelectStreamIterator<'a, T>> {
let (sql, params) = self.select.to_sql_with_params(DbType::Sqlite);
let conn = match &self.conn {
super::common::StreamConnection::Sqlite(c) => c.clone(),
_ => unreachable!("Expected Sqlite connection"),
};
let turso_params: Vec<turso::Value> = params
.into_iter()
.map(|v| match v {
crate::model::Value::Integer(i) => turso::Value::Integer(i),
crate::model::Value::Text(t) => turso::Value::Text(t),
crate::model::Value::Real(r) => turso::Value::Real(r),
crate::model::Value::Boolean(b) => turso::Value::Integer(if b { 1 } else { 0 }),
crate::model::Value::Bytes(b) => turso::Value::Blob(b.clone()),
crate::model::Value::DateTime(dt) => turso::Value::Text(dt.to_rfc3339()),
crate::model::Value::Json(j) => turso::Value::Text(j.to_string()),
crate::model::Value::Uuid(u) => turso::Value::Text(u.to_string()),
crate::model::Value::BigInt(b) => turso::Value::Integer(b as i64), crate::model::Value::Null => turso::Value::Null,
})
.collect();
let rows = if turso_params.is_empty() {
conn.query(&sql, ()).await?
} else {
conn.query(&sql, turso_params).await?
};
Ok(SelectStreamIterator {
conn: super::common::StreamConnection::Sqlite(conn),
rows,
polluted: false,
_marker: std::marker::PhantomData,
})
}
}
pub struct SelectStreamIterator<'a, T: Model> {
#[allow(dead_code)]
conn: super::common::StreamConnection<'a>,
rows: turso::Rows,
polluted: bool, _marker: std::marker::PhantomData<&'a T>,
}
impl<'a, T: Model> Drop for SelectStreamIterator<'a, T> {
fn drop(&mut self) {
}
}
impl<'a, T: Model + 'static> SelectStreamIterator<'a, T> {
pub async fn next(&mut self) -> Option<anyhow::Result<T>> {
if self.polluted {
return None;
}
match self.rows.next().await {
Ok(Some(row)) => {
let mut data = HashMap::new();
for (i, col_name) in T::COLUMNS.iter().enumerate() {
match row.get_value(i) {
Ok(value) => match convert_turso_value(&value) {
Ok(ormer_value) => {
data.insert(col_name.to_string(), ormer_value);
}
Err(e) => {
self.polluted = true;
return Some(Err(e));
}
},
Err(e) => {
self.polluted = true;
return Some(Err(
anyhow::anyhow!(e).context("Database operation failed")
));
}
}
}
let ormer_row = Row::new(data);
Some(T::from_row(&ormer_row))
}
Ok(None) => None,
Err(e) => {
self.polluted = true;
Some(Err(anyhow::anyhow!(e).context("Database operation failed")))
}
}
}
}