mod.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. mod migration;
  2. pub mod version_1;
  3. mod version_2;
  4. use crate::{
  5. event_map::WorkspaceDatabase,
  6. manager::FolderId,
  7. services::{folder_editor::FolderEditor, persistence::migration::FolderMigration},
  8. };
  9. use flowy_database::ConnectionPool;
  10. use flowy_error::{FlowyError, FlowyResult};
  11. use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
  12. use flowy_revision::disk::{RevisionRecord, RevisionState};
  13. use flowy_revision::mk_revision_disk_cache;
  14. use flowy_sync::client_folder::initial_folder_delta;
  15. use flowy_sync::{client_folder::FolderPad, entities::revision::Revision};
  16. use std::sync::Arc;
  17. use tokio::sync::RwLock;
  18. pub use version_1::{app_sql::*, trash_sql::*, v1_impl::V1Transaction, view_sql::*, workspace_sql::*};
  19. pub trait FolderPersistenceTransaction {
  20. fn create_workspace(&self, user_id: &str, workspace_rev: WorkspaceRevision) -> FlowyResult<()>;
  21. fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<WorkspaceRevision>>;
  22. fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()>;
  23. fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()>;
  24. fn create_app(&self, app_rev: AppRevision) -> FlowyResult<()>;
  25. fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()>;
  26. fn read_app(&self, app_id: &str) -> FlowyResult<AppRevision>;
  27. fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<AppRevision>>;
  28. fn delete_app(&self, app_id: &str) -> FlowyResult<AppRevision>;
  29. fn move_app(&self, app_id: &str, from: usize, to: usize) -> FlowyResult<()>;
  30. fn create_view(&self, view_rev: ViewRevision) -> FlowyResult<()>;
  31. fn read_view(&self, view_id: &str) -> FlowyResult<ViewRevision>;
  32. fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<ViewRevision>>;
  33. fn update_view(&self, changeset: ViewChangeset) -> FlowyResult<()>;
  34. fn delete_view(&self, view_id: &str) -> FlowyResult<()>;
  35. fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()>;
  36. fn create_trash(&self, trashes: Vec<TrashRevision>) -> FlowyResult<()>;
  37. fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<Vec<TrashRevision>>;
  38. fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()>;
  39. }
  40. pub struct FolderPersistence {
  41. database: Arc<dyn WorkspaceDatabase>,
  42. folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>,
  43. }
  44. impl FolderPersistence {
  45. pub fn new(database: Arc<dyn WorkspaceDatabase>, folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>) -> Self {
  46. Self {
  47. database,
  48. folder_editor,
  49. }
  50. }
  51. #[deprecated(
  52. since = "0.0.3",
  53. note = "please use `begin_transaction` instead, this interface will be removed in the future"
  54. )]
  55. #[allow(dead_code)]
  56. pub fn begin_transaction_v_1<F, O>(&self, f: F) -> FlowyResult<O>
  57. where
  58. F: for<'a> FnOnce(Box<dyn FolderPersistenceTransaction + 'a>) -> FlowyResult<O>,
  59. {
  60. //[[immediate_transaction]]
  61. // https://sqlite.org/lang_transaction.html
  62. // IMMEDIATE cause the database connection to start a new write immediately,
  63. // without waiting for a write statement. The BEGIN IMMEDIATE might fail
  64. // with SQLITE_BUSY if another write transaction is already active on another
  65. // database connection.
  66. //
  67. // EXCLUSIVE is similar to IMMEDIATE in that a write transaction is started
  68. // immediately. EXCLUSIVE and IMMEDIATE are the same in WAL mode, but in
  69. // other journaling modes, EXCLUSIVE prevents other database connections from
  70. // reading the database while the transaction is underway.
  71. let conn = self.database.db_connection()?;
  72. conn.immediate_transaction::<_, FlowyError, _>(|| f(Box::new(V1Transaction(&conn))))
  73. }
  74. pub async fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
  75. where
  76. F: FnOnce(Arc<dyn FolderPersistenceTransaction>) -> FlowyResult<O>,
  77. {
  78. match self.folder_editor.read().await.clone() {
  79. None => Err(FlowyError::internal().context("FolderEditor should be initialized after user login in.")),
  80. Some(editor) => f(editor),
  81. }
  82. }
  83. pub fn db_pool(&self) -> FlowyResult<Arc<ConnectionPool>> {
  84. self.database.db_pool()
  85. }
  86. pub async fn initialize(&self, user_id: &str, folder_id: &FolderId) -> FlowyResult<()> {
  87. let migrations = FolderMigration::new(user_id, self.database.clone());
  88. if let Some(migrated_folder) = migrations.run_v1_migration()? {
  89. self.save_folder(user_id, folder_id, migrated_folder).await?;
  90. }
  91. if let Some(migrated_folder) = migrations.run_v2_migration(user_id, folder_id).await? {
  92. self.save_folder(user_id, folder_id, migrated_folder).await?;
  93. }
  94. Ok(())
  95. }
  96. pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> {
  97. let pool = self.database.db_pool()?;
  98. let delta_data = initial_folder_delta(&folder)?.to_delta_bytes();
  99. let md5 = folder.md5();
  100. let revision = Revision::new(folder_id.as_ref(), 0, 0, delta_data, user_id, md5);
  101. let record = RevisionRecord {
  102. revision,
  103. state: RevisionState::Sync,
  104. write_to_disk: true,
  105. };
  106. let disk_cache = mk_revision_disk_cache(user_id, pool);
  107. disk_cache.delete_and_insert_records(folder_id.as_ref(), None, vec![record])
  108. }
  109. }