Skip to main content

storage/indexeddb/engines/
sqlite.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6
7use log::{info, warn};
8use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
9use rusqlite::{Connection, Error, OptionalExtension, params};
10use sea_query::{Condition, Expr, ExprTrait, IntoCondition, SqliteQueryBuilder};
11use sea_query_rusqlite::RusqliteBinder;
12use servo_base::threadpool::ThreadPool;
13use storage_traits::indexeddb::{
14    AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, BackendError,
15    CreateObjectResult, IndexedDBIndex, IndexedDBKeyRange, IndexedDBKeyType, IndexedDBRecord,
16    IndexedDBTxnMode, KeyPath, PutItemResult,
17};
18
19use crate::indexeddb::IndexedDBDescription;
20use crate::indexeddb::engines::{KvsEngine, KvsTransaction};
21use crate::shared::{DB_INIT_PRAGMAS, DB_PRAGMAS};
22
23mod create;
24mod database_model;
25mod encoding;
26mod object_data_model;
27mod object_store_index_model;
28mod object_store_model;
29
30fn range_to_query(range: IndexedDBKeyRange) -> Condition {
31    // Special case for optimization
32    if let Some(singleton) = range.as_singleton() {
33        let encoded = encoding::serialize(singleton);
34        return Expr::column(object_data_model::Column::Key)
35            .eq(encoded)
36            .into_condition();
37    }
38    let mut parts = vec![];
39    if let Some(upper) = range.upper.as_ref() {
40        let upper_bytes = encoding::serialize(upper);
41        let query = if range.upper_open {
42            Expr::column(object_data_model::Column::Key).lt(upper_bytes)
43        } else {
44            Expr::column(object_data_model::Column::Key).lte(upper_bytes)
45        };
46        parts.push(query);
47    }
48    if let Some(lower) = range.lower.as_ref() {
49        let lower_bytes = encoding::serialize(lower);
50        let query = if range.lower_open {
51            Expr::column(object_data_model::Column::Key).gt(lower_bytes)
52        } else {
53            Expr::column(object_data_model::Column::Key).gte(lower_bytes)
54        };
55        parts.push(query);
56    }
57    let mut condition = Condition::all();
58    for part in parts {
59        condition = condition.add(part);
60    }
61    condition
62}
63
64pub struct SqliteEngine {
65    db_path: PathBuf,
66    connection: Connection,
67    read_pool: Arc<ThreadPool>,
68    write_pool: Arc<ThreadPool>,
69    created_db_path: bool,
70}
71
72impl SqliteEngine {
73    fn object_store_by_name(
74        connection: &Connection,
75        store_name: &str,
76    ) -> Result<object_store_model::Model, Error> {
77        connection.query_row(
78            "SELECT * FROM object_store WHERE name = ?",
79            params![store_name.to_string()],
80            |row| object_store_model::Model::try_from(row),
81        )
82    }
83
84    // TODO: intake dual pools
85    pub fn new(
86        path: PathBuf,
87        created: bool,
88        db_info: &IndexedDBDescription,
89        pool: Arc<ThreadPool>,
90    ) -> Result<Self, Error> {
91        let db_path = path.join("indexeddb.sqlite");
92        let connection = Self::init_db(&db_path, db_info)?;
93
94        for stmt in DB_PRAGMAS {
95            // TODO: Handle errors properly
96            let _ = connection.execute(stmt, ());
97        }
98
99        Ok(Self {
100            connection,
101            db_path,
102            read_pool: pool.clone(),
103            write_pool: pool,
104            created_db_path: created,
105        })
106    }
107
108    /// Returns whether the physical db was created as part of `new`.
109    pub(crate) fn created_db_path(&self) -> bool {
110        self.created_db_path
111    }
112
113    fn init_db(path: &Path, db_info: &IndexedDBDescription) -> Result<Connection, Error> {
114        let connection = Connection::open(path)?;
115        if connection.table_exists(None, "database")? {
116            // Database already exists, no need to initialize
117            return Ok(connection);
118        }
119        info!("Initializing indexeddb database at {:?}", path);
120        for stmt in DB_INIT_PRAGMAS {
121            // FIXME(arihant2math): this fails occasionally
122            let _ = connection.execute(stmt, ());
123        }
124        create::create_tables(&connection)?;
125        // From https://w3c.github.io/IndexedDB/#database-version:
126        // "When a database is first created, its version is 0 (zero)."
127        connection.execute(
128            "INSERT INTO database (name, origin, version) VALUES (?, ?, ?)",
129            params![
130                db_info.name.to_owned(),
131                db_info.origin.to_owned().ascii_serialization(),
132                i64::from_ne_bytes(0_u64.to_ne_bytes())
133            ],
134        )?;
135        Ok(connection)
136    }
137
138    fn get(
139        connection: &Connection,
140        store: object_store_model::Model,
141        key_range: IndexedDBKeyRange,
142    ) -> Result<Option<object_data_model::Model>, Error> {
143        let query = range_to_query(key_range);
144        let (sql, values) = sea_query::Query::select()
145            .from(object_data_model::Column::Table)
146            .columns(vec![
147                object_data_model::Column::ObjectStoreId,
148                object_data_model::Column::Key,
149                object_data_model::Column::Data,
150            ])
151            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)))
152            .limit(1)
153            .build_rusqlite(SqliteQueryBuilder);
154        connection
155            .prepare(&sql)?
156            .query_one(&*values.as_params(), |row| {
157                object_data_model::Model::try_from(row)
158            })
159            .optional()
160    }
161
162    fn get_key(
163        connection: &Connection,
164        store: object_store_model::Model,
165        key_range: IndexedDBKeyRange,
166    ) -> Result<Option<Vec<u8>>, Error> {
167        Self::get(connection, store, key_range).map(|opt| opt.map(|model| model.key))
168    }
169
170    fn get_item(
171        connection: &Connection,
172        store: object_store_model::Model,
173        key_range: IndexedDBKeyRange,
174    ) -> Result<Option<Vec<u8>>, Error> {
175        Self::get(connection, store, key_range).map(|opt| opt.map(|model| model.data))
176    }
177
178    fn get_all(
179        connection: &Connection,
180        store: object_store_model::Model,
181        key_range: IndexedDBKeyRange,
182        count: Option<u32>,
183    ) -> Result<Vec<object_data_model::Model>, Error> {
184        let query = range_to_query(key_range);
185        let mut sql_query = sea_query::Query::select();
186        sql_query
187            .from(object_data_model::Column::Table)
188            .columns(vec![
189                object_data_model::Column::ObjectStoreId,
190                object_data_model::Column::Key,
191                object_data_model::Column::Data,
192            ])
193            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)));
194        if let Some(count) = count {
195            sql_query.limit(count as u64);
196        }
197        let (sql, values) = sql_query.build_rusqlite(SqliteQueryBuilder);
198        let mut stmt = connection.prepare(&sql)?;
199        let models = stmt
200            .query_and_then(&*values.as_params(), |row| {
201                object_data_model::Model::try_from(row)
202            })?
203            .collect::<Result<Vec<_>, _>>()?;
204        Ok(models)
205    }
206
207    fn get_all_keys(
208        connection: &Connection,
209        store: object_store_model::Model,
210        key_range: IndexedDBKeyRange,
211        count: Option<u32>,
212    ) -> Result<Vec<Vec<u8>>, Error> {
213        Self::get_all(connection, store, key_range, count)
214            .map(|models| models.into_iter().map(|m| m.key).collect())
215    }
216
217    fn get_all_items(
218        connection: &Connection,
219        store: object_store_model::Model,
220        key_range: IndexedDBKeyRange,
221        count: Option<u32>,
222    ) -> Result<Vec<Vec<u8>>, Error> {
223        Self::get_all(connection, store, key_range, count)
224            .map(|models| models.into_iter().map(|m| m.data).collect())
225    }
226
227    #[expect(clippy::type_complexity)]
228    fn get_all_records(
229        connection: &Connection,
230        store: object_store_model::Model,
231        key_range: IndexedDBKeyRange,
232    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, Error> {
233        Self::get_all(connection, store, key_range, None)
234            .map(|models| models.into_iter().map(|m| (m.key, m.data)).collect())
235    }
236
237    fn put_item(
238        connection: &Connection,
239        store: object_store_model::Model,
240        key: IndexedDBKeyType,
241        value: Vec<u8>,
242        should_overwrite: bool,
243        key_generator_current_number: Option<i32>,
244    ) -> Result<PutItemResult, Error> {
245        let no_overwrite = !should_overwrite;
246        let serialized_key: Vec<u8> = encoding::serialize(&key);
247        let existing_item = connection
248            .prepare("SELECT * FROM object_data WHERE key = ? AND object_store_id = ?")
249            .and_then(|mut stmt| {
250                stmt.query_row(params![serialized_key, store.id], |row| {
251                    object_data_model::Model::try_from(row)
252                })
253                .optional()
254            })?;
255        if existing_item.is_some() {
256            if no_overwrite {
257                return Ok(PutItemResult::CannotOverwrite);
258            }
259            // Preserve `put()` semantics by replacing the stored value when the primary
260            // key already exists.
261            connection.execute(
262                "UPDATE object_data SET data = ? WHERE object_store_id = ? AND key = ?",
263                params![value, store.id, serialized_key],
264            )?;
265        } else {
266            connection.execute(
267                "INSERT INTO object_data (object_store_id, key, data) VALUES (?, ?, ?)",
268                params![store.id, serialized_key, value],
269            )?;
270        }
271        if let Some(next_key_generator_current_number) = key_generator_current_number {
272            connection.execute(
273                "UPDATE object_store SET auto_increment = ? WHERE id = ?",
274                params![next_key_generator_current_number, store.id],
275            )?;
276        }
277        Ok(PutItemResult::Key(key))
278    }
279
280    fn delete_item(
281        connection: &Connection,
282        store: object_store_model::Model,
283        key_range: IndexedDBKeyRange,
284    ) -> Result<(), Error> {
285        let query = range_to_query(key_range);
286        let (sql, values) = sea_query::Query::delete()
287            .from_table(object_data_model::Column::Table)
288            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)))
289            .build_rusqlite(SqliteQueryBuilder);
290        connection.prepare(&sql)?.execute(&*values.as_params())?;
291        Ok(())
292    }
293
294    fn clear(connection: &Connection, store: object_store_model::Model) -> Result<(), Error> {
295        connection.execute(
296            "DELETE FROM object_data WHERE object_store_id = ?",
297            params![store.id],
298        )?;
299        Ok(())
300    }
301
302    fn count(
303        connection: &Connection,
304        store: object_store_model::Model,
305        key_range: IndexedDBKeyRange,
306    ) -> Result<usize, Error> {
307        let query = range_to_query(key_range);
308        let (sql, values) = sea_query::Query::select()
309            .expr(Expr::col(object_data_model::Column::Key).count())
310            .from(object_data_model::Column::Table)
311            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)))
312            .build_rusqlite(SqliteQueryBuilder);
313        connection
314            .prepare(&sql)?
315            .query_row(&*values.as_params(), |row| row.get(0))
316            .map(|count: i64| count as usize)
317    }
318}
319
320impl KvsEngine for SqliteEngine {
321    type Error = Error;
322
323    fn create_store(
324        &self,
325        store_name: &str,
326        key_path: Option<KeyPath>,
327        auto_increment: bool,
328    ) -> Result<CreateObjectResult, Self::Error> {
329        let mut stmt = self
330            .connection
331            .prepare("SELECT * FROM object_store WHERE name = ?")?;
332        if stmt.exists(params![store_name.to_string()])? {
333            // Store already exists
334            return Ok(CreateObjectResult::AlreadyExists);
335        }
336        self.connection.execute(
337            "INSERT INTO object_store (name, key_path, auto_increment) VALUES (?, ?, ?)",
338            params![
339                store_name.to_string(),
340                key_path.map(|v| postcard::to_stdvec(&v).unwrap()),
341                auto_increment as i32
342            ],
343        )?;
344
345        Ok(CreateObjectResult::Created)
346    }
347
348    fn delete_store(&self, store_name: &str) -> Result<(), Self::Error> {
349        // https://www.w3.org/TR/IndexedDB-3/#dom-idbdatabase-deleteobjectstore
350        // Step 7. Destroy store.
351        let object_store = Self::object_store_by_name(&self.connection, store_name)?;
352
353        self.connection.execute(
354            "DELETE FROM index_data WHERE object_store_id = ?",
355            params![object_store.id],
356        )?;
357        self.connection.execute(
358            "DELETE FROM unique_index_data WHERE object_store_id = ?",
359            params![object_store.id],
360        )?;
361        self.connection.execute(
362            "DELETE FROM object_store_index WHERE object_store_id = ?",
363            params![object_store.id],
364        )?;
365        self.connection.execute(
366            "DELETE FROM object_data WHERE object_store_id = ?",
367            params![object_store.id],
368        )?;
369        let result = self.connection.execute(
370            "DELETE FROM object_store WHERE id = ?",
371            params![object_store.id],
372        )?;
373        if result == 0 {
374            Err(Error::QueryReturnedNoRows)
375        } else if result > 1 {
376            Err(Error::QueryReturnedMoreThanOneRow)
377        } else {
378            Ok(())
379        }
380    }
381
382    fn close_store(&self, _store_name: &str) -> Result<(), Self::Error> {
383        // TODO: do something
384        Ok(())
385    }
386
387    fn process_transaction(
388        &self,
389        transaction: KvsTransaction,
390        on_complete: Box<dyn FnOnce() + Send + 'static>,
391    ) {
392        let spawning_pool = if transaction.mode == IndexedDBTxnMode::Readonly {
393            self.read_pool.clone()
394        } else {
395            self.write_pool.clone()
396        };
397        let path = self.db_path.clone();
398        spawning_pool.spawn(move || {
399            let connection = match Connection::open(path) {
400                Ok(connection) => connection,
401                Err(error) => {
402                    for request in transaction.requests {
403                        request
404                            .operation
405                            .notify_error(BackendError::DbErr(format!("{error:?}")));
406                    }
407                    on_complete();
408                    return;
409                },
410            };
411            for request in transaction.requests {
412                let object_store = connection
413                    .prepare("SELECT * FROM object_store WHERE name = ?")
414                    .and_then(|mut stmt| {
415                        stmt.query_row(params![request.store_name.to_string()], |row| {
416                            object_store_model::Model::try_from(row)
417                        })
418                        .optional()
419                    });
420                let object_store = match object_store {
421                    Ok(Some(store)) => store,
422                    Ok(None) => {
423                        request.operation.notify_error(BackendError::StoreNotFound);
424                        continue;
425                    },
426                    Err(error) => {
427                        request
428                            .operation
429                            .notify_error(BackendError::DbErr(format!("{error:?}")));
430                        continue;
431                    },
432                };
433
434                match request.operation {
435                    AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
436                        callback,
437                        key,
438                        value,
439                        should_overwrite,
440                        key_generator_current_number,
441                    }) => {
442                        let (key, key_generator_current_number) = match key {
443                            Some(key) => (key, key_generator_current_number),
444                            None => {
445                                if object_store.auto_increment == 0 {
446                                    if let Err(error) = callback.send(Err(BackendError::DbErr(
447                                        "Missing key for PutItem request".to_string(),
448                                    ))) {
449                                        warn!("Failed to send PutItem missing key error: {error:?}");
450                                    }
451                                    continue;
452                                }
453                                let Some(next_key_generator_current_number) =
454                                    object_store.auto_increment.checked_add(1)
455                                else {
456                                    if let Err(error) = callback.send(Err(BackendError::DbErr(
457                                        "Key generator overflow".to_string(),
458                                    ))) {
459                                        warn!(
460                                            "Failed to send PutItem key generator overflow error: {error:?}"
461                                        );
462                                    }
463                                    continue;
464                                };
465                                (
466                                    IndexedDBKeyType::Number(object_store.auto_increment as f64),
467                                    Some(next_key_generator_current_number),
468                                )
469                            },
470                        };
471                        let _ = callback.send(
472                            Self::put_item(
473                                &connection,
474                                object_store,
475                                key,
476                                value,
477                                should_overwrite,
478                                key_generator_current_number,
479                            )
480                            .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
481                        );
482                    },
483                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
484                        callback,
485                        key_range,
486                    }) => {
487                        let _ = callback.send(
488                            Self::get_item(&connection, object_store, key_range)
489                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
490                        );
491                    },
492                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllKeys {
493                        callback,
494                        key_range,
495                        count,
496                    }) => {
497                        let _ = callback.send(
498                            Self::get_all_keys(&connection, object_store, key_range, count)
499                                .map(|keys| {
500                                    keys.into_iter()
501                                        .map(|k| encoding::deserialize(&k).unwrap())
502                                        .collect()
503                                })
504                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
505                        );
506                    },
507                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
508                        callback,
509                        key_range,
510                        count,
511                    }) => {
512                        let _ = callback.send(
513                            Self::get_all_items(&connection, object_store, key_range, count)
514                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
515                        );
516                    },
517                    AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
518                        callback,
519                        key_range,
520                    }) => {
521                        let _ = callback.send(
522                            Self::delete_item(&connection, object_store, key_range)
523                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
524                        );
525                    },
526                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
527                        callback,
528                        key_range,
529                    }) => {
530                        let _ = callback.send(
531                            Self::count(&connection, object_store, key_range)
532                                .map(|r| r as u64)
533                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
534                        );
535                    },
536                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate {
537                        callback,
538                        key_range,
539                    }) => {
540                        let _ = callback.send(
541                            Self::get_all_records(&connection, object_store, key_range)
542                                .map(|records| {
543                                    records
544                                        .into_iter()
545                                        .map(|(key, data)| IndexedDBRecord {
546                                            key: encoding::deserialize(&key).unwrap(),
547                                            primary_key: encoding::deserialize(&key).unwrap(),
548                                            value: data,
549                                        })
550                                        .collect()
551                                })
552                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
553                        );
554                    },
555                    AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(sender)) => {
556                        let _ = sender.send(
557                            Self::clear(&connection, object_store)
558                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
559                        );
560                    },
561                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetKey {
562                        callback,
563                        key_range,
564                    }) => {
565                        let _ = callback.send(
566                            Self::get_key(&connection, object_store, key_range)
567                                .map(|key| key.map(|k| encoding::deserialize(&k).unwrap()))
568                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
569                        );
570                    },
571                }
572            }
573            on_complete();
574        });
575    }
576
577    fn key_generator_current_number(&self, store_name: &str) -> Option<i32> {
578        self.connection
579            .prepare("SELECT * FROM object_store WHERE name = ?")
580            .and_then(|mut stmt| {
581                stmt.query_row(params![store_name.to_string()], |r| {
582                    let object_store = object_store_model::Model::try_from(r).unwrap();
583                    Ok(object_store.auto_increment)
584                })
585            })
586            .optional()
587            .unwrap()
588            .and_then(|current_number| (current_number != 0).then_some(current_number))
589    }
590
591    fn key_path(&self, store_name: &str) -> Option<KeyPath> {
592        self.connection
593            .prepare("SELECT * FROM object_store WHERE name = ?")
594            .and_then(|mut stmt| {
595                stmt.query_row(params![store_name.to_string()], |r| {
596                    let object_store = object_store_model::Model::try_from(r).unwrap();
597                    Ok(object_store
598                        .key_path
599                        .map(|key_path| postcard::from_bytes(&key_path).unwrap()))
600                })
601            })
602            .optional()
603            .unwrap()
604            // TODO: Wrong, same issues as has_key_generator
605            .unwrap_or_default()
606    }
607
608    fn object_store_names(&self) -> Result<Vec<String>, Self::Error> {
609        let mut stmt = self.connection.prepare("SELECT name FROM object_store")?;
610        stmt.query_map([], |row| row.get(0))?
611            .collect::<Result<Vec<_>, _>>()
612    }
613
614    fn indexes(&self, store_name: &str) -> Result<Vec<IndexedDBIndex>, Self::Error> {
615        let object_store = self.connection.query_row(
616            "SELECT * FROM object_store WHERE name = ?",
617            params![store_name.to_string()],
618            |row| object_store_model::Model::try_from(row),
619        )?;
620
621        let mut stmt = self
622            .connection
623            .prepare("SELECT * FROM object_store_index WHERE object_store_id = ?")?;
624        let indexes = stmt
625            .query_map(params![object_store.id], |row| {
626                let model = object_store_index_model::Model::try_from(row)?;
627                Ok(IndexedDBIndex {
628                    name: model.name,
629                    key_path: postcard::from_bytes(&model.key_path).unwrap(),
630                    unique: model.unique_index,
631                    multi_entry: model.multi_entry_index,
632                })
633            })?
634            .collect::<Result<Vec<_>, _>>()?;
635        Ok(indexes)
636    }
637
638    fn create_index(
639        &self,
640        store_name: &str,
641        index_name: String,
642        key_path: KeyPath,
643        unique: bool,
644        multi_entry: bool,
645    ) -> Result<CreateObjectResult, Self::Error> {
646        let object_store = self.connection.query_row(
647            "SELECT * FROM object_store WHERE name = ?",
648            params![store_name.to_string()],
649            |row| object_store_model::Model::try_from(row),
650        )?;
651
652        let index_exists: bool = self.connection.query_row(
653            "SELECT EXISTS(SELECT * FROM object_store_index WHERE name = ? AND object_store_id = ?)",
654            params![index_name, object_store.id],
655            |row| row.get(0),
656        )?;
657        if index_exists {
658            return Ok(CreateObjectResult::AlreadyExists);
659        }
660
661        self.connection.execute(
662            "INSERT INTO object_store_index (object_store_id, name, key_path, unique_index, multi_entry_index)\
663            VALUES (?, ?, ?, ?, ?)",
664            params![
665                object_store.id,
666                index_name,
667                postcard::to_stdvec(&key_path).unwrap(),
668                unique,
669                multi_entry,
670            ],
671        )?;
672        Ok(CreateObjectResult::Created)
673    }
674
675    fn delete_index(&self, store_name: &str, index_name: String) -> Result<(), Self::Error> {
676        let object_store = self.connection.query_row(
677            "SELECT * FROM object_store WHERE name = ?",
678            params![store_name.to_string()],
679            |r| Ok(object_store_model::Model::try_from(r).unwrap()),
680        )?;
681
682        // Delete the index if it exists
683        let _ = self.connection.execute(
684            "DELETE FROM object_store_index WHERE name = ? AND object_store_id = ?",
685            params![index_name, object_store.id],
686        )?;
687        Ok(())
688    }
689
690    fn version(&self) -> Result<u64, Self::Error> {
691        let version: i64 =
692            self.connection
693                .query_row("SELECT version FROM database LIMIT 1", [], |row| row.get(0))?;
694        Ok(u64::from_ne_bytes(version.to_ne_bytes()))
695    }
696
697    fn set_version(&self, version: u64) -> Result<(), Self::Error> {
698        let rows_affected = self.connection.execute(
699            "UPDATE database SET version = ?",
700            params![i64::from_ne_bytes(version.to_ne_bytes())],
701        )?;
702        if rows_affected == 0 {
703            return Err(Error::QueryReturnedNoRows);
704        }
705        Ok(())
706    }
707}
708
709fn get_db_status(connection: &Connection, op: i32) -> Result<i32, i32> {
710    let mut p_curr = 0;
711    let mut p_hiwater = 0;
712    let res = unsafe {
713        rusqlite::ffi::sqlite3_db_status(connection.handle(), op, &mut p_curr, &mut p_hiwater, 0)
714    };
715    if res != 0 { Err(res) } else { Ok(p_curr) }
716}
717
718impl MallocSizeOf for SqliteEngine {
719    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
720        // 48 KB (3.3.1 at https://sqlite.org/malloc.html)
721        const DEFAULT_LOOKASIDE_SIZE: usize = 48 * 1024;
722        self.created_db_path.size_of(ops) +
723            DEFAULT_LOOKASIDE_SIZE +
724            get_db_status(
725                &self.connection,
726                rusqlite::ffi::SQLITE_DBSTATUS_CACHE_USED_SHARED,
727            )
728            .unwrap_or_default() as usize +
729            get_db_status(&self.connection, rusqlite::ffi::SQLITE_DBSTATUS_SCHEMA_USED)
730                .unwrap_or_default() as usize +
731            get_db_status(&self.connection, rusqlite::ffi::SQLITE_DBSTATUS_STMT_USED)
732                .unwrap_or_default() as usize
733    }
734}
735
736#[cfg(test)]
737mod tests {
738    use std::collections::VecDeque;
739    use std::path::PathBuf;
740    use std::sync::Arc;
741
742    use profile_traits::generic_callback::GenericCallback;
743    use profile_traits::time::ProfilerChan;
744    use serde::{Deserialize, Serialize};
745    use servo_base::generic_channel::{self, GenericReceiver, GenericSender};
746    use servo_base::id::{PIPELINE_NAMESPACE, PipelineNamespace, PipelineNamespaceId, WebViewId};
747    use servo_base::threadpool::ThreadPool;
748    use servo_url::ImmutableOrigin;
749    use storage_traits::client_storage::{
750        ClientStorageThreadHandle, StorageIdentifier, StorageProxyMap, StorageType,
751    };
752    use storage_traits::indexeddb::{
753        AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult,
754        IndexedDBKeyRange, IndexedDBKeyType, IndexedDBTxnMode, KeyPath, PutItemResult,
755    };
756    use url::Host;
757
758    use crate::ClientStorageThreadFactory;
759    use crate::indexeddb::IndexedDBDescription;
760    use crate::indexeddb::engines::sqlite::encoding;
761    use crate::indexeddb::engines::{KvsEngine, KvsOperation, KvsTransaction, SqliteEngine};
762
763    fn install_test_namespace() {
764        PipelineNamespace::install(PipelineNamespaceId(1));
765    }
766
767    fn test_origin() -> ImmutableOrigin {
768        ImmutableOrigin::Tuple(
769            "test_origin".to_string(),
770            Host::Domain("localhost".to_string()),
771            80,
772        )
773    }
774
775    fn get_pool() -> Arc<ThreadPool> {
776        ThreadPool::global()
777    }
778
779    fn create_db(
780        db_name: String,
781    ) -> (
782        tempfile::TempDir,
783        PathBuf,
784        bool,
785        StorageProxyMap,
786        ClientStorageThreadHandle,
787    ) {
788        if PIPELINE_NAMESPACE.get().is_none() {
789            install_test_namespace();
790        }
791        let tmp_dir = tempfile::tempdir().unwrap();
792        let handle: ClientStorageThreadHandle =
793            ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), true);
794
795        let storage_proxy_map = handle
796            .obtain_a_storage_bottle_map(
797                StorageType::Local,
798                Some(WebViewId::new(servo_base::id::TEST_PAINTER_ID)),
799                StorageIdentifier::IndexedDB,
800                test_origin(),
801            )
802            .recv()
803            .unwrap()
804            .unwrap();
805        let (path, created) = handle
806            .create_database(storage_proxy_map.bottle_id, db_name)
807            .recv()
808            .unwrap()
809            .unwrap();
810        (tmp_dir, path, created, storage_proxy_map, handle)
811    }
812
813    #[test]
814    fn test_cycle() {
815        let (_temp_dir, path, created, proxy_map, handle) = create_db("test_db".to_string());
816        let thread_pool = get_pool();
817        // Test create
818        let db = SqliteEngine::new(
819            path.clone(),
820            created,
821            &IndexedDBDescription {
822                name: "test_db".to_string(),
823                origin: test_origin(),
824            },
825            thread_pool.clone(),
826        )
827        .unwrap();
828        drop(db);
829
830        // Test open
831        let db = SqliteEngine::new(
832            path,
833            created,
834            &IndexedDBDescription {
835                name: "test_db".to_string(),
836                origin: test_origin(),
837            },
838            thread_pool.clone(),
839        )
840        .unwrap();
841        let version = db.version().expect("Failed to get version");
842        assert_eq!(version, 0);
843        db.set_version(5).unwrap();
844        let new_version = db.version().expect("Failed to get new version");
845        assert_eq!(new_version, 5);
846        drop(db);
847        handle
848            .delete_database(proxy_map.bottle_id, "test_db".to_string())
849            .recv()
850            .unwrap()
851            .expect("Failed to delete database");
852    }
853
854    #[test]
855    fn test_create_store() {
856        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
857        let thread_pool = get_pool();
858        let db = SqliteEngine::new(
859            path,
860            created,
861            &IndexedDBDescription {
862                name: "test_db".to_string(),
863                origin: test_origin(),
864            },
865            thread_pool,
866        )
867        .unwrap();
868        let store_name = "test_store";
869        let result = db.create_store(store_name, None, true);
870        assert!(result.is_ok());
871        let create_result = result.unwrap();
872        assert_eq!(create_result, CreateObjectResult::Created);
873        // Try to create the same store again
874        let result = db.create_store(store_name, None, false);
875        assert!(result.is_ok());
876        let create_result = result.unwrap();
877        assert_eq!(create_result, CreateObjectResult::AlreadyExists);
878        // Ensure store was not overwritten
879        assert!(db.key_generator_current_number(store_name).is_some());
880    }
881
882    #[test]
883    fn test_create_store_empty_name() {
884        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
885        let thread_pool = get_pool();
886        let db = SqliteEngine::new(
887            path,
888            created,
889            &IndexedDBDescription {
890                name: "test_db".to_string(),
891                origin: test_origin(),
892            },
893            thread_pool,
894        )
895        .unwrap();
896        let store_name = "";
897        let result = db.create_store(store_name, None, true);
898        assert!(result.is_ok());
899        let create_result = result.unwrap();
900        assert_eq!(create_result, CreateObjectResult::Created);
901    }
902
903    #[test]
904    fn test_injection() {
905        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
906        let thread_pool = get_pool();
907        let db = SqliteEngine::new(
908            path,
909            created,
910            &IndexedDBDescription {
911                name: "test_db".to_string(),
912                origin: test_origin(),
913            },
914            thread_pool,
915        )
916        .unwrap();
917        // Create a normal store
918        let store_name1 = "test_store";
919        let result = db.create_store(store_name1, None, true);
920        assert!(result.is_ok());
921        let create_result = result.unwrap();
922        assert_eq!(create_result, CreateObjectResult::Created);
923        // Injection
924        let store_name2 = "' OR 1=1 -- -";
925        let result = db.create_store(store_name2, None, false);
926        assert!(result.is_ok());
927        let create_result = result.unwrap();
928        assert_eq!(create_result, CreateObjectResult::Created);
929    }
930
931    #[test]
932    fn test_key_path() {
933        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
934        let thread_pool = get_pool();
935        let db = SqliteEngine::new(
936            path,
937            created,
938            &IndexedDBDescription {
939                name: "test_db".to_string(),
940                origin: test_origin(),
941            },
942            thread_pool,
943        )
944        .unwrap();
945        let store_name = "test_store";
946        let result = db.create_store(store_name, Some(KeyPath::String("test".to_string())), true);
947        assert!(result.is_ok());
948        assert_eq!(
949            db.key_path(store_name),
950            Some(KeyPath::String("test".to_string()))
951        );
952    }
953
954    #[test]
955    fn test_delete_store() {
956        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
957        let thread_pool = get_pool();
958        let db = SqliteEngine::new(
959            path,
960            created,
961            &IndexedDBDescription {
962                name: "test_db".to_string(),
963                origin: test_origin(),
964            },
965            thread_pool,
966        )
967        .unwrap();
968        db.create_store("test_store", None, false)
969            .expect("Failed to create store");
970        // Delete the store
971        db.delete_store("test_store")
972            .expect("Failed to delete store");
973        // Try to delete the same store again
974        let result = db.delete_store("test_store");
975        assert!(result.is_err());
976        // Try to delete a non-existing store
977        let result = db.delete_store("test_store");
978        // Should work as per spec
979        assert!(result.is_err());
980    }
981
982    #[test]
983    fn test_delete_store_removes_store_records() {
984        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
985        let thread_pool = get_pool();
986        let db = SqliteEngine::new(
987            path,
988            created,
989            &IndexedDBDescription {
990                name: "test_db".to_string(),
991                origin: test_origin(),
992            },
993            thread_pool,
994        )
995        .unwrap();
996
997        db.create_store("test_store", None, false)
998            .expect("Failed to create store");
999        let object_store = SqliteEngine::object_store_by_name(&db.connection, "test_store")
1000            .expect("Failed to fetch store metadata");
1001        SqliteEngine::put_item(
1002            &db.connection,
1003            object_store.clone(),
1004            IndexedDBKeyType::Number(1.0),
1005            vec![1, 2, 3],
1006            true,
1007            None,
1008        )
1009        .expect("Failed to insert item");
1010
1011        let row_count_before: i64 = db
1012            .connection
1013            .query_row(
1014                "SELECT COUNT(*) FROM object_data WHERE object_store_id = ?",
1015                rusqlite::params![object_store.id],
1016                |row| row.get(0),
1017            )
1018            .expect("Failed to count rows before delete");
1019        assert_eq!(row_count_before, 1);
1020
1021        db.delete_store("test_store")
1022            .expect("Failed to delete store");
1023
1024        let row_count_after: i64 = db
1025            .connection
1026            .query_row(
1027                "SELECT COUNT(*) FROM object_data WHERE object_store_id = ?",
1028                rusqlite::params![object_store.id],
1029                |row| row.get(0),
1030            )
1031            .expect("Failed to count rows after delete");
1032        assert_eq!(row_count_after, 0);
1033    }
1034
1035    #[test]
1036    fn test_async_operations() {
1037        fn get_channel<T>() -> (GenericSender<T>, GenericReceiver<T>)
1038        where
1039            T: for<'de> Deserialize<'de> + Serialize,
1040        {
1041            generic_channel::channel().unwrap()
1042        }
1043
1044        fn get_callback<T>(chan: GenericSender<T>) -> GenericCallback<T>
1045        where
1046            T: for<'de> Deserialize<'de> + Serialize + Send + Sync,
1047        {
1048            GenericCallback::new(ProfilerChan(None), move |r| {
1049                assert!(chan.send(r.unwrap()).is_ok());
1050            })
1051            .expect("Could not construct callback")
1052        }
1053
1054        let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
1055        let thread_pool = get_pool();
1056        let db = SqliteEngine::new(
1057            path,
1058            created,
1059            &IndexedDBDescription {
1060                name: "test_db".to_string(),
1061                origin: test_origin(),
1062            },
1063            thread_pool,
1064        )
1065        .unwrap();
1066        let store_name = "test_store";
1067        db.create_store(store_name, None, false)
1068            .expect("Failed to create store");
1069        let put = get_channel();
1070        let put2 = get_channel();
1071        let put3 = get_channel();
1072        let put_dup = get_channel();
1073        let put_overwrite = get_channel();
1074        let get_item_some = get_channel();
1075        let get_item_none = get_channel();
1076        let get_all_items = get_channel();
1077        let count = get_channel();
1078        let remove = get_channel();
1079        let clear = get_channel();
1080        let (done_tx, done_rx) = std::sync::mpsc::channel();
1081        db.process_transaction(
1082            KvsTransaction {
1083                mode: IndexedDBTxnMode::Readwrite,
1084                requests: VecDeque::from(vec![
1085                    KvsOperation {
1086                        store_name: store_name.to_owned(),
1087                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
1088                            callback: get_callback(put.0),
1089                            key: Some(IndexedDBKeyType::Number(1.0)),
1090                            value: vec![1, 2, 3],
1091                            should_overwrite: false,
1092                            key_generator_current_number: None,
1093                        }),
1094                    },
1095                    KvsOperation {
1096                        store_name: store_name.to_owned(),
1097                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
1098                            callback: get_callback(put2.0),
1099                            key: Some(IndexedDBKeyType::String("2.0".to_string())),
1100                            value: vec![4, 5, 6],
1101                            should_overwrite: false,
1102                            key_generator_current_number: None,
1103                        }),
1104                    },
1105                    KvsOperation {
1106                        store_name: store_name.to_owned(),
1107                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
1108                            callback: get_callback(put3.0),
1109                            key: Some(IndexedDBKeyType::Array(vec![
1110                                IndexedDBKeyType::String("3".to_string()),
1111                                IndexedDBKeyType::Number(0.0),
1112                            ])),
1113                            value: vec![7, 8, 9],
1114                            should_overwrite: false,
1115                            key_generator_current_number: None,
1116                        }),
1117                    },
1118                    // Try to put a duplicate key without overwrite
1119                    KvsOperation {
1120                        store_name: store_name.to_owned(),
1121                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
1122                            callback: get_callback(put_dup.0),
1123                            key: Some(IndexedDBKeyType::Number(1.0)),
1124                            value: vec![10, 11, 12],
1125                            should_overwrite: false,
1126                            key_generator_current_number: None,
1127                        }),
1128                    },
1129                    KvsOperation {
1130                        store_name: store_name.to_owned(),
1131                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
1132                            callback: get_callback(put_overwrite.0),
1133                            key: Some(IndexedDBKeyType::Number(1.0)),
1134                            value: vec![13, 14, 15],
1135                            should_overwrite: true,
1136                            key_generator_current_number: None,
1137                        }),
1138                    },
1139                    KvsOperation {
1140                        store_name: store_name.to_owned(),
1141                        operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
1142                            callback: get_callback(get_item_some.0),
1143                            key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
1144                        }),
1145                    },
1146                    KvsOperation {
1147                        store_name: store_name.to_owned(),
1148                        operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
1149                            callback: get_callback(get_item_none.0),
1150                            key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)),
1151                        }),
1152                    },
1153                    KvsOperation {
1154                        store_name: store_name.to_owned(),
1155                        operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
1156                            callback: get_callback(get_all_items.0),
1157                            key_range: IndexedDBKeyRange::lower_bound(
1158                                IndexedDBKeyType::Number(0.0),
1159                                false,
1160                            ),
1161                            count: None,
1162                        }),
1163                    },
1164                    KvsOperation {
1165                        store_name: store_name.to_owned(),
1166                        operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
1167                            callback: get_callback(count.0),
1168                            key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
1169                        }),
1170                    },
1171                    KvsOperation {
1172                        store_name: store_name.to_owned(),
1173                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
1174                            callback: get_callback(remove.0),
1175                            key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
1176                        }),
1177                    },
1178                    KvsOperation {
1179                        store_name: store_name.to_owned(),
1180                        operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(
1181                            get_callback(clear.0),
1182                        )),
1183                    },
1184                ]),
1185            },
1186            Box::new(move || {
1187                let _ = done_tx.send(());
1188            }),
1189        );
1190        let _ = done_rx.recv().unwrap();
1191        put.1.recv().unwrap().unwrap();
1192        put2.1.recv().unwrap().unwrap();
1193        put3.1.recv().unwrap().unwrap();
1194        let err = put_dup.1.recv().unwrap().unwrap();
1195        assert_eq!(err, PutItemResult::CannotOverwrite);
1196        let overwritten = put_overwrite.1.recv().unwrap().unwrap();
1197        assert_eq!(
1198            overwritten,
1199            PutItemResult::Key(IndexedDBKeyType::Number(1.0))
1200        );
1201        let get_result = get_item_some.1.recv().unwrap();
1202        let value = get_result.unwrap();
1203        assert_eq!(value, Some(vec![13, 14, 15]));
1204        let get_result = get_item_none.1.recv().unwrap();
1205        let value = get_result.unwrap();
1206        assert_eq!(value, None);
1207        let all_items = get_all_items.1.recv().unwrap().unwrap();
1208        assert_eq!(all_items.len(), 3);
1209        // Check that all three items are present
1210        assert!(all_items.contains(&vec![13, 14, 15]));
1211        assert!(all_items.contains(&vec![4, 5, 6]));
1212        assert!(all_items.contains(&vec![7, 8, 9]));
1213        let amount = count.1.recv().unwrap().unwrap();
1214        assert_eq!(amount, 1);
1215        remove.1.recv().unwrap().unwrap();
1216        clear.1.recv().unwrap().unwrap();
1217    }
1218
1219    #[test]
1220    fn test_delete_item_range_respects_open_bounds() {
1221        fn remaining_keys_after_delete(
1222            lower: i32,
1223            upper: i32,
1224            lower_open: bool,
1225            upper_open: bool,
1226        ) -> Vec<i32> {
1227            let (_temp_dir, path, created, _proxy_map, _handle) = create_db("test_db".to_string());
1228            let thread_pool = get_pool();
1229            let db = SqliteEngine::new(
1230                path,
1231                created,
1232                &IndexedDBDescription {
1233                    name: "test_db".to_string(),
1234                    origin: test_origin(),
1235                },
1236                thread_pool,
1237            )
1238            .unwrap();
1239            let store_name = "test_store";
1240            db.create_store(store_name, None, false)
1241                .expect("Failed to create store");
1242            let store = SqliteEngine::object_store_by_name(&db.connection, store_name)
1243                .expect("Failed to get object store");
1244
1245            for key in 1..=10 {
1246                SqliteEngine::put_item(
1247                    &db.connection,
1248                    store.clone(),
1249                    IndexedDBKeyType::Number(key as f64),
1250                    vec![key as u8],
1251                    false,
1252                    None,
1253                )
1254                .expect("Failed to seed object store");
1255            }
1256
1257            SqliteEngine::delete_item(
1258                &db.connection,
1259                store.clone(),
1260                IndexedDBKeyRange::new(
1261                    Some(IndexedDBKeyType::Number(lower as f64)),
1262                    Some(IndexedDBKeyType::Number(upper as f64)),
1263                    lower_open,
1264                    upper_open,
1265                ),
1266            )
1267            .expect("Failed to delete key range");
1268
1269            SqliteEngine::get_all_keys(&db.connection, store, IndexedDBKeyRange::default(), None)
1270                .expect("Failed to read remaining keys")
1271                .into_iter()
1272                .map(|raw_key| match encoding::deserialize(&raw_key).unwrap() {
1273                    IndexedDBKeyType::Number(number) => number as i32,
1274                    other => panic!("Expected numeric key, got {other:?}"),
1275                })
1276                .collect()
1277        }
1278
1279        assert_eq!(
1280            remaining_keys_after_delete(3, 8, false, false),
1281            vec![1, 2, 9, 10]
1282        );
1283        assert_eq!(
1284            remaining_keys_after_delete(3, 8, true, false),
1285            vec![1, 2, 3, 9, 10]
1286        );
1287        assert_eq!(
1288            remaining_keys_after_delete(3, 8, false, true),
1289            vec![1, 2, 8, 9, 10]
1290        );
1291        assert_eq!(
1292            remaining_keys_after_delete(3, 8, true, true),
1293            vec![1, 2, 3, 8, 9, 10]
1294        );
1295    }
1296}