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