diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5380bfe90f..b94f5b45ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,10 +30,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* Added `DatabaseConnection::close` https://github.com/SeaQL/sea-orm/pull/1236
* Refactor schema module to expose functions for database alteration https://github.com/SeaQL/sea-orm/pull/1256
* Added `is_null` getter for `ColumnDef` struct https://github.com/SeaQL/sea-orm/pull/1381
-* Postgres insert many will throw `RecordNotInserted` error if non of them are being inserted https://github.com/SeaQL/sea-orm/pull/1021
* `MockDatabase::append_exec_results()`, `MockDatabase::append_query_results()`, `MockDatabase::append_exec_errors()` and `MockDatabase::append_query_errors()` take any types implemented `IntoIterator` trait https://github.com/SeaQL/sea-orm/pull/1367
* `find_by_id` and `delete_by_id` take any Into primary key value https://github.com/SeaQL/sea-orm/pull/1362
* Added `ActiveValue::reset` to convert `Unchanged` into `Set` https://github.com/SeaQL/sea-orm/pull/1177
+* Generate compact entity with `#[sea_orm(column_type = "JsonBinary")]` macro attribute https://github.com/SeaQL/sea-orm/pull/1346
### Upgrades
@@ -47,11 +47,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* Removed dependency when not needed https://github.com/SeaQL/sea-orm/pull/1213
* Changed all version = "^x.y.z" into version = "x.y.z" and disabled default features and enable only the needed ones https://github.com/SeaQL/sea-orm/pull/1300
* Cleanup the use of `vec!` macros https://github.com/SeaQL/sea-orm/pull/1367
+* Fixed a small typo https://github.com/SeaQL/sea-orm/pull/1391
### Bug Fixes
* Fixes `DeriveColumn` (by qualifying `IdenStatic::as_str`) https://github.com/SeaQL/sea-orm/pull/1280
* Prevent returning connections to pool with a positive transaction depth https://github.com/SeaQL/sea-orm/pull/1283
+* Postgres insert many will throw `RecordNotInserted` error if non of them are being inserted https://github.com/SeaQL/sea-orm/pull/1021
+ * Fixes inserting active models by `insert_many` with `on_conflict` and `do_nothing` panics if no rows are inserted on Postgres https://github.com/SeaQL/sea-orm/issues/899
+* Don't call `last_insert_id` if not needed https://github.com/SeaQL/sea-orm/pull/1403
+ * Fixes hitting 'negative last_insert_rowid' panic with Sqlite https://github.com/SeaQL/sea-orm/issues/1357
### Breaking changes
@@ -83,6 +88,13 @@ impl ActiveModelBehavior for ActiveModel {
}
```
+## 0.10.7 - 2023-01-19
+
+### Bug Fixes
+
+* Inserting active models by `insert_many` with `on_conflict` and `do_nothing` panics if no rows are inserted on Postgres https://github.com/SeaQL/sea-orm/issues/899
+* Hitting 'negative last_insert_rowid' panic with Sqlite https://github.com/SeaQL/sea-orm/issues/1357
+
## 0.10.6 - 2022-12-23
### Enhancements
diff --git a/examples/jsonrpsee_example/api/Cargo.toml b/examples/jsonrpsee_example/api/Cargo.toml
index 9e98a03c3a..6440e00417 100644
--- a/examples/jsonrpsee_example/api/Cargo.toml
+++ b/examples/jsonrpsee_example/api/Cargo.toml
@@ -16,4 +16,4 @@ migration = { path = "../migration" }
anyhow = "1.0.52"
async-trait = "0.1.52"
log = { version = "0.4", features = ["std"] }
-simplelog = "*"
+simplelog = "0.12"
diff --git a/issues/1357/Cargo.toml b/issues/1357/Cargo.toml
new file mode 100644
index 0000000000..55e7bfc336
--- /dev/null
+++ b/issues/1357/Cargo.toml
@@ -0,0 +1,18 @@
+[workspace]
+# A separate workspace
+
+[package]
+name = "sea-orm-issues-1357"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+anyhow = "1"
+serde = "1"
+tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+
+[dependencies.sea-orm]
+path = "../../"
+default-features = false
+features = ["macros", "runtime-tokio-native-tls", "sqlx-sqlite"]
diff --git a/issues/1357/src/entity.rs b/issues/1357/src/entity.rs
new file mode 100644
index 0000000000..3ad25f9ff7
--- /dev/null
+++ b/issues/1357/src/entity.rs
@@ -0,0 +1,14 @@
+use sea_orm::entity::prelude::*;
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
+#[sea_orm(table_name = "pool")]
+pub struct Model {
+ #[sea_orm(primary_key, auto_increment = false)]
+ pub id: i64,
+ pub name: String,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {}
+
+impl ActiveModelBehavior for ActiveModel {}
diff --git a/issues/1357/src/main.rs b/issues/1357/src/main.rs
new file mode 100644
index 0000000000..f7488beb40
--- /dev/null
+++ b/issues/1357/src/main.rs
@@ -0,0 +1,42 @@
+use anyhow::Result;
+use sea_orm::{ConnectionTrait, Database, EntityTrait, IntoActiveModel, Schema};
+
+mod entity;
+
+use entity::*;
+
+#[tokio::main]
+async fn main() -> Result<()> {
+ let db = Database::connect("sqlite::memory:").await.unwrap();
+
+ let builder = db.get_database_backend();
+ let schema = Schema::new(builder);
+ let stmt = schema.create_table_from_entity(Entity);
+ db.execute(builder.build(&stmt)).await?;
+
+ let model = Model {
+ id: 100,
+ name: "Hello".to_owned(),
+ };
+
+ let res = Entity::insert(model.clone().into_active_model())
+ .exec(&db)
+ .await?;
+
+ assert_eq!(Entity::find().one(&db).await?, Some(model.clone()));
+ assert_eq!(res.last_insert_id, model.id);
+
+ let model = Model {
+ id: -10,
+ name: "World".to_owned(),
+ };
+
+ let res = Entity::insert(model.clone().into_active_model())
+ .exec(&db)
+ .await?;
+
+ assert_eq!(Entity::find().one(&db).await?, Some(model.clone()));
+ assert_eq!(res.last_insert_id, model.id);
+
+ Ok(())
+}
diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs
index bcc1932b3a..fecb71fa44 100644
--- a/sea-orm-codegen/src/entity/column.rs
+++ b/sea-orm-codegen/src/entity/column.rs
@@ -94,6 +94,7 @@ impl Column {
ColumnType::Decimal(Some((p, s))) => Some(format!("Decimal(Some(({}, {})))", p, s)),
ColumnType::Money(Some((p, s))) => Some(format!("Money(Some({}, {}))", p, s)),
ColumnType::Text => Some("Text".to_owned()),
+ ColumnType::JsonBinary => Some("JsonBinary".to_owned()),
ColumnType::Custom(iden) => {
Some(format!("Custom(\"{}\".to_owned())", iden.to_string()))
}
diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs
index 21568380b2..e14883e154 100644
--- a/sea-orm-codegen/src/entity/writer.rs
+++ b/sea-orm-codegen/src/entity/writer.rs
@@ -1957,4 +1957,117 @@ mod tests {
Ok(expected.to_string())
}
+
+ #[test]
+ fn test_gen_postgres() -> io::Result<()> {
+ let entities = vec![
+ // This tests that the JsonBinary column type is annotated
+ // correctly in compact entity form. More information can be found
+ // in this issue:
+ //
+ // https://github.com/SeaQL/sea-orm/issues/1344
+ Entity {
+ table_name: "task".to_owned(),
+ columns: vec![
+ Column {
+ name: "id".to_owned(),
+ col_type: ColumnType::Integer,
+ auto_increment: true,
+ not_null: true,
+ unique: false,
+ },
+ Column {
+ name: "payload".to_owned(),
+ col_type: ColumnType::Json,
+ auto_increment: false,
+ not_null: true,
+ unique: false,
+ },
+ Column {
+ name: "payload_binary".to_owned(),
+ col_type: ColumnType::JsonBinary,
+ auto_increment: false,
+ not_null: true,
+ unique: false,
+ },
+ ],
+ relations: vec![],
+ conjunct_relations: vec![],
+ primary_keys: vec![PrimaryKey {
+ name: "id".to_owned(),
+ }],
+ },
+ ];
+ const ENTITY_FILES: [&str; 1] = [include_str!("../../tests/postgres/binary_json.rs")];
+
+ const ENTITY_FILES_EXPANDED: [&str; 1] =
+ [include_str!("../../tests/postgres/binary_json_expanded.rs")];
+
+ assert_eq!(entities.len(), ENTITY_FILES.len());
+
+ for (i, entity) in entities.iter().enumerate() {
+ assert_eq!(
+ parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
+ EntityWriter::gen_compact_code_blocks(
+ entity,
+ &crate::WithSerde::None,
+ &crate::DateTimeCrate::Chrono,
+ &None,
+ false,
+ false,
+ &TokenStream::new(),
+ &TokenStream::new(),
+ )
+ .into_iter()
+ .skip(1)
+ .fold(TokenStream::new(), |mut acc, tok| {
+ acc.extend(tok);
+ acc
+ })
+ .to_string()
+ );
+ assert_eq!(
+ parse_from_file(ENTITY_FILES[i].as_bytes())?.to_string(),
+ EntityWriter::gen_compact_code_blocks(
+ entity,
+ &crate::WithSerde::None,
+ &crate::DateTimeCrate::Chrono,
+ &Some("public".to_owned()),
+ false,
+ false,
+ &TokenStream::new(),
+ &TokenStream::new(),
+ )
+ .into_iter()
+ .skip(1)
+ .fold(TokenStream::new(), |mut acc, tok| {
+ acc.extend(tok);
+ acc
+ })
+ .to_string()
+ );
+ assert_eq!(
+ parse_from_file(ENTITY_FILES_EXPANDED[i].as_bytes())?.to_string(),
+ EntityWriter::gen_expanded_code_blocks(
+ entity,
+ &crate::WithSerde::None,
+ &crate::DateTimeCrate::Chrono,
+ &Some("schema_name".to_owned()),
+ false,
+ false,
+ &TokenStream::new(),
+ &TokenStream::new(),
+ )
+ .into_iter()
+ .skip(1)
+ .fold(TokenStream::new(), |mut acc, tok| {
+ acc.extend(tok);
+ acc
+ })
+ .to_string()
+ );
+ }
+
+ Ok(())
+ }
}
diff --git a/sea-orm-codegen/tests/postgres/binary_json.rs b/sea-orm-codegen/tests/postgres/binary_json.rs
new file mode 100644
index 0000000000..3d7cf95075
--- /dev/null
+++ b/sea-orm-codegen/tests/postgres/binary_json.rs
@@ -0,0 +1,21 @@
+//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
+//!
+//! This file tests that the JsonBinary column type is annotated correctly is
+//! compact entity form. More information can be found in this issue:
+//!
+//! https://github.com/SeaQL/sea-orm/issues/1344
+
+use sea_orm::entity::prelude::*;
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
+#[sea_orm(table_name = "task")]
+pub struct Model {
+ #[sea_orm(primary_key)]
+ pub id: i32,
+ pub payload: Json,
+ #[sea_orm(column_type = "JsonBinary")]
+ pub payload_binary: Json,
+}
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {}
+impl ActiveModelBehavior for ActiveModel {}
diff --git a/sea-orm-codegen/tests/postgres/binary_json_expanded.rs b/sea-orm-codegen/tests/postgres/binary_json_expanded.rs
new file mode 100644
index 0000000000..543e77f1f6
--- /dev/null
+++ b/sea-orm-codegen/tests/postgres/binary_json_expanded.rs
@@ -0,0 +1,65 @@
+//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
+//!
+//! This file tests that the JsonBinary column type is annotated correctly is
+//! expanded entity form. More information can be found in this issue:
+//!
+//! https://github.com/SeaQL/sea-orm/issues/1344
+
+use sea_orm::entity::prelude::*;
+
+#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
+pub struct Entity;
+impl EntityName for Entity {
+ fn schema_name(&self) -> Option< &str > {
+ Some("schema_name")
+ }
+ fn table_name(&self) -> &str {
+ "task"
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)]
+pub struct Model {
+ pub id: i32,
+ pub payload: Json,
+ pub payload_binary: Json,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
+pub enum Column {
+ Id,
+ Payload,
+ PayloadBinary,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
+pub enum PrimaryKey {
+ Id,
+}
+
+impl PrimaryKeyTrait for PrimaryKey {
+ type ValueType = i32;
+ fn auto_increment() -> bool {
+ true
+ }
+}
+
+#[derive(Copy, Clone, Debug, EnumIter)]
+pub enum Relation {}
+impl ColumnTrait for Column {
+ type EntityName = Entity;
+ fn def(&self) -> ColumnDef {
+ match self {
+ Self::Id => ColumnType::Integer.def(),
+ // This is the part that is being tested.
+ Self::Payload => ColumnType::Json.def(),
+ Self::PayloadBinary => ColumnType::JsonBinary.def(),
+ }
+ }
+}
+impl RelationTrait for Relation {
+ fn def(&self) -> RelationDef {
+ panic!("No RelationDef")
+ }
+}
+impl ActiveModelBehavior for ActiveModel {}
diff --git a/src/error.rs b/src/error.rs
index 7988bd1b8a..2ed87ffdca 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -58,8 +58,8 @@ pub enum DbErr {
/// None of the records are being inserted into the database,
/// if you insert with upsert expression that means
/// all of them conflict with existing records in the database
- #[error("RecordNotInserted Error: {0}")]
- RecordNotInserted(String),
+ #[error("None of the records are being inserted")]
+ RecordNotInserted,
}
/// Runtime error
diff --git a/src/executor/execute.rs b/src/executor/execute.rs
index 3da4ec8a34..f3a7150de5 100644
--- a/src/executor/execute.rs
+++ b/src/executor/execute.rs
@@ -52,7 +52,7 @@ impl ExecResult {
}
}
- /// Get the number of rows affedted by the operation
+ /// Get the number of rows affected by the operation
pub fn rows_affected(&self) -> u64 {
match &self.result {
#[cfg(feature = "sqlx-mysql")]
diff --git a/src/executor/insert.rs b/src/executor/insert.rs
index 796cd36290..c4c988ba9d 100644
--- a/src/executor/insert.rs
+++ b/src/executor/insert.rs
@@ -137,29 +137,37 @@ where
{
type PrimaryKey = <::Entity as EntityTrait>::PrimaryKey;
type ValueTypeOf = as PrimaryKeyTrait>::ValueType;
- let last_insert_id_opt = match db.support_returning() {
- true => {
+
+ let last_insert_id = match (primary_key, db.support_returning()) {
+ (Some(value_tuple), _) => {
+ let res = db.execute(statement).await?;
+ if res.rows_affected() == 0 {
+ return Err(DbErr::RecordNotInserted);
+ }
+ FromValueTuple::from_value_tuple(value_tuple)
+ }
+ (None, true) => {
+ let mut rows = db.query_all(statement).await?;
+ let row = match rows.pop() {
+ Some(row) => row,
+ None => return Err(DbErr::RecordNotInserted),
+ };
let cols = PrimaryKey::::iter()
.map(|col| col.to_string())
.collect::>();
- let rows = db.query_all(statement).await?;
- let res = rows.last().ok_or_else(|| {
- DbErr::RecordNotInserted("None of the records are being inserted".to_owned())
- })?;
- res.try_get_many("", cols.as_ref()).ok()
+ row.try_get_many("", cols.as_ref())
+ .map_err(|_| DbErr::UnpackInsertId)?
}
- false => {
- let last_insert_id = db.execute(statement).await?.last_insert_id();
- ValueTypeOf::::try_from_u64(last_insert_id).ok()
+ (None, false) => {
+ let res = db.execute(statement).await?;
+ if res.rows_affected() == 0 {
+ return Err(DbErr::RecordNotInserted);
+ }
+ let last_insert_id = res.last_insert_id();
+ ValueTypeOf::::try_from_u64(last_insert_id).map_err(|_| DbErr::UnpackInsertId)?
}
};
- let last_insert_id = match primary_key {
- Some(value_tuple) => FromValueTuple::from_value_tuple(value_tuple),
- None => match last_insert_id_opt {
- Some(last_insert_id) => last_insert_id,
- None => return Err(DbErr::UnpackInsertId),
- },
- };
+
Ok(InsertResult { last_insert_id })
}
diff --git a/src/executor/select.rs b/src/executor/select.rs
index fbae345161..c36753f1eb 100644
--- a/src/executor/select.rs
+++ b/src/executor/select.rs
@@ -70,7 +70,7 @@ where
model: PhantomData,
}
-/// Defines a type to get two Modelss
+/// Defines a type to get two Models
#[derive(Clone, Debug)]
pub struct SelectTwoModel
where
diff --git a/tests/string_primary_key_tests.rs b/tests/string_primary_key_tests.rs
index 94d1ae554c..9ed4df4763 100644
--- a/tests/string_primary_key_tests.rs
+++ b/tests/string_primary_key_tests.rs
@@ -2,7 +2,7 @@ pub mod common;
pub use common::{features::*, setup::*, TestContext};
use pretty_assertions::assert_eq;
-use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection};
+use sea_orm::{entity::prelude::*, entity::*, sea_query::OnConflict, DatabaseConnection};
use serde_json::json;
#[sea_orm_macros::test]
@@ -30,7 +30,7 @@ pub async fn insert_and_delete_repository(db: &DatabaseConnection) -> Result<(),
}
.into_active_model();
- let result = repository.insert(db).await?;
+ let result = repository.clone().insert(db).await?;
assert_eq!(
result,
@@ -42,6 +42,17 @@ pub async fn insert_and_delete_repository(db: &DatabaseConnection) -> Result<(),
}
);
+ #[cfg(any(feature = "sqlx-sqlite", feature = "sqlx-postgres"))]
+ {
+ let err = Repository::insert(repository)
+ // MySQL does not support DO NOTHING, we might workaround that later
+ .on_conflict(OnConflict::new().do_nothing().to_owned())
+ .exec(db)
+ .await;
+
+ assert_eq!(err.err(), Some(DbErr::RecordNotInserted));
+ }
+
result.delete(db).await?;
assert_eq!(
diff --git a/tests/upsert_tests.rs b/tests/upsert_tests.rs
index ad874e79c8..748d5b0a33 100644
--- a/tests/upsert_tests.rs
+++ b/tests/upsert_tests.rs
@@ -54,12 +54,7 @@ pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr>
.exec(db)
.await;
- assert_eq!(
- res.err(),
- Some(DbErr::RecordNotInserted(
- "None of the records are being inserted".to_owned()
- ))
- );
+ assert_eq!(res.err(), Some(DbErr::RecordNotInserted));
Ok(())
}