Skip to main content

matrix_sdk_sqlite/
state_store.rs

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