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, SqliteTransactionExt,
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 if from < 17 {
516 debug!("Upgrading database to version 17");
517 conn.with_transaction(move |txn| {
518 txn.execute_batch(include_str!(
520 "../migrations/state_store/015_unavailable_global_profiles.sql"
521 ))?;
522 txn.set_db_version(17)
523 })
524 .await?;
525 }
526
527 if to == Some(17) {
528 return Ok(());
529 }
530
531 Ok(())
532 }
533
534 fn encode_state_store_data_key(&self, key: StateStoreDataKey<'_>) -> Key {
535 let key_s = match key {
536 StateStoreDataKey::SyncToken => Cow::Borrowed(StateStoreDataKey::SYNC_TOKEN),
537 StateStoreDataKey::SupportedVersions => {
538 Cow::Borrowed(StateStoreDataKey::SUPPORTED_VERSIONS)
539 }
540 StateStoreDataKey::WellKnown => Cow::Borrowed(StateStoreDataKey::WELL_KNOWN),
541 StateStoreDataKey::Filter(f) => {
542 Cow::Owned(format!("{}:{f}", StateStoreDataKey::FILTER))
543 }
544 StateStoreDataKey::UserAvatarUrl(u) => {
545 Cow::Owned(format!("{}:{u}", StateStoreDataKey::USER_AVATAR_URL))
546 }
547 StateStoreDataKey::RecentlyVisitedRooms(b) => {
548 Cow::Owned(format!("{}:{b}", StateStoreDataKey::RECENTLY_VISITED_ROOMS))
549 }
550 StateStoreDataKey::UtdHookManagerData => {
551 Cow::Borrowed(StateStoreDataKey::UTD_HOOK_MANAGER_DATA)
552 }
553 StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
554 Cow::Borrowed(StateStoreDataKey::ONE_TIME_KEY_ALREADY_UPLOADED)
555 }
556 StateStoreDataKey::ComposerDraft(room_id, thread_root) => {
557 if let Some(thread_root) = thread_root {
558 Cow::Owned(format!(
559 "{}:{room_id}:{thread_root}",
560 StateStoreDataKey::COMPOSER_DRAFT
561 ))
562 } else {
563 Cow::Owned(format!("{}:{room_id}", StateStoreDataKey::COMPOSER_DRAFT))
564 }
565 }
566 StateStoreDataKey::SeenKnockRequests(room_id) => {
567 Cow::Owned(format!("{}:{room_id}", StateStoreDataKey::SEEN_KNOCK_REQUESTS))
568 }
569 StateStoreDataKey::ThreadSubscriptionsCatchupTokens => {
570 Cow::Borrowed(StateStoreDataKey::THREAD_SUBSCRIPTIONS_CATCHUP_TOKENS)
571 }
572 StateStoreDataKey::HomeserverCapabilities => {
573 Cow::Borrowed(StateStoreDataKey::HOMESERVER_CAPABILITIES)
574 }
575 };
576
577 self.encode_key(keys::KV_BLOB, &*key_s)
578 }
579
580 fn encode_presence_key(&self, user_id: &UserId) -> Key {
581 self.encode_key(keys::KV_BLOB, format!("presence:{user_id}"))
582 }
583
584 fn encode_custom_key(&self, key: &[u8]) -> Key {
585 let mut full_key = b"custom:".to_vec();
586 full_key.extend(key);
587 self.encode_key(keys::KV_BLOB, full_key)
588 }
589
590 #[instrument(skip_all)]
593 async fn read(&self) -> Result<SqliteAsyncConn> {
594 let pool = {
595 let guard = self.connections.lock().await;
596 let conns = guard.as_ref().ok_or(Error::StoreClosed)?;
597 conns.pool.clone()
598 };
599 Ok(pool.get().await?)
600 }
601
602 #[instrument(skip_all)]
605 async fn write(&self) -> Result<OwnedMutexGuard<SqliteAsyncConn>> {
606 let write_conn = {
607 let guard = self.connections.lock().await;
608 let conns = guard.as_ref().ok_or(Error::StoreClosed)?;
609 conns.write_connection.clone()
610 };
611 Ok(write_conn.lock_owned().await)
612 }
613
614 fn remove_maybe_stripped_room_data(
615 &self,
616 txn: &Transaction<'_>,
617 room_id: &RoomId,
618 stripped: bool,
619 ) -> rusqlite::Result<()> {
620 let state_event_room_id = self.encode_key(keys::STATE_EVENT, room_id);
621 txn.remove_room_state_events(&state_event_room_id, Some(stripped))?;
622
623 let member_room_id = self.encode_key(keys::MEMBER, room_id);
624 txn.remove_room_members(&member_room_id, Some(stripped))
625 }
626
627 pub async fn vacuum(&self) -> Result<()> {
628 self.write().await?.vacuum().await
629 }
630
631 pub async fn get_db_size(&self) -> Result<Option<usize>> {
632 let read_conn = self.read().await?;
633 Ok(Some(read_conn.get_db_size().await?))
634 }
635}
636
637impl EncryptableStore for SqliteStateStore {
638 fn get_cypher(&self) -> Option<&StoreCipher> {
639 self.store_cipher.as_deref()
640 }
641}
642
643async fn init(conn: &SqliteAsyncConn) -> Result<()> {
645 conn.execute_batch("PRAGMA journal_mode = wal;").await?;
649 conn.with_transaction(|txn| {
650 txn.execute_batch(include_str!("../migrations/state_store/001_init.sql"))?;
651 txn.set_db_version(1)?;
652
653 Ok(())
654 })
655 .await
656}
657
658trait SqliteConnectionStateStoreExt {
659 fn set_kv_blob(&self, key: &[u8], value: &[u8]) -> rusqlite::Result<()>;
660
661 fn set_global_account_data(&self, event_type: &[u8], data: &[u8]) -> rusqlite::Result<()>;
662
663 fn set_room_account_data(
664 &self,
665 room_id: &[u8],
666 event_type: &[u8],
667 data: &[u8],
668 ) -> rusqlite::Result<()>;
669 fn remove_room_account_data(&self, room_id: &[u8]) -> rusqlite::Result<()>;
670
671 fn set_room_info(&self, room_id: &[u8], state: &[u8], data: &[u8]) -> rusqlite::Result<()>;
672 fn get_room_info(&self, room_id: &[u8]) -> rusqlite::Result<Option<Vec<u8>>>;
673 fn remove_room_info(&self, room_id: &[u8]) -> rusqlite::Result<()>;
674
675 fn set_state_event(
676 &self,
677 room_id: &[u8],
678 event_type: &[u8],
679 state_key: &[u8],
680 stripped: bool,
681 event_id: Option<&[u8]>,
682 data: &[u8],
683 ) -> rusqlite::Result<()>;
684 fn get_state_event_by_id(
685 &self,
686 room_id: &[u8],
687 event_id: &[u8],
688 ) -> rusqlite::Result<Option<Vec<u8>>>;
689 fn remove_room_state_events(
690 &self,
691 room_id: &[u8],
692 stripped: Option<bool>,
693 ) -> rusqlite::Result<()>;
694
695 fn set_member(
696 &self,
697 room_id: &[u8],
698 user_id: &[u8],
699 membership: &[u8],
700 stripped: bool,
701 data: &[u8],
702 ) -> rusqlite::Result<()>;
703 fn remove_room_members(&self, room_id: &[u8], stripped: Option<bool>) -> rusqlite::Result<()>;
704
705 fn set_profile(&self, room_id: &[u8], user_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
706 fn remove_room_profiles(&self, room_id: &[u8]) -> rusqlite::Result<()>;
707 fn remove_room_profile(&self, room_id: &[u8], user_id: &[u8]) -> rusqlite::Result<()>;
708
709 fn set_receipt(
710 &self,
711 room_id: &[u8],
712 user_id: &[u8],
713 receipt_type: &[u8],
714 thread_id: &[u8],
715 event_id: &[u8],
716 data: &[u8],
717 ) -> rusqlite::Result<()>;
718 fn remove_room_receipts(&self, room_id: &[u8]) -> rusqlite::Result<()>;
719
720 fn set_display_name(&self, room_id: &[u8], name: &[u8], data: &[u8]) -> rusqlite::Result<()>;
721 fn remove_display_name(&self, room_id: &[u8], name: &[u8]) -> rusqlite::Result<()>;
722 fn remove_room_display_names(&self, room_id: &[u8]) -> rusqlite::Result<()>;
723 fn remove_room_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()>;
724 fn remove_room_dependent_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()>;
725}
726
727impl SqliteConnectionStateStoreExt for rusqlite::Connection {
728 fn set_kv_blob(&self, key: &[u8], value: &[u8]) -> rusqlite::Result<()> {
729 self.execute("INSERT OR REPLACE INTO kv_blob VALUES (?, ?)", (key, value))?;
730 Ok(())
731 }
732
733 fn set_global_account_data(&self, event_type: &[u8], data: &[u8]) -> rusqlite::Result<()> {
734 self.prepare_cached(
735 "INSERT OR REPLACE INTO global_account_data (event_type, data)
736 VALUES (?, ?)",
737 )?
738 .execute((event_type, data))?;
739 Ok(())
740 }
741
742 fn set_room_account_data(
743 &self,
744 room_id: &[u8],
745 event_type: &[u8],
746 data: &[u8],
747 ) -> rusqlite::Result<()> {
748 self.prepare_cached(
749 "INSERT OR REPLACE INTO room_account_data (room_id, event_type, data)
750 VALUES (?, ?, ?)",
751 )?
752 .execute((room_id, event_type, data))?;
753 Ok(())
754 }
755
756 fn remove_room_account_data(&self, room_id: &[u8]) -> rusqlite::Result<()> {
757 self.prepare(
758 "DELETE FROM room_account_data
759 WHERE room_id = ?",
760 )?
761 .execute((room_id,))?;
762 Ok(())
763 }
764
765 fn set_room_info(&self, room_id: &[u8], state: &[u8], data: &[u8]) -> rusqlite::Result<()> {
766 self.prepare_cached(
767 "INSERT OR REPLACE INTO room_info (room_id, state, data)
768 VALUES (?, ?, ?)",
769 )?
770 .execute((room_id, state, data))?;
771 Ok(())
772 }
773
774 fn get_room_info(&self, room_id: &[u8]) -> rusqlite::Result<Option<Vec<u8>>> {
775 self.query_one("SELECT data FROM room_info WHERE room_id = ?", (room_id,), |row| row.get(0))
776 .optional()
777 }
778
779 fn remove_room_info(&self, room_id: &[u8]) -> rusqlite::Result<()> {
781 self.prepare_cached("DELETE FROM room_info WHERE room_id = ?")?.execute((room_id,))?;
782 Ok(())
783 }
784
785 fn set_state_event(
786 &self,
787 room_id: &[u8],
788 event_type: &[u8],
789 state_key: &[u8],
790 stripped: bool,
791 event_id: Option<&[u8]>,
792 data: &[u8],
793 ) -> rusqlite::Result<()> {
794 self.prepare_cached(
795 "INSERT OR REPLACE
796 INTO state_event (room_id, event_type, state_key, stripped, event_id, data)
797 VALUES (?, ?, ?, ?, ?, ?)",
798 )?
799 .execute((room_id, event_type, state_key, stripped, event_id, data))?;
800 Ok(())
801 }
802
803 fn get_state_event_by_id(
804 &self,
805 room_id: &[u8],
806 event_id: &[u8],
807 ) -> rusqlite::Result<Option<Vec<u8>>> {
808 self.query_one(
809 "SELECT data FROM state_event WHERE room_id = ? AND event_id = ?",
810 (room_id, event_id),
811 |row| row.get(0),
812 )
813 .optional()
814 }
815
816 fn remove_room_state_events(
822 &self,
823 room_id: &[u8],
824 stripped: Option<bool>,
825 ) -> rusqlite::Result<()> {
826 if let Some(stripped) = stripped {
827 self.prepare_cached("DELETE FROM state_event WHERE room_id = ? AND stripped = ?")?
828 .execute((room_id, stripped))?;
829 } else {
830 self.prepare_cached("DELETE FROM state_event WHERE room_id = ?")?
831 .execute((room_id,))?;
832 }
833 Ok(())
834 }
835
836 fn set_member(
837 &self,
838 room_id: &[u8],
839 user_id: &[u8],
840 membership: &[u8],
841 stripped: bool,
842 data: &[u8],
843 ) -> rusqlite::Result<()> {
844 self.prepare_cached(
845 "INSERT OR REPLACE
846 INTO member (room_id, user_id, membership, stripped, data)
847 VALUES (?, ?, ?, ?, ?)",
848 )?
849 .execute((room_id, user_id, membership, stripped, data))?;
850 Ok(())
851 }
852
853 fn remove_room_members(&self, room_id: &[u8], stripped: Option<bool>) -> rusqlite::Result<()> {
858 if let Some(stripped) = stripped {
859 self.prepare_cached("DELETE FROM member WHERE room_id = ? AND stripped = ?")?
860 .execute((room_id, stripped))?;
861 } else {
862 self.prepare_cached("DELETE FROM member WHERE room_id = ?")?.execute((room_id,))?;
863 }
864 Ok(())
865 }
866
867 fn set_profile(&self, room_id: &[u8], user_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
868 self.prepare_cached(
869 "INSERT OR REPLACE
870 INTO profile (room_id, user_id, data)
871 VALUES (?, ?, ?)",
872 )?
873 .execute((room_id, user_id, data))?;
874 Ok(())
875 }
876
877 fn remove_room_profiles(&self, room_id: &[u8]) -> rusqlite::Result<()> {
878 self.prepare("DELETE FROM profile WHERE room_id = ?")?.execute((room_id,))?;
879 Ok(())
880 }
881
882 fn remove_room_profile(&self, room_id: &[u8], user_id: &[u8]) -> rusqlite::Result<()> {
883 self.prepare("DELETE FROM profile WHERE room_id = ? AND user_id = ?")?
884 .execute((room_id, user_id))?;
885 Ok(())
886 }
887
888 fn set_receipt(
889 &self,
890 room_id: &[u8],
891 user_id: &[u8],
892 receipt_type: &[u8],
893 thread: &[u8],
894 event_id: &[u8],
895 data: &[u8],
896 ) -> rusqlite::Result<()> {
897 self.prepare_cached(
898 "INSERT OR REPLACE
899 INTO receipt (room_id, user_id, receipt_type, thread, event_id, data)
900 VALUES (?, ?, ?, ?, ?, ?)",
901 )?
902 .execute((room_id, user_id, receipt_type, thread, event_id, data))?;
903 Ok(())
904 }
905
906 fn remove_room_receipts(&self, room_id: &[u8]) -> rusqlite::Result<()> {
907 self.prepare("DELETE FROM receipt WHERE room_id = ?")?.execute((room_id,))?;
908 Ok(())
909 }
910
911 fn set_display_name(&self, room_id: &[u8], name: &[u8], data: &[u8]) -> rusqlite::Result<()> {
912 self.prepare_cached(
913 "INSERT OR REPLACE
914 INTO display_name (room_id, name, data)
915 VALUES (?, ?, ?)",
916 )?
917 .execute((room_id, name, data))?;
918 Ok(())
919 }
920
921 fn remove_display_name(&self, room_id: &[u8], name: &[u8]) -> rusqlite::Result<()> {
922 self.prepare("DELETE FROM display_name WHERE room_id = ? AND name = ?")?
923 .execute((room_id, name))?;
924 Ok(())
925 }
926
927 fn remove_room_display_names(&self, room_id: &[u8]) -> rusqlite::Result<()> {
928 self.prepare("DELETE FROM display_name WHERE room_id = ?")?.execute((room_id,))?;
929 Ok(())
930 }
931
932 fn remove_room_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()> {
933 self.prepare("DELETE FROM send_queue_events WHERE room_id = ?")?.execute((room_id,))?;
934 Ok(())
935 }
936
937 fn remove_room_dependent_send_queue(&self, room_id: &[u8]) -> rusqlite::Result<()> {
938 self.prepare("DELETE FROM dependent_send_queue_events WHERE room_id = ?")?
939 .execute((room_id,))?;
940 Ok(())
941 }
942}
943
944#[async_trait]
945trait SqliteObjectStateStoreExt: SqliteAsyncConnExt {
946 async fn get_kv_blob(&self, key: Key) -> Result<Option<Vec<u8>>> {
947 Ok(self
948 .query_one("SELECT value FROM kv_blob WHERE key = ?", (key,), |row| row.get(0))
949 .await
950 .optional()?)
951 }
952
953 async fn get_kv_blobs(&self, keys: Vec<Key>) -> Result<Vec<Vec<u8>>> {
954 let keys_length = keys.len();
955
956 self.chunk_large_query_over(keys, Some(keys_length), |txn, keys| {
957 let sql =
958 format!("SELECT value FROM kv_blob WHERE key IN ({})", keys.host_parameters());
959
960 let params = rusqlite::params_from_iter(keys);
961
962 Ok(txn
963 .prepare(&sql)?
964 .query(params)?
965 .mapped(|row| row.get(0))
966 .collect::<Result<_, _>>()?)
967 })
968 .await
969 }
970
971 async fn set_kv_blob(&self, key: Key, value: Vec<u8>) -> Result<()>;
972
973 async fn delete_kv_blob(&self, key: Key) -> Result<()> {
974 self.execute("DELETE FROM kv_blob WHERE key = ?", (key,)).await?;
975 Ok(())
976 }
977
978 async fn get_room_infos(&self, room_id: Option<Key>) -> Result<Vec<Vec<u8>>> {
979 Ok(match room_id {
980 None => {
981 self.prepare("SELECT data FROM room_info", move |mut stmt| {
982 stmt.query_map((), |row| row.get(0))?.collect()
983 })
984 .await?
985 }
986
987 Some(room_id) => {
988 self.prepare("SELECT data FROM room_info WHERE room_id = ?", move |mut stmt| {
989 stmt.query((room_id,))?.mapped(|row| row.get(0)).collect()
990 })
991 .await?
992 }
993 })
994 }
995
996 async fn get_maybe_stripped_state_events_for_keys(
997 &self,
998 room_id: Key,
999 event_type: Key,
1000 state_keys: Vec<Key>,
1001 ) -> Result<Vec<(bool, Vec<u8>)>> {
1002 self.chunk_large_query_over(state_keys, None, move |txn, state_keys| {
1003 let sql = format!(
1004 "SELECT stripped, data FROM state_event
1005 WHERE room_id = ? AND event_type = ? AND state_key IN ({})",
1006 state_keys.host_parameters()
1007 );
1008
1009 let params = rusqlite::params_from_iter(
1010 [room_id.clone(), event_type.clone()].into_iter().chain(state_keys),
1011 );
1012
1013 Ok(txn
1014 .prepare(&sql)?
1015 .query(params)?
1016 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1017 .collect::<Result<_, _>>()?)
1018 })
1019 .await
1020 }
1021
1022 async fn get_maybe_stripped_state_events(
1023 &self,
1024 room_id: Key,
1025 event_type: Key,
1026 ) -> Result<Vec<(bool, Vec<u8>)>> {
1027 Ok(self
1028 .prepare(
1029 "SELECT stripped, data FROM state_event
1030 WHERE room_id = ? AND event_type = ?",
1031 |mut stmt| {
1032 stmt.query((room_id, event_type))?
1033 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1034 .collect()
1035 },
1036 )
1037 .await?)
1038 }
1039
1040 async fn get_profiles(
1041 &self,
1042 room_id: Key,
1043 user_ids: Vec<Key>,
1044 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
1045 let user_ids_length = user_ids.len();
1046
1047 self.chunk_large_query_over(user_ids, Some(user_ids_length), move |txn, user_ids| {
1048 let sql = format!(
1049 "SELECT user_id, data FROM profile WHERE room_id = ? AND user_id IN ({})",
1050 user_ids.host_parameters(),
1051 );
1052
1053 let params = rusqlite::params_from_iter(iter::once(room_id.clone()).chain(user_ids));
1054
1055 Ok(txn
1056 .prepare(&sql)?
1057 .query(params)?
1058 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1059 .collect::<Result<_, _>>()?)
1060 })
1061 .await
1062 }
1063
1064 async fn get_global_profiles(&self, user_ids: Vec<Key>) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
1065 let user_ids_length = user_ids.len();
1066
1067 self.chunk_large_query_over(user_ids, Some(user_ids_length), move |txn, user_ids| {
1068 let sql = format!(
1069 "SELECT user_id, profile_data FROM global_profiles WHERE user_id IN ({})",
1070 user_ids.host_parameters(),
1071 );
1072
1073 let params = rusqlite::params_from_iter(user_ids);
1074
1075 Ok(txn
1076 .prepare(&sql)?
1077 .query(params)?
1078 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1079 .collect::<Result<_, _>>()?)
1080 })
1081 .await
1082 }
1083
1084 async fn get_user_ids(&self, room_id: Key, memberships: Vec<Key>) -> Result<Vec<Vec<u8>>> {
1085 let res = if memberships.is_empty() {
1086 self.prepare("SELECT data FROM member WHERE room_id = ?", |mut stmt| {
1087 stmt.query((room_id,))?.mapped(|row| row.get(0)).collect()
1088 })
1089 .await?
1090 } else {
1091 self.chunk_large_query_over(memberships, None, move |txn, memberships| {
1092 let sql = format!(
1093 "SELECT data FROM member WHERE room_id = ? AND membership IN ({})",
1094 memberships.host_parameters(),
1095 );
1096
1097 let params =
1098 rusqlite::params_from_iter(iter::once(room_id.clone()).chain(memberships));
1099
1100 Ok(txn
1101 .prepare(&sql)?
1102 .query(params)?
1103 .mapped(|row| row.get(0))
1104 .collect::<Result<_, _>>()?)
1105 })
1106 .await?
1107 };
1108
1109 Ok(res)
1110 }
1111
1112 async fn get_global_account_data(&self, event_type: Key) -> Result<Option<Vec<u8>>> {
1113 Ok(self
1114 .query_one(
1115 "SELECT data FROM global_account_data WHERE event_type = ?",
1116 (event_type,),
1117 |row| row.get(0),
1118 )
1119 .await
1120 .optional()?)
1121 }
1122
1123 async fn get_room_account_data(
1124 &self,
1125 room_id: Key,
1126 event_type: Key,
1127 ) -> Result<Option<Vec<u8>>> {
1128 Ok(self
1129 .query_one(
1130 "SELECT data FROM room_account_data WHERE room_id = ? AND event_type = ?",
1131 (room_id, event_type),
1132 |row| row.get(0),
1133 )
1134 .await
1135 .optional()?)
1136 }
1137
1138 async fn get_display_names(
1139 &self,
1140 room_id: Key,
1141 names: Vec<Key>,
1142 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
1143 let names_length = names.len();
1144
1145 self.chunk_large_query_over(names, Some(names_length), move |txn, names| {
1146 let sql = format!(
1147 "SELECT name, data FROM display_name WHERE room_id = ? AND name IN ({})",
1148 names.host_parameters()
1149 );
1150
1151 let params = rusqlite::params_from_iter(iter::once(room_id.clone()).chain(names));
1152
1153 Ok(txn
1154 .prepare(&sql)?
1155 .query(params)?
1156 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
1157 .collect::<Result<_, _>>()?)
1158 })
1159 .await
1160 }
1161
1162 async fn get_user_receipt(
1163 &self,
1164 room_id: Key,
1165 receipt_type: Key,
1166 receipt_thread: Key,
1167 user_id: Key,
1168 ) -> Result<Option<Vec<u8>>> {
1169 Ok(self
1170 .query_one(
1171 "SELECT data FROM receipt
1172 WHERE room_id = ? AND receipt_type = ? AND thread = ? and user_id = ?",
1173 (room_id, receipt_type, receipt_thread, user_id),
1174 |row| row.get(0),
1175 )
1176 .await
1177 .optional()?)
1178 }
1179
1180 async fn get_event_receipts(
1181 &self,
1182 room_id: Key,
1183 receipt_type: Key,
1184 thread: Key,
1185 event_id: Key,
1186 ) -> Result<Vec<Vec<u8>>> {
1187 Ok(self
1188 .prepare(
1189 "SELECT data FROM receipt
1190 WHERE room_id = ? AND receipt_type = ? AND thread = ? and event_id = ?",
1191 |mut stmt| {
1192 stmt.query((room_id, receipt_type, thread, event_id))?
1193 .mapped(|row| row.get(0))
1194 .collect()
1195 },
1196 )
1197 .await?)
1198 }
1199}
1200
1201#[async_trait]
1202impl SqliteObjectStateStoreExt for SqliteAsyncConn {
1203 async fn set_kv_blob(&self, key: Key, value: Vec<u8>) -> Result<()> {
1204 Ok(self.interact(move |conn| conn.set_kv_blob(&key, &value)).await.unwrap()?)
1205 }
1206}
1207
1208#[async_trait]
1209impl StateStore for SqliteStateStore {
1210 type Error = Error;
1211
1212 async fn get_kv_data(&self, key: StateStoreDataKey<'_>) -> Result<Option<StateStoreDataValue>> {
1213 self.read()
1214 .await?
1215 .get_kv_blob(self.encode_state_store_data_key(key))
1216 .await?
1217 .map(|data| {
1218 Ok(match key {
1219 StateStoreDataKey::SyncToken => {
1220 StateStoreDataValue::SyncToken(self.deserialize_value(&data)?)
1221 }
1222 StateStoreDataKey::SupportedVersions => {
1223 StateStoreDataValue::SupportedVersions(self.deserialize_value(&data)?)
1224 }
1225 StateStoreDataKey::WellKnown => {
1226 StateStoreDataValue::WellKnown(self.deserialize_value(&data)?)
1227 }
1228 StateStoreDataKey::Filter(_) => {
1229 StateStoreDataValue::Filter(self.deserialize_value(&data)?)
1230 }
1231 StateStoreDataKey::UserAvatarUrl(_) => {
1232 StateStoreDataValue::UserAvatarUrl(self.deserialize_value(&data)?)
1233 }
1234 StateStoreDataKey::RecentlyVisitedRooms(_) => {
1235 StateStoreDataValue::RecentlyVisitedRooms(self.deserialize_value(&data)?)
1236 }
1237 StateStoreDataKey::UtdHookManagerData => {
1238 StateStoreDataValue::UtdHookManagerData(self.deserialize_value(&data)?)
1239 }
1240 StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
1241 StateStoreDataValue::OneTimeKeyAlreadyUploaded
1242 }
1243 StateStoreDataKey::ComposerDraft(_, _) => {
1244 StateStoreDataValue::ComposerDraft(self.deserialize_value(&data)?)
1245 }
1246 StateStoreDataKey::SeenKnockRequests(_) => {
1247 StateStoreDataValue::SeenKnockRequests(self.deserialize_value(&data)?)
1248 }
1249 StateStoreDataKey::ThreadSubscriptionsCatchupTokens => {
1250 StateStoreDataValue::ThreadSubscriptionsCatchupTokens(
1251 self.deserialize_value(&data)?,
1252 )
1253 }
1254 StateStoreDataKey::HomeserverCapabilities => {
1255 StateStoreDataValue::HomeserverCapabilities(self.deserialize_value(&data)?)
1256 }
1257 })
1258 })
1259 .transpose()
1260 }
1261
1262 async fn set_kv_data(
1263 &self,
1264 key: StateStoreDataKey<'_>,
1265 value: StateStoreDataValue,
1266 ) -> Result<()> {
1267 let serialized_value = match key {
1268 StateStoreDataKey::SyncToken => self.serialize_value(
1269 &value.into_sync_token().expect("Session data not a sync token"),
1270 )?,
1271 StateStoreDataKey::SupportedVersions => self.serialize_value(
1272 &value
1273 .into_supported_versions()
1274 .expect("Session data not containing supported versions"),
1275 )?,
1276 StateStoreDataKey::WellKnown => self.serialize_value(
1277 &value.into_well_known().expect("Session data not containing well-known"),
1278 )?,
1279 StateStoreDataKey::Filter(_) => {
1280 self.serialize_value(&value.into_filter().expect("Session data not a filter"))?
1281 }
1282 StateStoreDataKey::UserAvatarUrl(_) => self.serialize_value(
1283 &value.into_user_avatar_url().expect("Session data not an user avatar url"),
1284 )?,
1285 StateStoreDataKey::RecentlyVisitedRooms(_) => self.serialize_value(
1286 &value.into_recently_visited_rooms().expect("Session data not breadcrumbs"),
1287 )?,
1288 StateStoreDataKey::UtdHookManagerData => self.serialize_value(
1289 &value.into_utd_hook_manager_data().expect("Session data not UtdHookManagerData"),
1290 )?,
1291 StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
1292 self.serialize_value(&true).expect("We should be able to serialize a boolean")
1293 }
1294 StateStoreDataKey::ComposerDraft(_, _) => self.serialize_value(
1295 &value.into_composer_draft().expect("Session data not a composer draft"),
1296 )?,
1297 StateStoreDataKey::SeenKnockRequests(_) => self.serialize_value(
1298 &value
1299 .into_seen_knock_requests()
1300 .expect("Session data is not a set of seen knock request ids"),
1301 )?,
1302 StateStoreDataKey::ThreadSubscriptionsCatchupTokens => self.serialize_value(
1303 &value
1304 .into_thread_subscriptions_catchup_tokens()
1305 .expect("Session data is not a list of thread subscription catchup tokens"),
1306 )?,
1307 StateStoreDataKey::HomeserverCapabilities => self.serialize_value(
1308 &value
1309 .into_homeserver_capabilities()
1310 .expect("Session data is not the homeserver capabilities"),
1311 )?,
1312 };
1313
1314 self.write()
1315 .await?
1316 .set_kv_blob(self.encode_state_store_data_key(key), serialized_value)
1317 .await
1318 }
1319
1320 async fn remove_kv_data(&self, key: StateStoreDataKey<'_>) -> Result<()> {
1321 self.write().await?.delete_kv_blob(self.encode_state_store_data_key(key)).await
1322 }
1323
1324 async fn save_changes(&self, changes: &StateChanges) -> Result<()> {
1325 let changes = changes.to_owned();
1326 let this = self.clone();
1327 self.write()
1328 .await?
1329 .with_transaction(move |txn| {
1330 let StateChanges {
1331 sync_token,
1332 account_data,
1333 presence,
1334 profiles,
1335 profiles_to_delete,
1336 state,
1337 room_account_data,
1338 room_infos,
1339 receipts,
1340 redactions,
1341 stripped_state,
1342 ambiguity_maps,
1343 global_profiles,
1344 unavailable_global_profiles,
1345 } = changes;
1346
1347 if let Some(sync_token) = sync_token {
1348 let key = this.encode_state_store_data_key(StateStoreDataKey::SyncToken);
1349 let value = this.serialize_value(&sync_token)?;
1350 txn.set_kv_blob(&key, &value)?;
1351 }
1352
1353 for (event_type, event) in account_data {
1354 let event_type =
1355 this.encode_key(keys::GLOBAL_ACCOUNT_DATA, event_type.to_string());
1356 let data = this.serialize_json(&event)?;
1357 txn.set_global_account_data(&event_type, &data)?;
1358 }
1359
1360 for (room_id, events) in room_account_data {
1361 let room_id = this.encode_key(keys::ROOM_ACCOUNT_DATA, room_id);
1362 for (event_type, event) in events {
1363 let event_type =
1364 this.encode_key(keys::ROOM_ACCOUNT_DATA, event_type.to_string());
1365 let data = this.serialize_json(&event)?;
1366 txn.set_room_account_data(&room_id, &event_type, &data)?;
1367 }
1368 }
1369
1370 for (user_id, event) in presence {
1371 let key = this.encode_presence_key(&user_id);
1372 let value = this.serialize_json(&event)?;
1373 txn.set_kv_blob(&key, &value)?;
1374 }
1375
1376 for (room_id, room_info) in room_infos {
1377 let stripped = matches!(room_info.state(), RoomState::Invited | RoomState::Knocked);
1379
1380 if !stripped || stripped_state.contains_key(&room_id) {
1388 this.remove_maybe_stripped_room_data(txn, &room_id, !stripped)?;
1389 }
1390
1391 let room_id = this.encode_key(keys::ROOM_INFO, room_id);
1392 let state = this
1393 .encode_key(keys::ROOM_INFO, serde_json::to_string(&room_info.state())?);
1394 let data = this.serialize_json(&room_info)?;
1395 txn.set_room_info(&room_id, &state, &data)?;
1396 }
1397
1398 for (room_id, user_ids) in profiles_to_delete {
1399 let room_id = this.encode_key(keys::PROFILE, room_id);
1400 for user_id in user_ids {
1401 let user_id = this.encode_key(keys::PROFILE, user_id);
1402 txn.remove_room_profile(&room_id, &user_id)?;
1403 }
1404 }
1405
1406 for (room_id, state_event_types) in state {
1407 let profiles = profiles.get(&room_id);
1408 let encoded_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
1409
1410 for (event_type, state_events) in state_event_types {
1411 let encoded_event_type =
1412 this.encode_key(keys::STATE_EVENT, event_type.to_string());
1413
1414 for (state_key, raw_state_event) in state_events {
1415 let encoded_state_key = this.encode_key(keys::STATE_EVENT, &state_key);
1416 let data = this.serialize_json(&raw_state_event)?;
1417
1418 let event_id: Option<String> =
1419 raw_state_event.get_field("event_id").ok().flatten();
1420 let encoded_event_id =
1421 event_id.as_ref().map(|e| this.encode_key(keys::STATE_EVENT, e));
1422
1423 txn.set_state_event(
1424 &encoded_room_id,
1425 &encoded_event_type,
1426 &encoded_state_key,
1427 false,
1428 encoded_event_id.as_deref(),
1429 &data,
1430 )?;
1431
1432 if event_type == StateEventType::RoomMember {
1433 let member_event = match raw_state_event
1434 .deserialize_as_unchecked::<SyncRoomMemberEvent>()
1435 {
1436 Ok(ev) => ev,
1437 Err(e) => {
1438 debug!(event_id, "Failed to deserialize member event: {e}");
1439 continue;
1440 }
1441 };
1442
1443 let encoded_room_id = this.encode_key(keys::MEMBER, &room_id);
1444 let user_id = this.encode_key(keys::MEMBER, &state_key);
1445 let membership = this
1446 .encode_key(keys::MEMBER, member_event.membership().as_str());
1447 let data = this.serialize_value(&state_key)?;
1448
1449 txn.set_member(
1450 &encoded_room_id,
1451 &user_id,
1452 &membership,
1453 false,
1454 &data,
1455 )?;
1456
1457 if let Some(profile) =
1458 profiles.and_then(|p| p.get(member_event.state_key()))
1459 {
1460 let room_id = this.encode_key(keys::PROFILE, &room_id);
1461 let user_id = this.encode_key(keys::PROFILE, &state_key);
1462 let data = this.serialize_json(&profile)?;
1463 txn.set_profile(&room_id, &user_id, &data)?;
1464 }
1465 }
1466 }
1467 }
1468 }
1469
1470 for (room_id, stripped_state_event_types) in stripped_state {
1471 let encoded_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
1472
1473 for (event_type, stripped_state_events) in stripped_state_event_types {
1474 let encoded_event_type =
1475 this.encode_key(keys::STATE_EVENT, event_type.to_string());
1476
1477 for (state_key, raw_stripped_state_event) in stripped_state_events {
1478 let encoded_state_key = this.encode_key(keys::STATE_EVENT, &state_key);
1479 let data = this.serialize_json(&raw_stripped_state_event)?;
1480 txn.set_state_event(
1481 &encoded_room_id,
1482 &encoded_event_type,
1483 &encoded_state_key,
1484 true,
1485 None,
1486 &data,
1487 )?;
1488
1489 if event_type == StateEventType::RoomMember {
1490 let member_event = match raw_stripped_state_event
1491 .deserialize_as_unchecked::<StrippedRoomMemberEvent>(
1492 ) {
1493 Ok(ev) => ev,
1494 Err(e) => {
1495 debug!("Failed to deserialize stripped member event: {e}");
1496 continue;
1497 }
1498 };
1499
1500 let room_id = this.encode_key(keys::MEMBER, &room_id);
1501 let user_id = this.encode_key(keys::MEMBER, &state_key);
1502 let membership = this.encode_key(
1503 keys::MEMBER,
1504 member_event.content.membership.as_str(),
1505 );
1506 let data = this.serialize_value(&state_key)?;
1507
1508 txn.set_member(&room_id, &user_id, &membership, true, &data)?;
1509 }
1510 }
1511 }
1512 }
1513
1514 for (room_id, receipt_event) in receipts {
1515 let room_id = this.encode_key(keys::RECEIPT, room_id);
1516
1517 for (event_id, receipt_types) in receipt_event {
1518 let encoded_event_id = this.encode_key(keys::RECEIPT, &event_id);
1519
1520 for (receipt_type, receipt_users) in receipt_types {
1521 let receipt_type =
1522 this.encode_key(keys::RECEIPT, receipt_type.as_str());
1523
1524 for (user_id, receipt) in receipt_users {
1525 let encoded_user_id = this.encode_key(keys::RECEIPT, &user_id);
1526 let thread = this.encode_key(
1530 keys::RECEIPT,
1531 rmp_serde::to_vec_named(&receipt.thread)?,
1532 );
1533 let data = this.serialize_json(&ReceiptData {
1534 receipt,
1535 event_id: event_id.clone(),
1536 user_id,
1537 })?;
1538
1539 txn.set_receipt(
1540 &room_id,
1541 &encoded_user_id,
1542 &receipt_type,
1543 &thread,
1544 &encoded_event_id,
1545 &data,
1546 )?;
1547 }
1548 }
1549 }
1550 }
1551
1552 for (room_id, redactions) in redactions {
1553 let make_redaction_rules = || {
1554 let encoded_room_id = this.encode_key(keys::ROOM_INFO, &room_id);
1555 txn.get_room_info(&encoded_room_id)
1556 .ok()
1557 .flatten()
1558 .and_then(|v| this.deserialize_json::<RoomInfo>(&v).ok())
1559 .map(|info| info.room_version_rules_or_default())
1560 .unwrap_or_else(|| {
1561 warn!(
1562 ?room_id,
1563 "Unable to get the room version rules, defaulting to rules for room version {ROOM_VERSION_FALLBACK}"
1564 );
1565 ROOM_VERSION_RULES_FALLBACK
1566 }).redaction
1567 };
1568
1569 let encoded_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
1570 let mut redaction_rules = None;
1571
1572 for (event_id, redaction) in redactions {
1573 let event_id = this.encode_key(keys::STATE_EVENT, event_id);
1574
1575 if let Some(Ok(raw_event)) = txn
1576 .get_state_event_by_id(&encoded_room_id, &event_id)?
1577 .map(|value| this.deserialize_json::<Raw<AnySyncStateEvent>>(&value))
1578 {
1579 let Some(event_type) = raw_event.get_field::<String>("type")? else {
1580 warn!("Skipping redaction of state event without a type");
1581 continue;
1582 };
1583 let Some(state_key) = raw_event.get_field::<String>("state_key")? else {
1584 warn!("Skipping redaction of state event without a state key");
1585 continue;
1586 };
1587 let redacted = redact(
1588 raw_event.deserialize_as::<CanonicalJsonObject>()?,
1589 redaction_rules.get_or_insert_with(make_redaction_rules),
1590 Some(RedactedBecause::from_raw_event(&redaction)?),
1591 )
1592 .map_err(Error::Redaction)?;
1593 let data = this.serialize_json(&redacted)?;
1594
1595 let event_type = this.encode_key(keys::STATE_EVENT, event_type);
1596 let state_key = this.encode_key(keys::STATE_EVENT, state_key);
1597
1598 txn.set_state_event(
1599 &encoded_room_id,
1600 &event_type,
1601 &state_key,
1602 false,
1603 Some(&event_id),
1604 &data,
1605 )?;
1606 }
1607 }
1608 }
1609
1610 for (room_id, display_names) in ambiguity_maps {
1611 let room_id = this.encode_key(keys::DISPLAY_NAME, room_id);
1612
1613 for (name, user_ids) in display_names {
1614 let encoded_name = this.encode_key(
1615 keys::DISPLAY_NAME,
1616 name.as_normalized_str().unwrap_or_else(|| name.as_raw_str()),
1617 );
1618 let data = this.serialize_json(&user_ids)?;
1619
1620 if user_ids.is_empty() {
1621 txn.remove_display_name(&room_id, &encoded_name)?;
1622
1623 let raw_name = this.encode_key(keys::DISPLAY_NAME, name.as_raw_str());
1642 txn.remove_display_name(&room_id, &raw_name)?;
1643 } else {
1644 txn.set_display_name(&room_id, &encoded_name, &data)?;
1646 }
1647 }
1648 }
1649
1650 for (raw_user_id, profile_update) in global_profiles {
1651 let user_id = this.encode_key(keys::GLOBAL_PROFILES, &raw_user_id);
1652 match profile_update {
1653 UserProfileUpdate::Updated(profile_changes) => {
1654 let existing_data: Option<Vec<u8>> = txn
1655 .prepare_cached(
1656 "SELECT profile_data FROM global_profiles WHERE user_id = ?",
1657 )?
1658 .query_one([&user_id], |row| row.get(0))
1659 .optional()?;
1660
1661 let mut profile: UserProfile = existing_data
1662 .map(|data| this.deserialize_json(&data))
1663 .transpose()?
1664 .unwrap_or_default();
1665 profile.apply(profile_changes);
1666
1667 let serialized = this.serialize_json(&profile)?;
1668 txn.prepare_cached(
1669 "INSERT OR REPLACE INTO global_profiles (user_id, profile_data) VALUES (?, ?)",
1670 )?
1671 .execute((&user_id, serialized))?;
1672
1673 txn.prepare_cached(
1676 "DELETE FROM unavailable_global_profiles WHERE user_id = ?",
1677 )?
1678 .execute([&user_id])?;
1679 }
1680 UserProfileUpdate::Dropped => {
1682 txn.prepare_cached("DELETE FROM global_profiles WHERE user_id = ?")?
1683 .execute([&user_id])?;
1684 }
1685 _ => {
1686 warn!(%raw_user_id, "Unhandled UserProfileUpdate variant; ignoring");
1687 }
1688 }
1689 }
1690
1691 for (user_id, created_at) in unavailable_global_profiles {
1692 let user_id = this.encode_key(keys::GLOBAL_PROFILES, &user_id);
1695 let created_at_ts: u64 = created_at.0.into();
1696 txn.prepare_cached(
1697 "INSERT OR REPLACE INTO unavailable_global_profiles (user_id, created_at) VALUES (?, ?)",
1698 )?
1699 .execute((&user_id, created_at_ts))?;
1700 }
1701
1702 Ok::<_, Error>(())
1703 })
1704 .await?;
1705
1706 Ok(())
1707 }
1708
1709 async fn get_presence_event(&self, user_id: &UserId) -> Result<Option<Raw<PresenceEvent>>> {
1710 self.read()
1711 .await?
1712 .get_kv_blob(self.encode_presence_key(user_id))
1713 .await?
1714 .map(|data| self.deserialize_json(&data))
1715 .transpose()
1716 }
1717
1718 async fn get_presence_events(
1719 &self,
1720 user_ids: &[OwnedUserId],
1721 ) -> Result<Vec<Raw<PresenceEvent>>> {
1722 if user_ids.is_empty() {
1723 return Ok(Vec::new());
1724 }
1725
1726 let user_ids = user_ids.iter().map(|u| self.encode_presence_key(u)).collect();
1727 self.read()
1728 .await?
1729 .get_kv_blobs(user_ids)
1730 .await?
1731 .into_iter()
1732 .map(|data| self.deserialize_json(&data))
1733 .collect()
1734 }
1735
1736 async fn get_state_event(
1737 &self,
1738 room_id: &RoomId,
1739 event_type: StateEventType,
1740 state_key: &str,
1741 ) -> Result<Option<RawAnySyncOrStrippedState>> {
1742 Ok(self
1743 .get_state_events_for_keys(room_id, event_type, &[state_key])
1744 .await?
1745 .into_iter()
1746 .next())
1747 }
1748
1749 async fn get_state_events(
1750 &self,
1751 room_id: &RoomId,
1752 event_type: StateEventType,
1753 ) -> Result<Vec<RawAnySyncOrStrippedState>> {
1754 let room_id = self.encode_key(keys::STATE_EVENT, room_id);
1755 let event_type = self.encode_key(keys::STATE_EVENT, event_type.to_string());
1756 self.read()
1757 .await?
1758 .get_maybe_stripped_state_events(room_id, event_type)
1759 .await?
1760 .into_iter()
1761 .map(|(stripped, data)| {
1762 let ev = if stripped {
1763 RawAnySyncOrStrippedState::Stripped(self.deserialize_json(&data)?)
1764 } else {
1765 RawAnySyncOrStrippedState::Sync(self.deserialize_json(&data)?)
1766 };
1767
1768 Ok(ev)
1769 })
1770 .collect()
1771 }
1772
1773 async fn get_state_events_for_keys(
1774 &self,
1775 room_id: &RoomId,
1776 event_type: StateEventType,
1777 state_keys: &[&str],
1778 ) -> Result<Vec<RawAnySyncOrStrippedState>, Self::Error> {
1779 if state_keys.is_empty() {
1780 return Ok(Vec::new());
1781 }
1782
1783 let room_id = self.encode_key(keys::STATE_EVENT, room_id);
1784 let event_type = self.encode_key(keys::STATE_EVENT, event_type.to_string());
1785 let state_keys = state_keys.iter().map(|k| self.encode_key(keys::STATE_EVENT, k)).collect();
1786 self.read()
1787 .await?
1788 .get_maybe_stripped_state_events_for_keys(room_id, event_type, state_keys)
1789 .await?
1790 .into_iter()
1791 .map(|(stripped, data)| {
1792 let ev = if stripped {
1793 RawAnySyncOrStrippedState::Stripped(self.deserialize_json(&data)?)
1794 } else {
1795 RawAnySyncOrStrippedState::Sync(self.deserialize_json(&data)?)
1796 };
1797
1798 Ok(ev)
1799 })
1800 .collect()
1801 }
1802
1803 async fn get_profile(
1804 &self,
1805 room_id: &RoomId,
1806 user_id: &UserId,
1807 ) -> Result<Option<MinimalRoomMemberEvent>> {
1808 let room_id = self.encode_key(keys::PROFILE, room_id);
1809 let user_ids = vec![self.encode_key(keys::PROFILE, user_id)];
1810
1811 self.read()
1812 .await?
1813 .get_profiles(room_id, user_ids)
1814 .await?
1815 .into_iter()
1816 .next()
1817 .map(|(_, data)| self.deserialize_json(&data))
1818 .transpose()
1819 }
1820
1821 async fn get_profiles<'a>(
1822 &self,
1823 room_id: &RoomId,
1824 user_ids: &'a [OwnedUserId],
1825 ) -> Result<BTreeMap<&'a UserId, MinimalRoomMemberEvent>> {
1826 if user_ids.is_empty() {
1827 return Ok(BTreeMap::new());
1828 }
1829
1830 let room_id = self.encode_key(keys::PROFILE, room_id);
1831 let mut user_ids_map = user_ids
1832 .iter()
1833 .map(|u| (self.encode_key(keys::PROFILE, u), u.as_ref()))
1834 .collect::<BTreeMap<_, _>>();
1835 let user_ids = user_ids_map.keys().cloned().collect();
1836
1837 self.read()
1838 .await?
1839 .get_profiles(room_id, user_ids)
1840 .await?
1841 .into_iter()
1842 .map(|(user_id, data)| {
1843 Ok((
1844 user_ids_map
1845 .remove(user_id.as_slice())
1846 .expect("returned user IDs were requested"),
1847 self.deserialize_json(&data)?,
1848 ))
1849 })
1850 .collect()
1851 }
1852
1853 async fn get_missing_global_profiles<'a>(
1854 &self,
1855 user_ids: &'a [OwnedUserId],
1856 ) -> Result<Vec<&'a UserId>, Self::Error> {
1857 if user_ids.is_empty() {
1858 return Ok(Vec::new());
1859 }
1860
1861 let user_keys: Vec<_> = user_ids
1863 .iter()
1864 .map(|user_id| self.encode_key(keys::GLOBAL_PROFILES, user_id))
1865 .collect();
1866
1867 let found: BTreeSet<_> = self
1869 .read()
1870 .await?
1871 .with_transaction({
1872 let user_keys = user_keys.clone();
1873 move |txn| -> Result<_> {
1874 let select_user_ids = |table: &'static str, user_ids: Vec<Key>| {
1875 txn.chunk_large_query_over(user_ids, None, move |txn, user_ids| {
1876 let sql = format!(
1877 "SELECT user_id FROM {table} WHERE user_id IN ({})",
1878 user_ids.host_parameters(),
1879 );
1880
1881 let params = rusqlite::params_from_iter(user_ids);
1882
1883 Ok(txn
1884 .prepare(&sql)?
1885 .query(params)?
1886 .mapped(|row| row.get(0))
1887 .collect::<Result<Vec<Vec<u8>>, _>>()?)
1888 })
1889 };
1890
1891 let mut found = select_user_ids("global_profiles", user_keys.clone())?;
1892 found.extend(select_user_ids("unavailable_global_profiles", user_keys)?);
1893 Ok(found)
1894 }
1895 })
1896 .await?
1897 .into_iter()
1898 .collect();
1899
1900 Ok(user_ids
1901 .iter()
1902 .zip(&user_keys)
1903 .filter(|(_, key)| !found.contains(&key[..]))
1904 .map(|(user_id, _)| &**user_id)
1905 .collect())
1906 }
1907
1908 async fn get_user_ids(
1909 &self,
1910 room_id: &RoomId,
1911 membership: RoomMemberships,
1912 ) -> Result<Vec<OwnedUserId>> {
1913 let room_id = self.encode_key(keys::MEMBER, room_id);
1914 let memberships = membership
1915 .as_vec()
1916 .into_iter()
1917 .map(|m| self.encode_key(keys::MEMBER, m.as_str()))
1918 .collect();
1919 self.read()
1920 .await?
1921 .get_user_ids(room_id, memberships)
1922 .await?
1923 .iter()
1924 .map(|data| self.deserialize_value(data))
1925 .collect()
1926 }
1927
1928 async fn get_room_infos(&self, room_load_settings: &RoomLoadSettings) -> Result<Vec<RoomInfo>> {
1929 self.read()
1930 .await?
1931 .get_room_infos(match room_load_settings {
1932 RoomLoadSettings::All => None,
1933 RoomLoadSettings::One(room_id) => Some(self.encode_key(keys::ROOM_INFO, room_id)),
1934 })
1935 .await?
1936 .into_iter()
1937 .map(|data| self.deserialize_json(&data))
1938 .collect()
1939 }
1940
1941 async fn get_users_with_display_name(
1942 &self,
1943 room_id: &RoomId,
1944 display_name: &DisplayName,
1945 ) -> Result<BTreeSet<OwnedUserId>> {
1946 let room_id = self.encode_key(keys::DISPLAY_NAME, room_id);
1947 let names = vec![self.encode_key(
1948 keys::DISPLAY_NAME,
1949 display_name.as_normalized_str().unwrap_or_else(|| display_name.as_raw_str()),
1950 )];
1951
1952 Ok(self
1953 .read()
1954 .await?
1955 .get_display_names(room_id, names)
1956 .await?
1957 .into_iter()
1958 .next()
1959 .map(|(_, data)| self.deserialize_json(&data))
1960 .transpose()?
1961 .unwrap_or_default())
1962 }
1963
1964 async fn get_users_with_display_names<'a>(
1965 &self,
1966 room_id: &RoomId,
1967 display_names: &'a [DisplayName],
1968 ) -> Result<HashMap<&'a DisplayName, BTreeSet<OwnedUserId>>> {
1969 let mut result = HashMap::new();
1970
1971 if display_names.is_empty() {
1972 return Ok(result);
1973 }
1974
1975 let room_id = self.encode_key(keys::DISPLAY_NAME, room_id);
1976 let mut names_map = display_names
1977 .iter()
1978 .flat_map(|display_name| {
1979 let raw =
1992 (self.encode_key(keys::DISPLAY_NAME, display_name.as_raw_str()), display_name);
1993 let normalized = display_name.as_normalized_str().map(|normalized| {
1994 (self.encode_key(keys::DISPLAY_NAME, normalized), display_name)
1995 });
1996
1997 iter::once(raw).chain(normalized)
1998 })
1999 .collect::<BTreeMap<_, _>>();
2000 let names = names_map.keys().cloned().collect();
2001
2002 for (name, data) in self.read().await?.get_display_names(room_id, names).await?.into_iter()
2003 {
2004 let display_name =
2005 names_map.remove(name.as_slice()).expect("returned display names were requested");
2006 let user_ids: BTreeSet<_> = self.deserialize_json(&data)?;
2007
2008 result.entry(display_name).or_insert_with(BTreeSet::new).extend(user_ids);
2009 }
2010
2011 Ok(result)
2012 }
2013
2014 async fn get_account_data_event(
2015 &self,
2016 event_type: GlobalAccountDataEventType,
2017 ) -> Result<Option<Raw<AnyGlobalAccountDataEvent>>> {
2018 let event_type = self.encode_key(keys::GLOBAL_ACCOUNT_DATA, event_type.to_string());
2019 self.read()
2020 .await?
2021 .get_global_account_data(event_type)
2022 .await?
2023 .map(|value| self.deserialize_json(&value))
2024 .transpose()
2025 }
2026
2027 async fn get_room_account_data_event(
2028 &self,
2029 room_id: &RoomId,
2030 event_type: RoomAccountDataEventType,
2031 ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>> {
2032 let room_id = self.encode_key(keys::ROOM_ACCOUNT_DATA, room_id);
2033 let event_type = self.encode_key(keys::ROOM_ACCOUNT_DATA, event_type.to_string());
2034 self.read()
2035 .await?
2036 .get_room_account_data(room_id, event_type)
2037 .await?
2038 .map(|value| self.deserialize_json(&value))
2039 .transpose()
2040 }
2041
2042 async fn get_user_room_receipt_event(
2043 &self,
2044 room_id: &RoomId,
2045 receipt_type: ReceiptType,
2046 receipt_thread: &ReceiptThread,
2047 user_id: &UserId,
2048 ) -> Result<Option<(OwnedEventId, Receipt)>> {
2049 let room_id = self.encode_key(keys::RECEIPT, room_id);
2050 let receipt_type = self.encode_key(keys::RECEIPT, receipt_type.to_string());
2051 let receipt_thread =
2054 self.encode_key(keys::RECEIPT, rmp_serde::to_vec_named(receipt_thread)?);
2055 let user_id = self.encode_key(keys::RECEIPT, user_id);
2056
2057 self.read()
2058 .await?
2059 .get_user_receipt(room_id, receipt_type, receipt_thread, user_id)
2060 .await?
2061 .map(|value| {
2062 self.deserialize_json::<ReceiptData>(&value).map(|d| (d.event_id, d.receipt))
2063 })
2064 .transpose()
2065 }
2066
2067 async fn get_event_room_receipt_events(
2068 &self,
2069 room_id: &RoomId,
2070 receipt_type: ReceiptType,
2071 receipt_thread: &ReceiptThread,
2072 event_id: &EventId,
2073 ) -> Result<Vec<(OwnedUserId, Receipt)>> {
2074 let room_id = self.encode_key(keys::RECEIPT, room_id);
2075 let receipt_type = self.encode_key(keys::RECEIPT, receipt_type.to_string());
2076 let receipt_thread =
2079 self.encode_key(keys::RECEIPT, rmp_serde::to_vec_named(receipt_thread)?);
2080 let event_id = self.encode_key(keys::RECEIPT, event_id);
2081
2082 self.read()
2083 .await?
2084 .get_event_receipts(room_id, receipt_type, receipt_thread, event_id)
2085 .await?
2086 .iter()
2087 .map(|value| {
2088 self.deserialize_json::<ReceiptData>(value).map(|d| (d.user_id, d.receipt))
2089 })
2090 .collect()
2091 }
2092
2093 async fn get_custom_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
2094 self.read().await?.get_kv_blob(self.encode_custom_key(key)).await
2095 }
2096
2097 async fn set_custom_value_no_read(&self, key: &[u8], value: Vec<u8>) -> Result<()> {
2098 let conn = self.write().await?;
2099 let key = self.encode_custom_key(key);
2100 conn.set_kv_blob(key, value).await?;
2101 Ok(())
2102 }
2103
2104 async fn set_custom_value(&self, key: &[u8], value: Vec<u8>) -> Result<Option<Vec<u8>>> {
2105 let conn = self.write().await?;
2106 let key = self.encode_custom_key(key);
2107 let previous = conn.get_kv_blob(key.clone()).await?;
2108 conn.set_kv_blob(key, value).await?;
2109 Ok(previous)
2110 }
2111
2112 async fn remove_custom_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
2113 let conn = self.write().await?;
2114 let key = self.encode_custom_key(key);
2115 let previous = conn.get_kv_blob(key.clone()).await?;
2116 if previous.is_some() {
2117 conn.delete_kv_blob(key).await?;
2118 }
2119 Ok(previous)
2120 }
2121
2122 async fn remove_room(&self, room_id: &RoomId) -> Result<()> {
2123 let this = self.clone();
2124 let room_id = room_id.to_owned();
2125
2126 let conn = self.write().await?;
2127
2128 conn.with_transaction(move |txn| -> Result<()> {
2129 let room_info_room_id = this.encode_key(keys::ROOM_INFO, &room_id);
2130 txn.remove_room_info(&room_info_room_id)?;
2131
2132 let state_event_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
2133 txn.remove_room_state_events(&state_event_room_id, None)?;
2134
2135 let member_room_id = this.encode_key(keys::MEMBER, &room_id);
2136 txn.remove_room_members(&member_room_id, None)?;
2137
2138 let profile_room_id = this.encode_key(keys::PROFILE, &room_id);
2139 txn.remove_room_profiles(&profile_room_id)?;
2140
2141 let room_account_data_room_id = this.encode_key(keys::ROOM_ACCOUNT_DATA, &room_id);
2142 txn.remove_room_account_data(&room_account_data_room_id)?;
2143
2144 let receipt_room_id = this.encode_key(keys::RECEIPT, &room_id);
2145 txn.remove_room_receipts(&receipt_room_id)?;
2146
2147 let display_name_room_id = this.encode_key(keys::DISPLAY_NAME, &room_id);
2148 txn.remove_room_display_names(&display_name_room_id)?;
2149
2150 let send_queue_room_id = this.encode_key(keys::SEND_QUEUE, &room_id);
2151 txn.remove_room_send_queue(&send_queue_room_id)?;
2152
2153 let dependent_send_queue_room_id =
2154 this.encode_key(keys::DEPENDENTS_SEND_QUEUE, &room_id);
2155 txn.remove_room_dependent_send_queue(&dependent_send_queue_room_id)?;
2156
2157 let thread_subscriptions_room_id =
2158 this.encode_key(keys::THREAD_SUBSCRIPTIONS, &room_id);
2159 txn.execute(
2160 "DELETE FROM thread_subscriptions WHERE room_id = ?",
2161 (thread_subscriptions_room_id,),
2162 )?;
2163
2164 Ok(())
2165 })
2166 .await?;
2167
2168 conn.vacuum().await
2169 }
2170
2171 async fn save_send_queue_request(
2172 &self,
2173 room_id: &RoomId,
2174 transaction_id: OwnedTransactionId,
2175 created_at: MilliSecondsSinceUnixEpoch,
2176 content: QueuedRequestKind,
2177 priority: usize,
2178 ) -> Result<(), Self::Error> {
2179 let room_id_key = self.encode_key(keys::SEND_QUEUE, room_id);
2180 let room_id_value = self.serialize_value(&room_id.to_owned())?;
2181
2182 let content = self.serialize_json(&content)?;
2183 let created_at_ts: u64 = created_at.0.into();
2190 self.write()
2191 .await?
2192 .with_transaction(move |txn| {
2193 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))?;
2194 Ok(())
2195 })
2196 .await
2197 }
2198
2199 async fn update_send_queue_request(
2200 &self,
2201 room_id: &RoomId,
2202 transaction_id: &TransactionId,
2203 content: QueuedRequestKind,
2204 ) -> Result<bool, Self::Error> {
2205 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2206
2207 let content = self.serialize_json(&content)?;
2208 let transaction_id = transaction_id.to_string();
2211
2212 let num_updated = self.write()
2213 .await?
2214 .with_transaction(move |txn| {
2215 txn.prepare_cached("UPDATE send_queue_events SET wedge_reason = NULL, content = ? WHERE room_id = ? AND transaction_id = ?")?.execute((content, room_id, transaction_id))
2216 })
2217 .await?;
2218
2219 Ok(num_updated > 0)
2220 }
2221
2222 async fn remove_send_queue_request(
2223 &self,
2224 room_id: &RoomId,
2225 transaction_id: &TransactionId,
2226 ) -> Result<bool, Self::Error> {
2227 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2228
2229 let transaction_id = transaction_id.to_string();
2231
2232 let num_deleted = self
2233 .write()
2234 .await?
2235 .with_transaction(move |txn| {
2236 txn.prepare_cached(
2237 "DELETE FROM send_queue_events WHERE room_id = ? AND transaction_id = ?",
2238 )?
2239 .execute((room_id, &transaction_id))
2240 })
2241 .await?;
2242
2243 Ok(num_deleted > 0)
2244 }
2245
2246 async fn load_send_queue_requests(
2247 &self,
2248 room_id: &RoomId,
2249 ) -> Result<Vec<QueuedRequest>, Self::Error> {
2250 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2251
2252 let res: Vec<(String, Vec<u8>, Option<Vec<u8>>, usize, Option<u64>)> = self
2257 .read()
2258 .await?
2259 .prepare(
2260 "SELECT transaction_id, content, wedge_reason, priority, created_at FROM send_queue_events WHERE room_id = ? ORDER BY priority DESC, ROWID",
2261 |mut stmt| {
2262 stmt.query((room_id,))?
2263 .mapped(|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?)))
2264 .collect()
2265 },
2266 )
2267 .await?;
2268
2269 let mut requests = Vec::with_capacity(res.len());
2270
2271 for entry in res {
2272 let created_at = entry
2273 .4
2274 .and_then(UInt::new)
2275 .map_or_else(MilliSecondsSinceUnixEpoch::now, MilliSecondsSinceUnixEpoch);
2276
2277 requests.push(QueuedRequest {
2278 transaction_id: entry.0.into(),
2279 kind: self.deserialize_json(&entry.1)?,
2280 error: entry.2.map(|v| self.deserialize_value(&v)).transpose()?,
2281 priority: entry.3,
2282 created_at,
2283 });
2284 }
2285
2286 Ok(requests)
2287 }
2288
2289 async fn update_send_queue_request_status(
2290 &self,
2291 room_id: &RoomId,
2292 transaction_id: &TransactionId,
2293 error: Option<QueueWedgeError>,
2294 ) -> Result<(), Self::Error> {
2295 let room_id = self.encode_key(keys::SEND_QUEUE, room_id);
2296
2297 let transaction_id = transaction_id.to_string();
2299
2300 let error_value = error.map(|e| self.serialize_value(&e)).transpose()?;
2303
2304 self.write()
2305 .await?
2306 .with_transaction(move |txn| {
2307 txn.prepare_cached("UPDATE send_queue_events SET wedge_reason = ? WHERE room_id = ? AND transaction_id = ?")?.execute((error_value, room_id, transaction_id))?;
2308 Ok(())
2309 })
2310 .await
2311 }
2312
2313 async fn load_rooms_with_unsent_requests(&self) -> Result<Vec<OwnedRoomId>, Self::Error> {
2314 let res: Vec<Vec<u8>> = self
2320 .read()
2321 .await?
2322 .prepare("SELECT room_id_val FROM send_queue_events", |mut stmt| {
2323 stmt.query(())?.mapped(|row| row.get(0)).collect()
2324 })
2325 .await?;
2326
2327 Ok(res
2330 .into_iter()
2331 .map(|entry| self.deserialize_value(&entry))
2332 .collect::<Result<BTreeSet<OwnedRoomId>, _>>()?
2333 .into_iter()
2334 .collect())
2335 }
2336
2337 async fn save_dependent_queued_request(
2338 &self,
2339 room_id: &RoomId,
2340 parent_txn_id: &TransactionId,
2341 own_txn_id: ChildTransactionId,
2342 created_at: MilliSecondsSinceUnixEpoch,
2343 content: DependentQueuedRequestKind,
2344 ) -> Result<()> {
2345 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2346 let content = self.serialize_json(&content)?;
2347
2348 let parent_txn_id = parent_txn_id.to_string();
2350 let own_txn_id = own_txn_id.to_string();
2351
2352 let created_at_ts: u64 = created_at.0.into();
2353 self.write()
2354 .await?
2355 .with_transaction(move |txn| {
2356 txn.prepare_cached(
2357 r#"INSERT INTO dependent_send_queue_events
2358 (room_id, parent_transaction_id, own_transaction_id, content, created_at)
2359 VALUES (?, ?, ?, ?, ?)"#,
2360 )?
2361 .execute((
2362 room_id,
2363 parent_txn_id,
2364 own_txn_id,
2365 content,
2366 created_at_ts,
2367 ))?;
2368 Ok(())
2369 })
2370 .await
2371 }
2372
2373 async fn update_dependent_queued_request(
2374 &self,
2375 room_id: &RoomId,
2376 own_transaction_id: &ChildTransactionId,
2377 new_content: DependentQueuedRequestKind,
2378 ) -> Result<bool> {
2379 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2380 let content = self.serialize_json(&new_content)?;
2381
2382 let own_txn_id = own_transaction_id.to_string();
2384
2385 let num_updated = self
2386 .write()
2387 .await?
2388 .with_transaction(move |txn| {
2389 txn.prepare_cached(
2390 r#"UPDATE dependent_send_queue_events
2391 SET content = ?
2392 WHERE own_transaction_id = ?
2393 AND room_id = ?"#,
2394 )?
2395 .execute((content, own_txn_id, room_id))
2396 })
2397 .await?;
2398
2399 if num_updated > 1 {
2400 return Err(Error::InconsistentUpdate);
2401 }
2402
2403 Ok(num_updated == 1)
2404 }
2405
2406 async fn mark_dependent_queued_requests_as_ready(
2407 &self,
2408 room_id: &RoomId,
2409 parent_txn_id: &TransactionId,
2410 parent_key: SentRequestKey,
2411 ) -> Result<usize> {
2412 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2413 let parent_key = self.serialize_json(&parent_key)?;
2414
2415 let parent_txn_id = parent_txn_id.to_string();
2417
2418 self.write()
2419 .await?
2420 .with_transaction(move |txn| {
2421 Ok(txn.prepare_cached(
2422 "UPDATE dependent_send_queue_events SET parent_key = ? WHERE parent_transaction_id = ? and room_id = ?",
2423 )?
2424 .execute((parent_key, parent_txn_id, room_id))?)
2425 })
2426 .await
2427 }
2428
2429 async fn remove_dependent_queued_request(
2430 &self,
2431 room_id: &RoomId,
2432 txn_id: &ChildTransactionId,
2433 ) -> Result<bool> {
2434 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2435
2436 let txn_id = txn_id.to_string();
2438
2439 let num_deleted = self
2440 .write()
2441 .await?
2442 .with_transaction(move |txn| {
2443 txn.prepare_cached(
2444 "DELETE FROM dependent_send_queue_events WHERE own_transaction_id = ? AND room_id = ?",
2445 )?
2446 .execute((txn_id, room_id))
2447 })
2448 .await?;
2449
2450 Ok(num_deleted > 0)
2451 }
2452
2453 async fn load_dependent_queued_requests(
2454 &self,
2455 room_id: &RoomId,
2456 ) -> Result<Vec<DependentQueuedRequest>> {
2457 let room_id = self.encode_key(keys::DEPENDENTS_SEND_QUEUE, room_id);
2458
2459 let res: Vec<(String, String, Option<Vec<u8>>, Vec<u8>, Option<u64>)> = self
2462 .read()
2463 .await?
2464 .prepare(
2465 "SELECT own_transaction_id, parent_transaction_id, parent_key, content, created_at FROM dependent_send_queue_events WHERE room_id = ? ORDER BY ROWID",
2466 |mut stmt| {
2467 stmt.query((room_id,))?
2468 .mapped(|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?)))
2469 .collect()
2470 },
2471 )
2472 .await?;
2473
2474 let mut dependent_events = Vec::with_capacity(res.len());
2475
2476 for entry in res {
2477 let created_at = entry
2478 .4
2479 .and_then(UInt::new)
2480 .map_or_else(MilliSecondsSinceUnixEpoch::now, MilliSecondsSinceUnixEpoch);
2481
2482 dependent_events.push(DependentQueuedRequest {
2483 own_transaction_id: entry.0.into(),
2484 parent_transaction_id: entry.1.into(),
2485 parent_key: entry.2.map(|json| self.deserialize_json(&json)).transpose()?,
2486 kind: self.deserialize_json(&entry.3)?,
2487 created_at,
2488 });
2489 }
2490
2491 Ok(dependent_events)
2492 }
2493
2494 async fn upsert_thread_subscriptions(
2495 &self,
2496 updates: Vec<(&RoomId, &EventId, StoredThreadSubscription)>,
2497 ) -> Result<(), Self::Error> {
2498 let values: Vec<_> = updates
2499 .into_iter()
2500 .map(|(room_id, thread_id, subscription)| {
2501 (
2502 self.encode_key(keys::THREAD_SUBSCRIPTIONS, room_id),
2503 self.encode_key(keys::THREAD_SUBSCRIPTIONS, thread_id),
2504 subscription.status.as_str(),
2505 subscription.bump_stamp,
2506 )
2507 })
2508 .collect();
2509
2510 self.write()
2511 .await?
2512 .with_transaction(move |txn| {
2513 let mut txn = txn.prepare_cached(
2514 "INSERT INTO thread_subscriptions (room_id, event_id, status, bump_stamp)
2515 VALUES (?, ?, ?, ?)
2516 ON CONFLICT (room_id, event_id) DO UPDATE
2517 SET
2518 status =
2519 CASE
2520 WHEN thread_subscriptions.bump_stamp IS NULL THEN EXCLUDED.status
2521 WHEN EXCLUDED.bump_stamp IS NULL THEN EXCLUDED.status
2522 WHEN thread_subscriptions.bump_stamp < EXCLUDED.bump_stamp THEN EXCLUDED.status
2523 ELSE thread_subscriptions.status
2524 END,
2525 bump_stamp =
2526 CASE
2527 WHEN thread_subscriptions.bump_stamp IS NULL THEN EXCLUDED.bump_stamp
2528 WHEN EXCLUDED.bump_stamp IS NULL THEN thread_subscriptions.bump_stamp
2529 WHEN thread_subscriptions.bump_stamp < EXCLUDED.bump_stamp THEN EXCLUDED.bump_stamp
2530 ELSE thread_subscriptions.bump_stamp
2531 END",
2532 )?;
2533
2534 for value in values {
2535 txn.execute(value)?;
2536 }
2537
2538 Result::<_, Error>::Ok(())
2539 })
2540 .await?;
2541
2542 Ok(())
2543 }
2544
2545 async fn load_thread_subscription(
2546 &self,
2547 room_id: &RoomId,
2548 thread_id: &EventId,
2549 ) -> Result<Option<StoredThreadSubscription>, Self::Error> {
2550 let room_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, room_id);
2551 let thread_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, thread_id);
2552
2553 Ok(self
2554 .read()
2555 .await?
2556 .query_one(
2557 "SELECT status, bump_stamp FROM thread_subscriptions WHERE room_id = ? AND event_id = ?",
2558 (room_id, thread_id),
2559 |row| Ok((row.get::<_, String>(0)?, row.get::<_, Option<u64>>(1)?))
2560 )
2561 .await
2562 .optional()?
2563 .map(|(status, bump_stamp)| -> Result<_, Self::Error> {
2564 let status = ThreadSubscriptionStatus::from_str(&status).map_err(|_| {
2565 Error::InvalidData { details: format!("Invalid thread status: {status}") }
2566 })?;
2567 Ok(StoredThreadSubscription { status, bump_stamp })
2568 })
2569 .transpose()?)
2570 }
2571
2572 async fn remove_thread_subscription(
2573 &self,
2574 room_id: &RoomId,
2575 thread_id: &EventId,
2576 ) -> Result<(), Self::Error> {
2577 let room_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, room_id);
2578 let thread_id = self.encode_key(keys::THREAD_SUBSCRIPTIONS, thread_id);
2579
2580 self.write()
2581 .await?
2582 .execute(
2583 "DELETE FROM thread_subscriptions WHERE room_id = ? AND event_id = ?",
2584 (room_id, thread_id),
2585 )
2586 .await?;
2587
2588 Ok(())
2589 }
2590
2591 async fn get_global_profile(
2592 &self,
2593 user_id: &UserId,
2594 ) -> Result<Option<UserProfile>, Self::Error> {
2595 self.read()
2596 .await?
2597 .get_global_profiles(vec![self.encode_key(keys::GLOBAL_PROFILES, user_id)])
2598 .await?
2599 .into_iter()
2600 .next()
2601 .map(|(_, data)| self.deserialize_json(&data))
2602 .transpose()
2603 }
2604
2605 async fn get_global_profiles<'a>(
2606 &self,
2607 user_ids: &'a [OwnedUserId],
2608 ) -> Result<BTreeMap<&'a UserId, UserProfile>, Self::Error> {
2609 if user_ids.is_empty() {
2610 return Ok(BTreeMap::new());
2611 }
2612
2613 let mut user_ids_map = user_ids
2614 .iter()
2615 .map(|u| (self.encode_key(keys::GLOBAL_PROFILES, u), u.as_ref()))
2616 .collect::<BTreeMap<_, _>>();
2617 let user_ids = user_ids_map.keys().cloned().collect();
2618
2619 self.read()
2620 .await?
2621 .get_global_profiles(user_ids)
2622 .await?
2623 .into_iter()
2624 .map(|(user_id, data)| {
2625 Ok((
2626 user_ids_map
2627 .remove(user_id.as_slice())
2628 .expect("returned user IDs were requested"),
2629 self.deserialize_json(&data)?,
2630 ))
2631 })
2632 .collect()
2633 }
2634
2635 async fn optimize(&self) -> Result<(), Self::Error> {
2636 Ok(self.vacuum().await?)
2637 }
2638
2639 async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
2640 self.get_db_size().await
2641 }
2642
2643 async fn close(&self) -> Result<(), Self::Error> {
2644 connection::close_connections(&self.connections, "State store").await;
2645 Ok(())
2646 }
2647
2648 async fn reopen(&self) -> Result<(), Self::Error> {
2649 connection::reopen_connections(
2650 &self.connections,
2651 self.db_path.clone(),
2652 self.pool_config,
2653 self.runtime_config,
2654 )
2655 .await?;
2656 Ok(())
2657 }
2658}
2659
2660#[derive(Debug, Clone, Serialize, Deserialize)]
2661struct ReceiptData {
2662 receipt: Receipt,
2663 event_id: OwnedEventId,
2664 user_id: OwnedUserId,
2665}
2666
2667#[cfg(test)]
2668mod tests {
2669 use std::sync::{
2670 LazyLock,
2671 atomic::{AtomicU32, Ordering::SeqCst},
2672 };
2673
2674 use matrix_sdk_base::{StateStore, StoreError, statestore_integration_tests};
2675 use tempfile::{TempDir, tempdir};
2676
2677 use super::SqliteStateStore;
2678
2679 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2680 static NUM: AtomicU32 = AtomicU32::new(0);
2681
2682 async fn get_store() -> Result<impl StateStore, StoreError> {
2683 let name = NUM.fetch_add(1, SeqCst).to_string();
2684 let tmpdir_path = TMP_DIR.path().join(name);
2685
2686 tracing::info!("using store @ {}", tmpdir_path.to_str().unwrap());
2687
2688 Ok(SqliteStateStore::open(tmpdir_path.to_str().unwrap(), None).await.unwrap())
2689 }
2690
2691 #[matrix_sdk_test::async_test]
2692 async fn test_redaction_of_state_events_with_invalid_content() {
2693 use std::collections::BTreeMap;
2694
2695 use matrix_sdk_base::StateChanges;
2696 use ruma::{
2697 event_id,
2698 events::{AnySyncStateEvent, StateEventType},
2699 room_id,
2700 serde::Raw,
2701 };
2702 use serde_json::json;
2703
2704 for (event_type, state_key) in
2705 [(StateEventType::RoomTopic, ""), (StateEventType::SpaceChild, "not a room ID")]
2706 {
2707 let store = get_store().await.unwrap();
2708 let room_id = room_id!("!redacted-state:localhost");
2709 let event: Raw<AnySyncStateEvent> = Raw::new(&json!({
2710 "type": event_type,
2711 "state_key": state_key,
2712 "content": {},
2713 "event_id": "$state",
2714 "sender": "@alice:localhost",
2715 "origin_server_ts": 1,
2716 }))
2717 .unwrap()
2718 .cast_unchecked();
2719 assert!(event.deserialize().is_err());
2720
2721 let mut changes = StateChanges::default();
2722 changes.state.insert(
2723 room_id.to_owned(),
2724 BTreeMap::from([(
2725 event_type.clone(),
2726 BTreeMap::from([(state_key.to_owned(), event)]),
2727 )]),
2728 );
2729 store.save_changes(&changes).await.unwrap();
2730
2731 let redaction = Raw::new(&json!({
2732 "type": "m.room.redaction",
2733 "content": {},
2734 "redacts": "$state",
2735 "event_id": "$redaction",
2736 "sender": "@alice:localhost",
2737 "origin_server_ts": 2,
2738 }))
2739 .unwrap()
2740 .cast_unchecked();
2741 let mut changes = StateChanges::default();
2742 changes.add_redaction(room_id, event_id!("$state"), redaction);
2743 store.save_changes(&changes).await.unwrap();
2744
2745 let saved =
2746 store.get_state_event(room_id, event_type, state_key).await.unwrap().unwrap();
2747 let json = serde_json::to_value(saved).unwrap();
2748 assert_eq!(json["state_key"], state_key);
2749 assert_eq!(json["content"], json!({}));
2750 assert_eq!(json["unsigned"]["redacted_because"]["event_id"], "$redaction");
2751 }
2752 }
2753
2754 #[matrix_sdk_test::async_test]
2757 async fn test_stripped_data_is_dropped_once_the_room_is_joined() {
2758 use matrix_sdk_base::{
2759 RoomInfo, RoomMemberships, RoomState, StateChanges, store::StateStoreExt,
2760 };
2761 use ruma::{
2762 events::{
2763 StateEventType,
2764 room::member::{MembershipState, RoomMemberEventContent},
2765 },
2766 room_id,
2767 serde::Raw,
2768 user_id,
2769 };
2770
2771 let store = get_store().await.unwrap();
2772 let room_id = room_id!("!test_stripped_data_is_dropped:localhost");
2773 let user_id = user_id!("@u:localhost");
2774
2775 let member: Raw<ruma::events::room::member::StrippedRoomMemberEvent> =
2777 Raw::new(&serde_json::json!({
2778 "type": "m.room.member",
2779 "content": RoomMemberEventContent::new(MembershipState::Invite),
2780 "sender": "@inviter:localhost",
2781 "state_key": user_id,
2782 }))
2783 .unwrap()
2784 .cast_unchecked();
2785
2786 let mut changes = StateChanges::default();
2787 changes.add_stripped_member(room_id, user_id, member);
2788 changes.add_room(RoomInfo::new(room_id, RoomState::Invited));
2789 store.save_changes(&changes).await.unwrap();
2790
2791 assert!(store.get_member_event(room_id, user_id).await.unwrap().is_some());
2792
2793 let mut changes = StateChanges::default();
2796 changes.add_room(RoomInfo::new(room_id, RoomState::Joined));
2797 store.save_changes(&changes).await.unwrap();
2798
2799 assert!(
2800 store.get_member_event(room_id, user_id).await.unwrap().is_none(),
2801 "the stripped member event must be dropped once the room is joined"
2802 );
2803 assert!(
2804 store.get_user_ids(room_id, RoomMemberships::empty()).await.unwrap().is_empty(),
2805 "the stripped member must no longer be listed"
2806 );
2807 assert!(
2808 store
2809 .get_state_event(room_id, StateEventType::RoomMember, user_id.as_str())
2810 .await
2811 .unwrap()
2812 .is_none()
2813 );
2814 }
2815
2816 statestore_integration_tests!();
2817}
2818
2819#[cfg(test)]
2820mod encrypted_tests {
2821 use std::{
2822 path::PathBuf,
2823 sync::{
2824 LazyLock,
2825 atomic::{AtomicU32, Ordering::SeqCst},
2826 },
2827 };
2828
2829 use base64::Engine as _;
2830 use matrix_sdk_base::{StateStore, StoreError, statestore_integration_tests};
2831 use matrix_sdk_test::async_test;
2832 use tempfile::{TempDir, tempdir};
2833
2834 use super::SqliteStateStore;
2835 use crate::{Base64Variant, SqliteStoreConfig, Synchronous, utils::SqliteAsyncConnExt};
2836
2837 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2838 static NUM: AtomicU32 = AtomicU32::new(0);
2839
2840 fn new_state_store_workspace() -> PathBuf {
2841 let name = NUM.fetch_add(1, SeqCst).to_string();
2842 TMP_DIR.path().join(name)
2843 }
2844
2845 async fn get_store() -> Result<impl StateStore, StoreError> {
2846 let tmpdir_path = new_state_store_workspace();
2847
2848 tracing::info!("using store @ {}", tmpdir_path.to_str().unwrap());
2849
2850 Ok(SqliteStateStore::open(tmpdir_path.to_str().unwrap(), Some("default_test_password"))
2851 .await
2852 .unwrap())
2853 }
2854
2855 #[async_test]
2857 async fn test_high_entropy_passphrase_migrates_a_passphrase_store() {
2858 const KEY: &[u8; 32] = b"a randomly generated passphrase ";
2859 let tmpdir_path = new_state_store_workspace();
2860
2861 let passphrase = base64::prelude::BASE64_STANDARD.encode(KEY);
2862
2863 let config = SqliteStoreConfig::new(&tmpdir_path).passphrase(Some(&passphrase));
2864 drop(SqliteStateStore::open_with_config(&config).await.unwrap());
2865
2866 let config = SqliteStoreConfig::new(&tmpdir_path)
2868 .high_entropy_passphrase(Some(KEY), Base64Variant::Padded);
2869 drop(SqliteStateStore::open_with_config(&config).await.unwrap());
2870
2871 drop(SqliteStateStore::open_with_config(&config).await.unwrap());
2873
2874 let config = SqliteStoreConfig::new(&tmpdir_path).passphrase(Some(&passphrase));
2877 drop(
2878 SqliteStateStore::open_with_config(&config)
2879 .await
2880 .expect_err("The old passphrase-only method shouldn't work anymore"),
2881 );
2882
2883 let config = SqliteStoreConfig::new(&tmpdir_path)
2886 .high_entropy_passphrase(Some(KEY), Base64Variant::Padded);
2887 drop(
2888 SqliteStateStore::open_with_config(&config)
2889 .await
2890 .expect("The high-entropy method should continue to work"),
2891 );
2892
2893 let config = SqliteStoreConfig::new(&tmpdir_path).key(Some(KEY));
2894 drop(
2895 SqliteStateStore::open_with_config(&config).await.expect("The key should work as well"),
2896 );
2897
2898 let config = SqliteStoreConfig::new(&tmpdir_path).high_entropy_passphrase(
2899 Some(b"wrong passphrase can't work 1234"),
2900 Base64Variant::Padded,
2901 );
2902 assert!(SqliteStateStore::open_with_config(&config).await.is_err());
2903 let config = SqliteStoreConfig::new(&tmpdir_path).passphrase(Some("wrong"));
2904 assert!(SqliteStateStore::open_with_config(&config).await.is_err());
2905 }
2906
2907 #[async_test]
2908 async fn test_pool_size() {
2909 let tmpdir_path = new_state_store_workspace();
2910 let store_open_config = SqliteStoreConfig::new(tmpdir_path).pool_max_size(42);
2911
2912 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2913
2914 let guard = store.connections.lock().await;
2915 assert_eq!(guard.as_ref().unwrap().pool.status().max_size, 42);
2916 }
2917
2918 #[async_test]
2919 async fn test_cache_size() {
2920 let tmpdir_path = new_state_store_workspace();
2921 let store_open_config = SqliteStoreConfig::new(tmpdir_path).cache_size(1500);
2922
2923 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2924
2925 let conn = store.read().await.unwrap();
2926 let cache_size =
2927 conn.query_row("PRAGMA cache_size", (), |row| row.get::<_, i32>(0)).await.unwrap();
2928
2929 assert_eq!(cache_size, -(1500 / 1024));
2933 }
2934
2935 #[async_test]
2936 async fn test_journal_size_limit() {
2937 let tmpdir_path = new_state_store_workspace();
2938 let store_open_config = SqliteStoreConfig::new(tmpdir_path).journal_size_limit(1500);
2939
2940 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2941
2942 let conn = store.read().await.unwrap();
2943 let journal_size_limit = conn
2944 .query_row("PRAGMA journal_size_limit", (), |row| row.get::<_, u32>(0))
2945 .await
2946 .unwrap();
2947
2948 assert_eq!(journal_size_limit, 1500);
2951 }
2952
2953 #[async_test]
2954 async fn test_synchronous() {
2955 let all = [
2957 (Synchronous::Off, 0),
2958 (Synchronous::Normal, 1),
2959 (Synchronous::Full, 2),
2960 (Synchronous::Extra, 3),
2961 ];
2962
2963 for (synchronous, expected) in all {
2964 let tmpdir_path = new_state_store_workspace();
2965 let store_open_config = SqliteStoreConfig::new(tmpdir_path).synchronous(synchronous);
2966
2967 let store = SqliteStateStore::open_with_config(&store_open_config).await.unwrap();
2968
2969 let write_conn = store.write().await.unwrap();
2972 let read_conn = store.read().await.unwrap();
2973
2974 for conn in [&*write_conn, &read_conn] {
2975 let value = conn
2976 .query_row("PRAGMA synchronous", (), |row| row.get::<_, u8>(0))
2977 .await
2978 .unwrap();
2979
2980 assert_eq!(value, expected);
2981 }
2982 }
2983 }
2984
2985 statestore_integration_tests!();
2986}
2987
2988#[cfg(test)]
2989mod migration_tests {
2990 use std::{
2991 path::{Path, PathBuf},
2992 sync::{
2993 Arc, LazyLock,
2994 atomic::{AtomicU32, Ordering::SeqCst},
2995 },
2996 };
2997
2998 use as_variant::as_variant;
2999 use matrix_sdk_base::{
3000 RoomState, StateStore,
3001 media::{MediaFormat, MediaRequestParameters},
3002 store::{
3003 ChildTransactionId, DependentQueuedRequestKind, RoomLoadSettings,
3004 SerializableEventContent,
3005 },
3006 sync::UnreadNotificationsCount,
3007 };
3008 use matrix_sdk_test::async_test;
3009 use ruma::{
3010 EventId, MilliSecondsSinceUnixEpoch, OwnedTransactionId, RoomId, TransactionId, UserId,
3011 events::{
3012 StateEventType,
3013 room::{MediaSource, create::RoomCreateEventContent, message::RoomMessageEventContent},
3014 },
3015 room_id, server_name, user_id,
3016 };
3017 use rusqlite::Transaction;
3018 use serde::{Deserialize, Serialize};
3019 use serde_json::json;
3020 use tempfile::{TempDir, tempdir};
3021 use tokio::{fs, sync::Mutex};
3022 use zeroize::Zeroizing;
3023
3024 use super::{DATABASE_NAME, SqliteStateStore, init, keys};
3025 use crate::{
3026 OpenStoreError, Secret, SqliteStoreConfig, connection,
3027 error::{Error, Result},
3028 utils::{EncryptableStore as _, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt},
3029 };
3030
3031 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
3032 static NUM: AtomicU32 = AtomicU32::new(0);
3033 const SECRET: &str = "secret";
3034
3035 fn new_path() -> PathBuf {
3036 let name = NUM.fetch_add(1, SeqCst).to_string();
3037 TMP_DIR.path().join(name)
3038 }
3039
3040 async fn create_fake_db(path: &Path, version: u8) -> Result<SqliteStateStore> {
3041 let config = SqliteStoreConfig::new(path);
3042
3043 fs::create_dir_all(&config.path).await.map_err(OpenStoreError::CreateDir).unwrap();
3044
3045 let pool = config.build_pool_of_connections(DATABASE_NAME).unwrap();
3046 let db_path = pool.manager().database_path.clone();
3047 let conn = pool.get().await?;
3048
3049 init(&conn).await?;
3050
3051 let store_cipher = Some(Arc::new(
3052 conn.get_or_create_store_cipher(Secret::PassPhrase(Zeroizing::new(SECRET.to_owned())))
3053 .await
3054 .unwrap(),
3055 ));
3056 let this = SqliteStateStore {
3057 store_cipher,
3058 connections: Arc::new(
3059 connection::SqliteConnections::new(connection::SqliteConnectionsInner {
3060 pool,
3061 write_connection: Arc::new(Mutex::new(conn)),
3062 })
3063 .await,
3064 ),
3065 db_path,
3066 pool_config: deadpool::managed::PoolConfig::default(),
3067 runtime_config: crate::RuntimeConfig::default(),
3068 };
3069 this.run_migrations(1, Some(version)).await?;
3070
3071 Ok(this)
3072 }
3073
3074 fn room_info_v1_json(
3075 room_id: &RoomId,
3076 state: RoomState,
3077 name: Option<&str>,
3078 creator: Option<&UserId>,
3079 ) -> serde_json::Value {
3080 let name_content = match name {
3082 Some(name) => json!({ "name": name }),
3083 None => json!({ "name": null }),
3084 };
3085 let create_content = match creator {
3087 Some(creator) => RoomCreateEventContent::new_v1(creator.to_owned()),
3088 None => RoomCreateEventContent::new_v11(),
3089 };
3090
3091 json!({
3092 "room_id": room_id,
3093 "room_type": state,
3094 "notification_counts": UnreadNotificationsCount::default(),
3095 "summary": {
3096 "heroes": [],
3097 "joined_member_count": 0,
3098 "invited_member_count": 0,
3099 },
3100 "members_synced": false,
3101 "base_info": {
3102 "dm_targets": [],
3103 "max_power_level": 100,
3104 "name": {
3105 "Original": {
3106 "content": name_content,
3107 },
3108 },
3109 "create": {
3110 "Original": {
3111 "content": create_content,
3112 }
3113 }
3114 },
3115 })
3116 }
3117
3118 #[async_test]
3119 pub async fn test_migrating_v1_to_v2() {
3120 let path = new_path();
3121 {
3123 let db = create_fake_db(&path, 1).await.unwrap();
3124 let conn = db.read().await.unwrap();
3125
3126 let this = db.clone();
3127 conn.with_transaction(move |txn| {
3128 for i in 0..5 {
3129 let room_id = RoomId::parse(format!("!room_{i}:localhost")).unwrap();
3130 let (state, stripped) =
3131 if i < 3 { (RoomState::Joined, false) } else { (RoomState::Invited, true) };
3132 let info = room_info_v1_json(&room_id, state, None, None);
3133
3134 let room_id = this.encode_key(keys::ROOM_INFO, room_id);
3135 let data = this.serialize_json(&info)?;
3136
3137 txn.prepare_cached(
3138 "INSERT INTO room_info (room_id, stripped, data)
3139 VALUES (?, ?, ?)",
3140 )?
3141 .execute((room_id, stripped, data))?;
3142 }
3143
3144 Result::<_, Error>::Ok(())
3145 })
3146 .await
3147 .unwrap();
3148 }
3149
3150 let store = SqliteStateStore::open(path, Some(SECRET)).await.unwrap();
3152
3153 assert_eq!(store.get_room_infos(&RoomLoadSettings::default()).await.unwrap().len(), 5);
3155 }
3156
3157 fn add_room_v2(
3159 this: &SqliteStateStore,
3160 txn: &Transaction<'_>,
3161 room_id: &RoomId,
3162 name: Option<&str>,
3163 create_creator: Option<&UserId>,
3164 create_sender: Option<&UserId>,
3165 ) -> Result<(), Error> {
3166 let room_info_json = room_info_v1_json(room_id, RoomState::Joined, name, create_creator);
3167
3168 let encoded_room_id = this.encode_key(keys::ROOM_INFO, room_id);
3169 let encoded_state =
3170 this.encode_key(keys::ROOM_INFO, serde_json::to_string(&RoomState::Joined)?);
3171 let data = this.serialize_json(&room_info_json)?;
3172
3173 txn.prepare_cached(
3174 "INSERT INTO room_info (room_id, state, data)
3175 VALUES (?, ?, ?)",
3176 )?
3177 .execute((encoded_room_id, encoded_state, data))?;
3178
3179 let Some(create_sender) = create_sender else {
3181 return Ok(());
3182 };
3183
3184 let create_content = match create_creator {
3185 Some(creator) => RoomCreateEventContent::new_v1(creator.to_owned()),
3186 None => RoomCreateEventContent::new_v11(),
3187 };
3188
3189 let event_id = EventId::new_v1(server_name!("dummy.local"));
3190 let create_event = json!({
3191 "content": create_content,
3192 "event_id": event_id,
3193 "sender": create_sender.to_owned(),
3194 "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
3195 "state_key": "",
3196 "type": "m.room.create",
3197 "unsigned": {},
3198 });
3199
3200 let encoded_room_id = this.encode_key(keys::STATE_EVENT, room_id);
3201 let encoded_event_type =
3202 this.encode_key(keys::STATE_EVENT, StateEventType::RoomCreate.to_string());
3203 let encoded_state_key = this.encode_key(keys::STATE_EVENT, "");
3204 let stripped = false;
3205 let encoded_event_id = this.encode_key(keys::STATE_EVENT, event_id);
3206 let data = this.serialize_json(&create_event)?;
3207
3208 txn.prepare_cached(
3209 "INSERT
3210 INTO state_event (room_id, event_type, state_key, stripped, event_id, data)
3211 VALUES (?, ?, ?, ?, ?, ?)",
3212 )?
3213 .execute((
3214 encoded_room_id,
3215 encoded_event_type,
3216 encoded_state_key,
3217 stripped,
3218 encoded_event_id,
3219 data,
3220 ))?;
3221
3222 Ok(())
3223 }
3224
3225 #[async_test]
3226 pub async fn test_migrating_v2_to_v3() {
3227 let path = new_path();
3228
3229 let room_a_id = room_id!("!room_a:dummy.local");
3231 let room_a_name = "Room A";
3232 let room_a_creator = user_id!("@creator:dummy.local");
3233 let room_a_create_sender = user_id!("@sender:dummy.local");
3236
3237 let room_b_id = room_id!("!room_b:dummy.local");
3239
3240 let room_c_id = room_id!("!room_c:dummy.local");
3242 let room_c_create_sender = user_id!("@creator:dummy.local");
3243
3244 {
3246 let db = create_fake_db(&path, 2).await.unwrap();
3247 let conn = db.read().await.unwrap();
3248
3249 let this = db.clone();
3250 conn.with_transaction(move |txn| {
3251 add_room_v2(
3252 &this,
3253 txn,
3254 room_a_id,
3255 Some(room_a_name),
3256 Some(room_a_creator),
3257 Some(room_a_create_sender),
3258 )?;
3259 add_room_v2(&this, txn, room_b_id, None, None, None)?;
3260 add_room_v2(&this, txn, room_c_id, None, None, Some(room_c_create_sender))?;
3261
3262 Result::<_, Error>::Ok(())
3263 })
3264 .await
3265 .unwrap();
3266 }
3267
3268 let store = SqliteStateStore::open(path, Some(SECRET)).await.unwrap();
3270
3271 let room_infos = store.get_room_infos(&RoomLoadSettings::default()).await.unwrap();
3273 assert_eq!(room_infos.len(), 3);
3274
3275 let room_a = room_infos.iter().find(|r| r.room_id() == room_a_id).unwrap();
3276 assert_eq!(room_a.name(), Some(room_a_name));
3277 assert_eq!(room_a.creators(), Some(vec![room_a_create_sender.to_owned()]));
3278
3279 let room_b = room_infos.iter().find(|r| r.room_id() == room_b_id).unwrap();
3280 assert_eq!(room_b.name(), None);
3281 assert_eq!(room_b.creators(), None);
3282
3283 let room_c = room_infos.iter().find(|r| r.room_id() == room_c_id).unwrap();
3284 assert_eq!(room_c.name(), None);
3285 assert_eq!(room_c.creators(), Some(vec![room_c_create_sender.to_owned()]));
3286 }
3287
3288 #[async_test]
3289 pub async fn test_migrating_v7_to_v9() {
3290 let path = new_path();
3291
3292 let room_id = room_id!("!room_a:dummy.local");
3293 let wedged_event_transaction_id = TransactionId::new();
3294 let local_event_transaction_id = TransactionId::new();
3295
3296 {
3298 let db = create_fake_db(&path, 7).await.unwrap();
3299 let conn = db.read().await.unwrap();
3300
3301 let wedge_tx = wedged_event_transaction_id.clone();
3302 let local_tx = local_event_transaction_id.clone();
3303
3304 conn.with_transaction(move |txn| {
3305 add_dependent_send_queue_event_v7(
3306 &db,
3307 txn,
3308 room_id,
3309 &local_tx,
3310 ChildTransactionId::new(),
3311 DependentQueuedRequestKind::RedactEvent,
3312 )?;
3313 add_send_queue_event_v7(&db, txn, &wedge_tx, room_id, true)?;
3314 add_send_queue_event_v7(&db, txn, &local_tx, room_id, false)?;
3315 Result::<_, Error>::Ok(())
3316 })
3317 .await
3318 .unwrap();
3319 }
3320
3321 let store = SqliteStateStore::open(path, Some(SECRET)).await.unwrap();
3324
3325 let requests = store.load_send_queue_requests(room_id).await.unwrap();
3326 assert!(requests.is_empty());
3327
3328 let dependent_requests = store.load_dependent_queued_requests(room_id).await.unwrap();
3329 assert!(dependent_requests.is_empty());
3330 }
3331
3332 fn add_send_queue_event_v7(
3333 this: &SqliteStateStore,
3334 txn: &Transaction<'_>,
3335 transaction_id: &TransactionId,
3336 room_id: &RoomId,
3337 is_wedged: bool,
3338 ) -> Result<(), Error> {
3339 let content =
3340 SerializableEventContent::new(&RoomMessageEventContent::text_plain("Hello").into())?;
3341
3342 let room_id_key = this.encode_key(keys::SEND_QUEUE, room_id);
3343 let room_id_value = this.serialize_value(&room_id.to_owned())?;
3344
3345 let content = this.serialize_json(&content)?;
3346
3347 txn.prepare_cached("INSERT INTO send_queue_events (room_id, room_id_val, transaction_id, content, wedged) VALUES (?, ?, ?, ?, ?)")?
3348 .execute((room_id_key, room_id_value, transaction_id.to_string(), content, is_wedged))?;
3349
3350 Ok(())
3351 }
3352
3353 fn add_dependent_send_queue_event_v7(
3354 this: &SqliteStateStore,
3355 txn: &Transaction<'_>,
3356 room_id: &RoomId,
3357 parent_txn_id: &TransactionId,
3358 own_txn_id: ChildTransactionId,
3359 content: DependentQueuedRequestKind,
3360 ) -> Result<(), Error> {
3361 let room_id_value = this.serialize_value(&room_id.to_owned())?;
3362
3363 let parent_txn_id = parent_txn_id.to_string();
3364 let own_txn_id = own_txn_id.to_string();
3365 let content = this.serialize_json(&content)?;
3366
3367 txn.prepare_cached(
3368 "INSERT INTO dependent_send_queue_events
3369 (room_id, parent_transaction_id, own_transaction_id, content)
3370 VALUES (?, ?, ?, ?)",
3371 )?
3372 .execute((room_id_value, parent_txn_id, own_txn_id, content))?;
3373
3374 Ok(())
3375 }
3376
3377 #[derive(Clone, Debug, Serialize, Deserialize)]
3378 pub enum LegacyDependentQueuedRequestKind {
3379 UploadFileWithThumbnail {
3380 content_type: String,
3381 cache_key: MediaRequestParameters,
3382 related_to: OwnedTransactionId,
3383 },
3384 }
3385
3386 #[async_test]
3387 pub async fn test_dependent_queued_request_variant_renaming() {
3388 let path = new_path();
3389 let db = create_fake_db(&path, 7).await.unwrap();
3390
3391 let cache_key = MediaRequestParameters {
3392 format: MediaFormat::File,
3393 source: MediaSource::Plain("https://server.local/foobar".into()),
3394 };
3395 let related_to = TransactionId::new();
3396 let request = LegacyDependentQueuedRequestKind::UploadFileWithThumbnail {
3397 content_type: "image/png".to_owned(),
3398 cache_key,
3399 related_to: related_to.clone(),
3400 };
3401
3402 let data = db
3403 .serialize_json(&request)
3404 .expect("should be able to serialize legacy dependent request");
3405 let deserialized: DependentQueuedRequestKind = db.deserialize_json(&data).expect(
3406 "should be able to deserialize dependent request from legacy dependent request",
3407 );
3408
3409 as_variant!(deserialized, DependentQueuedRequestKind::UploadFileOrThumbnail { related_to: de_related_to, .. } => {
3410 assert_eq!(de_related_to, related_to);
3411 });
3412 }
3413}
3414
3415#[cfg(test)]
3416mod close_reopen_tests {
3417 use std::sync::{
3418 LazyLock,
3419 atomic::{AtomicU32, Ordering::SeqCst},
3420 };
3421
3422 use matrix_sdk_base::StateStore;
3423 use matrix_sdk_test::async_test;
3424 use tempfile::{TempDir, tempdir};
3425
3426 use super::SqliteStateStore;
3427
3428 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
3429 static NUM: AtomicU32 = AtomicU32::new(0);
3430
3431 async fn new_store() -> SqliteStateStore {
3432 let name = NUM.fetch_add(1, SeqCst).to_string();
3433 let tmpdir_path = TMP_DIR.path().join(name);
3434 SqliteStateStore::open(tmpdir_path.to_str().unwrap(), None).await.unwrap()
3435 }
3436
3437 #[async_test]
3438 async fn test_close_completes_without_timeout() {
3439 let store = new_store().await;
3440
3441 let start = std::time::Instant::now();
3443 store.close().await.unwrap();
3444 let elapsed = start.elapsed();
3445
3446 assert!(
3447 elapsed < std::time::Duration::from_secs(2),
3448 "close() took {elapsed:?}, expected < 2s (no timeout)"
3449 );
3450
3451 let guard = store.connections.lock().await;
3453 assert!(guard.is_none(), "connections should be None after close");
3454 }
3455
3456 #[async_test]
3457 async fn test_reopen_restores_connections() {
3458 let store = new_store().await;
3459
3460 store.close().await.unwrap();
3461
3462 {
3464 let guard = store.connections.lock().await;
3465 assert!(guard.is_none());
3466 }
3467
3468 store.reopen().await.unwrap();
3469
3470 {
3472 let guard = store.connections.lock().await;
3473 assert!(guard.is_some(), "connections should be Some after reopen");
3474 }
3475 }
3476
3477 #[async_test]
3478 async fn test_close_is_idempotent() {
3479 let store = new_store().await;
3480
3481 store.close().await.unwrap();
3483 store.close().await.unwrap();
3485
3486 let guard = store.connections.lock().await;
3487 assert!(guard.is_none());
3488 }
3489
3490 #[async_test]
3491 async fn test_reopen_is_idempotent() {
3492 let store = new_store().await;
3493
3494 store.reopen().await.unwrap();
3496
3497 let guard = store.connections.lock().await;
3499 assert!(guard.is_some());
3500 }
3501
3502 #[async_test]
3503 async fn test_read_fails_when_closed() {
3504 let store = new_store().await;
3505 store.close().await.unwrap();
3506
3507 let err = store.get_custom_value(b"some_key").await;
3508 assert!(err.is_err(), "read should fail when closed");
3509
3510 let err_msg = err.unwrap_err().to_string();
3511 assert!(err_msg.contains("closed"), "error should mention 'closed', got: {err_msg}");
3512 }
3513
3514 #[async_test]
3515 async fn test_write_fails_when_closed() {
3516 let store = new_store().await;
3517 store.close().await.unwrap();
3518
3519 let err = store.set_custom_value(b"key", b"value".to_vec()).await;
3520 assert!(err.is_err(), "write should fail when closed");
3521
3522 let err_msg = err.unwrap_err().to_string();
3523 assert!(err_msg.contains("closed"), "error should mention 'closed', got: {err_msg}");
3524 }
3525
3526 #[async_test]
3527 async fn test_data_persists_across_close_reopen() {
3528 let store = new_store().await;
3529
3530 store.set_custom_value(b"test_key", b"test_value".to_vec()).await.unwrap();
3532
3533 let value = store.get_custom_value(b"test_key").await.unwrap();
3535 assert_eq!(value.as_deref(), Some(b"test_value".as_slice()));
3536
3537 store.close().await.unwrap();
3539 store.reopen().await.unwrap();
3540
3541 let value = store.get_custom_value(b"test_key").await.unwrap();
3543 assert_eq!(
3544 value.as_deref(),
3545 Some(b"test_value".as_slice()),
3546 "data should persist across close/reopen"
3547 );
3548 }
3549
3550 #[async_test]
3551 async fn test_multiple_close_reopen_cycles() {
3552 let store = new_store().await;
3553
3554 for i in 0..3 {
3555 let key = format!("key_{i}");
3556 let value = format!("value_{i}");
3557
3558 store.set_custom_value(key.as_bytes(), value.as_bytes().to_vec()).await.unwrap();
3559
3560 store.close().await.unwrap();
3561 store.reopen().await.unwrap();
3562
3563 for j in 0..=i {
3565 let k = format!("key_{j}");
3566 let v = format!("value_{j}");
3567 let retrieved = store.get_custom_value(k.as_bytes()).await.unwrap();
3568 assert_eq!(
3569 retrieved.as_deref(),
3570 Some(v.as_bytes()),
3571 "data for key_{j} should persist after cycle {i}"
3572 );
3573 }
3574 }
3575 }
3576
3577 #[async_test]
3578 async fn test_pool_is_fully_drained_after_close() {
3579 let store = new_store().await;
3580
3581 let _ = store.get_custom_value(b"key1").await;
3583 let _ = store.get_custom_value(b"key2").await;
3584
3585 store.close().await.unwrap();
3586
3587 let guard = store.connections.lock().await;
3590 assert!(guard.is_none(), "all connections should be released after close");
3591 }
3592
3593 #[async_test]
3594 async fn test_operations_work_immediately_after_reopen() {
3595 let store = new_store().await;
3596
3597 store.close().await.unwrap();
3598 store.reopen().await.unwrap();
3599
3600 store.set_custom_value(b"after_reopen", b"works".to_vec()).await.unwrap();
3602
3603 let value = store.get_custom_value(b"after_reopen").await.unwrap();
3605 assert_eq!(value.as_deref(), Some(b"works".as_slice()));
3606 }
3607
3608 #[async_test]
3609 async fn test_close_waits_for_held_read_connection_to_drain() {
3610 let store = new_store().await;
3611
3612 let held_conn = store.read().await.unwrap();
3614
3615 let store_clone = store.clone();
3618 let close_handle = tokio::spawn(async move {
3619 store_clone.close().await.unwrap();
3620 });
3621
3622 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
3624
3625 assert!(!close_handle.is_finished(), "close should be waiting for the held connection");
3627
3628 drop(held_conn);
3630
3631 let timeout = tokio::time::timeout(std::time::Duration::from_secs(3), close_handle).await;
3633 assert!(timeout.is_ok(), "close should complete after the held connection is released");
3634 timeout.unwrap().unwrap();
3635
3636 let guard = store.connections.lock().await;
3638 assert!(guard.is_none(), "connections should be None after close");
3639 }
3640}