Skip to content

Commit

Permalink
Merge pull request #205 from SeaQL/last-insert-id
Browse files Browse the repository at this point in the history
Drop `Default` trait bound of `PrimaryKeyTrait::ValueType`
  • Loading branch information
tyt2y3 authored Oct 12, 2021
2 parents edc2e65 + 3fd33ab commit 213a3fb
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ futures-util = { version = "^0.3" }
log = { version = "^0.4", optional = true }
rust_decimal = { version = "^1", optional = true }
sea-orm-macros = { version = "^0.2.6", path = "sea-orm-macros", optional = true }
sea-query = { version = "^0.17.0", features = ["thread-safe"] }
sea-query = { version = "^0.17.1", features = ["thread-safe"] }
sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] }
serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1", optional = true }
Expand Down
8 changes: 6 additions & 2 deletions src/driver/sqlx_sqlite.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use sqlx::{
sqlite::{SqliteArguments, SqliteQueryResult, SqliteRow},
sqlite::{SqliteArguments, SqlitePoolOptions, SqliteQueryResult, SqliteRow},
Sqlite, SqlitePool,
};

Expand All @@ -24,7 +24,11 @@ impl SqlxSqliteConnector {
}

pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> {
if let Ok(pool) = SqlitePool::connect(string).await {
if let Ok(pool) = SqlitePoolOptions::new()
.max_connections(1)
.connect(string)
.await
{
Ok(DatabaseConnection::SqlxSqlitePoolConnection(
SqlxSqlitePoolConnection { pool },
))
Expand Down
62 changes: 46 additions & 16 deletions src/entity/active_model.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use crate::{
error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn,
PrimaryKeyTrait, Value,
error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, Value,
};
use async_trait::async_trait;
use sea_query::ValueTuple;
use std::fmt::Debug;

#[derive(Clone, Debug, Default)]
pub struct ActiveValue<V>
where
V: Into<Value>,
{
value: Option<V>,
// Don't want to call ActiveValue::unwrap() and cause panic
pub(self) value: Option<V>,
state: ActiveValueState,
}

Expand Down Expand Up @@ -67,26 +68,55 @@ pub trait ActiveModelTrait: Clone + Debug {

fn default() -> Self;

#[allow(clippy::question_mark)]
fn get_primary_key_value(&self) -> Option<ValueTuple> {
let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter();
macro_rules! next {
() => {
if let Some(col) = cols.next() {
if let Some(val) = self.get(col.into_column()).value {
val
} else {
return None;
}
} else {
return None;
}
};
}
match <Self::Entity as EntityTrait>::PrimaryKey::iter().count() {
1 => {
let s1 = next!();
Some(ValueTuple::One(s1))
}
2 => {
let s1 = next!();
let s2 = next!();
Some(ValueTuple::Two(s1, s2))
}
3 => {
let s1 = next!();
let s2 = next!();
let s3 = next!();
Some(ValueTuple::Three(s1, s2, s3))
}
_ => panic!("The arity cannot be larger than 3"),
}
}

async fn insert(self, db: &DatabaseConnection) -> Result<Self, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
{
let am = self;
let exec = <Self::Entity as EntityTrait>::insert(am).exec(db);
let res = exec.await?;
// Assume valid last_insert_id is not equals to Default::default()
if res.last_insert_id
!= <<Self::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType::default()
{
let found = <Self::Entity as EntityTrait>::find_by_id(res.last_insert_id)
.one(db)
.await?;
match found {
Some(model) => Ok(model.into_active_model()),
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
}
} else {
Ok(Self::default())
let found = <Self::Entity as EntityTrait>::find_by_id(res.last_insert_id)
.one(db)
.await?;
match found {
Some(model) => Ok(model.into_active_model()),
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/entity/primary_key.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use super::{ColumnTrait, IdenStatic, Iterable};
use crate::{TryFromU64, TryGetableMany};
use sea_query::IntoValueTuple;
use sea_query::{FromValueTuple, IntoValueTuple};
use std::fmt::Debug;

//LINT: composite primary key cannot auto increment
pub trait PrimaryKeyTrait: IdenStatic + Iterable {
type ValueType: Sized
+ Send
+ Default
+ Debug
+ PartialEq
+ IntoValueTuple
+ FromValueTuple
+ TryGetableMany
+ TryFromU64;

Expand Down
30 changes: 18 additions & 12 deletions src/executor/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use crate::{
error::*, ActiveModelTrait, DatabaseConnection, DbBackend, EntityTrait, Insert,
PrimaryKeyTrait, Statement, TryFromU64,
};
use sea_query::InsertStatement;
use sea_query::{FromValueTuple, InsertStatement, ValueTuple};
use std::{future::Future, marker::PhantomData};

#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Inserter<A>
where
A: ActiveModelTrait,
{
primary_key: Option<ValueTuple>,
query: InsertStatement,
model: PhantomData<A>,
}
Expand All @@ -34,7 +35,6 @@ where
where
A: 'a,
{
// TODO: extract primary key's value from query
// so that self is dropped before entering await
let mut query = self.query;
if db.get_database_backend() == DbBackend::Postgres {
Expand All @@ -47,17 +47,17 @@ where
);
}
}
Inserter::<A>::new(query).exec(db)
// TODO: return primary key if extracted before, otherwise use InsertResult
Inserter::<A>::new(self.primary_key, query).exec(db)
}
}

impl<A> Inserter<A>
where
A: ActiveModelTrait,
{
pub fn new(query: InsertStatement) -> Self {
pub fn new(primary_key: Option<ValueTuple>, query: InsertStatement) -> Self {
Self {
primary_key,
query,
model: PhantomData,
}
Expand All @@ -71,12 +71,13 @@ where
A: 'a,
{
let builder = db.get_database_backend();
exec_insert(builder.build(&self.query), db)
exec_insert(self.primary_key, builder.build(&self.query), db)
}
}

// Only Statement impl Send
async fn exec_insert<A>(
primary_key: Option<ValueTuple>,
statement: Statement,
db: &DatabaseConnection,
) -> Result<InsertResult<A>, DbErr>
Expand All @@ -85,21 +86,26 @@ where
{
type PrimaryKey<A> = <<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey;
type ValueTypeOf<A> = <PrimaryKey<A> as PrimaryKeyTrait>::ValueType;
let last_insert_id = match db.get_database_backend() {
let last_insert_id_opt = match db.get_database_backend() {
DbBackend::Postgres => {
use crate::{sea_query::Iden, Iterable};
let cols = PrimaryKey::<A>::iter()
.map(|col| col.to_string())
.collect::<Vec<_>>();
let res = db.query_one(statement).await?.unwrap();
res.try_get_many("", cols.as_ref()).unwrap_or_default()
res.try_get_many("", cols.as_ref()).ok()
}
_ => {
let last_insert_id = db.execute(statement).await?.last_insert_id();
ValueTypeOf::<A>::try_from_u64(last_insert_id)
.ok()
.unwrap_or_default()
ValueTypeOf::<A>::try_from_u64(last_insert_id).ok()
}
};
let last_insert_id = match last_insert_id_opt {
Some(last_insert_id) => last_insert_id,
None => match primary_key {
Some(value_tuple) => FromValueTuple::from_value_tuple(value_tuple),
None => return Err(DbErr::Exec("Fail to unpack last_insert_id".to_owned())),
},
};
Ok(InsertResult { last_insert_id })
}
17 changes: 14 additions & 3 deletions src/query/insert.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use crate::{ActiveModelTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, QueryTrait};
use crate::{
ActiveModelTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, PrimaryKeyTrait,
QueryTrait,
};
use core::marker::PhantomData;
use sea_query::InsertStatement;
use sea_query::{InsertStatement, ValueTuple};

#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Insert<A>
where
A: ActiveModelTrait,
{
pub(crate) query: InsertStatement,
pub(crate) columns: Vec<bool>,
pub(crate) primary_key: Option<ValueTuple>,
pub(crate) model: PhantomData<A>,
}

Expand All @@ -31,6 +35,7 @@ where
.into_table(A::Entity::default().table_ref())
.to_owned(),
columns: Vec::new(),
primary_key: None,
model: PhantomData,
}
}
Expand Down Expand Up @@ -107,6 +112,12 @@ where
M: IntoActiveModel<A>,
{
let mut am: A = m.into_active_model();
self.primary_key =
if !<<A::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::auto_increment() {
am.get_primary_key_value()
} else {
None
};
let mut columns = Vec::new();
let mut values = Vec::new();
let columns_empty = self.columns.is_empty();
Expand Down
6 changes: 1 addition & 5 deletions tests/crud/create_cake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ pub async fn test_create_cake(db: &DbConn) {
.expect("could not insert cake_baker");
assert_eq!(
cake_baker_res.last_insert_id,
if cfg!(feature = "sqlx-postgres") {
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
} else {
Default::default()
}
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
);

assert!(cake.is_some());
Expand Down
6 changes: 1 addition & 5 deletions tests/crud/create_lineitem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
.expect("could not insert cake_baker");
assert_eq!(
cake_baker_res.last_insert_id,
if cfg!(feature = "sqlx-postgres") {
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
} else {
Default::default()
}
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
);

// Customer
Expand Down
6 changes: 1 addition & 5 deletions tests/crud/create_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ pub async fn test_create_order(db: &DbConn) {
.expect("could not insert cake_baker");
assert_eq!(
cake_baker_res.last_insert_id,
if cfg!(feature = "sqlx-postgres") {
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
} else {
Default::default()
}
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
);

// Customer
Expand Down
12 changes: 2 additions & 10 deletions tests/sequential_op_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,7 @@ async fn init_setup(db: &DatabaseConnection) {
.expect("could not insert cake_baker");
assert_eq!(
cake_baker_res.last_insert_id,
if cfg!(feature = "sqlx-postgres") {
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
} else {
Default::default()
}
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
);

let customer_kate = customer::ActiveModel {
Expand Down Expand Up @@ -225,11 +221,7 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option<cak
.expect("could not insert cake_baker");
assert_eq!(
cake_baker_res.last_insert_id,
if cfg!(feature = "sqlx-postgres") {
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
} else {
Default::default()
}
(cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap())
);

Cake::find_by_id(cake_insert_res.last_insert_id)
Expand Down
9 changes: 1 addition & 8 deletions tests/uuid_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,7 @@ pub async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> {

assert_eq!(Metadata::find().one(db).await?, Some(metadata.clone()));

assert_eq!(
res.last_insert_id,
if cfg!(feature = "sqlx-postgres") {
metadata.uuid
} else {
Default::default()
}
);
assert_eq!(res.last_insert_id, metadata.uuid);

let update_res = Metadata::update(metadata::ActiveModel {
value: Set("0.22".to_owned()),
Expand Down

0 comments on commit 213a3fb

Please sign in to comment.