From e73b56f9354596c556cfbb2d45584a6bb86ad60e Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 16:00:54 -0700 Subject: batch inserts by default --- rust/src/api_server.rs | 1 + rust/src/database_entity_crud.rs | 76 +++++++++++++++++++++++++++++----------- rust/src/database_models.rs | 45 ++++++++++++++++++++---- 3 files changed, 95 insertions(+), 27 deletions(-) diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 20f71c7a..5060ba0f 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -87,6 +87,7 @@ fn make_edit_context(conn: &DbConn, editgroup_id: Option) -> Result, + pub autoapprove: bool, } /* One goal here is to abstract the non-entity-specific bits into generic traits or functions, @@ -35,8 +36,11 @@ pub struct EditContext { // Associated Type, not parametric pub trait EntityCrud where Self: Sized { + // TODO: could these be generic structs? Or do they need to be bound to a specific table? type EditRow; // EntityEditRow + type EditNewRow; type IdentRow; // EntityIdentRow + type IdentNewRow; type RevRow; fn parse_editgroup_id(&self) -> Result>; @@ -45,14 +49,15 @@ pub trait EntityCrud where Self: Sized { fn db_get(conn: &DbConn, ident: FatCatId) -> Result; fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result; fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result; - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result>; + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result>; fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result>; // Entity-specific Methods - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; + fn from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; fn db_insert_rev(&self, conn: &DbConn) -> Result; + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result>; } // TODO: this could be a separate trait on all entities? @@ -75,7 +80,7 @@ macro_rules! generic_db_get { .inner_join($rev_table::table) .first(conn)?; - Self::db_from_row(conn, rev, Some(ident)) + Self::from_row(conn, rev, Some(ident)) } } } @@ -87,7 +92,7 @@ macro_rules! generic_db_get_rev { .find(rev_id) .first(conn)?; - Self::db_from_row(conn, rev, None) + Self::from_row(conn, rev, None) } } } @@ -113,13 +118,32 @@ macro_rules! generic_db_create { } macro_rules! generic_db_create_batch { - () => { - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result> { - let mut ret: Vec = vec![]; - for entity in models { - ret.push(entity.db_create(conn, edit_context)?); - } - Ok(ret) + ($ident_table: ident, $edit_table: ident) => { + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result> { + let rev_ids: Vec = Self::db_insert_revs(conn, models)?; + let ident_ids: Vec = insert_into($ident_table::table) + .values(rev_ids.iter() + .map(|rev_id| Self::IdentNewRow { + rev_id: Some(rev_id.clone()), + is_live: edit_context.autoapprove, + redirect_id: None, + }) + .collect::>()) + .returning($ident_table::id) + .get_results(conn)?; + let edits: Vec = insert_into($edit_table::table) + .values(rev_ids.into_iter().zip(ident_ids.into_iter()) + .map(|(rev_id, ident_id)| Self::EditNewRow { + editgroup_id: edit_context.editgroup_id.to_uuid(), + rev_id: Some(rev_id), + ident_id: ident_id, + redirect_id: None, + prev_rev: None, + extra_json: edit_context.extra_json.clone(), + }) + .collect::>()) + .get_results(conn)?; + Ok(edits) } } } @@ -183,7 +207,6 @@ macro_rules! generic_db_delete { } macro_rules! generic_db_get_history { - // TODO: only actually need edit table? and maybe not that? ($edit_table:ident) => { fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result> { let limit = limit.unwrap_or(50); // XXX: make a static @@ -208,21 +231,32 @@ macro_rules! generic_db_get_history { } } +macro_rules! generic_db_insert_rev { + () => { + fn db_insert_rev(&self, conn: &DbConn) -> Result { + Self::db_insert_revs(conn, &vec![self]).map(|id_list| id_list[0]) + } + } +} + impl EntityCrud for WorkEntity { type EditRow = WorkEditRow; + type EditNewRow = WorkEditNewRow; type IdentRow = WorkIdentRow; + type IdentNewRow = WorkIdentNewRow; type RevRow = WorkRevRow; generic_parse_editgroup_id!(); generic_db_get!(work_ident, work_rev); generic_db_get_rev!(work_rev); generic_db_create!(work_ident, work_edit); - generic_db_create_batch!(); + generic_db_create_batch!(work_ident, work_edit); generic_db_update!(work_ident, work_edit); generic_db_delete!(work_ident, work_edit); generic_db_get_history!(work_edit); + generic_db_insert_rev!(); - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + fn from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( @@ -243,14 +277,14 @@ impl EntityCrud for WorkEntity { }) } - fn db_insert_rev(&self, conn: &DbConn) -> Result { - let rev_row: Uuid = insert_into(work_rev::table) - .values(( - work_rev::extra_json.eq(&self.extra) - )) + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { + let rev_ids: Vec = insert_into(work_rev::table) + .values(models.iter() + .map(|model| WorkRevNewRow { extra_json: model.extra.clone() } ) + .collect::>()) .returning(work_rev::id) - .get_result(conn)?; - Ok(rev_row) + .get_results(conn)?; + Ok(rev_ids) } } diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 47e00bcf..14215a3c 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -37,7 +37,9 @@ pub trait EntityEditRow { // Helper for constructing tables macro_rules! entity_structs { - ($edit_table:expr, $edit_struct:ident, $ident_table:expr, $ident_struct:ident) => { + ($edit_table:expr, $edit_struct:ident, $edit_new_struct:ident, $ident_table:expr, + $ident_struct:ident, $ident_new_struct:ident) => { + #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset, QueryableByName)] #[table_name = $edit_table] pub struct $edit_struct { @@ -51,6 +53,17 @@ macro_rules! entity_structs { pub extra_json: Option, } + #[derive(Debug, Associations, AsChangeset, QueryableByName, Insertable)] + #[table_name = $edit_table] + pub struct $edit_new_struct { + pub editgroup_id: Uuid, + pub ident_id: Uuid, + pub rev_id: Option, + pub redirect_id: Option, + pub prev_rev: Option, + pub extra_json: Option, + } + impl EntityEditRow for $edit_struct { /// Go from a row (SQL model) to an API model fn into_model(self) -> Result { @@ -75,6 +88,14 @@ macro_rules! entity_structs { pub redirect_id: Option, } + #[derive(Debug, Associations, AsChangeset, Insertable)] + #[table_name = $ident_table] + pub struct $ident_new_struct { + pub is_live: bool, + pub rev_id: Option, + pub redirect_id: Option, + } + impl EntityIdentRow for $ident_struct { fn state(&self) -> Result { if !self.is_live { @@ -107,8 +128,10 @@ pub struct ContainerRevRow { entity_structs!( "container_edit", ContainerEditRow, + ContainerEditNewRow, "container_ident", - ContainerIdentRow + ContainerIdentRow, + ContainerIdentNewRow ); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -126,8 +149,10 @@ pub struct CreatorRevRow { entity_structs!( "creator_edit", CreatorEditRow, + CreatorEditNewRow, "creator_ident", - CreatorIdentRow + CreatorIdentRow, + CreatorIdentNewRow ); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -159,7 +184,7 @@ pub struct FileRevRow { pub mimetype: Option, } -entity_structs!("file_edit", FileEditRow, "file_ident", FileIdentRow); +entity_structs!("file_edit", FileEditRow, FileEditNewRow, "file_ident", FileIdentRow, FileIdentNewRow); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "release_rev"] @@ -188,8 +213,10 @@ pub struct ReleaseRevRow { entity_structs!( "release_edit", ReleaseEditRow, + ReleaseEditNewRow, "release_ident", - ReleaseIdentRow + ReleaseIdentRow, + ReleaseIdentNewRow ); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -199,7 +226,13 @@ pub struct WorkRevRow { pub extra_json: Option, } -entity_structs!("work_edit", WorkEditRow, "work_ident", WorkIdentRow); +#[derive(Debug, Associations, AsChangeset, Insertable)] +#[table_name = "work_rev"] +pub struct WorkRevNewRow { + pub extra_json: Option, +} + +entity_structs!("work_edit", WorkEditRow, WorkEditNewRow, "work_ident", WorkIdentRow, WorkIdentNewRow); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "release_rev_abstract"] -- cgit v1.2.3