diff options
| author | Bryan Newbold <bnewbold@robocracy.org> | 2018-09-09 12:26:37 -0700 | 
|---|---|---|
| committer | Bryan Newbold <bnewbold@robocracy.org> | 2018-09-09 12:26:39 -0700 | 
| commit | da10d0e0cebc1142852b959cea93c8b80801d565 (patch) | |
| tree | 6ac39dcc33f23114eab6f42dd83bf81fde63a088 /rust | |
| parent | 6b50b2d963719f5860f6fa46548abe2dbc9f2bc3 (diff) | |
| download | fatcat-da10d0e0cebc1142852b959cea93c8b80801d565.tar.gz fatcat-da10d0e0cebc1142852b959cea93c8b80801d565.zip | |
generic edit accept, and per-row variant
The per-row variant is for use with cockroach.
Diffstat (limited to 'rust')
| -rw-r--r-- | rust/src/api_helpers.rs | 50 | ||||
| -rw-r--r-- | rust/src/database_entity_crud.rs | 152 | 
2 files changed, 158 insertions, 44 deletions
| diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 925a6073..8ab9dcb3 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -1,18 +1,20 @@  use data_encoding::BASE32_NOPAD;  use database_models::*;  use database_schema::*; +use fatcat_api::models::*;  use diesel;  use diesel::prelude::*;  use errors::*;  use regex::Regex;  use std::str::FromStr;  use uuid::Uuid; +use database_entity_crud::EntityCrud;  pub type DbConn =      diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;  /// This function should always be run within a transaction -pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result<Uuid> { +pub fn get_or_create_editgroup(editor_id: Uuid, conn: &DbConn) -> Result<Uuid> {      // check for current active      let ed_row: EditorRow = editor::table.find(editor_id).first(conn)?;      if let Some(current) = ed_row.active_editgroup_id { @@ -30,7 +32,7 @@ pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result<U  }  /// This function should always be run within a transaction -pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result<ChangelogRow> { +pub fn accept_editgroup(editgroup_id: Uuid, conn: &DbConn) -> Result<ChangelogRow> {      // check that we haven't accepted already (in changelog)      // NB: could leave this to a UNIQUE constraint      let count: i64 = changelog::table @@ -41,41 +43,13 @@ pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result<Chang          return Err(ErrorKind::EditgroupAlreadyAccepted(uuid2fcid(&editgroup_id)).into());      } -    // for each entity type... -    //for entity in (container_edit, creator_edit, file_edit, release_edit, work_edit) { -        /* -        // This would be the clean and efficient way, but see: -        // https://github.com/diesel-rs/diesel/issues/1478 -        diesel::update(container_ident::table) -            .inner_join(container_edit::table.on( -                container_ident::id.eq(container_edit::ident_id) -            )) -            .filter(container_edit::editgroup_id.eq(editgroup_id)) -            .values(( -                container_ident::is_live.eq(true), -                container_ident::rev_id.eq(container_edit::rev_id), -                container_ident::redirect_id.eq(container_edit::redirect_id), -            )) -            .execute()?; -        */ - -    // Sketchy... but fast? Only a few queries per accept. -    for entity in &["container", "creator", "file", "work", "release"] { -        diesel::sql_query(format!( -            " -                UPDATE {entity}_ident -                SET -                    is_live = true, -                    rev_id = {entity}_edit.rev_id, -                    redirect_id = {entity}_edit.redirect_id -                FROM {entity}_edit -                WHERE -                    {entity}_ident.id = {entity}_edit.ident_id -                    AND {entity}_edit.editgroup_id = $1", -            entity = entity -        )).bind::<diesel::sql_types::Uuid, _>(editgroup_id) -            .execute(conn)?; -    } +    // copy edit columns to ident table +    let eg_id = FatCatId::from_uuid(&editgroup_id); +    ContainerEntity::db_accept_edits(conn, eg_id)?; +    CreatorEntity::db_accept_edits(conn, eg_id)?; +    FileEntity::db_accept_edits(conn, eg_id)?; +    ReleaseEntity::db_accept_edits(conn, eg_id)?; +    WorkEntity::db_accept_edits(conn, eg_id)?;      // append log/changelog row      let entry: ChangelogRow = diesel::insert_into(changelog::table) @@ -91,7 +65,7 @@ pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result<Chang      Ok(entry)  } -#[derive(Clone)] +#[derive(Clone, Copy)]  pub struct FatCatId(Uuid);  impl ToString for FatCatId { diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index 88c89e84..01323637 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -3,7 +3,7 @@ use chrono;  use database_models::*;  use database_schema::*;  use diesel::prelude::*; -use diesel::insert_into; +use diesel::{self, insert_into};  use errors::*;  use fatcat_api::models::*;  use serde_json; @@ -51,9 +51,19 @@ where      fn parse_editgroup_id(&self) -> Result<Option<FatCatId>>;      // Generic Methods -    fn db_get(conn: &DbConn, ident: FatCatId) -> Result<Self>; -    fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result<Self>; -    fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result<Self::EditRow>; +    fn db_get( +        conn: &DbConn, +        ident: FatCatId +    ) -> Result<Self>; +    fn db_get_rev( +        conn: &DbConn, +        rev_id: Uuid +    ) -> Result<Self>; +    fn db_create( +        &self, +        conn: &DbConn, +        edit_context: &EditContext +    ) -> Result<Self::EditRow>;      fn db_create_batch(          conn: &DbConn,          edit_context: &EditContext, @@ -75,6 +85,10 @@ where          ident: FatCatId,          limit: Option<i64>,      ) -> Result<Vec<EntityHistoryEntry>>; +    fn db_accept_edits( +        conn: &DbConn, +        editgroup_id: FatCatId +    ) -> Result<u64>;      // Entity-specific Methods      fn db_from_row( @@ -82,8 +96,14 @@ where          rev_row: Self::RevRow,          ident_row: Option<Self::IdentRow>,      ) -> Result<Self>; -    fn db_insert_rev(&self, conn: &DbConn) -> Result<Uuid>; -    fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result<Vec<Uuid>>; +    fn db_insert_rev( +        &self, +        conn: &DbConn +    ) -> Result<Uuid>; +    fn db_insert_revs( +        conn: &DbConn, +        models: &[&Self] +    ) -> Result<Vec<Uuid>>;  }  // TODO: this could be a separate trait on all entities @@ -276,6 +296,116 @@ macro_rules! generic_db_get_history {      };  } +/* +// This would be the clean and efficient way, but see: +// https://github.com/diesel-rs/diesel/issues/1478 +// +    diesel::update(container_ident::table) +        .inner_join(container_edit::table.on( +            container_ident::id.eq(container_edit::ident_id) +        )) +        .filter(container_edit::editgroup_id.eq(editgroup_id)) +        .values(( +            container_ident::is_live.eq(true), +            container_ident::rev_id.eq(container_edit::rev_id), +            container_ident::redirect_id.eq(container_edit::redirect_id), +        )) +        .execute()?; + +// Was previously: + +    for entity in &["container", "creator", "file", "work", "release"] { +        diesel::sql_query(format!( +            " +                UPDATE {entity}_ident +                SET +                    is_live = true, +                    rev_id = {entity}_edit.rev_id, +                    redirect_id = {entity}_edit.redirect_id +                FROM {entity}_edit +                WHERE +                    {entity}_ident.id = {entity}_edit.ident_id +                    AND {entity}_edit.editgroup_id = $1", +            entity = entity +        )).bind::<diesel::sql_types::Uuid, _>(editgroup_id) +            .execute(conn)?; +*/ + +// UPDATE FROM version: single query for many rows +// Works with Postgres, not Cockroach +macro_rules! generic_db_accept_edits_batch { +    ($entity_name_str:expr) => { +        fn db_accept_edits( +            conn: &DbConn, +            editgroup_id: FatCatId, +        ) -> Result<u64> { + +            let count = diesel::sql_query(format!( +                " +                    UPDATE {entity}_ident +                    SET +                        is_live = true, +                        rev_id = {entity}_edit.rev_id, +                        redirect_id = {entity}_edit.redirect_id +                    FROM {entity}_edit +                    WHERE +                        {entity}_ident.id = {entity}_edit.ident_id +                        AND {entity}_edit.editgroup_id = $1", +                entity = $entity_name_str  +            )).bind::<diesel::sql_types::Uuid, _>(editgroup_id.to_uuid()) +                .execute(conn)?; +            Ok(count as u64) +        } +    } +} + +// UPDATE ROW version: single query per row +// CockroachDB version (slow, single query per row) +macro_rules! generic_db_accept_edits_each { +    ($ident_table:ident, $edit_table:ident) => { +        fn db_accept_edits( +            conn: &DbConn, +            editgroup_id: FatCatId, +        ) -> Result<u64> { + +            // 1. select edit rows (in sql) +            let edit_rows: Vec<Self::EditRow> = $edit_table::table +                .filter($edit_table::editgroup_id.eq(&editgroup_id.to_uuid())) +                .get_results(conn)?; +            // 2. create ident rows (in rust) +            let ident_rows: Vec<Self::IdentRow> = edit_rows +                .iter() +                .map(|edit| +                    Self::IdentRow { +                        id: edit.ident_id, +                        is_live: true, +                        rev_id: edit.rev_id, +                        redirect_id: edit.redirect_id, + +                    } +                ) +                .collect(); +            /* +            // 3. upsert ident rows (in sql) +            let count: u64 = diesel::insert_into($ident_table::table) +                .values(ident_rows) +                .on_conflict() +                .do_update() +                .set(ident_rows) +                .execute(conn)?; +            */ +            // 3. update every row individually +            let count = ident_rows.len() as u64; +            for row in ident_rows { +                diesel::update(&row) +                    .set(&row) +                    .execute(conn)?; +            } +            Ok(count) +        } +    }; +} +  macro_rules! generic_db_insert_rev {      () => {          fn db_insert_rev(&self, conn: &DbConn) -> Result<Uuid> { @@ -299,6 +429,8 @@ impl EntityCrud for ContainerEntity {      generic_db_update!(container_ident, container_edit);      generic_db_delete!(container_ident, container_edit);      generic_db_get_history!(container_edit); +    generic_db_accept_edits_batch!("container"); +    //generic_db_accept_edits_each!(container_ident, container_edit);      generic_db_insert_rev!();      fn db_from_row( @@ -378,6 +510,8 @@ impl EntityCrud for CreatorEntity {      generic_db_update!(creator_ident, creator_edit);      generic_db_delete!(creator_ident, creator_edit);      generic_db_get_history!(creator_edit); +    generic_db_accept_edits_batch!("creator"); +    //generic_db_accept_edits_each!(creator_ident, creator_edit);      generic_db_insert_rev!();      fn db_from_row( @@ -454,6 +588,8 @@ impl EntityCrud for FileEntity {      generic_db_update!(file_ident, file_edit);      generic_db_delete!(file_ident, file_edit);      generic_db_get_history!(file_edit); +    generic_db_accept_edits_batch!("file"); +    //generic_db_accept_edits_each!(file_ident, file_edit);      generic_db_insert_rev!();      fn db_from_row( @@ -590,6 +726,8 @@ impl EntityCrud for ReleaseEntity {      generic_db_update!(release_ident, release_edit);      generic_db_delete!(release_ident, release_edit);      generic_db_get_history!(release_edit); +    generic_db_accept_edits_batch!("release"); +    //generic_db_accept_edits_each!(release_ident, release_edit);      generic_db_insert_rev!();      fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result<Self::EditRow> { @@ -962,6 +1100,8 @@ impl EntityCrud for WorkEntity {      generic_db_update!(work_ident, work_edit);      generic_db_delete!(work_ident, work_edit);      generic_db_get_history!(work_edit); +    generic_db_accept_edits_batch!("work"); +    //generic_db_accept_edits_each!(work_ident, work_edit);      generic_db_insert_rev!();      fn db_from_row( | 
