1use std::{
2 borrow::Cow,
3 collections::{BTreeMap, BTreeSet, HashMap},
4 fmt, iter,
5 path::{Path, PathBuf},
6 str::FromStr as _,
7 sync::Arc,
8};
9
10use async_trait::async_trait;
11use deadpool::managed::PoolConfig;
12use matrix_sdk_base::{
13 MinimalRoomMemberEvent, ROOM_VERSION_FALLBACK, ROOM_VERSION_RULES_FALLBACK, RoomInfo,
14 RoomMemberships, RoomState, StateChanges, StateStore, StateStoreDataKey, StateStoreDataValue,
15 deserialized_responses::{DisplayName, RawAnySyncOrStrippedState, SyncOrStrippedState},
16 store::{
17 ChildTransactionId, DependentQueuedRequest, DependentQueuedRequestKind, QueueWedgeError,
18 QueuedRequest, QueuedRequestKind, RoomLoadSettings, SentRequestKey,
19 StoredThreadSubscription, ThreadSubscriptionStatus, migration_helpers::RoomInfoV1,
20 },
21};
22use matrix_sdk_store_encryption::StoreCipher;
23use ruma::{
24 CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
25 OwnedTransactionId, OwnedUserId, RoomId, TransactionId, UInt, UserId,
26 canonical_json::{RedactedBecause, redact},
27 events::{
28 AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncStateEvent,
29 GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
30 presence::PresenceEvent,
31 receipt::{Receipt, ReceiptThread, ReceiptType},
32 room::{
33 create::RoomCreateEventContent,
34 member::{StrippedRoomMemberEvent, SyncRoomMemberEvent},
35 },
36 },
37 profile::{UserProfile, UserProfileUpdate},
38 serde::Raw,
39};
40use rusqlite::{OptionalExtension, Transaction};
41use serde::{Deserialize, Serialize};
42use tokio::{
43 fs,
44 sync::{Mutex, OwnedMutexGuard},
45};
46use tracing::{debug, instrument, warn};
47
48use crate::{
49 OpenStoreError, RuntimeConfig, Secret, SqliteStoreConfig,
50 connection::{
51 self, Connection as SqliteAsyncConn, Pool as SqlitePool, SqliteConnections,
52 SqliteConnectionsInner,
53 },
54 error::{Error, Result},
55 utils::{
56 EncryptableStore, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
57 SqliteKeyValueStoreConnExt,
58 },
59};
60
61mod keys {
62 pub const KV_BLOB: &str = "kv_blob";
64 pub const ROOM_INFO: &str = "room_info";
65 pub const STATE_EVENT: &str = "state_event";
66 pub const GLOBAL_ACCOUNT_DATA: &str = "global_account_data";
67 pub const ROOM_ACCOUNT_DATA: &str = "room_account_data";
68 pub const MEMBER: &str = "member";
69 pub const PROFILE: &str = "profile";
70 pub const RECEIPT: &str = "receipt";
71 pub const DISPLAY_NAME: &str = "display_name";
72 pub const SEND_QUEUE: &str = "send_queue_events";
73 pub const DEPENDENTS_SEND_QUEUE: &str = "dependent_send_queue_events";
74 pub const THREAD_SUBSCRIPTIONS: &str = "thread_subscriptions";
75 pub const GLOBAL_PROFILES: &str = "global_profiles";
76}
77
78pub const DATABASE_NAME: &str = "matrix-sdk-state.sqlite3";
80
81#[derive(Clone)]
83pub struct SqliteStateStore {
84 store_cipher: Option<Arc<StoreCipher>>,
85
86 connections: Arc<SqliteConnections>,
88
89 db_path: PathBuf,
91
92 pool_config: PoolConfig,
94
95 runtime_config: RuntimeConfig,
97}
98
99#[cfg(not(tarpaulin_include))]
100impl fmt::Debug for SqliteStateStore {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 f.debug_struct("SqliteStateStore").finish_non_exhaustive()
103 }
104}
105
106impl SqliteStateStore {
107 pub async fn open(
110 path: impl AsRef<Path>,
111 passphrase: Option<&str>,
112 ) -> Result<Self, OpenStoreError> {
113 Self::open_with_config(&SqliteStoreConfig::new(path).passphrase(passphrase)).await
114 }
115
116 pub async fn open_with_key(
119 path: impl AsRef<Path>,
120 key: Option<&[u8]>,
121 ) -> Result<Self, OpenStoreError> {
122 Self::open_with_config(&SqliteStoreConfig::new(path).key(key)).await
123 }
124
125 pub async fn open_with_config(config: &SqliteStoreConfig) -> Result<Self, OpenStoreError> {
127 fs::create_dir_all(&config.path).await.map_err(OpenStoreError::CreateDir)?;
128
129 let pool = config.build_pool_of_connections(DATABASE_NAME)?;
130 let pool_config = config.pool_config;
131 let runtime_config = config.runtime_config;
132
133 let this =
134 Self::open_with_pool(pool, config.secret.clone(), pool_config, runtime_config).await?;
135 this.read().await?.apply_runtime_config(runtime_config).await?;
136
137 Ok(this)
138 }
139
140 pub(crate) async fn open_with_pool(
143 pool: SqlitePool,
144 secret: Option<Secret>,
145 pool_config: PoolConfig,
146 runtime_config: RuntimeConfig,
147 ) -> Result<Self, OpenStoreError> {
148 let db_path = pool.manager().database_path.clone();
149 let conn = pool.get().await?;
150
151 let mut version = conn.db_version().await?;
152
153 if version == 0 {
154 init(&conn).await?;
155 version = 1;
156 }
157
158 let store_cipher = match secret {
159 Some(s) => Some(Arc::new(conn.get_or_create_store_cipher(s).await?)),
160 None => None,
161 };
162 let this = Self {
163 store_cipher,
164 connections: Arc::new(
165 SqliteConnections::new(SqliteConnectionsInner {
166 pool,
167 write_connection: Arc::new(Mutex::new(conn)),
168 })
169 .await,
170 ),
171 db_path,
172 pool_config,
173 runtime_config,
174 };
175 this.run_migrations(version, None).await?;
176
177 this.read().await?.wal_checkpoint().await;
178
179 Ok(this)
180 }
181
182 async fn run_migrations(&self, from: u8, to: Option<u8>) -> Result<()> {
187 if to == Some(1) {
188 return Ok(());
189 }
190
191 let conn = self.write().await?;
192
193 if from < 2 {
194 debug!("Upgrading database to version 2");
195 let this = self.clone();
196 conn.with_transaction(move |txn| {
197 txn.execute_batch(include_str!(
199 "../migrations/state_store/002_a_create_new_room_info.sql"
200 ))?;
201
202 for data in txn
204 .prepare("SELECT data FROM room_info")?
205 .query_map((), |row| row.get::<_, Vec<u8>>(0))?
206 {
207 let data = data?;
208 let room_info: RoomInfoV1 = this.deserialize_json(&data)?;
209
210 let room_id = this.encode_key(keys::ROOM_INFO, room_info.room_id());
211 let state = this
212 .encode_key(keys::ROOM_INFO, serde_json::to_string(&room_info.state())?);
213 txn.prepare_cached(
214 "INSERT OR REPLACE INTO new_room_info (room_id, state, data)
215 VALUES (?, ?, ?)",
216 )?
217 .execute((room_id, state, data))?;
218 }
219
220 txn.execute_batch(include_str!(
222 "../migrations/state_store/002_b_replace_room_info.sql"
223 ))?;
224
225 txn.set_db_version(2)?;
226 Result::<_, Error>::Ok(())
227 })
228 .await?;
229 }
230
231 if to == Some(2) {
232 return Ok(());
233 }
234
235 if from < 3 {
237 debug!("Upgrading database to version 3");
238 let this = self.clone();
239 conn.with_transaction(move |txn| {
240 for data in txn
242 .prepare("SELECT data FROM room_info")?
243 .query_map((), |row| row.get::<_, Vec<u8>>(0))?
244 {
245 let data = data?;
246 let room_info_v1: RoomInfoV1 = this.deserialize_json(&data)?;
247
248 let room_id = this.encode_key(keys::STATE_EVENT, room_info_v1.room_id());
250 let event_type =
251 this.encode_key(keys::STATE_EVENT, StateEventType::RoomCreate.to_string());
252 let create_res = txn
253 .prepare(
254 "SELECT stripped, data FROM state_event
255 WHERE room_id = ? AND event_type = ?",
256 )?
257 .query_one([room_id, event_type], |row| {
258 Ok((row.get::<_, bool>(0)?, row.get::<_, Vec<u8>>(1)?))
259 })
260 .optional()?;
261
262 let create = create_res.and_then(|(stripped, data)| {
263 let create = if stripped {
264 SyncOrStrippedState::<RoomCreateEventContent>::Stripped(
265 this.deserialize_json(&data).ok()?,
266 )
267 } else {
268 SyncOrStrippedState::Sync(this.deserialize_json(&data).ok()?)
269 };
270 Some(create)
271 });
272
273 let migrated_room_info = room_info_v1.migrate(create.as_ref());
274
275 let data = this.serialize_json(&migrated_room_info)?;
276 let room_id = this.encode_key(keys::ROOM_INFO, migrated_room_info.room_id());
277 txn.prepare_cached("UPDATE room_info SET data = ? WHERE room_id = ?")?
278 .execute((data, room_id))?;
279 }
280
281 txn.set_db_version(3)?;
282 Result::<_, Error>::Ok(())
283 })
284 .await?;
285 }
286
287 if to == Some(3) {
288 return Ok(());
289 }
290
291 if from < 4 {
292 debug!("Upgrading database to version 4");
293 conn.with_transaction(move |txn| {
294 txn.execute_batch(include_str!("../migrations/state_store/003_send_queue.sql"))?;
296 txn.set_db_version(4)
297 })
298 .await?;
299 }
300
301 if to == Some(4) {
302 return Ok(());
303 }
304
305 if from < 5 {
306 debug!("Upgrading database to version 5");
307 conn.with_transaction(move |txn| {
308 txn.execute_batch(include_str!(
310 "../migrations/state_store/004_send_queue_with_roomid_value.sql"
311 ))?;
312 txn.set_db_version(4)
313 })
314 .await?;
315 }
316
317 if to == Some(5) {
318 return Ok(());
319 }
320
321 if from < 6 {
322 debug!("Upgrading database to version 6");
323 conn.with_transaction(move |txn| {
324 txn.execute_batch(include_str!(
326 "../migrations/state_store/005_send_queue_dependent_events.sql"
327 ))?;
328 txn.set_db_version(6)
329 })
330 .await?;
331 }
332
333 if to == Some(6) {
334 return Ok(());
335 }
336
337 if from < 7 {
338 debug!("Upgrading database to version 7");
339 conn.with_transaction(move |txn| {
340 txn.execute_batch(include_str!("../migrations/state_store/006_drop_media.sql"))?;
342 txn.set_db_version(7)
343 })
344 .await?;
345 }
346
347 if to == Some(7) {
348 return Ok(());
349 }
350
351 if from < 8 {
352 debug!("Upgrading database to version 8");
353 let error = QueueWedgeError::GenericApiError {
355 msg: "local echo failed to send in a previous session".into(),
356 };
357 let default_err = self.serialize_value(&error)?;
358
359 conn.with_transaction(move |txn| {
360 txn.execute_batch(include_str!("../migrations/state_store/007_a_send_queue_wedge_reason.sql"))?;
362
363 for wedged_entries in txn
366 .prepare("SELECT room_id, transaction_id FROM send_queue_events WHERE wedged = 1")?
367 .query_map((), |row| {
368 Ok(
369 (row.get::<_, Vec<u8>>(0)?,row.get::<_, String>(1)?)
370 )
371 })? {
372
373 let (room_id, transaction_id) = wedged_entries?;
374
375 txn.prepare_cached("UPDATE send_queue_events SET wedge_reason = ? WHERE room_id = ? AND transaction_id = ?")?
376 .execute((default_err.clone(), room_id, transaction_id))?;
377 }
378
379
380 txn.execute_batch(include_str!("../migrations/state_store/007_b_send_queue_clean.sql"))?;
382
383 txn.set_db_version(8)
384 })
385 .await?;
386 }
387
388 if to == Some(8) {
389 return Ok(());
390 }
391
392 if from < 9 {
393 debug!("Upgrading database to version 9");
394 conn.with_transaction(move |txn| {
395 txn.execute_batch(include_str!("../migrations/state_store/008_send_queue.sql"))?;
397 txn.set_db_version(9)
398 })
399 .await?;
400 }
401
402 if to == Some(9) {
403 return Ok(());
404 }
405
406 if from < 10 {
407 debug!("Upgrading database to version 10");
408 conn.with_transaction(move |txn| {
409 txn.execute_batch(include_str!(
411 "../migrations/state_store/009_send_queue_priority.sql"
412 ))?;
413 txn.set_db_version(10)
414 })
415 .await?;
416 }
417
418 if to == Some(10) {
419 return Ok(());
420 }
421
422 if from < 11 {
423 debug!("Upgrading database to version 11");
424 conn.with_transaction(move |txn| {
425 txn.execute_batch(include_str!(
427 "../migrations/state_store/010_send_queue_enqueue_time.sql"
428 ))?;
429 txn.set_db_version(11)
430 })
431 .await?;
432 }
433
434 if to == Some(11) {
435 return Ok(());
436 }
437
438 if from < 12 {
439 debug!("Upgrading database to version 12");
440 conn.vacuum().await?;
444 conn.set_kv("version", vec![12]).await?;
445 }
446
447 if to == Some(12) {
448 return Ok(());
449 }
450
451 if from < 13 {
452 debug!("Upgrading database to version 13");
453 conn.with_transaction(move |txn| {
454 txn.execute_batch(include_str!(
456 "../migrations/state_store/011_thread_subscriptions.sql"
457 ))?;
458 txn.set_db_version(13)
459 })
460 .await?;
461 }
462
463 if to == Some(13) {
464 return Ok(());
465 }
466
467 if from < 14 {
468 debug!("Upgrading database to version 14");
469 conn.with_transaction(move |txn| {
470 txn.execute_batch(include_str!(
472 "../migrations/state_store/012_thread_subscriptions_bumpstamp.sql"
473 ))?;
474 txn.set_db_version(14)
475 })
476 .await?;
477 }
478
479 if to == Some(14) {
480 return Ok(());
481 }
482
483 if from < 15 {
484 debug!("Upgrading database to version 15");
485 conn.with_transaction(move |txn| {
486 txn.execute_batch(include_str!(
488 "../migrations/state_store/013_send_queue_new_parent_key_format.sql"
489 ))?;
490 txn.set_db_version(15)
491 })
492 .await?;
493 }
494
495 if to == Some(15) {
496 return Ok(());
497 }
498
499 if from < 16 {
500 debug!("Upgrading database to version 16");
501 conn.with_transaction(move |txn| {
502 txn.execute_batch(include_str!(
504 "../migrations/state_store/014_global_profiles.sql"
505 ))?;
506 txn.set_db_version(16)
507 })
508 .await?;
509 }
510
511 if to == Some(16) {
512 return Ok(());
513 }
514
515 Ok(())
516 }
517
518 fn encode_state_store_data_key(&self, key: StateStoreDataKey<'_>) -> Key {
519 let key_s = match key {
520 StateStoreDataKey::SyncToken => Cow::Borrowed(StateStoreDataKey::SYNC_TOKEN),
521 StateStoreDataKey::SupportedVersions => {
522 Cow::Borrowed(StateStoreDataKey::SUPPORTED_VERSIONS)
523 }
524 StateStoreDataKey::WellKnown => Cow::Borrowed(StateStoreDataKey::WELL_KNOWN),
525 StateStoreDataKey::Filter(f) => {
526 Cow::Owned(format!("{}:{f}", StateStoreDataKey::FILTER))
527 }
528 StateStoreDataKey::UserAvatarUrl(u) => {
529 Cow::Owned(format!("{}:{u}", StateStoreDataKey::USER_AVATAR_URL))
530 }
531 StateStoreDataKey::RecentlyVisitedRooms(b) => {
532 Cow::Owned(format!("{}:{b}", StateStoreDataKey::RECENTLY_VISITED_ROOMS))
533 }
534 StateStoreDataKey::UtdHookManagerData => {
535 Cow::Borrowed(StateStoreDataKey::UTD_HOOK_MANAGER_DATA)
536 }
537 StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
538 Cow::Borrowed(StateStoreDataKey::ONE_TIME_KEY_ALREADY_UPLOADED)
539 }
540 StateStoreDataKey::ComposerDraft(room_id, thread_root) => {
541 if let Some(thread_root) = thread_root {
542 Cow::Owned(format!(
543 "{}:{room_id}:{thread_root}",
544 StateStoreDataKey::COMPOSER_DRAFT
545 ))
546 } else {
547 Cow::Owned(format!("{}:{room_id}", StateStoreDataKey::COMPOSER_DRAFT))
548 }
549 }
550 StateStoreDataKey::SeenKnockRequests(room_id) => {
551 Cow::Owned(format!("{}:{room_id}", StateStoreDataKey::SEEN_KNOCK_REQUESTS))
552 }
553 StateStoreDataKey::ThreadSubscriptionsCatchupTokens => {
554 Cow::Borrowed(StateStoreDataKey::THREAD_SUBSCRIPTIONS_CATCHUP_TOKENS)
555 }
556 StateStoreDataKey::HomeserverCapabilities => {
557 Cow::Borrowed(StateStoreDataKey::HOMESERVER_CAPABILITIES)
558 }
559 };
560
561 self.encode_key(keys::KV_BLOB, &*key_s)
562 }
563
564 fn encode_presence_key(&self, user_id: &UserId) -> Key {
565 self.encode_key(keys::KV_BLOB, format!("presence:{user_id}"))
566 }
567
568 fn encode_custom_key(&self, key: &[u8]) -> Key {
569 let mut full_key = b"custom:".to_vec();
570 full_key.extend(key);
571 self.encode_key(keys::KV_BLOB, full_key)
572 }
573
574 #[instrument(skip_all)]
577 async fn read(&self) -> Result<SqliteAsyncConn> {
578 let pool = {
579 let guard = self.connections.lock().await;
580 let conns = guard.as_ref().ok_or(Error::StoreClosed)?;
581 conns.pool.clone()
582 };
583 Ok(pool.get().await?)
584 }
585
586 #[instrument(skip_all)]
589 async fn write(&self) -> Result<OwnedMutexGuard<SqliteAsyncConn>> {
590 let write_conn = {
591 let guard = self.connections.lock().await;
592 let conns = guard.as_ref().ok_or(Error::StoreClosed)?;
593 conns.write_connection.clone()
594 };
595 Ok(write_conn.lock_owned().await)
596 }
597
598 fn remove_maybe_stripped_room_data(
599 &self,
600 txn: &Transaction<'_>,
601 room_id: &RoomId,
602 stripped: bool,
603 ) -> rusqlite::Result<()> {
604 let state_event_room_id = self.encode_key(keys::STATE_EVENT, room_id);
605 txn.remove_room_state_events(&state_event_room_id, Some(stripped))?;
606
607 let member_room_id = self.encode_key(keys::MEMBER, room_id);
608 txn.remove_room_members(&member_room_id, Some(stripped))
609 }
610
611 pub async fn vacuum(&self) -> Result<()> {
612 self.write().await?.vacuum().await
613 }
614
615 pub async fn get_db_size(&self) -> Result<Option<usize>> {
616 let read_conn = self.read().await?;
617 Ok(Some(read_conn.get_db_size().await?))
618 }
619}
620
621impl EncryptableStore for SqliteStateStore {
622 fn get_cypher(&self) -> Option<&StoreCipher> {
623 self.store_cipher.as_deref()
624 }
625}
626
627async fn init(conn: &SqliteAsyncConn) -> Result<()> {
629 conn.execute_batch("PRAGMA journal_mode = wal;").await?;
633 conn.with_transaction(|txn| {
634 txn.execute_batch(include_str!("../migrations/state_store/001_init.sql"))?;
635 txn.set_db_version(1)?;
636
637 Ok(())
638 })
639 .await
640}
641
642trait SqliteConnectionStateStoreExt {
643 fn set_kv_blob(&self, key: &[u8], value: &[u8]) -> rusqlite::Result<()>;
644
645 fn set_global_account_data(&self, event_type: &[u8], data: &[u8]) -> rusqlite::Result<()>;
646
647 fn set_room_account_data(
648 &self,
649 room_id: &[u8],
650 event_type: &[u8],
651 data: &[u8],
652 ) -> rusqlite::Result<()>;
653 fn remove_room_account_data(&self, room_id: &[u8]) -> rusqlite::Result<()>;
654
655 fn set_room_info(&self, room_id: &[u8], state: &[u8], data: &[u8]) -> rusqlite::Result<()>;
656 fn get_room_info(&self, room_id: &[u8]) -> rusqlite::Result<Option<Vec<u8>>>;
657 fn remove_room_info(&self, room_id: &[u8]) -> rusqlite::Result<()>;
658
659 fn set_state_event(
660 &self,
661 room_id: &[u8],
662 event_type: &[u8],
663 state_key: &[u8],
664 stripped: bool,
665 event_id: Option<&[u8]>,
666 data: &[u8],
667 ) -> rusqlite::Result<()>;
668 fn get_state_event_by_id(
669 &self,
670 room_id: &[u8],
671 event_id: &[u8],
672 ) -> rusqlite::Result<Option<Vec<u8>>>;
673 fn remove_room_state_events(
674 &self,
675 room_id: &[u8],
676 stripped: Option<bool>,
677 ) -> rusqlite::Result<()>;
678
679 fn set_member(
680 &self,
681 room_id: &[u8],
682 user_id: &[u8],
683 membership: &[u8],
684 stripped: bool,
685 data: &[u8],
686 ) -> rusqlite::Result<()>;
687 fn remove_room_members(&self, room_id: &[u8], stripped: Option<bool>) -> rusqlite::Result<()>;
688
689 fn set_profile(&self, room_id: &[u8], user_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
690 fn remove_room_profiles(&self, room_id: &[u8]) -> rusqlite::Result<()>;
691 fn remove_room_profile(&self, room_id: &[u8], user_id: &[u8]) -> rusqlite::Result<()>;
692
693 fn set_receipt(
694 &self,
695 room_id: &[u8],
696 user_id: &[u8],
697 receipt_type: &[u8],
698 thread_id: &[u8],
699 event_id: &[u8],
700 data: &[u8],
701 ) -> rusqlite::Result<()>;
702 fn remove_room_receipts(&self, room_id: &[u8]) -> rusqlite::Result<()>;
703
704 fn set_display_name(&self, room_id: &[u8], name: &[u8], data: &[u8]) -> rusqlite::Result<()>;
705 fn remove_display_name(&self, room_id: &[u8], name: &[u8]) -> rusqlite::Result<()>;
706 fn remove_room_display_names(&self, room_id: &[u8]) -> rusqlite::Result<()>;
707 fn remove_room_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()>;
708 fn remove_room_dependent_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()>;
709}
710
711impl SqliteConnectionStateStoreExt for rusqlite::Connection {
712 fn set_kv_blob(&self, key: &[u8], value: &[u8]) -> rusqlite::Result<()> {
713 self.execute("INSERT OR REPLACE INTO kv_blob VALUES (?, ?)", (key, value))?;
714 Ok(())
715 }
716
717 fn set_global_account_data(&self, event_type: &[u8], data: &[u8]) -> rusqlite::Result<()> {
718 self.prepare_cached(
719 "INSERT OR REPLACE INTO global_account_data (event_type, data)
720 VALUES (?, ?)",
721 )?
722 .execute((event_type, data))?;
723 Ok(())
724 }
725
726 fn set_room_account_data(
727 &self,
728 room_id: &[u8],
729 event_type: &[u8],
730 data: &[u8],
731 ) -> rusqlite::Result<()> {
732 self.prepare_cached(
733 "INSERT OR REPLACE INTO room_account_data (room_id, event_type, data)
734 VALUES (?, ?, ?)",
735 )?
736 .execute((room_id, event_type, data))?;
737 Ok(())
738 }
739
740 fn remove_room_account_data(&self, room_id: &[u8]) -> rusqlite::Result<()> {
741 self.prepare(
742 "DELETE FROM room_account_data
743 WHERE room_id = ?",
744 )?
745 .execute((room_id,))?;
746 Ok(())
747 }
748
749 fn set_room_info(&self, room_id: &[u8], state: &[u8], data: &[u8]) -> rusqlite::Result<()> {
750 self.prepare_cached(
751 "INSERT OR REPLACE INTO room_info (room_id, state, data)
752 VALUES (?, ?, ?)",
753 )?
754 .execute((room_id, state, data))?;
755 Ok(())
756 }
757
758 fn get_room_info(&self, room_id: &[u8]) -> rusqlite::Result<Option<Vec<u8>>> {
759 self.query_one("SELECT data FROM room_info WHERE room_id = ?", (room_id,), |row| row.get(0))
760 .optional()
761 }
762
763 fn remove_room_info(&self, room_id: &[u8]) -> rusqlite::Result<()> {
765 self.prepare_cached("DELETE FROM room_info WHERE room_id = ?")?.execute((room_id,))?;
766 Ok(())
767 }
768
769 fn set_state_event(
770 &self,
771 room_id: &[u8],
772 event_type: &[u8],
773 state_key: &[u8],
774 stripped: bool,
775 event_id: Option<&[u8]>,
776 data: &[u8],
777 ) -> rusqlite::Result<()> {
778 self.prepare_cached(
779 "INSERT OR REPLACE
780 INTO state_event (room_id, event_type, state_key, stripped, event_id, data)
781 VALUES (?, ?, ?, ?, ?, ?)",
782 )?
783 .execute((room_id, event_type, state_key, stripped, event_id, data))?;
784 Ok(())
785 }
786
787 fn get_state_event_by_id(
788 &self,
789 room_id: &[u8],
790 event_id: &[u8],
791 ) -> rusqlite::Result<Option<Vec<u8>>> {
792 self.query_one(
793 "SELECT data FROM state_event WHERE room_id = ? AND event_id = ?",
794 (room_id, event_id),
795 |row| row.get(0),
796 )
797 .optional()
798 }
799
800 fn remove_room_state_events(
806 &self,
807 room_id: &[u8],
808 stripped: Option<bool>,
809 ) -> rusqlite::Result<()> {
810 if let Some(stripped) = stripped {
811 self.prepare_cached("DELETE FROM state_event WHERE room_id = ? AND stripped = ?")?
812 .execute((room_id, stripped))?;
813 } else {
814 self.prepare_cached("DELETE FROM state_event WHERE room_id = ?")?
815 .execute((room_id,))?;
816 }
817 Ok(())
818 }
819
820 fn set_member(
821 &self,
822 room_id: &[u8],
823 user_id: &[u8],
824 membership: &[u8],
825 stripped: bool,
826 data: &[u8],
827 ) -> rusqlite::Result<()> {
828 self.prepare_cached(
829 "INSERT OR REPLACE
830 INTO member (room_id, user_id, membership, stripped, data)
831 VALUES (?, ?, ?, ?, ?)",
832 )?
833 .execute((room_id, user_id, membership, stripped, data))?;
834 Ok(())
835 }
836
837 fn remove_room_members(&self, room_id: &[u8], stripped: Option<bool>) -> rusqlite::Result<()> {
842 if let Some(stripped) = stripped {
843 self.prepare_cached("DELETE FROM member WHERE room_id = ? AND stripped = ?")?
844 .execute((room_id, stripped))?;
845 } else {
846 self.prepare_cached("DELETE FROM member WHERE room_id = ?")?.execute((room_id,))?;
847 }
848 Ok(())
849 }
850
851 fn set_profile(&self, room_id: &[u8], user_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
852 self.prepare_cached(
853 "INSERT OR REPLACE
854 INTO profile (room_id, user_id, data)
855 VALUES (?, ?, ?)",
856 )?
857 .execute((room_id, user_id, data))?;
858 Ok(())
859 }
860
861 fn remove_room_profiles(&self, room_id: &[u8]) -> rusqlite::Result<()> {
862 self.prepare("DELETE FROM profile WHERE room_id = ?")?.execute((room_id,))?;
863 Ok(())
864 }
865
866 fn remove_room_profile(&self, room_id: &[u8], user_id: &[u8]) -> rusqlite::Result<()> {
867 self.prepare("DELETE FROM profile WHERE room_id = ? AND user_id = ?")?
868 .execute((room_id, user_id))?;
869 Ok(())
870 }
871
872 fn set_receipt(
873 &self,
874 room_id: &[u8],
875 user_id: &[u8],
876 receipt_type: &[u8],
877 thread: &[u8],
878 event_id: &[u8],
879 data: &[u8],
880 ) -> rusqlite::Result<()> {
881 self.prepare_cached(
882 "INSERT OR REPLACE
883 INTO receipt (room_id, user_id, receipt_type, thread, event_id, data)
884 VALUES (?, ?, ?, ?, ?, ?)",
885 )?
886 .execute((room_id, user_id, receipt_type, thread, event_id, data))?;
887 Ok(())
888 }
889
890 fn remove_room_receipts(&self, room_id: &[u8]) -> rusqlite::Result<()> {
891 self.prepare("DELETE FROM receipt WHERE room_id = ?")?.execute((room_id,))?;
892 Ok(())
893 }
894
895 fn set_display_name(&self, room_id: &[u8], name: &[u8], data: &[u8]) -> rusqlite::Result<()> {
896 self.prepare_cached(
897 "INSERT OR REPLACE
898 INTO display_name (room_id, name, data)
899 VALUES (?, ?, ?)",
900 )?
901 .execute((room_id, name, data))?;
902 Ok(())
903 }
904
905 fn remove_display_name(&self, room_id: &[u8], name: &[u8]) -> rusqlite::Result<()> {
906 self.prepare("DELETE FROM display_name WHERE room_id = ? AND name = ?")?
907 .execute((room_id, name))?;
908 Ok(())
909 }
910
911 fn remove_room_display_names(&self, room_id: &[u8]) -> rusqlite::Result<()> {
912 self.prepare("DELETE FROM display_name WHERE room_id = ?")?.execute((room_id,))?;
913 Ok(())
914 }
915
916 fn remove_room_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()> {
917 self.prepare("DELETE FROM send_queue_events WHERE room_id = ?")?.execute((room_id,))?;
918 Ok(())
919 }
920
921 fn remove_room_dependent_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()> {
922 self.prepare("DELETE FROM dependent_send_queue_events WHERE room_id = ?")?
923 .execute((room_id,))?;
924 Ok(())
925 }
926}
927
928#[async_trait]
929trait SqliteObjectStateStoreExt: SqliteAsyncConnExt {
930 async fn get_kv_blob(&self, key: Key) -> Result<Option<Vec<u8>>> {
931 Ok(self
932 .query_one("SELECT value FROM kv_blob WHERE key = ?", (key,), |row| row.get(0))
933 .await
934 .optional()?)
935 }
936
937 async fn get_kv_blobs(&self, keys: Vec<Key>) -> Result<Vec<Vec<u8>>> {
938 let keys_length = keys.len();
939
940 self.chunk_large_query_over(keys, Some(keys_length), |txn, keys| {
941 let sql =
942 format!("SELECT value FROM kv_blob WHERE key IN ({})", keys.host_parameters());
943
944 let params = rusqlite::params_from_iter(keys);
945
946 Ok(txn
947 .prepare(&sql)?
948 .query(params)?
949 .mapped(|row| row.get(0))
950 .collect::<Result<_, _>>()?)
951 })
952 .await
953 }
954
955 async fn set_kv_blob(&self, key: Key, value: Vec<u8>) -> Result<()>;
956
957 async fn delete_kv_blob(&self, key: Key) -> Result<()> {
958 self.execute("DELETE FROM kv_blob WHERE key = ?", (key,)).await?;
959 Ok(())
960 }
961
962 async fn get_room_infos(&self, room_id: Option<Key>) -> Result<Vec<Vec<u8>>> {
963 Ok(match room_id {
964 None => {
965 self.prepare("SELECT data FROM room_info", move |mut stmt| {
966 stmt.query_map((), |row| row.get(0))?.collect()
967 })
968 .await?
969 }
970
971 Some(room_id) => {
972 self.prepare("SELECT data FROM room_info WHERE room_id = ?", move |mut stmt| {
973 stmt.query((room_id,))?.mapped(|row| row.get(0)).collect()
974 })
975 .await?
976 }
977 })
978 }
979
980 async fn get_maybe_stripped_state_events_for_keys(
981 &self,
982 room_id: Key,
983 event_type: Key,
984 state_keys: Vec<Key>,
985 ) -> Result<Vec<(bool, Vec<u8>)>> {
986 self.chunk_large_query_over(state_keys, None, move |txn, state_keys| {
987 let sql = format!(
988 "SELECT stripped, data FROM state_event
989 WHERE room_id = ? AND event_type = ? AND state_key IN ({})",
990 state_keys.host_parameters()
991 );
992
993 let params = rusqlite::params_from_iter(
994 [room_id.clone(), event_type.clone()].into_iter().chain(state_keys),
995 );
996
997 Ok(txn
998 .prepare(&sql)?
999 .query(params)?
1000 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1001 .collect::<Result<_, _>>()?)
1002 })
1003 .await
1004 }
1005
1006 async fn get_maybe_stripped_state_events(
1007 &self,
1008 room_id: Key,
1009 event_type: Key,
1010 ) -> Result<Vec<(bool, Vec<u8>)>> {
1011 Ok(self
1012 .prepare(
1013 "SELECT stripped, data FROM state_event
1014 WHERE room_id = ? AND event_type = ?",
1015 |mut stmt| {
1016 stmt.query((room_id, event_type))?
1017 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1018 .collect()
1019 },
1020 )
1021 .await?)
1022 }
1023
1024 async fn get_profiles(
1025 &self,
1026 room_id: Key,
1027 user_ids: Vec<Key>,
1028 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
1029 let user_ids_length = user_ids.len();
1030
1031 self.chunk_large_query_over(user_ids, Some(user_ids_length), move |txn, user_ids| {
1032 let sql = format!(
1033 "SELECT user_id, data FROM profile WHERE room_id = ? AND user_id IN ({})",
1034 user_ids.host_parameters(),
1035 );
1036
1037 let params = rusqlite::params_from_iter(iter::once(room_id.clone()).chain(user_ids));
1038
1039 Ok(txn
1040 .prepare(&sql)?
1041 .query(params)?
1042 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1043 .collect::<Result<_, _>>()?)
1044 })
1045 .await
1046 }
1047
1048 async fn get_global_profiles(&self, user_ids: Vec<Key>) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
1049 let user_ids_length = user_ids.len();
1050
1051 self.chunk_large_query_over(user_ids, Some(user_ids_length), move |txn, user_ids| {
1052 let sql = format!(
1053 "SELECT user_id, profile_data FROM global_profiles WHERE user_id IN ({})",
1054 user_ids.host_parameters(),
1055 );
1056
1057 let params = rusqlite::params_from_iter(user_ids);
1058
1059 Ok(txn
1060 .prepare(&sql)?
1061 .query(params)?
1062 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1063 .collect::<Result<_, _>>()?)
1064 })
1065 .await
1066 }
1067
1068 async fn get_user_ids(&self, room_id: Key, memberships: Vec<Key>) -> Result<Vec<Vec<u8>>> {
1069 let res = if memberships.is_empty() {
1070 self.prepare("SELECT data FROM member WHERE room_id = ?", |mut stmt| {
1071 stmt.query((room_id,))?.mapped(|row| row.get(0)).collect()
1072 })
1073 .await?
1074 } else {
1075 self.chunk_large_query_over(memberships, None, move |txn, memberships| {
1076 let sql = format!(
1077 "SELECT data FROM member WHERE room_id = ? AND membership IN ({})",
1078 memberships.host_parameters(),
1079 );
1080
1081 let params =
1082 rusqlite::params_from_iter(iter::once(room_id.clone()).chain(memberships));
1083
1084 Ok(txn
1085 .prepare(&sql)?
1086 .query(params)?
1087 .mapped(|row| row.get(0))
1088 .collect::<Result<_, _>>()?)
1089 })
1090 .await?
1091 };
1092
1093 Ok(res)
1094 }
1095
1096 async fn get_global_account_data(&self, event_type: Key) -> Result<Option<Vec<u8>>> {
1097 Ok(self
1098 .query_one(
1099 "SELECT data FROM global_account_data WHERE event_type = ?",
1100 (event_type,),
1101 |row| row.get(0),
1102 )
1103 .await
1104 .optional()?)
1105 }
1106
1107 async fn get_room_account_data(
1108 &self,
1109 room_id: Key,
1110 event_type: Key,
1111 ) -> Result<Option<Vec<u8>>> {
1112 Ok(self
1113 .query_one(
1114 "SELECT data FROM room_account_data WHERE room_id = ? AND event_type = ?",
1115 (room_id, event_type),
1116 |row| row.get(0),
1117 )
1118 .await
1119 .optional()?)
1120 }
1121
1122 async fn get_display_names(
1123 &self,
1124 room_id: Key,
1125 names: Vec<Key>,
1126 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
1127 let names_length = names.len();
1128
1129 self.chunk_large_query_over(names, Some(names_length), move |txn, names| {
1130 let sql = format!(
1131 "SELECT name, data FROM display_name WHERE room_id = ? AND name IN ({})",
1132 names.host_parameters()
1133 );
1134
1135 let params = rusqlite::params_from_iter(iter::once(room_id.clone()).chain(names));
1136
1137 Ok(txn
1138 .prepare(&sql)?
1139 .query(params)?
1140 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1141 .collect::<Result<_, _>>()?)
1142 })
1143 .await
1144 }
1145
1146 async fn get_user_receipt(
1147 &self,
1148 room_id: Key,
1149 receipt_type: Key,
1150 receipt_thread: Key,
1151 user_id: Key,
1152 ) -> Result<Option<Vec<u8>>> {
1153 Ok(self
1154 .query_one(
1155 "SELECT data FROM receipt
1156 WHERE room_id = ? AND receipt_type = ? AND thread = ? and user_id = ?",
1157 (room_id, receipt_type, receipt_thread, user_id),
1158 |row| row.get(0),
1159 )
1160 .await
1161 .optional()?)
1162 }
1163
1164 async fn get_event_receipts(
1165 &self,
1166 room_id: Key,
1167 receipt_type: Key,
1168 thread: Key,
1169 event_id: Key,
1170 ) -> Result<Vec<Vec<u8>>> {
1171 Ok(self
1172 .prepare(
1173 "SELECT data FROM receipt
1174 WHERE room_id = ? AND receipt_type = ? AND thread = ? and event_id = ?",
1175 |mut stmt| {
1176 stmt.query((room_id, receipt_type, thread, event_id))?
1177 .mapped(|row| row.get(0))
1178 .collect()
1179 },
1180 )
1181 .await?)
1182 }
1183}
1184
1185#[async_trait]
1186impl SqliteObjectStateStoreExt for SqliteAsyncConn {
1187 async fn set_kv_blob(&self, key: Key, value: Vec<u8>) -> Result<()> {
1188 Ok(self.interact(move |conn| conn.set_kv_blob(&key, &value)).await.unwrap()?)
1189 }
1190}
1191
1192#[async_trait]
1193impl StateStore for SqliteStateStore {
1194 type Error = Error;
1195
1196 async fn get_kv_data(&self, key: StateStoreDataKey<'_>) -> Result<Option<StateStoreDataValue>> {
1197 self.read()
1198 .await?
1199 .get_kv_blob(self.encode_state_store_data_key(key))
1200 .await?
1201 .map(|data| {
1202 Ok(match key {
1203 StateStoreDataKey::SyncToken => {
1204 StateStoreDataValue::SyncToken(self.deserialize_value(&data)?)
1205 }
1206 StateStoreDataKey::SupportedVersions => {
1207 StateStoreDataValue::SupportedVersions(self.deserialize_value(&data)?)
1208 }
1209 StateStoreDataKey::WellKnown => {
1210 StateStoreDataValue::WellKnown(self.deserialize_value(&data)?)
1211 }
1212 StateStoreDataKey::Filter(_) => {
1213 StateStoreDataValue::Filter(self.deserialize_value(&data)?)
1214 }
1215 StateStoreDataKey::UserAvatarUrl(_) => {
1216 StateStoreDataValue::UserAvatarUrl(self.deserialize_value(&data)?)
1217 }
1218 StateStoreDataKey::RecentlyVisitedRooms(_) => {
1219 StateStoreDataValue::RecentlyVisitedRooms(self.deserialize_value(&data)?)
1220 }
1221 StateStoreDataKey::UtdHookManagerData => {
1222 StateStoreDataValue::UtdHookManagerData(self.deserialize_value(&data)?)
1223 }
1224 StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
1225 StateStoreDataValue::OneTimeKeyAlreadyUploaded
1226 }
1227 StateStoreDataKey::ComposerDraft(_, _) => {
1228 StateStoreDataValue::ComposerDraft(self.deserialize_value(&data)?)
1229 }
1230 StateStoreDataKey::SeenKnockRequests(_) => {
1231 StateStoreDataValue::SeenKnockRequests(self.deserialize_value(&data)?)
1232 }
1233 StateStoreDataKey::ThreadSubscriptionsCatchupTokens => {
1234 StateStoreDataValue::ThreadSubscriptionsCatchupTokens(
1235 self.deserialize_value(&data)?,
1236 )
1237 }
1238 StateStoreDataKey::HomeserverCapabilities => {
1239 StateStoreDataValue::HomeserverCapabilities(self.deserialize_value(&data)?)
1240 }
1241 })
1242 })
1243 .transpose()
1244 }
1245
1246 async fn set_kv_data(
1247 &self,
1248 key: StateStoreDataKey<'_>,
1249 value: StateStoreDataValue,
1250 ) -> Result<()> {
1251 let serialized_value = match key {
1252 StateStoreDataKey::SyncToken => self.serialize_value(
1253 &value.into_sync_token().expect("Session data not a sync token"),
1254 )?,
1255 StateStoreDataKey::SupportedVersions => self.serialize_value(
1256 &value
1257 .into_supported_versions()
1258 .expect("Session data not containing supported versions"),
1259 )?,
1260 StateStoreDataKey::WellKnown => self.serialize_value(
1261 &value.into_well_known().expect("Session data not containing well-known"),
1262 )?,
1263 StateStoreDataKey::Filter(_) => {
1264 self.serialize_value(&value.into_filter().expect("Session data not a filter"))?
1265 }
1266 StateStoreDataKey::UserAvatarUrl(_) => self.serialize_value(
1267 &value.into_user_avatar_url().expect("Session data not an user avatar url"),
1268 )?,
1269 StateStoreDataKey::RecentlyVisitedRooms(_) => self.serialize_value(
1270 &value.into_recently_visited_rooms().expect("Session data not breadcrumbs"),
1271 )?,
1272 StateStoreDataKey::UtdHookManagerData => self.serialize_value(
1273 &value.into_utd_hook_manager_data().expect("Session data not UtdHookManagerData"),
1274 )?,
1275 StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
1276 self.serialize_value(&true).expect("We should be able to serialize a boolean")
1277 }
1278 StateStoreDataKey::ComposerDraft(_, _) => self.serialize_value(
1279 &value.into_composer_draft().expect("Session data not a composer draft"),
1280 )?,
1281 StateStoreDataKey::SeenKnockRequests(_) => self.serialize_value(
1282 &value
1283 .into_seen_knock_requests()
1284 .expect("Session data is not a set of seen knock request ids"),
1285 )?,
1286 StateStoreDataKey::ThreadSubscriptionsCatchupTokens => self.serialize_value(
1287 &value
1288 .into_thread_subscriptions_catchup_tokens()
1289 .expect("Session data is not a list of thread subscription catchup tokens"),
1290 )?,
1291 StateStoreDataKey::HomeserverCapabilities => self.serialize_value(
1292 &value
1293 .into_homeserver_capabilities()
1294 .expect("Session data is not the homeserver capabilities"),
1295 )?,
1296 };
1297
1298 self.write()
1299 .await?
1300 .set_kv_blob(self.encode_state_store_data_key(key), serialized_value)
1301 .await
1302 }
1303
1304 async fn remove_kv_data(&self, key: StateStoreDataKey<'_>) -> Result<()> {
1305 self.write().await?.delete_kv_blob(self.encode_state_store_data_key(key)).await
1306 }
1307
1308 async fn save_changes(&self, changes: &StateChanges) -> Result<()> {
1309 let changes = changes.to_owned();
1310 let this = self.clone();
1311 self.write()
1312 .await?
1313 .with_transaction(move |txn| {
1314 let StateChanges {
1315 sync_token,
1316 account_data,
1317 presence,
1318 profiles,
1319 profiles_to_delete,
1320 state,
1321 room_account_data,
1322 room_infos,
1323 receipts,
1324 redactions,
1325 stripped_state,
1326 ambiguity_maps,
1327 global_profiles,
1328 } = changes;
1329
1330 if let Some(sync_token) = sync_token {
1331 let key = this.encode_state_store_data_key(StateStoreDataKey::SyncToken);
1332 let value = this.serialize_value(&sync_token)?;
1333 txn.set_kv_blob(&key, &value)?;
1334 }
1335
1336 for (event_type, event) in account_data {
1337 let event_type =
1338 this.encode_key(keys::GLOBAL_ACCOUNT_DATA, event_type.to_string());
1339 let data = this.serialize_json(&event)?;
1340 txn.set_global_account_data(&event_type, &data)?;
1341 }
1342
1343 for (room_id, events) in room_account_data {
1344 let room_id = this.encode_key(keys::ROOM_ACCOUNT_DATA, room_id);
1345 for (event_type, event) in events {
1346 let event_type =
1347 this.encode_key(keys::ROOM_ACCOUNT_DATA, event_type.to_string());
1348 let data = this.serialize_json(&event)?;
1349 txn.set_room_account_data(&room_id, &event_type, &data)?;
1350 }
1351 }
1352
1353 for (user_id, event) in presence {
1354 let key = this.encode_presence_key(&user_id);
1355 let value = this.serialize_json(&event)?;
1356 txn.set_kv_blob(&key, &value)?;
1357 }
1358
1359 for (room_id, room_info) in room_infos {
1360 let stripped = matches!(room_info.state(), RoomState::Invited | RoomState::Knocked);
1362
1363 if !stripped || stripped_state.contains_key(&room_id) {
1371 this.remove_maybe_stripped_room_data(txn, &room_id, !stripped)?;
1372 }
1373
1374 let room_id = this.encode_key(keys::ROOM_INFO, room_id);
1375 let state = this
1376 .encode_key(keys::ROOM_INFO, serde_json::to_string(&room_info.state())?);
1377 let data = this.serialize_json(&room_info)?;
1378 txn.set_room_info(&room_id, &state, &data)?;
1379 }
1380
1381 for (room_id, user_ids) in profiles_to_delete {
1382 let room_id = this.encode_key(keys::PROFILE, room_id);
1383 for user_id in user_ids {
1384 let user_id = this.encode_key(keys::PROFILE, user_id);
1385 txn.remove_room_profile(&room_id, &user_id)?;
1386 }
1387 }
1388
1389 for (room_id, state_event_types) in state {
1390 let profiles = profiles.get(&room_id);
1391 let encoded_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
1392
1393 for (event_type, state_events) in state_event_types {
1394 let encoded_event_type =
1395 this.encode_key(keys::STATE_EVENT, event_type.to_string());
1396
1397 for (state_key, raw_state_event) in state_events {
1398 let encoded_state_key = this.encode_key(keys::STATE_EVENT, &state_key);
1399 let data = this.serialize_json(&raw_state_event)?;
1400
1401 let event_id: Option<String> =
1402 raw_state_event.get_field("event_id").ok().flatten();
1403 let encoded_event_id =
1404 event_id.as_ref().map(|e| this.encode_key(keys::STATE_EVENT, e));
1405
1406 txn.set_state_event(
1407 &encoded_room_id,
1408 &encoded_event_type,
1409 &encoded_state_key,
1410 false,
1411 encoded_event_id.as_deref(),
1412 &data,
1413 )?;
1414
1415 if event_type == StateEventType::RoomMember {
1416 let member_event = match raw_state_event
1417 .deserialize_as_unchecked::<SyncRoomMemberEvent>()
1418 {
1419 Ok(ev) => ev,
1420 Err(e) => {
1421 debug!(event_id, "Failed to deserialize member event: {e}");
1422 continue;
1423 }
1424 };
1425
1426 let encoded_room_id = this.encode_key(keys::MEMBER, &room_id);
1427 let user_id = this.encode_key(keys::MEMBER, &state_key);
1428 let membership = this
1429 .encode_key(keys::MEMBER, member_event.membership().as_str());
1430 let data = this.serialize_value(&state_key)?;
1431
1432 txn.set_member(
1433 &encoded_room_id,
1434 &user_id,
1435 &membership,
1436 false,
1437 &data,
1438 )?;
1439
1440 if let Some(profile) =
1441 profiles.and_then(|p| p.get(member_event.state_key()))
1442 {
1443 let room_id = this.encode_key(keys::PROFILE, &room_id);
1444 let user_id = this.encode_key(keys::PROFILE, &state_key);
1445 let data = this.serialize_json(&profile)?;
1446 txn.set_profile(&room_id, &user_id, &data)?;
1447 }
1448 }
1449 }
1450 }
1451 }
1452
1453 for (room_id, stripped_state_event_types) in stripped_state {
1454 let encoded_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
1455
1456 for (event_type, stripped_state_events) in stripped_state_event_types {
1457 let encoded_event_type =
1458 this.encode_key(keys::STATE_EVENT, event_type.to_string());
1459
1460 for (state_key, raw_stripped_state_event) in stripped_state_events {
1461 let encoded_state_key = this.encode_key(keys::STATE_EVENT, &state_key);
1462 let data = this.serialize_json(&raw_stripped_state_event)?;
1463 txn.set_state_event(
1464 &encoded_room_id,
1465 &encoded_event_type,
1466 &encoded_state_key,
1467 true,
1468 None,
1469 &data,
1470 )?;
1471
1472 if event_type == StateEventType::RoomMember {
1473 let member_event = match raw_stripped_state_event
1474 .deserialize_as_unchecked::<StrippedRoomMemberEvent>(
1475 ) {
1476 Ok(ev) => ev,
1477 Err(e) => {
1478 debug!("Failed to deserialize stripped member event: {e}");
1479 continue;
1480 }
1481 };
1482
1483 let room_id = this.encode_key(keys::MEMBER, &room_id);
1484 let user_id = this.encode_key(keys::MEMBER, &state_key);
1485 let membership = this.encode_key(
1486 keys::MEMBER,
1487 member_event.content.membership.as_str(),
1488 );
1489 let data = this.serialize_value(&state_key)?;
1490
1491 txn.set_member(&room_id, &user_id, &membership, true, &data)?;
1492 }
1493 }
1494 }
1495 }
1496
1497 for (room_id, receipt_event) in receipts {
1498 let room_id = this.encode_key(keys::RECEIPT, room_id);
1499
1500 for (event_id, receipt_types) in receipt_event {
1501 let encoded_event_id = this.encode_key(keys::RECEIPT, &event_id);
1502
1503 for (receipt_type, receipt_users) in receipt_types {
1504 let receipt_type =
1505 this.encode_key(keys::RECEIPT, receipt_type.as_str());
1506
1507 for (user_id, receipt) in receipt_users {
1508 let encoded_user_id = this.encode_key(keys::RECEIPT, &user_id);
1509 let thread = this.encode_key(
1513 keys::RECEIPT,
1514 rmp_serde::to_vec_named(&receipt.thread)?,
1515 );
1516 let data = this.serialize_json(&ReceiptData {
1517 receipt,
1518 event_id: event_id.clone(),
1519 user_id,
1520 })?;
1521
1522 txn.set_receipt(
1523 &room_id,
1524 &encoded_user_id,
1525 &receipt_type,
1526 &thread,
1527 &encoded_event_id,
1528 &data,
1529 )?;
1530 }
1531 }
1532 }
1533 }
1534
1535 for (room_id, redactions) in redactions {
1536 let make_redaction_rules = || {
1537 let encoded_room_id = this.encode_key(keys::ROOM_INFO, &room_id);
1538 txn.get_room_info(&encoded_room_id)
1539 .ok()
1540 .flatten()
1541 .and_then(|v| this.deserialize_json::<RoomInfo>(&v).ok())
1542 .map(|info| info.room_version_rules_or_default())
1543 .unwrap_or_else(|| {
1544 warn!(
1545 ?room_id,
1546 "Unable to get the room version rules, defaulting to rules for room version {ROOM_VERSION_FALLBACK}"
1547 );
1548 ROOM_VERSION_RULES_FALLBACK
1549 }).redaction
1550 };
1551
1552 let encoded_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
1553 let mut redaction_rules = None;
1554
1555 for (event_id, redaction) in redactions {
1556 let event_id = this.encode_key(keys::STATE_EVENT, event_id);
1557
1558 if let Some(Ok(raw_event)) = txn
1559 .get_state_event_by_id(&encoded_room_id, &event_id)?
1560 .map(|value| this.deserialize_json::<Raw<AnySyncStateEvent>>(&value))
1561 {
1562 let event = raw_event.deserialize()?;
1563 let redacted = redact(
1564 raw_event.deserialize_as::<CanonicalJsonObject>()?,
1565 redaction_rules.get_or_insert_with(make_redaction_rules),
1566 Some(RedactedBecause::from_raw_event(&redaction)?),
1567 )
1568 .map_err(Error::Redaction)?;
1569 let data = this.serialize_json(&redacted)?;
1570
1571 let event_type =
1572 this.encode_key(keys::STATE_EVENT, event.event_type().to_string());
1573 let state_key = this.encode_key(keys::STATE_EVENT, event.state_key());
1574
1575 txn.set_state_event(
1576 &encoded_room_id,
1577 &event_type,
1578 &state_key,
1579 false,
1580 Some(&event_id),
1581 &data,
1582 )?;
1583 }
1584 }
1585 }
1586
1587 for (room_id, display_names) in ambiguity_maps {
1588 let room_id = this.encode_key(keys::DISPLAY_NAME, room_id);
1589
1590 for (name, user_ids) in display_names {
1591 let encoded_name = this.encode_key(
1592 keys::DISPLAY_NAME,
1593 name.as_normalized_str().unwrap_or_else(|| name.as_raw_str()),
1594 );
1595 let data = this.serialize_json(&user_ids)?;
1596
1597 if user_ids.is_empty() {
1598 txn.remove_display_name(&room_id, &encoded_name)?;
1599
1600 let raw_name = this.encode_key(keys::DISPLAY_NAME, name.as_raw_str());
1619 txn.remove_display_name(&room_id, &raw_name)?;
1620 } else {
1621 txn.set_display_name(&room_id, &encoded_name, &data)?;
1623 }
1624 }
1625 }
1626
1627 for (raw_user_id, profile_update) in global_profiles {
1628 let user_id = this.encode_key(keys::GLOBAL_PROFILES, &raw_user_id);
1629 match profile_update {
1630 UserProfileUpdate::Updated(profile_changes) => {
1631 let existing_data: Option<Vec<u8>> = txn
1632 .prepare_cached(
1633 "SELECT profile_data FROM global_profiles WHERE user_id = ?",
1634 )?
1635 .query_one([&user_id], |row| row.get(0))
1636 .optional()?;
1637
1638 let mut profile: UserProfile = existing_data
1639 .map(|data| this.deserialize_json(&data))
1640 .transpose()?
1641 .unwrap_or_default();
1642 profile.apply(profile_changes);
1643
1644 let serialized = this.serialize_json(&profile)?;
1645 txn.prepare_cached(
1646 "INSERT OR REPLACE INTO global_profiles (user_id, profile_data) VALUES (?, ?)",
1647 )?
1648 .execute((&user_id, serialized))?;
1649 }
1650 UserProfileUpdate::Dropped => {
1652 txn.prepare_cached("DELETE FROM global_profiles WHERE user_id = ?")?
1653 .execute([&user_id])?;
1654 }
1655 _ => {
1656 warn!(%raw_user_id, "Unhandled UserProfileUpdate variant; ignoring");
1657 }
1658 }
1659 }
1660
1661 Ok::<_, Error>(())
1662 })
1663 .await?;
1664
1665 Ok(())
1666 }
1667
1668 async fn get_presence_event(&self, user_id: &UserId) -> Result<Option<Raw<PresenceEvent>>> {
1669 self.read()
1670 .await?
1671 .get_kv_blob(self.encode_presence_key(user_id))
1672 .await?
1673 .map(|data| self.deserialize_json(&data))
1674 .transpose()
1675 }
1676
1677 async fn get_presence_events(
1678 &self,
1679 user_ids: &[OwnedUserId],
1680 ) -> Result<Vec<Raw<PresenceEvent>>> {
1681 if user_ids.is_empty() {
1682 return Ok(Vec::new());
1683 }
1684
1685 let user_ids = user_ids.iter().map(|u| self.encode_presence_key(u)).collect();
1686 self.read()
1687 .await?
1688 .get_kv_blobs(user_ids)
1689 .await?
1690 .into_iter()
1691 .map(|data| self.deserialize_json(&data))
1692 .collect()
1693 }
1694
1695 async fn get_state_event(
1696 &self,
1697 room_id: &RoomId,
1698 event_type: StateEventType,
1699 state_key: &str,
1700 ) -> Result<Option<RawAnySyncOrStrippedState>> {
1701 Ok(self
1702 .get_state_events_for_keys(room_id, event_type, &[state_key])
1703 .await?
1704 .into_iter()
1705 .next())
1706 }
1707
1708 async fn get_state_events(
1709 &self,
1710 room_id: &RoomId,
1711 event_type: StateEventType,
1712 ) -> Result<Vec<RawAnySyncOrStrippedState>> {
1713 let room_id = self.encode_key(keys::STATE_EVENT, room_id);
1714 let event_type = self.encode_key(keys::STATE_EVENT, event_type.to_string());
1715 self.read()
1716 .await?
1717 .get_maybe_stripped_state_events(room_id, event_type)
1718 .await?
1719 .into_iter()
1720 .map(|(stripped, data)| {
1721 let ev = if stripped {
1722 RawAnySyncOrStrippedState::Stripped(self.deserialize_json(&data)?)
1723 } else {
1724 RawAnySyncOrStrippedState::Sync(self.deserialize_json(&data)?)
1725 };
1726
1727 Ok(ev)
1728 })
1729 .collect()
1730 }
1731
1732 async fn get_state_events_for_keys(
1733 &self,
1734 room_id: &RoomId,
1735 event_type: StateEventType,
1736 state_keys: &[&str],
1737 ) -> Result<Vec<RawAnySyncOrStrippedState>, Self::Error> {
1738 if state_keys.is_empty() {
1739 return Ok(Vec::new());
1740 }
1741
1742 let room_id = self.encode_key(keys::STATE_EVENT, room_id);
1743 let event_type = self.encode_key(keys::STATE_EVENT, event_type.to_string());
1744 let state_keys = state_keys.iter().map(|k| self.encode_key(keys::STATE_EVENT, k)).collect();
1745 self.read()
1746 .await?
1747 .get_maybe_stripped_state_events_for_keys(room_id, event_type, state_keys)
1748 .await?
1749 .into_iter()
1750 .map(|(stripped, data)| {
1751 let ev = if stripped {
1752 RawAnySyncOrStrippedState::Stripped(self.deserialize_json(&data)?)
1753 } else {
1754 RawAnySyncOrStrippedState::Sync(self.deserialize_json(&data)?)
1755 };
1756
1757 Ok(ev)
1758 })
1759 .collect()
1760 }
1761
1762 async fn get_profile(
1763 &self,
1764 room_id: &RoomId,
1765 user_id: &UserId,
1766 ) -> Result<Option<MinimalRoomMemberEvent>> {
1767 let room_id = self.encode_key(keys::PROFILE, room_id);
1768 let user_ids = vec![self.encode_key(keys::PROFILE, user_id)];
1769
1770 self.read()
1771 .await?
1772 .get_profiles(room_id, user_ids)
1773 .await?
1774 .into_iter()
1775 .next()
1776 .map(|(_, data)| self.deserialize_json(&data))
1777 .transpose()
1778 }
1779
1780 async fn get_profiles<'a>(
1781 &self,
1782 room_id: &RoomId,
1783 user_ids: &'a [OwnedUserId],
1784 ) -> Result<BTreeMap<&'a UserId, MinimalRoomMemberEvent>> {
1785 if user_ids.is_empty() {
1786 return Ok(BTreeMap::new());
1787 }
1788
1789 let room_id = self.encode_key(keys::PROFILE, room_id);
1790 let mut user_ids_map = user_ids
1791 .iter()
1792 .map(|u| (self.encode_key(keys::PROFILE, u), u.as_ref()))
1793 .collect::<BTreeMap<_, _>>();
1794 let user_ids = user_ids_map.keys().cloned().collect();
1795
1796 self.read()
1797 .await?
1798 .get_profiles(room_id, user_ids)
1799 .await?
1800 .into_iter()
1801 .map(|(user_id, data)| {
1802 Ok((
1803 user_ids_map
1804 .remove(user_id.as_slice())
1805 .expect("returned user IDs were requested"),
1806 self.deserialize_json(&data)?,
1807 ))
1808 })
1809 .collect()
1810 }
1811
1812 async fn get_user_ids(
1813 &self,
1814 room_id: &RoomId,
1815 membership: RoomMemberships,
1816 ) -> Result<Vec<OwnedUserId>> {
1817 let room_id = self.encode_key(keys::MEMBER, room_id);
1818 let memberships = membership
1819 .as_vec()
1820 .into_iter()
1821 .map(|m| self.encode_key(keys::MEMBER, m.as_str()))
1822 .collect();
1823 self.read()
1824 .await?
1825 .get_user_ids(room_id, memberships)
1826 .await?
1827 .iter()
1828 .map(|data| self.deserialize_value(data))
1829 .collect()
1830 }
1831
1832 async fn get_room_infos(&self, room_load_settings: &RoomLoadSettings) -> Result<Vec<RoomInfo>> {
1833 self.read()
1834 .await?
1835 .get_room_infos(match room_load_settings {
1836 RoomLoadSettings::All => None,
1837 RoomLoadSettings::One(room_id) => Some(self.encode_key(keys::ROOM_INFO, room_id)),
1838 })
1839 .await?
1840 .into_iter()
1841 .map(|data| self.deserialize_json(&data))
1842 .collect()
1843 }
1844
1845 async fn get_users_with_display_name(
1846 &self,
1847 room_id: &RoomId,
1848 display_name: &DisplayName,
1849 ) -> Result<BTreeSet<OwnedUserId>> {
1850 let room_id = self.encode_key(keys::DISPLAY_NAME, room_id);
1851 let names = vec![self.encode_key(
1852 keys::DISPLAY_NAME,
1853 display_name.as_normalized_str().unwrap_or_else(|| display_name.as_raw_str()),
1854 )];
1855
1856 Ok(self
1857 .read()
1858 .await?
1859 .get_display_names(room_id, names)
1860 .await?
1861 .into_iter()
1862 .next()
1863 .map(|(_, data)| self.deserialize_json(&data))
1864 .transpose()?
1865 .unwrap_or_default())
1866 }
1867
1868 async fn get_users_with_display_names<'a>(
1869 &self,
1870 room_id: &RoomId,
1871 display_names: &'a [DisplayName],
1872 ) -> Result<HashMap<&'a DisplayName, BTreeSet<OwnedUserId>>> {
1873 let mut result = HashMap::new();
1874
1875 if display_names.is_empty() {
1876 return Ok(result);
1877 }
1878
1879 let room_id = self.encode_key(keys::DISPLAY_NAME, room_id);
1880 let mut names_map = display_names
1881 .iter()
1882 .flat_map(|display_name| {
1883 let raw =
1896 (self.encode_key(keys::DISPLAY_NAME, display_name.as_raw_str()), display_name);
1897 let normalized = display_name.as_normalized_str().map(|normalized| {
1898 (self.encode_key(keys::DISPLAY_NAME, normalized), display_name)
1899 });
1900
1901 iter::once(raw).chain(normalized)
1902 })
1903 .collect::<BTreeMap<_, _>>();
1904 let names = names_map.keys().cloned().collect();
1905
1906 for (name, data) in self.read().await?.get_display_names(room_id, names).await?.into_iter()
1907 {
1908 let display_name =
1909 names_map.remove(name.as_slice()).expect("returned display names were requested");
1910 let user_ids: BTreeSet<_> = self.deserialize_json(&data)?;
1911
1912 result.entry(display_name).or_insert_with(BTreeSet::new).extend(user_ids);
1913 }
1914
1915 Ok(result)
1916 }
1917
1918 async fn get_account_data_event(
1919 &self,
1920 event_type: GlobalAccountDataEventType,
1921 ) -> Result<Option<Raw<AnyGlobalAccountDataEvent>>> {
1922 let event_type = self.encode_key(keys::GLOBAL_ACCOUNT_DATA, event_type.to_string());
1923 self.read()
1924 .await?
1925 .get_global_account_data(event_type)
1926 .await?
1927 .map(|value| self.deserialize_json(&value))
1928 .transpose()
1929 }
1930
1931 async fn get_room_account_data_event(
1932 &self,
1933 room_id: &RoomId,
1934 event_type: RoomAccountDataEventType,
1935 ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>> {
1936 let room_id = self.encode_key(keys::ROOM_ACCOUNT_DATA, room_id);
1937 let event_type = self.encode_key(keys::ROOM_ACCOUNT_DATA, event_type.to_string());
1938 self.read()
1939 .await?
1940 .get_room_account_data(room_id, event_type)
1941 .await?
1942 .map(|value| self.deserialize_json(&value))
1943 .transpose()
1944 }
1945
1946 async fn get_user_room_receipt_event(
1947 &self,
1948 room_id: &RoomId,
1949 receipt_type: ReceiptType,
1950 receipt_thread: &ReceiptThread,
1951 user_id: &UserId,
1952 ) -> Result<Option<(OwnedEventId, Receipt)>> {
1953 let room_id = self.encode_key(keys::RECEIPT, room_id);
1954 let receipt_type = self.encode_key(keys::RECEIPT, receipt_type.to_string());
1955 let receipt_thread =
1958 self.encode_key(keys::RECEIPT, rmp_serde::to_vec_named(receipt_thread)?);
1959 let user_id = self.encode_key(keys::RECEIPT, user_id);
1960
1961 self.read()
1962 .await?
1963 .get_user_receipt(room_id, receipt_type, receipt_thread, user_id)
1964 .await?
1965 .map(|value| {
1966 self.deserialize_json::<ReceiptData>(&value).map(|d| (d.event_id, d.receipt))
1967 })
1968 .transpose()
1969 }
1970
1971 async fn get_event_room_receipt_events(
1972 &self,
1973 room_id: &RoomId,
1974 receipt_type: ReceiptType,
1975 receipt_thread: &ReceiptThread,
1976 event_id: &EventId,
1977 ) -> Result<Vec<(OwnedUserId, Receipt)>> {
1978 let room_id = self.encode_key(keys::RECEIPT, room_id);
1979 let receipt_type = self.encode_key(keys::RECEIPT, receipt_type.to_string());
1980 let receipt_thread =
1983 self.encode_key(keys::RECEIPT, rmp_serde::to_vec_named(receipt_thread)?);
1984 let event_id = self.encode_key(keys::RECEIPT, event_id);
1985
1986 self.read()
1987 .await?
1988 .get_event_receipts(room_id, receipt_type, receipt_thread, event_id)
1989 .await?
1990 .iter()
1991 .map(|value| {
1992 self.deserialize_json::<ReceiptData>(value).map(|d| (d.user_id, d.receipt))
1993 })
1994 .collect()
1995 }
1996
1997 async fn get_custom_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
1998 self.read().await?.get_kv_blob(self.encode_custom_key(key)).await
1999 }
2000
2001 async fn set_custom_value_no_read(&self, key: &[u8], value: Vec<u8>) -> Result<()> {
2002 let conn = self.write().await?;
2003 let key = self.encode_custom_key(key);
2004 conn.set_kv_blob(key, value).await?;
2005 Ok(())
2006 }
2007
2008 async fn set_custom_value(&self, key: &[u8], value: Vec<u8>) -> Result<Option<Vec<u8>>> {
2009 let conn = self.write().await?;
2010 let key = self.encode_custom_key(key);
2011 let previous = conn.get_kv_blob(key.clone()).await?;
2012 conn.set_kv_blob(key, value).await?;
2013 Ok(previous)
2014 }
2015
2016 async fn remove_custom_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
2017 let conn = self.write().await?;
2018 let key = self.encode_custom_key(key);
2019 let previous = conn.get_kv_blob(key.clone()).await?;
2020 if previous.is_some() {
2021 conn.delete_kv_blob(key).await?;
2022 }
2023 Ok(previous)
2024 }
2025
2026 async fn remove_room(&self, room_id: &RoomId) -> Result<()> {
2027 let this = self.clone();
2028 let room_id = room_id.to_owned();
2029
2030 let conn = self.write().await?;
2031
2032 conn.with_transaction(move |txn| -> Result<()> {
2033 let room_info_room_id = this.encode_key(keys::ROOM_INFO, &room_id);
2034 txn.remove_room_info(&room_info_room_id)?;
2035
2036 let state_event_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
2037 txn.remove_room_state_events(&state_event_room_id, None)?;
2038
2039 let member_room_id = this.encode_key(keys::MEMBER, &room_id);
2040 txn.remove_room_members(&member_room_id, None)?;
2041
2042 let profile_room_id = this.encode_key(keys::PROFILE, &room_id);
2043 txn.remove_room_profiles(&profile_room_id)?;
2044
2045 let room_account_data_room_id = this.encode_key(keys::ROOM_ACCOUNT_DATA, &room_id);
2046 txn.remove_room_account_data(&room_account_data_room_id)?;
2047
2048 let receipt_room_id = this.encode_key(keys::RECEIPT, &room_id);
2049 txn.remove_room_receipts(&receipt_room_id)?;
2050
2051 let display_name_room_id = this.encode_key(keys::DISPLAY_NAME, &room_id);
2052 txn.remove_room_display_names(&display_name_room_id)?;
2053
2054 let send_queue_room_id = this.encode_key(keys::SEND_QUEUE, &room_id);
2055 txn.remove_room_send_queue(&send_queue_room_id)?;
2056
2057 let dependent_send_queue_room_id =
2058 this.encode_key(keys::DEPENDENTS_SEND_QUEUE, &room_id);
2059 txn.remove_room_dependent_send_queue(&dependent_send_queue_room_id)?;
2060
2061 let thread_subscriptions_room_id =
2062 this.encode_key(keys::THREAD_SUBSCRIPTIONS, &room_id);
2063 txn.execute(
2064 "DELETE FROM thread_subscriptions WHERE room_id = ?",
2065 (thread_subscriptions_room_id,),
2066 )?;
2067
2068 Ok(())
2069 })
2070 .await?;
2071
2072 conn.vacuum().await
2073 }
2074
2075 async fn save_send_queue_request(
2076 &self,
2077 room_id: &RoomId,
2078 transaction_id: OwnedTransactionId,
2079 created_at: MilliSecondsSinceUnixEpoch,
2080 content: QueuedRequestKind,
2081 priority: usize,
2082 ) -> Result<(), Self::Error> {
2083 let room_id_key = self.encode_key(keys::SEND_QUEUE, room_id);
2084 let room_id_value = self.serialize_value(&room_id.to_owned())?;
2085
2086 let content = self.serialize_json(&content)?;
2087 let created_at_ts: u64 = created_at.0.into();
2094 self.write()
2095 .await?
2096 .with_transaction(move |txn| {
2097 txn.prepare_cached("INSERT INTO send_queue_events (room_id, room_id_val, transaction_id, content, priority, created_at) VALUES (?, ?, ?, ?, ?, ?)")?.execute((room_id_key, room_id_value, transaction_id.to_string(), content, priority, created_at_ts))?;
2098 Ok(())
2099 })
2100 .await
2101 }
2102
2103 async fn update_send_queue_request(
2104 &self,
2105 room_id: &RoomId,
2106 transaction_id: &TransactionId,
2107 content: QueuedRequestKind,
2108 ) -> Result<bool, Self::Error> {
2109 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2110
2111 let content = self.serialize_json(&content)?;
2112 let transaction_id = transaction_id.to_string();
2115
2116 let num_updated = self.write()
2117 .await?
2118 .with_transaction(move |txn| {
2119 txn.prepare_cached("UPDATE send_queue_events SET wedge_reason = NULL, content = ? WHERE room_id = ? AND transaction_id = ?")?.execute((content, room_id, transaction_id))
2120 })
2121 .await?;
2122
2123 Ok(num_updated > 0)
2124 }
2125
2126 async fn remove_send_queue_request(
2127 &self,
2128 room_id: &RoomId,
2129 transaction_id: &TransactionId,
2130 ) -> Result<bool, Self::Error> {
2131 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2132
2133 let transaction_id = transaction_id.to_string();
2135
2136 let num_deleted = self
2137 .write()
2138 .await?
2139 .with_transaction(move |txn| {
2140 txn.prepare_cached(
2141 "DELETE FROM send_queue_events WHERE room_id = ? AND transaction_id = ?",
2142 )?
2143 .execute((room_id, &transaction_id))
2144 })
2145 .await?;
2146
2147 Ok(num_deleted > 0)
2148 }
2149
2150 async fn load_send_queue_requests(
2151 &self,
2152 room_id: &RoomId,
2153 ) -> Result<Vec<QueuedRequest>, Self::Error> {
2154 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2155
2156 let res: Vec<(String, Vec<u8>, Option<Vec<u8>>, usize, Option<u64>)> = self
2161 .read()
2162 .await?
2163 .prepare(
2164 "SELECT transaction_id, content, wedge_reason, priority, created_at FROM send_queue_events WHERE room_id = ? ORDER BY priority DESC, ROWID",
2165 |mut stmt| {
2166 stmt.query((room_id,))?
2167 .mapped(|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?)))
2168 .collect()
2169 },
2170 )
2171 .await?;
2172
2173 let mut requests = Vec::with_capacity(res.len());
2174
2175 for entry in res {
2176 let created_at = entry
2177 .4
2178 .and_then(UInt::new)
2179 .map_or_else(MilliSecondsSinceUnixEpoch::now, MilliSecondsSinceUnixEpoch);
2180
2181 requests.push(QueuedRequest {
2182 transaction_id: entry.0.into(),
2183 kind: self.deserialize_json(&entry.1)?,
2184 error: entry.2.map(|v| self.deserialize_value(&v)).transpose()?,
2185 priority: entry.3,
2186 created_at,
2187 });
2188 }
2189
2190 Ok(requests)
2191 }
2192
2193 async fn update_send_queue_request_status(
2194 &self,
2195 room_id: &RoomId,
2196 transaction_id: &TransactionId,
2197 error: Option<QueueWedgeError>,
2198 ) -> Result<(), Self::Error> {
2199 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2200
2201 let transaction_id = transaction_id.to_string();
2203
2204 let error_value = error.map(|e| self.serialize_value(&e)).transpose()?;
2207
2208 self.write()
2209 .await?
2210 .with_transaction(move |txn| {
2211 txn.prepare_cached("UPDATE send_queue_events SET wedge_reason = ? WHERE room_id = ? AND transaction_id = ?")?.execute((error_value, room_id, transaction_id))?;
2212 Ok(())
2213 })
2214 .await
2215 }
2216
2217 async fn load_rooms_with_unsent_requests(&self) -> Result<Vec<OwnedRoomId>, Self::Error> {
2218 let res: Vec<Vec<u8>> = self
2224 .read()
2225 .await?
2226 .prepare("SELECT room_id_val FROM send_queue_events", |mut stmt| {
2227 stmt.query(())?.mapped(|row| row.get(0)).collect()
2228 })
2229 .await?;
2230
2231 Ok(res
2234 .into_iter()
2235 .map(|entry| self.deserialize_value(&entry))
2236 .collect::<Result<BTreeSet<OwnedRoomId>, _>>()?
2237 .into_iter()
2238 .collect())
2239 }
2240
2241 async fn save_dependent_queued_request(
2242 &self,
2243 room_id: &RoomId,
2244 parent_txn_id: &TransactionId,
2245 own_txn_id: ChildTransactionId,
2246 created_at: MilliSecondsSinceUnixEpoch,
2247 content: DependentQueuedRequestKind,
2248 ) -> Result<()> {
2249 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2250 let content = self.serialize_json(&content)?;
2251
2252 let parent_txn_id = parent_txn_id.to_string();
2254 let own_txn_id = own_txn_id.to_string();
2255
2256 let created_at_ts: u64 = created_at.0.into();
2257 self.write()
2258 .await?
2259 .with_transaction(move |txn| {
2260 txn.prepare_cached(
2261 r#"INSERT INTO dependent_send_queue_events
2262 (room_id, parent_transaction_id, own_transaction_id, content, created_at)
2263 VALUES (?, ?, ?, ?, ?)"#,
2264 )?
2265 .execute((
2266 room_id,
2267 parent_txn_id,
2268 own_txn_id,
2269 content,
2270 created_at_ts,
2271 ))?;
2272 Ok(())
2273 })
2274 .await
2275 }
2276
2277 async fn update_dependent_queued_request(
2278 &self,
2279 room_id: &RoomId,
2280 own_transaction_id: &ChildTransactionId,
2281 new_content: DependentQueuedRequestKind,
2282 ) -> Result<bool> {
2283 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2284 let content = self.serialize_json(&new_content)?;
2285
2286 let own_txn_id = own_transaction_id.to_string();
2288
2289 let num_updated = self
2290 .write()
2291 .await?
2292 .with_transaction(move |txn| {
2293 txn.prepare_cached(
2294 r#"UPDATE dependent_send_queue_events
2295 SET content = ?
2296 WHERE own_transaction_id = ?
2297 AND room_id = ?"#,
2298 )?
2299 .execute((content, own_txn_id, room_id))
2300 })
2301 .await?;
2302
2303 if num_updated > 1 {
2304 return Err(Error::InconsistentUpdate);
2305 }
2306
2307 Ok(num_updated == 1)
2308 }
2309
2310 async fn mark_dependent_queued_requests_as_ready(
2311 &self,
2312 room_id: &RoomId,
2313 parent_txn_id: &TransactionId,
2314 parent_key: SentRequestKey,
2315 ) -> Result<usize> {
2316 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2317 let parent_key = self.serialize_json(&parent_key)?;
2318
2319 let parent_txn_id = parent_txn_id.to_string();
2321
2322 self.write()
2323 .await?
2324 .with_transaction(move |txn| {
2325 Ok(txn.prepare_cached(
2326 "UPDATE dependent_send_queue_events SET parent_key = ? WHERE parent_transaction_id = ? and room_id = ?",
2327 )?
2328 .execute((parent_key, parent_txn_id, room_id))?)
2329 })
2330 .await
2331 }
2332
2333 async fn remove_dependent_queued_request(
2334 &self,
2335 room_id: &RoomId,
2336 txn_id: &ChildTransactionId,
2337 ) -> Result<bool> {
2338 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2339
2340 let txn_id = txn_id.to_string();
2342
2343 let num_deleted = self
2344 .write()
2345 .await?
2346 .with_transaction(move |txn| {
2347 txn.prepare_cached(
2348 "DELETE FROM dependent_send_queue_events WHERE own_transaction_id = ? AND room_id = ?",
2349 )?
2350 .execute((txn_id, room_id))
2351 })
2352 .await?;
2353
2354 Ok(num_deleted > 0)
2355 }
2356
2357 async fn load_dependent_queued_requests(
2358 &self,
2359 room_id: &RoomId,
2360 ) -> Result<Vec<DependentQueuedRequest>> {
2361 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2362
2363 let res: Vec<(String, String, Option<Vec<u8>>, Vec<u8>, Option<u64>)> = self
2366 .read()
2367 .await?
2368 .prepare(
2369 "SELECT own_transaction_id, parent_transaction_id, parent_key, content, created_at FROM dependent_send_queue_events WHERE room_id = ? ORDER BY ROWID",
2370 |mut stmt| {
2371 stmt.query((room_id,))?
2372 .mapped(|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?)))
2373 .collect()
2374 },
2375 )
2376 .await?;
2377
2378 let mut dependent_events = Vec::with_capacity(res.len());
2379
2380 for entry in res {
2381 let created_at = entry
2382 .4
2383 .and_then(UInt::new)
2384 .map_or_else(MilliSecondsSinceUnixEpoch::now, MilliSecondsSinceUnixEpoch);
2385
2386 dependent_events.push(DependentQueuedRequest {
2387 own_transaction_id: entry.0.into(),
2388 parent_transaction_id: entry.1.into(),
2389 parent_key: entry.2.map(|json| self.deserialize_json(&json)).transpose()?,
2390 kind: self.deserialize_json(&entry.3)?,
2391 created_at,
2392 });
2393 }
2394
2395 Ok(dependent_events)
2396 }
2397
2398 async fn upsert_thread_subscriptions(
2399 &self,
2400 updates: Vec<(&RoomId, &EventId, StoredThreadSubscription)>,
2401 ) -> Result<(), Self::Error> {
2402 let values: Vec<_> = updates
2403 .into_iter()
2404 .map(|(room_id, thread_id, subscription)| {
2405 (
2406 self.encode_key(keys::THREAD_SUBSCRIPTIONS, room_id),
2407 self.encode_key(keys::THREAD_SUBSCRIPTIONS, thread_id),
2408 subscription.status.as_str(),
2409 subscription.bump_stamp,
2410 )
2411 })
2412 .collect();
2413
2414 self.write()
2415 .await?
2416 .with_transaction(move |txn| {
2417 let mut txn = txn.prepare_cached(
2418 "INSERT INTO thread_subscriptions (room_id, event_id, status, bump_stamp)
2419 VALUES (?, ?, ?, ?)
2420 ON CONFLICT (room_id, event_id) DO UPDATE
2421 SET
2422 status =
2423 CASE
2424 WHEN thread_subscriptions.bump_stamp IS NULL THEN EXCLUDED.status
2425 WHEN EXCLUDED.bump_stamp IS NULL THEN EXCLUDED.status
2426 WHEN thread_subscriptions.bump_stamp < EXCLUDED.bump_stamp THEN EXCLUDED.status
2427 ELSE thread_subscriptions.status
2428 END,
2429 bump_stamp =
2430 CASE
2431 WHEN thread_subscriptions.bump_stamp IS NULL THEN EXCLUDED.bump_stamp
2432 WHEN EXCLUDED.bump_stamp IS NULL THEN thread_subscriptions.bump_stamp
2433 WHEN thread_subscriptions.bump_stamp < EXCLUDED.bump_stamp THEN EXCLUDED.bump_stamp
2434 ELSE thread_subscriptions.bump_stamp
2435 END",
2436 )?;
2437
2438 for value in values {
2439 txn.execute(value)?;
2440 }
2441
2442 Result::<_, Error>::Ok(())
2443 })
2444 .await?;
2445
2446 Ok(())
2447 }
2448
2449 async fn load_thread_subscription(
2450 &self,
2451 room_id: &RoomId,
2452 thread_id: &EventId,
2453 ) -> Result<Option<StoredThreadSubscription>, Self::Error> {
2454 let room_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, room_id);
2455 let thread_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, thread_id);
2456
2457 Ok(self
2458 .read()
2459 .await?
2460 .query_one(
2461 "SELECT status, bump_stamp FROM thread_subscriptions WHERE room_id = ? AND event_id = ?",
2462 (room_id, thread_id),
2463 |row| Ok((row.get::<_, String>(0)?, row.get::<_, Option<u64>>(1)?))
2464 )
2465 .await
2466 .optional()?
2467 .map(|(status, bump_stamp)| -> Result<_, Self::Error> {
2468 let status = ThreadSubscriptionStatus::from_str(&status).map_err(|_| {
2469 Error::InvalidData { details: format!("Invalid thread status: {status}") }
2470 })?;
2471 Ok(StoredThreadSubscription { status, bump_stamp })
2472 })
2473 .transpose()?)
2474 }
2475
2476 async fn remove_thread_subscription(
2477 &self,
2478 room_id: &RoomId,
2479 thread_id: &EventId,
2480 ) -> Result<(), Self::Error> {
2481 let room_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, room_id);
2482 let thread_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, thread_id);
2483
2484 self.write()
2485 .await?
2486 .execute(
2487 "DELETE FROM thread_subscriptions WHERE room_id = ? AND event_id = ?",
2488 (room_id, thread_id),
2489 )
2490 .await?;
2491
2492 Ok(())
2493 }
2494
2495 async fn get_global_profile(
2496 &self,
2497 user_id: &UserId,
2498 ) -> Result<Option<UserProfile>, Self::Error> {
2499 self.read()
2500 .await?
2501 .get_global_profiles(vec![self.encode_key(keys::GLOBAL_PROFILES, user_id)])
2502 .await?
2503 .into_iter()
2504 .next()
2505 .map(|(_, data)| self.deserialize_json(&data))
2506 .transpose()
2507 }
2508
2509 async fn get_global_profiles<'a>(
2510 &self,
2511 user_ids: &'a [OwnedUserId],
2512 ) -> Result<BTreeMap<&'a UserId, UserProfile>, Self::Error> {
2513 if user_ids.is_empty() {
2514 return Ok(BTreeMap::new());
2515 }
2516
2517 let mut user_ids_map = user_ids
2518 .iter()
2519 .map(|u| (self.encode_key(keys::GLOBAL_PROFILES, u), u.as_ref()))
2520 .collect::<BTreeMap<_, _>>();
2521 let user_ids = user_ids_map.keys().cloned().collect();
2522
2523 self.read()
2524 .await?
2525 .get_global_profiles(user_ids)
2526 .await?
2527 .into_iter()
2528 .map(|(user_id, data)| {
2529 Ok((
2530 user_ids_map
2531 .remove(user_id.as_slice())
2532 .expect("returned user IDs were requested"),
2533 self.deserialize_json(&data)?,
2534 ))
2535 })
2536 .collect()
2537 }
2538
2539 async fn optimize(&self) -> Result<(), Self::Error> {
2540 Ok(self.vacuum().await?)
2541 }
2542
2543 async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
2544 self.get_db_size().await
2545 }
2546
2547 async fn close(&self) -> Result<(), Self::Error> {
2548 connection::close_connections(&self.connections, "State store").await;
2549 Ok(())
2550 }
2551
2552 async fn reopen(&self) -> Result<(), Self::Error> {
2553 connection::reopen_connections(
2554 &self.connections,
2555 self.db_path.clone(),
2556 self.pool_config,
2557 self.runtime_config,
2558 )
2559 .await?;
2560 Ok(())
2561 }
2562}
2563
2564#[derive(Debug, Clone, Serialize, Deserialize)]
2565struct ReceiptData {
2566 receipt: Receipt,
2567 event_id: OwnedEventId,
2568 user_id: OwnedUserId,
2569}
2570
2571#[cfg(test)]
2572mod tests {
2573 use std::sync::{
2574 LazyLock,
2575 atomic::{AtomicU32, Ordering::SeqCst},
2576 };
2577
2578 use matrix_sdk_base::{StateStore, StoreError, statestore_integration_tests};
2579 use tempfile::{TempDir, tempdir};
2580
2581 use super::SqliteStateStore;
2582
2583 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2584 static NUM: AtomicU32 = AtomicU32::new(0);
2585
2586 async fn get_store() -> Result<impl StateStore, StoreError> {
2587 let name = NUM.fetch_add(1, SeqCst).to_string();
2588 let tmpdir_path = TMP_DIR.path().join(name);
2589
2590 tracing::info!("using store @ {}", tmpdir_path.to_str().unwrap());
2591
2592 Ok(SqliteStateStore::open(tmpdir_path.to_str().unwrap(), None).await.unwrap())
2593 }
2594
2595 #[matrix_sdk_test::async_test]
2598 async fn test_stripped_data_is_dropped_once_the_room_is_joined() {
2599 use matrix_sdk_base::{
2600 RoomInfo, RoomMemberships, RoomState, StateChanges, store::StateStoreExt,
2601 };
2602 use ruma::{
2603 events::{
2604 StateEventType,
2605 room::member::{MembershipState, RoomMemberEventContent},
2606 },
2607 room_id,
2608 serde::Raw,
2609 user_id,
2610 };
2611
2612 let store = get_store().await.unwrap();
2613 let room_id = room_id!("!test_stripped_data_is_dropped:localhost");
2614 let user_id = user_id!("@u:localhost");
2615
2616 let member: Raw<ruma::events::room::member::StrippedRoomMemberEvent> =
2618 Raw::new(&serde_json::json!({
2619 "type": "m.room.member",
2620 "content": RoomMemberEventContent::new(MembershipState::Invite),
2621 "sender": "@inviter:localhost",
2622 "state_key": user_id,
2623 }))
2624 .unwrap()
2625 .cast_unchecked();
2626
2627 let mut changes = StateChanges::default();
2628 changes.add_stripped_member(room_id, user_id, member);
2629 changes.add_room(RoomInfo::new(room_id, RoomState::Invited));
2630 store.save_changes(&changes).await.unwrap();
2631
2632 assert!(store.get_member_event(room_id, user_id).await.unwrap().is_some());
2633
2634 let mut changes = StateChanges::default();
2637 changes.add_room(RoomInfo::new(room_id, RoomState::Joined));
2638 store.save_changes(&changes).await.unwrap();
2639
2640 assert!(
2641 store.get_member_event(room_id, user_id).await.unwrap().is_none(),
2642 "the stripped member event must be dropped once the room is joined"
2643 );
2644 assert!(
2645 store.get_user_ids(room_id, RoomMemberships::empty()).await.unwrap().is_empty(),
2646 "the stripped member must no longer be listed"
2647 );
2648 assert!(
2649 store
2650 .get_state_event(room_id, StateEventType::RoomMember, user_id.as_str())
2651 .await
2652 .unwrap()
2653 .is_none()
2654 );
2655 }
2656
2657 statestore_integration_tests!();
2658}
2659
2660#[cfg(test)]
2661mod encrypted_tests {
2662 use std::{
2663 path::PathBuf,
2664 sync::{
2665 LazyLock,
2666 atomic::{AtomicU32, Ordering::SeqCst},
2667 },
2668 };
2669
2670 use base64::Engine as _;
2671 use matrix_sdk_base::{StateStore, StoreError, statestore_integration_tests};
2672 use matrix_sdk_test::async_test;
2673 use tempfile::{TempDir, tempdir};
2674
2675 use super::SqliteStateStore;
2676 use crate::{Base64Variant, SqliteStoreConfig, Synchronous, utils::SqliteAsyncConnExt};
2677
2678 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2679 static NUM: AtomicU32 = AtomicU32::new(0);
2680
2681 fn new_state_store_workspace() -> PathBuf {
2682 let name = NUM.fetch_add(1, SeqCst).to_string();
2683 TMP_DIR.path().join(name)
2684 }
2685
2686 async fn get_store() -> Result<impl StateStore, StoreError> {
2687 let tmpdir_path = new_state_store_workspace();
2688
2689 tracing::info!("using store @ {}", tmpdir_path.to_str().unwrap());
2690
2691 Ok(SqliteStateStore::open(tmpdir_path.to_str().unwrap(), Some("default_test_password"))
2692 .await
2693 .unwrap())
2694 }
2695
2696 #[async_test]
2698 async fn test_high_entropy_passphrase_migrates_a_passphrase_store() {
2699 const KEY: &[u8; 32] = b"a randomly generated passphrase ";
2700 let tmpdir_path = new_state_store_workspace();
2701
2702 let passphrase = base64::prelude::BASE64_STANDARD.encode(KEY);
2703
2704 let config = SqliteStoreConfig::new(&tmpdir_path).passphrase(Some(&passphrase));
2705 drop(SqliteStateStore::open_with_config(&config).await.unwrap());
2706
2707 let config = SqliteStoreConfig::new(&tmpdir_path)
2709 .high_entropy_passphrase(Some(KEY), Base64Variant::Padded);
2710 drop(SqliteStateStore::open_with_config(&config).await.unwrap());
2711
2712 drop(SqliteStateStore::open_with_config(&config).await.unwrap());
2714
2715 let config = SqliteStoreConfig::new(&tmpdir_path).passphrase(Some(&passphrase));
2718 drop(
2719 SqliteStateStore::open_with_config(&config)
2720 .await
2721 .expect_err("The old passphrase-only method shouldn't work anymore"),
2722 );
2723
2724 let config = SqliteStoreConfig::new(&tmpdir_path)
2727 .high_entropy_passphrase(Some(KEY), Base64Variant::Padded);
2728 drop(
2729 SqliteStateStore::open_with_config(&config)
2730 .await
2731 .expect("The high-entropy method should continue to work"),
2732 );
2733
2734 let config = SqliteStoreConfig::new(&tmpdir_path).key(Some(KEY));
2735 drop(
2736 SqliteStateStore::open_with_config(&config).await.expect("The key should work as well"),
2737 );
2738
2739 let config = SqliteStoreConfig::new(&tmpdir_path).high_entropy_passphrase(
2740 Some(b"wrong passphrase can't work 1234"),
2741 Base64Variant::Padded,
2742 );
2743 assert!(SqliteStateStore::open_with_config(&config).await.is_err());
2744 let config = SqliteStoreConfig::new(&tmpdir_path).passphrase(Some("wrong"));
2745 assert!(SqliteStateStore::open_with_config(&config).await.is_err());
2746 }
2747
2748 #[async_test]
2749 async fn test_pool_size() {
2750 let tmpdir_path = new_state_store_workspace();
2751 let store_open_config = SqliteStoreConfig::new(tmpdir_path).pool_max_size(42);
2752
2753 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2754
2755 let guard = store.connections.lock().await;
2756 assert_eq!(guard.as_ref().unwrap().pool.status().max_size, 42);
2757 }
2758
2759 #[async_test]
2760 async fn test_cache_size() {
2761 let tmpdir_path = new_state_store_workspace();
2762 let store_open_config = SqliteStoreConfig::new(tmpdir_path).cache_size(1500);
2763
2764 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2765
2766 let conn = store.read().await.unwrap();
2767 let cache_size =
2768 conn.query_row("PRAGMA cache_size", (), |row| row.get::<_, i32>(0)).await.unwrap();
2769
2770 assert_eq!(cache_size, -(1500 / 1024));
2774 }
2775
2776 #[async_test]
2777 async fn test_journal_size_limit() {
2778 let tmpdir_path = new_state_store_workspace();
2779 let store_open_config = SqliteStoreConfig::new(tmpdir_path).journal_size_limit(1500);
2780
2781 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2782
2783 let conn = store.read().await.unwrap();
2784 let journal_size_limit = conn
2785 .query_row("PRAGMA journal_size_limit", (), |row| row.get::<_, u32>(0))
2786 .await
2787 .unwrap();
2788
2789 assert_eq!(journal_size_limit, 1500);
2792 }
2793
2794 #[async_test]
2795 async fn test_synchronous() {
2796 let all = [
2798 (Synchronous::Off, 0),
2799 (Synchronous::Normal, 1),
2800 (Synchronous::Full, 2),
2801 (Synchronous::Extra, 3),
2802 ];
2803
2804 for (synchronous, expected) in all {
2805 let tmpdir_path = new_state_store_workspace();
2806 let store_open_config = SqliteStoreConfig::new(tmpdir_path).synchronous(synchronous);
2807
2808 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2809
2810 let write_conn = store.write().await.unwrap();
2813 let read_conn = store.read().await.unwrap();
2814
2815 for conn in [&*write_conn, &read_conn] {
2816 let value = conn
2817 .query_row("PRAGMA synchronous", (), |row| row.get::<_, u8>(0))
2818 .await
2819 .unwrap();
2820
2821 assert_eq!(value, expected);
2822 }
2823 }
2824 }
2825
2826 statestore_integration_tests!();
2827}
2828
2829#[cfg(test)]
2830mod migration_tests {
2831 use std::{
2832 path::{Path, PathBuf},
2833 sync::{
2834 Arc, LazyLock,
2835 atomic::{AtomicU32, Ordering::SeqCst},
2836 },
2837 };
2838
2839 use as_variant::as_variant;
2840 use matrix_sdk_base::{
2841 RoomState, StateStore,
2842 media::{MediaFormat, MediaRequestParameters},
2843 store::{
2844 ChildTransactionId, DependentQueuedRequestKind, RoomLoadSettings,
2845 SerializableEventContent,
2846 },
2847 sync::UnreadNotificationsCount,
2848 };
2849 use matrix_sdk_test::async_test;
2850 use ruma::{
2851 EventId, MilliSecondsSinceUnixEpoch, OwnedTransactionId, RoomId, TransactionId, UserId,
2852 events::{
2853 StateEventType,
2854 room::{MediaSource, create::RoomCreateEventContent, message::RoomMessageEventContent},
2855 },
2856 room_id, server_name, user_id,
2857 };
2858 use rusqlite::Transaction;
2859 use serde::{Deserialize, Serialize};
2860 use serde_json::json;
2861 use tempfile::{TempDir, tempdir};
2862 use tokio::{fs, sync::Mutex};
2863 use zeroize::Zeroizing;
2864
2865 use super::{DATABASE_NAME, SqliteStateStore, init, keys};
2866 use crate::{
2867 OpenStoreError, Secret, SqliteStoreConfig, connection,
2868 error::{Error, Result},
2869 utils::{EncryptableStore as _, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt},
2870 };
2871
2872 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2873 static NUM: AtomicU32 = AtomicU32::new(0);
2874 const SECRET: &str = "secret";
2875
2876 fn new_path() -> PathBuf {
2877 let name = NUM.fetch_add(1, SeqCst).to_string();
2878 TMP_DIR.path().join(name)
2879 }
2880
2881 async fn create_fake_db(path: &Path, version: u8) -> Result<SqliteStateStore> {
2882 let config = SqliteStoreConfig::new(path);
2883
2884 fs::create_dir_all(&config.path).await.map_err(OpenStoreError::CreateDir).unwrap();
2885
2886 let pool = config.build_pool_of_connections(DATABASE_NAME).unwrap();
2887 let db_path = pool.manager().database_path.clone();
2888 let conn = pool.get().await?;
2889
2890 init(&conn).await?;
2891
2892 let store_cipher = Some(Arc::new(
2893 conn.get_or_create_store_cipher(Secret::PassPhrase(Zeroizing::new(SECRET.to_owned())))
2894 .await
2895 .unwrap(),
2896 ));
2897 let this = SqliteStateStore {
2898 store_cipher,
2899 connections: Arc::new(
2900 connection::SqliteConnections::new(connection::SqliteConnectionsInner {
2901 pool,
2902 write_connection: Arc::new(Mutex::new(conn)),
2903 })
2904 .await,
2905 ),
2906 db_path,
2907 pool_config: deadpool::managed::PoolConfig::default(),
2908 runtime_config: crate::RuntimeConfig::default(),
2909 };
2910 this.run_migrations(1, Some(version)).await?;
2911
2912 Ok(this)
2913 }
2914
2915 fn room_info_v1_json(
2916 room_id: &RoomId,
2917 state: RoomState,
2918 name: Option<&str>,
2919 creator: Option<&UserId>,
2920 ) -> serde_json::Value {
2921 let name_content = match name {
2923 Some(name) => json!({ "name": name }),
2924 None => json!({ "name": null }),
2925 };
2926 let create_content = match creator {
2928 Some(creator) => RoomCreateEventContent::new_v1(creator.to_owned()),
2929 None => RoomCreateEventContent::new_v11(),
2930 };
2931
2932 json!({
2933 "room_id": room_id,
2934 "room_type": state,
2935 "notification_counts": UnreadNotificationsCount::default(),
2936 "summary": {
2937 "heroes": [],
2938 "joined_member_count": 0,
2939 "invited_member_count": 0,
2940 },
2941 "members_synced": false,
2942 "base_info": {
2943 "dm_targets": [],
2944 "max_power_level": 100,
2945 "name": {
2946 "Original": {
2947 "content": name_content,
2948 },
2949 },
2950 "create": {
2951 "Original": {
2952 "content": create_content,
2953 }
2954 }
2955 },
2956 })
2957 }
2958
2959 #[async_test]
2960 pub async fn test_migrating_v1_to_v2() {
2961 let path = new_path();
2962 {
2964 let db = create_fake_db(&path, 1).await.unwrap();
2965 let conn = db.read().await.unwrap();
2966
2967 let this = db.clone();
2968 conn.with_transaction(move |txn| {
2969 for i in 0..5 {
2970 let room_id = RoomId::parse(format!("!room_{i}:localhost")).unwrap();
2971 let (state, stripped) =
2972 if i < 3 { (RoomState::Joined, false) } else { (RoomState::Invited, true) };
2973 let info = room_info_v1_json(&room_id, state, None, None);
2974
2975 let room_id = this.encode_key(keys::ROOM_INFO, room_id);
2976 let data = this.serialize_json(&info)?;
2977
2978 txn.prepare_cached(
2979 "INSERT INTO room_info (room_id, stripped, data)
2980 VALUES (?, ?, ?)",
2981 )?
2982 .execute((room_id, stripped, data))?;
2983 }
2984
2985 Result::<_, Error>::Ok(())
2986 })
2987 .await
2988 .unwrap();
2989 }
2990
2991 let store = SqliteStateStore::open(path, Some(SECRET)).await.unwrap();
2993
2994 assert_eq!(store.get_room_infos(&RoomLoadSettings::default()).await.unwrap().len(), 5);
2996 }
2997
2998 fn add_room_v2(
3000 this: &SqliteStateStore,
3001 txn: &Transaction<'_>,
3002 room_id: &RoomId,
3003 name: Option<&str>,
3004 create_creator: Option<&UserId>,
3005 create_sender: Option<&UserId>,
3006 ) -> Result<(), Error> {
3007 let room_info_json = room_info_v1_json(room_id, RoomState::Joined, name, create_creator);
3008
3009 let encoded_room_id = this.encode_key(keys::ROOM_INFO, room_id);
3010 let encoded_state =
3011 this.encode_key(keys::ROOM_INFO, serde_json::to_string(&RoomState::Joined)?);
3012 let data = this.serialize_json(&room_info_json)?;
3013
3014 txn.prepare_cached(
3015 "INSERT INTO room_info (room_id, state, data)
3016 VALUES (?, ?, ?)",
3017 )?
3018 .execute((encoded_room_id, encoded_state, data))?;
3019
3020 let Some(create_sender) = create_sender else {
3022 return Ok(());
3023 };
3024
3025 let create_content = match create_creator {
3026 Some(creator) => RoomCreateEventContent::new_v1(creator.to_owned()),
3027 None => RoomCreateEventContent::new_v11(),
3028 };
3029
3030 let event_id = EventId::new_v1(server_name!("dummy.local"));
3031 let create_event = json!({
3032 "content": create_content,
3033 "event_id": event_id,
3034 "sender": create_sender.to_owned(),
3035 "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
3036 "state_key": "",
3037 "type": "m.room.create",
3038 "unsigned": {},
3039 });
3040
3041 let encoded_room_id = this.encode_key(keys::STATE_EVENT, room_id);
3042 let encoded_event_type =
3043 this.encode_key(keys::STATE_EVENT, StateEventType::RoomCreate.to_string());
3044 let encoded_state_key = this.encode_key(keys::STATE_EVENT, "");
3045 let stripped = false;
3046 let encoded_event_id = this.encode_key(keys::STATE_EVENT, event_id);
3047 let data = this.serialize_json(&create_event)?;
3048
3049 txn.prepare_cached(
3050 "INSERT
3051 INTO state_event (room_id, event_type, state_key, stripped, event_id, data)
3052 VALUES (?, ?, ?, ?, ?, ?)",
3053 )?
3054 .execute((
3055 encoded_room_id,
3056 encoded_event_type,
3057 encoded_state_key,
3058 stripped,
3059 encoded_event_id,
3060 data,
3061 ))?;
3062
3063 Ok(())
3064 }
3065
3066 #[async_test]
3067 pub async fn test_migrating_v2_to_v3() {
3068 let path = new_path();
3069
3070 let room_a_id = room_id!("!room_a:dummy.local");
3072 let room_a_name = "Room A";
3073 let room_a_creator = user_id!("@creator:dummy.local");
3074 let room_a_create_sender = user_id!("@sender:dummy.local");
3077
3078 let room_b_id = room_id!("!room_b:dummy.local");
3080
3081 let room_c_id = room_id!("!room_c:dummy.local");
3083 let room_c_create_sender = user_id!("@creator:dummy.local");
3084
3085 {
3087 let db = create_fake_db(&path, 2).await.unwrap();
3088 let conn = db.read().await.unwrap();
3089
3090 let this = db.clone();
3091 conn.with_transaction(move |txn| {
3092 add_room_v2(
3093 &this,
3094 txn,
3095 room_a_id,
3096 Some(room_a_name),
3097 Some(room_a_creator),
3098 Some(room_a_create_sender),
3099 )?;
3100 add_room_v2(&this, txn, room_b_id, None, None, None)?;
3101 add_room_v2(&this, txn, room_c_id, None, None, Some(room_c_create_sender))?;
3102
3103 Result::<_, Error>::Ok(())
3104 })
3105 .await
3106 .unwrap();
3107 }
3108
3109 let store = SqliteStateStore::open(path, Some(SECRET)).await.unwrap();
3111
3112 let room_infos = store.get_room_infos(&RoomLoadSettings::default()).await.unwrap();
3114 assert_eq!(room_infos.len(), 3);
3115
3116 let room_a = room_infos.iter().find(|r| r.room_id() == room_a_id).unwrap();
3117 assert_eq!(room_a.name(), Some(room_a_name));
3118 assert_eq!(room_a.creators(), Some(vec![room_a_create_sender.to_owned()]));
3119
3120 let room_b = room_infos.iter().find(|r| r.room_id() == room_b_id).unwrap();
3121 assert_eq!(room_b.name(), None);
3122 assert_eq!(room_b.creators(), None);
3123
3124 let room_c = room_infos.iter().find(|r| r.room_id() == room_c_id).unwrap();
3125 assert_eq!(room_c.name(), None);
3126 assert_eq!(room_c.creators(), Some(vec![room_c_create_sender.to_owned()]));
3127 }
3128
3129 #[async_test]
3130 pub async fn test_migrating_v7_to_v9() {
3131 let path = new_path();
3132
3133 let room_id = room_id!("!room_a:dummy.local");
3134 let wedged_event_transaction_id = TransactionId::new();
3135 let local_event_transaction_id = TransactionId::new();
3136
3137 {
3139 let db = create_fake_db(&path, 7).await.unwrap();
3140 let conn = db.read().await.unwrap();
3141
3142 let wedge_tx = wedged_event_transaction_id.clone();
3143 let local_tx = local_event_transaction_id.clone();
3144
3145 conn.with_transaction(move |txn| {
3146 add_dependent_send_queue_event_v7(
3147 &db,
3148 txn,
3149 room_id,
3150 &local_tx,
3151 ChildTransactionId::new(),
3152 DependentQueuedRequestKind::RedactEvent,
3153 )?;
3154 add_send_queue_event_v7(&db, txn, &wedge_tx, room_id, true)?;
3155 add_send_queue_event_v7(&db, txn, &local_tx, room_id, false)?;
3156 Result::<_, Error>::Ok(())
3157 })
3158 .await
3159 .unwrap();
3160 }
3161
3162 let store = SqliteStateStore::open(path, Some(SECRET)).await.unwrap();
3165
3166 let requests = store.load_send_queue_requests(room_id).await.unwrap();
3167 assert!(requests.is_empty());
3168
3169 let dependent_requests = store.load_dependent_queued_requests(room_id).await.unwrap();
3170 assert!(dependent_requests.is_empty());
3171 }
3172
3173 fn add_send_queue_event_v7(
3174 this: &SqliteStateStore,
3175 txn: &Transaction<'_>,
3176 transaction_id: &TransactionId,
3177 room_id: &RoomId,
3178 is_wedged: bool,
3179 ) -> Result<(), Error> {
3180 let content =
3181 SerializableEventContent::new(&RoomMessageEventContent::text_plain("Hello").into())?;
3182
3183 let room_id_key = this.encode_key(keys::SEND_QUEUE, room_id);
3184 let room_id_value = this.serialize_value(&room_id.to_owned())?;
3185
3186 let content = this.serialize_json(&content)?;
3187
3188 txn.prepare_cached("INSERT INTO send_queue_events (room_id, room_id_val, transaction_id, content, wedged) VALUES (?, ?, ?, ?, ?)")?
3189 .execute((room_id_key, room_id_value, transaction_id.to_string(), content, is_wedged))?;
3190
3191 Ok(())
3192 }
3193
3194 fn add_dependent_send_queue_event_v7(
3195 this: &SqliteStateStore,
3196 txn: &Transaction<'_>,
3197 room_id: &RoomId,
3198 parent_txn_id: &TransactionId,
3199 own_txn_id: ChildTransactionId,
3200 content: DependentQueuedRequestKind,
3201 ) -> Result<(), Error> {
3202 let room_id_value = this.serialize_value(&room_id.to_owned())?;
3203
3204 let parent_txn_id = parent_txn_id.to_string();
3205 let own_txn_id = own_txn_id.to_string();
3206 let content = this.serialize_json(&content)?;
3207
3208 txn.prepare_cached(
3209 "INSERT INTO dependent_send_queue_events
3210 (room_id, parent_transaction_id, own_transaction_id, content)
3211 VALUES (?, ?, ?, ?)",
3212 )?
3213 .execute((room_id_value, parent_txn_id, own_txn_id, content))?;
3214
3215 Ok(())
3216 }
3217
3218 #[derive(Clone, Debug, Serialize, Deserialize)]
3219 pub enum LegacyDependentQueuedRequestKind {
3220 UploadFileWithThumbnail {
3221 content_type: String,
3222 cache_key: MediaRequestParameters,
3223 related_to: OwnedTransactionId,
3224 },
3225 }
3226
3227 #[async_test]
3228 pub async fn test_dependent_queued_request_variant_renaming() {
3229 let path = new_path();
3230 let db = create_fake_db(&path, 7).await.unwrap();
3231
3232 let cache_key = MediaRequestParameters {
3233 format: MediaFormat::File,
3234 source: MediaSource::Plain("https://server.local/foobar".into()),
3235 };
3236 let related_to = TransactionId::new();
3237 let request = LegacyDependentQueuedRequestKind::UploadFileWithThumbnail {
3238 content_type: "image/png".to_owned(),
3239 cache_key,
3240 related_to: related_to.clone(),
3241 };
3242
3243 let data = db
3244 .serialize_json(&request)
3245 .expect("should be able to serialize legacy dependent request");
3246 let deserialized: DependentQueuedRequestKind = db.deserialize_json(&data).expect(
3247 "should be able to deserialize dependent request from legacy dependent request",
3248 );
3249
3250 as_variant!(deserialized, DependentQueuedRequestKind::UploadFileOrThumbnail { related_to: de_related_to, .. } => {
3251 assert_eq!(de_related_to, related_to);
3252 });
3253 }
3254}
3255
3256#[cfg(test)]
3257mod close_reopen_tests {
3258 use std::sync::{
3259 LazyLock,
3260 atomic::{AtomicU32, Ordering::SeqCst},
3261 };
3262
3263 use matrix_sdk_base::StateStore;
3264 use matrix_sdk_test::async_test;
3265 use tempfile::{TempDir, tempdir};
3266
3267 use super::SqliteStateStore;
3268
3269 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
3270 static NUM: AtomicU32 = AtomicU32::new(0);
3271
3272 async fn new_store() -> SqliteStateStore {
3273 let name = NUM.fetch_add(1, SeqCst).to_string();
3274 let tmpdir_path = TMP_DIR.path().join(name);
3275 SqliteStateStore::open(tmpdir_path.to_str().unwrap(), None).await.unwrap()
3276 }
3277
3278 #[async_test]
3279 async fn test_close_completes_without_timeout() {
3280 let store = new_store().await;
3281
3282 let start = std::time::Instant::now();
3284 store.close().await.unwrap();
3285 let elapsed = start.elapsed();
3286
3287 assert!(
3288 elapsed < std::time::Duration::from_secs(2),
3289 "close() took {elapsed:?}, expected < 2s (no timeout)"
3290 );
3291
3292 let guard = store.connections.lock().await;
3294 assert!(guard.is_none(), "connections should be None after close");
3295 }
3296
3297 #[async_test]
3298 async fn test_reopen_restores_connections() {
3299 let store = new_store().await;
3300
3301 store.close().await.unwrap();
3302
3303 {
3305 let guard = store.connections.lock().await;
3306 assert!(guard.is_none());
3307 }
3308
3309 store.reopen().await.unwrap();
3310
3311 {
3313 let guard = store.connections.lock().await;
3314 assert!(guard.is_some(), "connections should be Some after reopen");
3315 }
3316 }
3317
3318 #[async_test]
3319 async fn test_close_is_idempotent() {
3320 let store = new_store().await;
3321
3322 store.close().await.unwrap();
3324 store.close().await.unwrap();
3326
3327 let guard = store.connections.lock().await;
3328 assert!(guard.is_none());
3329 }
3330
3331 #[async_test]
3332 async fn test_reopen_is_idempotent() {
3333 let store = new_store().await;
3334
3335 store.reopen().await.unwrap();
3337
3338 let guard = store.connections.lock().await;
3340 assert!(guard.is_some());
3341 }
3342
3343 #[async_test]
3344 async fn test_read_fails_when_closed() {
3345 let store = new_store().await;
3346 store.close().await.unwrap();
3347
3348 let err = store.get_custom_value(b"some_key").await;
3349 assert!(err.is_err(), "read should fail when closed");
3350
3351 let err_msg = err.unwrap_err().to_string();
3352 assert!(err_msg.contains("closed"), "error should mention 'closed', got: {err_msg}");
3353 }
3354
3355 #[async_test]
3356 async fn test_write_fails_when_closed() {
3357 let store = new_store().await;
3358 store.close().await.unwrap();
3359
3360 let err = store.set_custom_value(b"key", b"value".to_vec()).await;
3361 assert!(err.is_err(), "write should fail when closed");
3362
3363 let err_msg = err.unwrap_err().to_string();
3364 assert!(err_msg.contains("closed"), "error should mention 'closed', got: {err_msg}");
3365 }
3366
3367 #[async_test]
3368 async fn test_data_persists_across_close_reopen() {
3369 let store = new_store().await;
3370
3371 store.set_custom_value(b"test_key", b"test_value".to_vec()).await.unwrap();
3373
3374 let value = store.get_custom_value(b"test_key").await.unwrap();
3376 assert_eq!(value.as_deref(), Some(b"test_value".as_slice()));
3377
3378 store.close().await.unwrap();
3380 store.reopen().await.unwrap();
3381
3382 let value = store.get_custom_value(b"test_key").await.unwrap();
3384 assert_eq!(
3385 value.as_deref(),
3386 Some(b"test_value".as_slice()),
3387 "data should persist across close/reopen"
3388 );
3389 }
3390
3391 #[async_test]
3392 async fn test_multiple_close_reopen_cycles() {
3393 let store = new_store().await;
3394
3395 for i in 0..3 {
3396 let key = format!("key_{i}");
3397 let value = format!("value_{i}");
3398
3399 store.set_custom_value(key.as_bytes(), value.as_bytes().to_vec()).await.unwrap();
3400
3401 store.close().await.unwrap();
3402 store.reopen().await.unwrap();
3403
3404 for j in 0..=i {
3406 let k = format!("key_{j}");
3407 let v = format!("value_{j}");
3408 let retrieved = store.get_custom_value(k.as_bytes()).await.unwrap();
3409 assert_eq!(
3410 retrieved.as_deref(),
3411 Some(v.as_bytes()),
3412 "data for key_{j} should persist after cycle {i}"
3413 );
3414 }
3415 }
3416 }
3417
3418 #[async_test]
3419 async fn test_pool_is_fully_drained_after_close() {
3420 let store = new_store().await;
3421
3422 let _ = store.get_custom_value(b"key1").await;
3424 let _ = store.get_custom_value(b"key2").await;
3425
3426 store.close().await.unwrap();
3427
3428 let guard = store.connections.lock().await;
3431 assert!(guard.is_none(), "all connections should be released after close");
3432 }
3433
3434 #[async_test]
3435 async fn test_operations_work_immediately_after_reopen() {
3436 let store = new_store().await;
3437
3438 store.close().await.unwrap();
3439 store.reopen().await.unwrap();
3440
3441 store.set_custom_value(b"after_reopen", b"works".to_vec()).await.unwrap();
3443
3444 let value = store.get_custom_value(b"after_reopen").await.unwrap();
3446 assert_eq!(value.as_deref(), Some(b"works".as_slice()));
3447 }
3448
3449 #[async_test]
3450 async fn test_close_waits_for_held_read_connection_to_drain() {
3451 let store = new_store().await;
3452
3453 let held_conn = store.read().await.unwrap();
3455
3456 let store_clone = store.clone();
3459 let close_handle = tokio::spawn(async move {
3460 store_clone.close().await.unwrap();
3461 });
3462
3463 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
3465
3466 assert!(!close_handle.is_finished(), "close should be waiting for the held connection");
3468
3469 drop(held_conn);
3471
3472 let timeout = tokio::time::timeout(std::time::Duration::from_secs(3), close_handle).await;
3474 assert!(timeout.is_ok(), "close should complete after the held connection is released");
3475 timeout.unwrap().unwrap();
3476
3477 let guard = store.connections.lock().await;
3479 assert!(guard.is_none(), "connections should be None after close");
3480 }
3481}