summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rust/src/api_helpers.rs50
-rw-r--r--rust/src/database_entity_crud.rs152
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(