Skip to content

Commit

Permalink
update create implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
soulww committed Jan 12, 2024
1 parent f28ec4f commit 7a980c0
Show file tree
Hide file tree
Showing 21 changed files with 313 additions and 37 deletions.
11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "luna-orm"
version = "0.3.5"
version = "0.3.6"
edition = "2021"
license-file = "LICENSE"
description = "ORM based on sqlx"
homepage = "https://github.com/thegenius/luna-orm"

[dependencies]
luna-orm-trait = { path = "luna-orm-trait", version = "0.3.5" }
luna-orm-macro = { path = "luna-orm-macro", version = "0.3.5" }
luna-orm-trait = { path = "luna-orm-trait", version = "0.3.6" }
luna-orm-macro = { path = "luna-orm-macro", version = "0.3.6" }
thiserror = {workspace = true}
sqlx = {workspace = true}
path-absolutize = {workspace = true}
Expand Down Expand Up @@ -36,7 +36,7 @@ members = [
]

[workspace.package]
version = "0.3.5"
version = "0.3.6"


[workspace.dependencies]
Expand All @@ -58,3 +58,6 @@ serde_yaml = "0.9.27"
nom = "7.1.3"

runtime-fmt = "0.4.1"

[profile.test]
test-threads = 1
2 changes: 1 addition & 1 deletion luna-orm-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ syn = { version = "1.0", features = ["full"] }
proc-macro2 = "1.0"
case = "1.0"

luna-orm-trait = { path = "../luna-orm-trait", version = "0.3.5" }
luna-orm-trait = { path = "../luna-orm-trait", version = "0.3.6" }
sqlx = { workspace = true }
15 changes: 15 additions & 0 deletions luna-orm-trait/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ impl SelectedEntity for RecordCount {
}
}

#[derive(Clone, Debug)]
pub struct LastRowId {
pub id: i64,
}

impl SelectedEntity for LastRowId {
fn from_any_row(row: AnyRow) -> Result<Self, SqlxError>
where
Self: Sized,
{
let last_row_id: i64 = row.try_get("last_row_id")?;
Ok(Self { id: last_row_id })
}
}

#[derive(Clone, Debug)]
pub struct Pagination {
pub page_size: usize,
Expand Down
19 changes: 12 additions & 7 deletions src/command_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,19 @@ pub trait CommandExecutor: SqlExecutor + Debug {
return Ok(result);
}

async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<&'a dyn Entity> {
debug!(target: "luna_orm", command = "insert", entity = ?entity);
let sql = self.get_generator().get_insert_sql(entity);
debug!(target: "luna_orm", command = "insert", sql = sql);
async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<bool> {
debug!(target: "luna_orm2", command = "create", entity = ?entity);
let sql = self.get_generator().get_create_sql(entity);
debug!(target: "luna_orm", command = "create", sql = sql);
let args = entity.any_arguments_of_insert();
self.execute(&sql, args).await?;
debug!(target: "luna_orm", command = "insert", result = ?entity);
return Ok(entity);
if entity.get_auto_increment_field().is_some() {
let last_row_id: LastRowId = self.fetch_one(&sql, args).await?;
entity.set_auto_increment_field(Some(last_row_id.id));
} else {
self.execute(&sql, args).await?;
}
debug!(target: "luna_orm", command = "create", result = ?entity);
return Ok(true);
}

#[timed]
Expand Down
3 changes: 2 additions & 1 deletion src/database/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,14 @@ pub trait Database: CommandExecutor + SqlExecutor + std::fmt::Debug {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum DatabaseType {
SqliteLocal,
MySql,
PostgreSql,
}

#[derive(Debug, Clone)]
pub struct DB<T: Database>(pub T);

impl<T> Deref for DB<T>
Expand Down
10 changes: 5 additions & 5 deletions src/database/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ impl CommandExecutor for MysqlDatabase {
&self.sql_generator
}

async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<&'a dyn Entity> {
debug!(target: "luna_orm", command = "insert", entity = ?entity);
async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<bool> {
debug!(target: "luna_orm", command = "create", entity = ?entity);
let sql = self.get_generator().get_insert_sql(entity);
debug!(target: "luna_orm", command = "insert", sql = sql);
debug!(target: "luna_orm", command = "create", sql = sql);
let args = entity.any_arguments_of_insert();
let result = self.execute(&sql, args).await?;
entity.set_auto_increment_field(result.last_insert_id());
debug!(target: "luna_orm", command = "insert", result = ?entity);
return Ok(entity);
debug!(target: "luna_orm", command = "create", result = ?entity);
return Ok(result.rows_affected() > 0);
}
}

Expand Down
22 changes: 20 additions & 2 deletions src/database/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ use crate::{error::LunaOrmError, LunaOrmResult};
use sqlx::any::AnyConnectOptions;
use sqlx::AnyPool;

use std::str::FromStr;

use crate::command_executor::CommandExecutor;
use crate::sql_executor::SqlExecutor;
use crate::sql_generator::PostgresGenerator;
use crate::sql_generator::SqlGenerator;
use luna_orm_trait::Entity;
use luna_orm_trait::LastRowId;
use std::str::FromStr;
use tracing::debug;

#[derive(Debug)]
pub struct PostgresDatabase {
Expand All @@ -31,6 +34,21 @@ impl CommandExecutor for PostgresDatabase {
fn get_generator(&self) -> &Self::G {
&self.sql_generator
}

async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<bool> {
debug!(target: "luna_orm", command = "create", entity = ?entity);
let sql = self.get_generator().get_create_sql(entity);
debug!(target: "luna_orm", command = "create", sql = sql);
let args = entity.any_arguments_of_insert();
if entity.get_auto_increment_field().is_some() {
let last_row_id: LastRowId = self.fetch_one(&sql, args).await?;
entity.set_auto_increment_field(Some(last_row_id.id));
} else {
self.execute(&sql, args).await?;
}
debug!(target: "luna_orm", command = "create", result = ?entity);
return Ok(true);
}
}

impl Database for PostgresDatabase {
Expand Down
44 changes: 43 additions & 1 deletion src/database/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use crate::database::lib::DatabaseType;
use crate::database::DB;
use crate::{error::LunaOrmError, LunaOrmResult};

use luna_orm_trait::LastRowId;
use sqlx::any::AnyConnectOptions;
use sqlx::any::AnyPoolOptions;
use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqliteSynchronous};
use sqlx::AnyPool;

Expand All @@ -14,6 +16,12 @@ use std::str::FromStr;
use crate::command_executor::CommandExecutor;
use crate::sql_executor::SqlExecutor;
use crate::sql_generator::DefaultSqlGenerator;
use crate::sql_generator::SqlGenerator;
use luna_orm_trait::Entity;
use luna_orm_trait::SelectedEntity;
use sqlx::any::AnyArguments;
use sqlx::any::AnyRow;
use tracing::debug;

use path_absolutize::*;

Expand All @@ -34,7 +42,7 @@ impl SqliteLocalConfig {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct SqliteDatabase {
database_type: DatabaseType,
pool: AnyPool,
Expand All @@ -53,6 +61,28 @@ impl CommandExecutor for SqliteDatabase {
fn get_generator(&self) -> &Self::G {
&self.sql_generator
}

// sqlx sqlite driver has bug #2099, it returns result before the actual commit on insert returning clause
// the work around is create a transaction
async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<bool> {
debug!(target: "luna_orm2", command = "create", entity = ?entity);
let sql = self.get_generator().get_create_sql(entity);
debug!(target: "luna_orm", command = "create", sql = sql);
let args = entity.any_arguments_of_insert();
if entity.get_auto_increment_field().is_some() {
let last_row_id: LastRowId = self.fetch_one(&sql, args).await?;
entity.set_auto_increment_field(Some(last_row_id.id));
} else {
self.execute(&sql, args).await?;
}
debug!(target: "luna_orm", command = "create", result = ?entity);

// the work around
let trx = self.pool.begin().await?;
// query the record
trx.commit().await?;
return Ok(true);
}
}

impl Database for SqliteDatabase {
Expand Down Expand Up @@ -107,4 +137,16 @@ impl SqliteDatabase {
};
return Ok(database);
}

/*
pub async fn from_sqlite_pool(pool: SqlitePool) -> Self {
let generator = DefaultSqlGenerator::new();
Self {
database_type: DatabaseType::SqliteLocal,
pool:
sql_generator: generator,
}
}
*/
}
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
//!
//! # LUNA-ORM
//! luna-orm is build for time saving
//!
//! **LUNA-ORM** is an async orm framework based on SQLx. Built with :heart:
//! - **Intuitive** : Simple API, the most simple orm in this world.
//! - **Time Saving** : Most useful API is implemented by default, no need to waste your life.
//! - **Smooth Transaction** : Transaction is almost same as normal.
//! - **Template SQL** : You can execute your own sql with no pain.
//! - **Dynamic Parameters** : Handle complex dynamic sql with default.
//! - **Truly Asynchronous** : Based on SQLx, luna-orm is fully async.
//! - **Error Soundly** : Every error has its meaning.
//!
//!

#![allow(dead_code)]
#![allow(async_fn_in_trait)]
#![forbid(unsafe_code)]
Expand Down
19 changes: 18 additions & 1 deletion src/sql_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ pub trait SqlExecutor {
Err(LunaOrmError::NotImplement)
}

async fn fetch_one_plain<SE>(&mut self, stmt: &str) -> LunaOrmResult<SE>
where
SE: SelectedEntity + Send + Unpin,
{
let query = sqlx::query(stmt).try_map(|row: AnyRow| SE::from_any_row(row));
let result_opt: SE = query.fetch_one(self.get_pool()?).await?;
Ok(result_opt)
}

async fn fetch_one<SE>(&mut self, stmt: &str, args: AnyArguments<'_>) -> LunaOrmResult<SE>
where
SE: SelectedEntity + Send + Unpin,
{
let query = sqlx::query_with(stmt, args).try_map(|row: AnyRow| SE::from_any_row(row));
let result_opt: SE = query.fetch_one(self.get_pool()?).await?;
Ok(result_opt)
}

async fn fetch_optional_plain<SE>(&mut self, stmt: &str) -> LunaOrmResult<Option<SE>>
where
SE: SelectedEntity + Send + Unpin,
Expand All @@ -21,7 +39,6 @@ pub trait SqlExecutor {
let result_opt: Option<SE> = query.fetch_optional(self.get_pool()?).await?;
Ok(result_opt)
}

async fn fetch_optional<SE>(
&mut self,
stmt: &str,
Expand Down
61 changes: 58 additions & 3 deletions src/sql_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use luna_orm_trait::FromClause;
use luna_orm_trait::JoinedConditions;
use luna_orm_trait::{Entity, Location, Mutation, OrderBy, Pagination, Primary, Selection};

#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct DefaultSqlGenerator {}
impl DefaultSqlGenerator {
pub fn new() -> Self {
Expand All @@ -12,7 +12,7 @@ impl DefaultSqlGenerator {
}
impl SqlGenerator for DefaultSqlGenerator {}

#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct MySqlGenerator {}
impl MySqlGenerator {
pub fn new() -> Self {
Expand Down Expand Up @@ -45,9 +45,26 @@ impl SqlGenerator for MySqlGenerator {
.to_string();
self.post_process(upsert_sql)
}

fn get_create_sql(&self, entity: &dyn Entity) -> String {
let table_name = entity.get_table_name();
let field_names = entity.get_insert_fields();
let fields = wrap_fields(&field_names, self.get_wrap_char());
let marks = generate_question_mark_list(&field_names);
let insert_sql = format!(
"INSERT INTO {}{}{} ({}) VALUES({})",
self.get_wrap_char(),
table_name,
self.get_wrap_char(),
fields,
marks
)
.to_string();
self.post_process(insert_sql)
}
}

#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct PostgresGenerator {}
impl PostgresGenerator {
pub fn new() -> Self {
Expand Down Expand Up @@ -90,6 +107,10 @@ pub trait SqlGenerator {
origin
}

fn get_last_row_id_sql(&self) -> &'static str {
"SELECT last_insert_rowid() as `last_row_id`"
}

fn get_select_sql(&self, selection: &dyn Selection, primay: &dyn Primary) -> String {
let table_name = primay.get_table_name();
let selected_fields: Vec<String> = selection.get_selected_fields();
Expand Down Expand Up @@ -281,6 +302,40 @@ pub trait SqlGenerator {
self.post_process(insert_sql)
}

fn get_create_sql(&self, entity: &dyn Entity) -> String {
let table_name = entity.get_table_name();
let field_names = entity.get_insert_fields();
let fields = wrap_fields(&field_names, self.get_wrap_char());
let marks = generate_question_mark_list(&field_names);
let auto_field_name = entity.get_auto_increment_field();
let create_sql = if auto_field_name.is_some() {
let auto_field_name = auto_field_name.unwrap();
format!(
"INSERT INTO {}{}{} ({}) VALUES({}) RETURNING {}{}{} AS last_row_id",
self.get_wrap_char(),
table_name,
self.get_wrap_char(),
fields,
marks,
self.get_wrap_char(),
auto_field_name,
self.get_wrap_char()
)
.to_string()
} else {
format!(
"INSERT INTO {}{}{} ({}) VALUES({})",
self.get_wrap_char(),
table_name,
self.get_wrap_char(),
fields,
marks
)
.to_string()
};
self.post_process(create_sql)
}

fn get_upsert_sql(&self, entity: &dyn Entity) -> String {
let table_name = entity.get_table_name();

Expand Down
Loading

0 comments on commit 7a980c0

Please sign in to comment.