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 base::threadpool::ThreadPool;
8use ipc_channel::ipc::IpcSender;
9use log::{error, info};
10use rusqlite::{Connection, Error, OptionalExtension, params};
11use sea_query::{Condition, Expr, ExprTrait, IntoCondition, SqliteQueryBuilder};
12use sea_query_rusqlite::RusqliteBinder;
13use serde::Serialize;
14use storage_traits::indexeddb::{
15    AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, BackendError, BackendResult,
16    CreateObjectResult, IndexedDBKeyRange, IndexedDBKeyType, IndexedDBRecord, IndexedDBTxnMode,
17    KeyPath, PutItemResult,
18};
19use tokio::sync::oneshot;
20
21use crate::indexeddb::IndexedDBDescription;
22use crate::indexeddb::engines::{KvsEngine, KvsTransaction};
23use crate::shared::{DB_INIT_PRAGMAS, DB_PRAGMAS};
24
25mod create;
26mod database_model;
27mod object_data_model;
28mod object_store_index_model;
29mod object_store_model;
30
31fn range_to_query(range: IndexedDBKeyRange) -> Condition {
32    // Special case for optimization
33    if let Some(singleton) = range.as_singleton() {
34        let encoded = bincode::serialize(singleton).unwrap();
35        return Expr::column(object_data_model::Column::Key)
36            .eq(encoded)
37            .into_condition();
38    }
39    let mut parts = vec![];
40    if let Some(upper) = range.upper.as_ref() {
41        let upper_bytes = bincode::serialize(upper).unwrap();
42        let query = if range.upper_open {
43            Expr::column(object_data_model::Column::Key).lt(upper_bytes)
44        } else {
45            Expr::column(object_data_model::Column::Key).lte(upper_bytes)
46        };
47        parts.push(query);
48    }
49    if let Some(lower) = range.lower.as_ref() {
50        let lower_bytes = bincode::serialize(lower).unwrap();
51        let query = if range.lower_open {
52            Expr::column(object_data_model::Column::Key).gt(lower_bytes)
53        } else {
54            Expr::column(object_data_model::Column::Key).gte(lower_bytes)
55        };
56        parts.push(query);
57    }
58    let mut condition = Condition::all();
59    for part in parts {
60        condition = condition.add(part);
61    }
62    condition
63}
64
65pub struct SqliteEngine {
66    db_path: PathBuf,
67    connection: Connection,
68    read_pool: Arc<ThreadPool>,
69    write_pool: Arc<ThreadPool>,
70}
71
72impl SqliteEngine {
73    // TODO: intake dual pools
74    pub fn new(
75        base_dir: &Path,
76        db_info: &IndexedDBDescription,
77        pool: Arc<ThreadPool>,
78    ) -> Result<Self, Error> {
79        let mut db_path = PathBuf::new();
80        db_path.push(base_dir);
81        db_path.push(db_info.as_path());
82        let db_parent = db_path.clone();
83        db_path.push("db.sqlite");
84
85        if !db_path.exists() {
86            std::fs::create_dir_all(db_parent).unwrap();
87            std::fs::File::create(&db_path).unwrap();
88        }
89        let connection = Self::init_db(&db_path, db_info)?;
90
91        for stmt in DB_PRAGMAS {
92            // TODO: Handle errors properly
93            let _ = connection.execute(stmt, ());
94        }
95
96        Ok(Self {
97            connection,
98            db_path,
99            read_pool: pool.clone(),
100            write_pool: pool,
101        })
102    }
103
104    fn init_db(path: &Path, db_info: &IndexedDBDescription) -> Result<Connection, Error> {
105        let connection = Connection::open(path)?;
106        if connection.table_exists(None, "database")? {
107            // Database already exists, no need to initialize
108            return Ok(connection);
109        }
110        info!("Initializing indexeddb database at {:?}", path);
111        for stmt in DB_INIT_PRAGMAS {
112            // FIXME(arihant2math): this fails occasionally
113            let _ = connection.execute(stmt, ());
114        }
115        create::create_tables(&connection)?;
116        // From https://w3c.github.io/IndexedDB/#database-version:
117        // "When a database is first created, its version is 0 (zero)."
118        connection.execute(
119            "INSERT INTO database (name, origin, version) VALUES (?, ?, ?)",
120            params![
121                db_info.name.to_owned(),
122                db_info.origin.to_owned().ascii_serialization(),
123                i64::from_ne_bytes(0_u64.to_ne_bytes())
124            ],
125        )?;
126        Ok(connection)
127    }
128
129    fn get(
130        connection: &Connection,
131        store: object_store_model::Model,
132        key_range: IndexedDBKeyRange,
133    ) -> Result<Option<object_data_model::Model>, Error> {
134        let query = range_to_query(key_range);
135        let (sql, values) = sea_query::Query::select()
136            .from(object_data_model::Column::Table)
137            .columns(vec![
138                object_data_model::Column::ObjectStoreId,
139                object_data_model::Column::Key,
140                object_data_model::Column::Data,
141            ])
142            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)))
143            .limit(1)
144            .build_rusqlite(SqliteQueryBuilder);
145        connection
146            .prepare(&sql)?
147            .query_one(&*values.as_params(), |row| {
148                object_data_model::Model::try_from(row)
149            })
150            .optional()
151    }
152
153    fn get_key(
154        connection: &Connection,
155        store: object_store_model::Model,
156        key_range: IndexedDBKeyRange,
157    ) -> Result<Option<Vec<u8>>, Error> {
158        Self::get(connection, store, key_range).map(|opt| opt.map(|model| model.key))
159    }
160
161    fn get_item(
162        connection: &Connection,
163        store: object_store_model::Model,
164        key_range: IndexedDBKeyRange,
165    ) -> Result<Option<Vec<u8>>, Error> {
166        Self::get(connection, store, key_range).map(|opt| opt.map(|model| model.data))
167    }
168
169    fn get_all(
170        connection: &Connection,
171        store: object_store_model::Model,
172        key_range: IndexedDBKeyRange,
173        count: Option<u32>,
174    ) -> Result<Vec<object_data_model::Model>, Error> {
175        let query = range_to_query(key_range);
176        let mut sql_query = sea_query::Query::select();
177        sql_query
178            .from(object_data_model::Column::Table)
179            .columns(vec![
180                object_data_model::Column::ObjectStoreId,
181                object_data_model::Column::Key,
182                object_data_model::Column::Data,
183            ])
184            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)));
185        if let Some(count) = count {
186            sql_query.limit(count as u64);
187        }
188        let (sql, values) = sql_query.build_rusqlite(SqliteQueryBuilder);
189        let mut stmt = connection.prepare(&sql)?;
190        let models = stmt
191            .query_and_then(&*values.as_params(), |row| {
192                object_data_model::Model::try_from(row)
193            })?
194            .collect::<Result<Vec<_>, _>>()?;
195        Ok(models)
196    }
197
198    fn get_all_keys(
199        connection: &Connection,
200        store: object_store_model::Model,
201        key_range: IndexedDBKeyRange,
202        count: Option<u32>,
203    ) -> Result<Vec<Vec<u8>>, Error> {
204        Self::get_all(connection, store, key_range, count)
205            .map(|models| models.into_iter().map(|m| m.key).collect())
206    }
207
208    fn get_all_items(
209        connection: &Connection,
210        store: object_store_model::Model,
211        key_range: IndexedDBKeyRange,
212        count: Option<u32>,
213    ) -> Result<Vec<Vec<u8>>, Error> {
214        Self::get_all(connection, store, key_range, count)
215            .map(|models| models.into_iter().map(|m| m.data).collect())
216    }
217
218    #[allow(clippy::type_complexity)]
219    fn get_all_records(
220        connection: &Connection,
221        store: object_store_model::Model,
222        key_range: IndexedDBKeyRange,
223    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, Error> {
224        Self::get_all(connection, store, key_range, None)
225            .map(|models| models.into_iter().map(|m| (m.key, m.data)).collect())
226    }
227
228    fn put_item(
229        connection: &Connection,
230        store: object_store_model::Model,
231        serialized_key: Vec<u8>,
232        value: Vec<u8>,
233        should_overwrite: bool,
234    ) -> Result<PutItemResult, Error> {
235        let existing_item = connection
236            .prepare("SELECT * FROM object_data WHERE key = ? AND object_store_id = ?")
237            .and_then(|mut stmt| {
238                stmt.query_row(params![serialized_key, store.id], |row| {
239                    object_data_model::Model::try_from(row)
240                })
241                .optional()
242            })?;
243        if should_overwrite || existing_item.is_none() {
244            connection.execute(
245                "INSERT INTO object_data (object_store_id, key, data) VALUES (?, ?, ?)",
246                params![store.id, serialized_key, value],
247            )?;
248            Ok(PutItemResult::Success)
249        } else {
250            Ok(PutItemResult::CannotOverwrite)
251        }
252    }
253
254    fn delete_item(
255        connection: &Connection,
256        store: object_store_model::Model,
257        key_range: IndexedDBKeyRange,
258    ) -> Result<(), Error> {
259        let query = range_to_query(key_range);
260        let (sql, values) = sea_query::Query::delete()
261            .from_table(object_data_model::Column::Table)
262            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)))
263            .build_rusqlite(SqliteQueryBuilder);
264        connection.prepare(&sql)?.execute(&*values.as_params())?;
265        Ok(())
266    }
267
268    fn clear(connection: &Connection, store: object_store_model::Model) -> Result<(), Error> {
269        connection.execute(
270            "DELETE FROM object_data WHERE object_store_id = ?",
271            params![store.id],
272        )?;
273        Ok(())
274    }
275
276    fn count(
277        connection: &Connection,
278        store: object_store_model::Model,
279        key_range: IndexedDBKeyRange,
280    ) -> Result<usize, Error> {
281        let query = range_to_query(key_range);
282        let (sql, values) = sea_query::Query::select()
283            .expr(Expr::col(object_data_model::Column::Key).count())
284            .from(object_data_model::Column::Table)
285            .and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)))
286            .build_rusqlite(SqliteQueryBuilder);
287        connection
288            .prepare(&sql)?
289            .query_row(&*values.as_params(), |row| row.get(0))
290            .map(|count: i64| count as usize)
291    }
292
293    fn generate_key(
294        connection: &Connection,
295        store: &object_store_model::Model,
296    ) -> Result<IndexedDBKeyType, Error> {
297        if store.auto_increment == 0 {
298            unreachable!("Should be caught in the script thread");
299        }
300        // TODO: handle overflows, this also needs to be able to handle 2^53 as per spec
301        let new_key = store.auto_increment + 1;
302        connection.execute(
303            "UPDATE object_store SET auto_increment = ? WHERE id = ?",
304            params![new_key, store.id],
305        )?;
306        Ok(IndexedDBKeyType::Number(new_key as f64))
307    }
308}
309
310impl KvsEngine for SqliteEngine {
311    type Error = Error;
312
313    fn create_store(
314        &self,
315        store_name: &str,
316        key_path: Option<KeyPath>,
317        auto_increment: bool,
318    ) -> Result<CreateObjectResult, Self::Error> {
319        let mut stmt = self
320            .connection
321            .prepare("SELECT * FROM object_store WHERE name = ?")?;
322        if stmt.exists(params![store_name.to_string()])? {
323            // Store already exists
324            return Ok(CreateObjectResult::AlreadyExists);
325        }
326        self.connection.execute(
327            "INSERT INTO object_store (name, key_path, auto_increment) VALUES (?, ?, ?)",
328            params![
329                store_name.to_string(),
330                key_path.map(|v| bincode::serialize(&v).unwrap()),
331                auto_increment as i32
332            ],
333        )?;
334
335        Ok(CreateObjectResult::Created)
336    }
337
338    fn delete_store(&self, store_name: &str) -> Result<(), Self::Error> {
339        let result = self.connection.execute(
340            "DELETE FROM object_store WHERE name = ?",
341            params![store_name.to_string()],
342        )?;
343        if result == 0 {
344            Err(Error::QueryReturnedNoRows)
345        } else if result > 1 {
346            Err(Error::QueryReturnedMoreThanOneRow)
347        } else {
348            Ok(())
349        }
350    }
351
352    fn close_store(&self, _store_name: &str) -> Result<(), Self::Error> {
353        // TODO: do something
354        Ok(())
355    }
356
357    fn delete_database(self) -> Result<(), Self::Error> {
358        // attempt to close the connection first
359        let _ = self.connection.close();
360        if self.db_path.exists() {
361            if let Err(e) = std::fs::remove_dir_all(self.db_path.parent().unwrap()) {
362                error!("Failed to delete database: {:?}", e);
363            }
364        }
365        Ok(())
366    }
367
368    fn process_transaction(
369        &self,
370        transaction: KvsTransaction,
371    ) -> oneshot::Receiver<Option<Vec<u8>>> {
372        let (tx, rx) = oneshot::channel();
373
374        let spawning_pool = if transaction.mode == IndexedDBTxnMode::Readonly {
375            self.read_pool.clone()
376        } else {
377            self.write_pool.clone()
378        };
379        let path = self.db_path.clone();
380        spawning_pool.spawn(move || {
381            let connection = Connection::open(path).unwrap();
382            for request in transaction.requests {
383                let object_store = connection
384                    .prepare("SELECT * FROM object_store WHERE name = ?")
385                    .and_then(|mut stmt| {
386                        stmt.query_row(params![request.store_name.to_string()], |row| {
387                            object_store_model::Model::try_from(row)
388                        })
389                        .optional()
390                    });
391                fn process_object_store<T>(
392                    object_store: Result<Option<object_store_model::Model>, Error>,
393                    sender: &IpcSender<BackendResult<T>>,
394                ) -> Result<object_store_model::Model, ()>
395                where
396                    T: Serialize,
397                {
398                    match object_store {
399                        Ok(Some(store)) => Ok(store),
400                        Ok(None) => {
401                            let _ = sender.send(Err(BackendError::StoreNotFound));
402                            Err(())
403                        },
404                        Err(e) => {
405                            let _ = sender.send(Err(BackendError::DbErr(format!("{:?}", e))));
406                            Err(())
407                        },
408                    }
409                }
410
411                match request.operation {
412                    AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
413                        sender,
414                        key,
415                        value,
416                        should_overwrite,
417                    }) => {
418                        let Ok(object_store) = process_object_store(object_store, &sender) else {
419                            continue;
420                        };
421                        let key = match key
422                            .map(Ok)
423                            .unwrap_or_else(|| Self::generate_key(&connection, &object_store))
424                        {
425                            Ok(key) => key,
426                            Err(e) => {
427                                let _ = sender.send(Err(BackendError::DbErr(format!("{:?}", e))));
428                                continue;
429                            },
430                        };
431                        let serialized_key: Vec<u8> = bincode::serialize(&key).unwrap();
432                        let _ = sender.send(
433                            Self::put_item(
434                                &connection,
435                                object_store,
436                                serialized_key,
437                                value,
438                                should_overwrite,
439                            )
440                            .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
441                        );
442                    },
443                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
444                        sender,
445                        key_range,
446                    }) => {
447                        let Ok(object_store) = process_object_store(object_store, &sender) else {
448                            continue;
449                        };
450                        let _ = sender.send(
451                            Self::get_item(&connection, object_store, key_range)
452                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
453                        );
454                    },
455                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllKeys {
456                        sender,
457                        key_range,
458                        count,
459                    }) => {
460                        let Ok(object_store) = process_object_store(object_store, &sender) else {
461                            continue;
462                        };
463                        let _ = sender.send(
464                            Self::get_all_keys(&connection, object_store, key_range, count)
465                                .map(|keys| {
466                                    keys.into_iter()
467                                        .map(|k| bincode::deserialize(&k).unwrap())
468                                        .collect()
469                                })
470                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
471                        );
472                    },
473                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
474                        sender,
475                        key_range,
476                        count,
477                    }) => {
478                        let Ok(object_store) = process_object_store(object_store, &sender) else {
479                            continue;
480                        };
481                        let _ = sender.send(
482                            Self::get_all_items(&connection, object_store, key_range, count)
483                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
484                        );
485                    },
486                    AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
487                        sender,
488                        key_range,
489                    }) => {
490                        let Ok(object_store) = process_object_store(object_store, &sender) else {
491                            continue;
492                        };
493                        let _ = sender.send(
494                            Self::delete_item(&connection, object_store, key_range)
495                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
496                        );
497                    },
498                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
499                        sender,
500                        key_range,
501                    }) => {
502                        let Ok(object_store) = process_object_store(object_store, &sender) else {
503                            continue;
504                        };
505                        let _ = sender.send(
506                            Self::count(&connection, object_store, key_range)
507                                .map(|r| r as u64)
508                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
509                        );
510                    },
511                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate {
512                        sender,
513                        key_range,
514                    }) => {
515                        let Ok(object_store) = process_object_store(object_store, &sender) else {
516                            continue;
517                        };
518                        let _ = sender.send(
519                            Self::get_all_records(&connection, object_store, key_range)
520                                .map(|records| {
521                                    records
522                                        .into_iter()
523                                        .map(|(key, data)| IndexedDBRecord {
524                                            key: bincode::deserialize(&key).unwrap(),
525                                            primary_key: bincode::deserialize(&key).unwrap(),
526                                            value: data,
527                                        })
528                                        .collect()
529                                })
530                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
531                        );
532                    },
533                    AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(sender)) => {
534                        let Ok(object_store) = process_object_store(object_store, &sender) else {
535                            continue;
536                        };
537                        let _ = sender.send(
538                            Self::clear(&connection, object_store)
539                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
540                        );
541                    },
542                    AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetKey {
543                        sender,
544                        key_range,
545                    }) => {
546                        let Ok(object_store) = process_object_store(object_store, &sender) else {
547                            continue;
548                        };
549                        let _ = sender.send(
550                            Self::get_key(&connection, object_store, key_range)
551                                .map(|key| key.map(|k| bincode::deserialize(&k).unwrap()))
552                                .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
553                        );
554                    },
555                }
556            }
557            let _ = tx.send(None);
558        });
559        rx
560    }
561
562    // TODO: we should be able to error out here, maybe change the trait definition?
563    fn has_key_generator(&self, store_name: &str) -> bool {
564        self.connection
565            .prepare("SELECT * FROM object_store WHERE name = ?")
566            .and_then(|mut stmt| {
567                stmt.query_row(params![store_name.to_string()], |r| {
568                    let object_store = object_store_model::Model::try_from(r).unwrap();
569                    Ok(object_store.auto_increment)
570                })
571            })
572            .optional()
573            .unwrap()
574            // TODO: Wrong (change trait definition for this function)
575            .unwrap_or_default() !=
576            0
577    }
578
579    fn key_path(&self, store_name: &str) -> Option<KeyPath> {
580        self.connection
581            .prepare("SELECT * FROM object_store WHERE name = ?")
582            .and_then(|mut stmt| {
583                stmt.query_row(params![store_name.to_string()], |r| {
584                    let object_store = object_store_model::Model::try_from(r).unwrap();
585                    Ok(object_store
586                        .key_path
587                        .map(|key_path| bincode::deserialize(&key_path).unwrap()))
588                })
589            })
590            .optional()
591            .unwrap()
592            // TODO: Wrong, same issues as has_key_generator
593            .unwrap_or_default()
594    }
595
596    fn create_index(
597        &self,
598        store_name: &str,
599        index_name: String,
600        key_path: KeyPath,
601        unique: bool,
602        multi_entry: bool,
603    ) -> Result<CreateObjectResult, Self::Error> {
604        let object_store = self.connection.query_row(
605            "SELECT * FROM object_store WHERE name = ?",
606            params![store_name.to_string()],
607            |row| object_store_model::Model::try_from(row),
608        )?;
609
610        let index_exists: bool = self.connection.query_row(
611            "SELECT EXISTS(SELECT * FROM object_store_index WHERE name = ? AND object_store_id = ?)",
612            params![index_name.to_string(), object_store.id],
613            |row| row.get(0),
614        )?;
615        if index_exists {
616            return Ok(CreateObjectResult::AlreadyExists);
617        }
618
619        self.connection.execute(
620            "INSERT INTO object_store_index (object_store_id, name, key_path, unique_index, multi_entry_index)\
621            VALUES (?, ?, ?, ?, ?)",
622            params![
623                object_store.id,
624                index_name.to_string(),
625                bincode::serialize(&key_path).unwrap(),
626                unique,
627                multi_entry,
628            ],
629        )?;
630        Ok(CreateObjectResult::Created)
631    }
632
633    fn delete_index(&self, store_name: &str, index_name: String) -> Result<(), Self::Error> {
634        let object_store = self.connection.query_row(
635            "SELECT * FROM object_store WHERE name = ?",
636            params![store_name.to_string()],
637            |r| Ok(object_store_model::Model::try_from(r).unwrap()),
638        )?;
639
640        // Delete the index if it exists
641        let _ = self.connection.execute(
642            "DELETE FROM object_store_index WHERE name = ? AND object_store_id = ?",
643            params![index_name.to_string(), object_store.id],
644        )?;
645        Ok(())
646    }
647
648    fn version(&self) -> Result<u64, Self::Error> {
649        let version: i64 =
650            self.connection
651                .query_row("SELECT version FROM database LIMIT 1", [], |row| row.get(0))?;
652        Ok(u64::from_ne_bytes(version.to_ne_bytes()))
653    }
654
655    fn set_version(&self, version: u64) -> Result<(), Self::Error> {
656        let rows_affected = self.connection.execute(
657            "UPDATE database SET version = ?",
658            params![i64::from_ne_bytes(version.to_ne_bytes())],
659        )?;
660        if rows_affected == 0 {
661            return Err(Error::QueryReturnedNoRows);
662        }
663        Ok(())
664    }
665}
666
667#[cfg(test)]
668mod tests {
669    use std::collections::VecDeque;
670    use std::sync::Arc;
671
672    use base::threadpool::ThreadPool;
673    use serde::{Deserialize, Serialize};
674    use servo_url::ImmutableOrigin;
675    use storage_traits::indexeddb::{
676        AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult,
677        IndexedDBKeyRange, IndexedDBKeyType, IndexedDBTxnMode, KeyPath, PutItemResult,
678    };
679    use url::Host;
680
681    use crate::indexeddb::IndexedDBDescription;
682    use crate::indexeddb::engines::{KvsEngine, KvsOperation, KvsTransaction, SqliteEngine};
683
684    fn test_origin() -> ImmutableOrigin {
685        ImmutableOrigin::Tuple(
686            "test_origin".to_string(),
687            Host::Domain("localhost".to_string()),
688            80,
689        )
690    }
691
692    fn get_pool() -> Arc<ThreadPool> {
693        Arc::new(ThreadPool::new(1, "test".to_string()))
694    }
695
696    #[test]
697    fn test_cycle() {
698        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
699        let thread_pool = get_pool();
700        // Test create
701        let _ = SqliteEngine::new(
702            base_dir.path(),
703            &IndexedDBDescription {
704                name: "test_db".to_string(),
705                origin: test_origin(),
706            },
707            thread_pool.clone(),
708        )
709        .unwrap();
710        // Test open
711        let db = SqliteEngine::new(
712            base_dir.path(),
713            &IndexedDBDescription {
714                name: "test_db".to_string(),
715                origin: test_origin(),
716            },
717            thread_pool.clone(),
718        )
719        .unwrap();
720        let version = db.version().expect("Failed to get version");
721        assert_eq!(version, 0);
722        db.set_version(5).unwrap();
723        let new_version = db.version().expect("Failed to get new version");
724        assert_eq!(new_version, 5);
725        db.delete_database().expect("Failed to delete database");
726    }
727
728    #[test]
729    fn test_create_store() {
730        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
731        let thread_pool = get_pool();
732        let db = SqliteEngine::new(
733            base_dir.path(),
734            &IndexedDBDescription {
735                name: "test_db".to_string(),
736                origin: test_origin(),
737            },
738            thread_pool,
739        )
740        .unwrap();
741        let store_name = "test_store";
742        let result = db.create_store(store_name, None, true);
743        assert!(result.is_ok());
744        let create_result = result.unwrap();
745        assert_eq!(create_result, CreateObjectResult::Created);
746        // Try to create the same store again
747        let result = db.create_store(store_name, None, false);
748        assert!(result.is_ok());
749        let create_result = result.unwrap();
750        assert_eq!(create_result, CreateObjectResult::AlreadyExists);
751        // Ensure store was not overwritten
752        assert!(db.has_key_generator(store_name));
753    }
754
755    #[test]
756    fn test_create_store_empty_name() {
757        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
758        let thread_pool = get_pool();
759        let db = SqliteEngine::new(
760            base_dir.path(),
761            &IndexedDBDescription {
762                name: "test_db".to_string(),
763                origin: test_origin(),
764            },
765            thread_pool,
766        )
767        .unwrap();
768        let store_name = "";
769        let result = db.create_store(store_name, None, true);
770        assert!(result.is_ok());
771        let create_result = result.unwrap();
772        assert_eq!(create_result, CreateObjectResult::Created);
773    }
774
775    #[test]
776    fn test_injection() {
777        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
778        let thread_pool = get_pool();
779        let db = SqliteEngine::new(
780            base_dir.path(),
781            &IndexedDBDescription {
782                name: "test_db".to_string(),
783                origin: test_origin(),
784            },
785            thread_pool,
786        )
787        .unwrap();
788        // Create a normal store
789        let store_name1 = "test_store";
790        let result = db.create_store(store_name1, None, true);
791        assert!(result.is_ok());
792        let create_result = result.unwrap();
793        assert_eq!(create_result, CreateObjectResult::Created);
794        // Injection
795        let store_name2 = "' OR 1=1 -- -";
796        let result = db.create_store(store_name2, None, false);
797        assert!(result.is_ok());
798        let create_result = result.unwrap();
799        assert_eq!(create_result, CreateObjectResult::Created);
800    }
801
802    #[test]
803    fn test_key_path() {
804        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
805        let thread_pool = get_pool();
806        let db = SqliteEngine::new(
807            base_dir.path(),
808            &IndexedDBDescription {
809                name: "test_db".to_string(),
810                origin: test_origin(),
811            },
812            thread_pool,
813        )
814        .unwrap();
815        let store_name = "test_store";
816        let result = db.create_store(store_name, Some(KeyPath::String("test".to_string())), true);
817        assert!(result.is_ok());
818        assert_eq!(
819            db.key_path(store_name),
820            Some(KeyPath::String("test".to_string()))
821        );
822    }
823
824    #[test]
825    fn test_delete_store() {
826        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
827        let thread_pool = get_pool();
828        let db = SqliteEngine::new(
829            base_dir.path(),
830            &IndexedDBDescription {
831                name: "test_db".to_string(),
832                origin: test_origin(),
833            },
834            thread_pool,
835        )
836        .unwrap();
837        db.create_store("test_store", None, false)
838            .expect("Failed to create store");
839        // Delete the store
840        db.delete_store("test_store")
841            .expect("Failed to delete store");
842        // Try to delete the same store again
843        let result = db.delete_store("test_store");
844        assert!(result.is_err());
845        // Try to delete a non-existing store
846        let result = db.delete_store("test_store");
847        // Should work as per spec
848        assert!(result.is_err());
849    }
850
851    #[test]
852    fn test_async_operations() {
853        fn get_channel<T>() -> (
854            ipc_channel::ipc::IpcSender<T>,
855            ipc_channel::ipc::IpcReceiver<T>,
856        )
857        where
858            T: for<'de> Deserialize<'de> + Serialize,
859        {
860            ipc_channel::ipc::channel().unwrap()
861        }
862
863        let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
864        let thread_pool = get_pool();
865        let db = SqliteEngine::new(
866            base_dir.path(),
867            &IndexedDBDescription {
868                name: "test_db".to_string(),
869                origin: test_origin(),
870            },
871            thread_pool,
872        )
873        .unwrap();
874        let store_name = "test_store";
875        db.create_store(store_name, None, false)
876            .expect("Failed to create store");
877        let put = get_channel();
878        let put2 = get_channel();
879        let put3 = get_channel();
880        let put_dup = get_channel();
881        let get_item_some = get_channel();
882        let get_item_none = get_channel();
883        let get_all_items = get_channel();
884        let count = get_channel();
885        let remove = get_channel();
886        let clear = get_channel();
887        let rx = db.process_transaction(KvsTransaction {
888            mode: IndexedDBTxnMode::Readwrite,
889            requests: VecDeque::from(vec![
890                KvsOperation {
891                    store_name: store_name.to_owned(),
892                    operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
893                        sender: put.0,
894                        key: Some(IndexedDBKeyType::Number(1.0)),
895                        value: vec![1, 2, 3],
896                        should_overwrite: false,
897                    }),
898                },
899                KvsOperation {
900                    store_name: store_name.to_owned(),
901                    operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
902                        sender: put2.0,
903                        key: Some(IndexedDBKeyType::String("2.0".to_string())),
904                        value: vec![4, 5, 6],
905                        should_overwrite: false,
906                    }),
907                },
908                KvsOperation {
909                    store_name: store_name.to_owned(),
910                    operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
911                        sender: put3.0,
912                        key: Some(IndexedDBKeyType::Array(vec![
913                            IndexedDBKeyType::String("3".to_string()),
914                            IndexedDBKeyType::Number(0.0),
915                        ])),
916                        value: vec![7, 8, 9],
917                        should_overwrite: false,
918                    }),
919                },
920                // Try to put a duplicate key without overwrite
921                KvsOperation {
922                    store_name: store_name.to_owned(),
923                    operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
924                        sender: put_dup.0,
925                        key: Some(IndexedDBKeyType::Number(1.0)),
926                        value: vec![10, 11, 12],
927                        should_overwrite: false,
928                    }),
929                },
930                KvsOperation {
931                    store_name: store_name.to_owned(),
932                    operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
933                        sender: get_item_some.0,
934                        key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
935                    }),
936                },
937                KvsOperation {
938                    store_name: store_name.to_owned(),
939                    operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
940                        sender: get_item_none.0,
941                        key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)),
942                    }),
943                },
944                KvsOperation {
945                    store_name: store_name.to_owned(),
946                    operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
947                        sender: get_all_items.0,
948                        key_range: IndexedDBKeyRange::lower_bound(
949                            IndexedDBKeyType::Number(0.0),
950                            false,
951                        ),
952                        count: None,
953                    }),
954                },
955                KvsOperation {
956                    store_name: store_name.to_owned(),
957                    operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
958                        sender: count.0,
959                        key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
960                    }),
961                },
962                KvsOperation {
963                    store_name: store_name.to_owned(),
964                    operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
965                        sender: remove.0,
966                        key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
967                    }),
968                },
969                KvsOperation {
970                    store_name: store_name.to_owned(),
971                    operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(clear.0)),
972                },
973            ]),
974        });
975        let _ = rx.blocking_recv().unwrap();
976        put.1.recv().unwrap().unwrap();
977        put2.1.recv().unwrap().unwrap();
978        put3.1.recv().unwrap().unwrap();
979        let err = put_dup.1.recv().unwrap().unwrap();
980        assert_eq!(err, PutItemResult::CannotOverwrite);
981        let get_result = get_item_some.1.recv().unwrap();
982        let value = get_result.unwrap();
983        assert_eq!(value, Some(vec![1, 2, 3]));
984        let get_result = get_item_none.1.recv().unwrap();
985        let value = get_result.unwrap();
986        assert_eq!(value, None);
987        let all_items = get_all_items.1.recv().unwrap().unwrap();
988        assert_eq!(all_items.len(), 3);
989        // Check that all three items are present
990        assert!(all_items.contains(&vec![1, 2, 3]));
991        assert!(all_items.contains(&vec![4, 5, 6]));
992        assert!(all_items.contains(&vec![7, 8, 9]));
993        let amount = count.1.recv().unwrap().unwrap();
994        assert_eq!(amount, 1);
995        remove.1.recv().unwrap().unwrap();
996        clear.1.recv().unwrap().unwrap();
997    }
998}