1use 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 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 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 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 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 return Ok(connection);
118 }
119 info!("Initializing indexeddb database at {:?}", path);
120 for stmt in DB_INIT_PRAGMAS {
121 let _ = connection.execute(stmt, ());
123 }
124 create::create_tables(&connection)?;
125 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 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 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 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 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 .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 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 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 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 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 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 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 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 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 db.delete_store("test_store")
972 .expect("Failed to delete store");
973 let result = db.delete_store("test_store");
975 assert!(result.is_err());
976 let result = db.delete_store("test_store");
978 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 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 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}