Skip to content

Commit 39c34ef

Browse files
committed
Refactored webext-storage database close function
1 parent 1cb0a99 commit 39c34ef

File tree

10 files changed

+257
-181
lines changed

10 files changed

+257
-181
lines changed

components/webext-storage/src/api.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,9 @@ mod tests {
438438
#[test]
439439
fn test_simple() -> Result<()> {
440440
let ext_id = "x";
441-
let mut db = new_mem_db();
442-
let tx = db.transaction()?;
441+
let db = new_mem_db();
442+
let conn = db.get_connection().expect("should retrieve connection");
443+
let tx = conn.unchecked_transaction()?;
443444

444445
// an empty store.
445446
for q in vec![JsonValue::Null, json!("foo"), json!(["foo"])].into_iter() {
@@ -529,8 +530,9 @@ mod tests {
529530
fn test_check_get_impl() -> Result<()> {
530531
// This is a port of checkGetImpl in test_ext_storage.js in Desktop.
531532
let ext_id = "x";
532-
let mut db = new_mem_db();
533-
let tx = db.transaction()?;
533+
let db = new_mem_db();
534+
let conn = db.get_connection().expect("should retrieve connection");
535+
let tx = conn.unchecked_transaction()?;
534536

535537
let prop = "test-prop";
536538
let value = "test-value";
@@ -584,8 +586,9 @@ mod tests {
584586
fn test_bug_1621162() -> Result<()> {
585587
// apparently Firefox, unlike Chrome, will not optimize the changes.
586588
// See bug 1621162 for more!
587-
let mut db = new_mem_db();
588-
let tx = db.transaction()?;
589+
let db = new_mem_db();
590+
let conn = db.get_connection().expect("should retrieve connection");
591+
let tx = conn.unchecked_transaction()?;
589592
let ext_id = "xyz";
590593

591594
set(&tx, ext_id, json!({"foo": "bar" }))?;
@@ -599,8 +602,9 @@ mod tests {
599602

600603
#[test]
601604
fn test_quota_maxitems() -> Result<()> {
602-
let mut db = new_mem_db();
603-
let tx = db.transaction()?;
605+
let db = new_mem_db();
606+
let conn = db.get_connection().expect("should retrieve connection");
607+
let tx = conn.unchecked_transaction()?;
604608
let ext_id = "xyz";
605609
for i in 1..SYNC_MAX_ITEMS + 1 {
606610
set(
@@ -619,8 +623,9 @@ mod tests {
619623

620624
#[test]
621625
fn test_quota_bytesperitem() -> Result<()> {
622-
let mut db = new_mem_db();
623-
let tx = db.transaction()?;
626+
let db = new_mem_db();
627+
let conn = db.get_connection().expect("should retrieve connection");
628+
let tx = conn.unchecked_transaction()?;
624629
let ext_id = "xyz";
625630
// A string 5 bytes less than the max. This should be counted as being
626631
// 3 bytes less than the max as the quotes are counted. Plus the length
@@ -645,8 +650,9 @@ mod tests {
645650

646651
#[test]
647652
fn test_quota_bytes() -> Result<()> {
648-
let mut db = new_mem_db();
649-
let tx = db.transaction()?;
653+
let db = new_mem_db();
654+
let conn = db.get_connection().expect("should retrieve connection");
655+
let tx = conn.unchecked_transaction()?;
650656
let ext_id = "xyz";
651657
let val = "x".repeat(SYNC_QUOTA_BYTES + 1);
652658

@@ -682,8 +688,9 @@ mod tests {
682688

683689
#[test]
684690
fn test_get_bytes_in_use() -> Result<()> {
685-
let mut db = new_mem_db();
686-
let tx = db.transaction()?;
691+
let db = new_mem_db();
692+
let conn = db.get_connection().expect("should retrieve connection");
693+
let tx = conn.unchecked_transaction()?;
687694
let ext_id = "xyz";
688695

689696
assert_eq!(get_bytes_in_use(&tx, ext_id, json!(null))?, 0);
@@ -714,8 +721,9 @@ mod tests {
714721

715722
#[test]
716723
fn test_usage() {
717-
let mut db = new_mem_db();
718-
let tx = db.transaction().unwrap();
724+
let db = new_mem_db();
725+
let conn = db.get_connection().expect("should retrieve connection");
726+
let tx = conn.unchecked_transaction().unwrap();
719727
// '{"a":"a","b":"bb","c":"ccc","n":999999}': 39 bytes
720728
set(&tx, "xyz", json!({ "a": "a" })).unwrap();
721729
set(&tx, "xyz", json!({ "b": "bb" })).unwrap();
@@ -727,7 +735,7 @@ mod tests {
727735

728736
tx.commit().unwrap();
729737

730-
let usage = usage(&db).unwrap();
738+
let usage = usage(conn).unwrap();
731739
let expect = [
732740
UsageInfo {
733741
ext_id: "abc".to_string(),

components/webext-storage/src/db.rs

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rusqlite::Connection;
1111
use rusqlite::OpenFlags;
1212
use sql_support::open_database::open_database_with_flags;
1313
use sql_support::ConnExt;
14-
use std::ops::{Deref, DerefMut};
14+
use std::ops::Deref;
1515
use std::path::{Path, PathBuf};
1616
use std::sync::Arc;
1717
use url::Url;
@@ -23,10 +23,16 @@ use url::Url;
2323
///
2424
/// We only support a single writer connection - so that's the only thing we
2525
/// store. It's still a bit overkill, but there's only so many yaks in a day.
26+
pub enum WebExtStorageDb {
27+
Open(Connection),
28+
Closed,
29+
}
30+
2631
pub struct StorageDb {
27-
writer: Connection,
32+
pub writer: WebExtStorageDb,
2833
interrupt_handle: Arc<SqlInterruptHandle>,
2934
}
35+
3036
impl StorageDb {
3137
/// Create a new, or fetch an already open, StorageDb backed by a file on disk.
3238
pub fn new(db_path: impl AsRef<Path>) -> Result<Self> {
@@ -54,7 +60,7 @@ impl StorageDb {
5460
let conn = open_database_with_flags(db_path, flags, &schema::WebExtMigrationLogin)?;
5561
Ok(Self {
5662
interrupt_handle: Arc::new(SqlInterruptHandle::new(&conn)),
57-
writer: conn,
63+
writer: WebExtStorageDb::Open(conn),
5864
})
5965
}
6066

@@ -73,29 +79,20 @@ impl StorageDb {
7379
/// underlying connection so the caller can retry but (a) that's very tricky
7480
/// in an Arc<Mutex<>> world and (b) we never actually took advantage of
7581
/// that retry capability.
76-
pub fn close(self) -> Result<()> {
77-
self.writer.close().map_err(|(writer, err)| {
78-
// In rusqlite 0.28.0 and earlier, if we just let `writer` drop,
79-
// the close would panic on failure.
80-
// Later rusqlite versions will not panic, but this behavior doesn't
81-
// hurt there.
82-
std::mem::forget(writer);
83-
err.into()
84-
})
85-
}
86-
}
87-
88-
impl Deref for StorageDb {
89-
type Target = Connection;
90-
91-
fn deref(&self) -> &Self::Target {
92-
&self.writer
82+
pub fn close(&mut self) -> Result<()> {
83+
let conn = match std::mem::replace(&mut self.writer, WebExtStorageDb::Closed) {
84+
WebExtStorageDb::Open(conn) => conn,
85+
WebExtStorageDb::Closed => return Ok(()),
86+
};
87+
conn.close().map_err(|(_, y)| Error::SqlError(y))
9388
}
94-
}
9589

96-
impl DerefMut for StorageDb {
97-
fn deref_mut(&mut self) -> &mut Self::Target {
98-
&mut self.writer
90+
pub(crate) fn get_connection(&self) -> Result<&Connection> {
91+
let db = &self.writer;
92+
match db {
93+
WebExtStorageDb::Open(y) => Ok(y),
94+
WebExtStorageDb::Closed => Err(Error::DatabaseConnectionClosed),
95+
}
9996
}
10097
}
10198

@@ -290,12 +287,13 @@ mod tests {
290287

291288
#[test]
292289
fn test_meta() -> Result<()> {
293-
let writer = new_mem_db();
294-
assert_eq!(get_meta::<String>(&writer, "foo")?, None);
295-
put_meta(&writer, "foo", &"bar".to_string())?;
296-
assert_eq!(get_meta(&writer, "foo")?, Some("bar".to_string()));
297-
delete_meta(&writer, "foo")?;
298-
assert_eq!(get_meta::<String>(&writer, "foo")?, None);
290+
let db = new_mem_db();
291+
let conn = &db.get_connection()?;
292+
assert_eq!(get_meta::<String>(conn, "foo")?, None);
293+
put_meta(conn, "foo", &"bar".to_string())?;
294+
assert_eq!(get_meta(conn, "foo")?, Some("bar".to_string()));
295+
delete_meta(conn, "foo")?;
296+
assert_eq!(get_meta::<String>(conn, "foo")?, None);
299297
Ok(())
300298
}
301299
}

components/webext-storage/src/migration.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,9 @@ mod tests {
346346
init_source_db(path, f);
347347

348348
// now migrate
349-
let mut db = new_mem_db();
350-
let tx = db.transaction().expect("tx should work");
349+
let db = new_mem_db();
350+
let conn = db.get_connection().expect("should retrieve connection");
351+
let tx = conn.unchecked_transaction().expect("tx should work");
351352

352353
let mi = migrate(&tx, &tmpdir.path().join("source.db")).expect("migrate should work");
353354
tx.commit().expect("should work");
@@ -384,17 +385,18 @@ mod tests {
384385
#[test]
385386
fn test_happy_paths() {
386387
// some real data.
387-
let conn = do_migrate(HAPPY_PATH_MIGRATION_INFO, |c| {
388+
let db = do_migrate(HAPPY_PATH_MIGRATION_INFO, |c| {
388389
c.execute_batch(HAPPY_PATH_SQL).expect("should populate")
389390
});
391+
let conn = db.get_connection().expect("should retrieve connection");
390392

391393
assert_has(
392-
&conn,
394+
conn,
393395
"{e7fefcf3-b39c-4f17-5215-ebfe120a7031}",
394396
json!({"userWelcomed": 1570659224457u64, "isWho": "4ec8109f"}),
395397
);
396398
assert_has(
397-
&conn,
399+
conn,
398400
399401
json!({"userRules": [], "ruleActiveStates": {}, "migration_version": 2}),
400402
);

components/webext-storage/src/schema.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,22 +94,24 @@ mod tests {
9494
#[test]
9595
fn test_create_schema_twice() {
9696
let db = new_mem_db();
97-
db.execute_batch(CREATE_SCHEMA_SQL)
97+
let conn = db.get_connection().expect("should retrieve connection");
98+
conn.execute_batch(CREATE_SCHEMA_SQL)
9899
.expect("should allow running twice");
99100
}
100101

101102
#[test]
102103
fn test_create_empty_sync_temp_tables_twice() {
103104
let db = new_mem_db();
104-
create_empty_sync_temp_tables(&db).expect("should work first time");
105+
let conn = db.get_connection().expect("should retrieve connection");
106+
create_empty_sync_temp_tables(conn).expect("should work first time");
105107
// insert something into our new temp table and check it's there.
106-
db.execute_batch(
108+
conn.execute_batch(
107109
"INSERT INTO temp.storage_sync_staging
108110
(guid, ext_id) VALUES
109111
('guid', 'ext_id');",
110112
)
111113
.expect("should work once");
112-
let count = db
114+
let count = conn
113115
.query_row_and_then(
114116
"SELECT COUNT(*) FROM temp.storage_sync_staging;",
115117
[],
@@ -119,9 +121,9 @@ mod tests {
119121
assert_eq!(count, 1, "should be one row");
120122

121123
// re-execute
122-
create_empty_sync_temp_tables(&db).expect("should second first time");
124+
create_empty_sync_temp_tables(conn).expect("should second first time");
123125
// and it should have deleted existing data.
124-
let count = db
126+
let count = conn
125127
.query_row_and_then(
126128
"SELECT COUNT(*) FROM temp.storage_sync_staging;",
127129
[],

components/webext-storage/src/store.rs

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,19 @@ impl WebExtStorageStore {
5959
/// Sets one or more JSON key-value pairs for an extension ID. Returns a
6060
/// list of changes, with existing and new values for each key in `val`.
6161
pub fn set(&self, ext_id: &str, val: JsonValue) -> Result<StorageChanges> {
62-
let db = self.db.lock();
63-
let tx = db.unchecked_transaction()?;
62+
let db = &self.db.lock();
63+
let conn = db.get_connection()?;
64+
let tx = conn.unchecked_transaction()?;
6465
let result = api::set(&tx, ext_id, val)?;
6566
tx.commit()?;
6667
Ok(result)
6768
}
6869

6970
/// Returns information about per-extension usage
7071
pub fn usage(&self) -> Result<Vec<crate::UsageInfo>> {
71-
let db = self.db.lock();
72-
api::usage(&db)
72+
let db = &self.db.lock();
73+
let conn = db.get_connection()?;
74+
api::usage(conn)
7375
}
7476

7577
/// Returns the values for one or more keys `keys` can be:
@@ -90,17 +92,19 @@ impl WebExtStorageStore {
9092
/// `serde_json::Value::Object`).
9193
pub fn get(&self, ext_id: &str, keys: JsonValue) -> Result<JsonValue> {
9294
// Don't care about transactions here.
93-
let db = self.db.lock();
94-
api::get(&db, ext_id, keys)
95+
let db = &self.db.lock();
96+
let conn = db.get_connection()?;
97+
api::get(conn, ext_id, keys)
9598
}
9699

97100
/// Deletes the values for one or more keys. As with `get`, `keys` can be
98101
/// either a single string key, or an array of string keys. Returns a list
99102
/// of changes, where each change contains the old value for each deleted
100103
/// key.
101104
pub fn remove(&self, ext_id: &str, keys: JsonValue) -> Result<StorageChanges> {
102-
let db = self.db.lock();
103-
let tx = db.unchecked_transaction()?;
105+
let db = &self.db.lock();
106+
let conn = db.get_connection()?;
107+
let tx = conn.unchecked_transaction()?;
104108
let result = api::remove(&tx, ext_id, keys)?;
105109
tx.commit()?;
106110
Ok(result)
@@ -110,8 +114,9 @@ impl WebExtStorageStore {
110114
/// a list of changes, where each change contains the old value for each
111115
/// deleted key.
112116
pub fn clear(&self, ext_id: &str) -> Result<StorageChanges> {
113-
let db = self.db.lock();
114-
let tx = db.unchecked_transaction()?;
117+
let db = &self.db.lock();
118+
let conn = db.get_connection()?;
119+
let tx = conn.unchecked_transaction()?;
115120
let result = api::clear(&tx, ext_id)?;
116121
tx.commit()?;
117122
Ok(result)
@@ -120,8 +125,9 @@ impl WebExtStorageStore {
120125
/// Returns the bytes in use for the specified items (which can be null,
121126
/// a string, or an array)
122127
pub fn get_bytes_in_use(&self, ext_id: &str, keys: JsonValue) -> Result<usize> {
123-
let db = self.db.lock();
124-
api::get_bytes_in_use(&db, ext_id, keys)
128+
let db = &self.db.lock();
129+
let conn = db.get_connection()?;
130+
api::get_bytes_in_use(conn, ext_id, keys)
125131
}
126132

127133
/// Returns a bridged sync engine for Desktop for this store.
@@ -135,8 +141,8 @@ impl WebExtStorageStore {
135141
// Even though this consumes `self`, the fact we use an Arc<> means
136142
// we can't guarantee we can actually consume the inner DB - so do
137143
// the best we can.
138-
let shared: ThreadSafeStorageDb = match Arc::try_unwrap(self.db) {
139-
Ok(shared) => shared,
144+
let shared: ThreadSafeStorageDb = match Arc::into_inner(self.db) {
145+
Some(shared) => shared,
140146
_ => {
141147
// The only way this is possible is if the sync engine has an operation
142148
// running - but that shouldn't be possible in practice because desktop
@@ -157,7 +163,7 @@ impl WebExtStorageStore {
157163
}
158164
};
159165
// consume the mutex and get back the inner.
160-
let db = shared.into_inner();
166+
let mut db = shared.into_inner();
161167
db.close()
162168
}
163169

@@ -177,12 +183,13 @@ impl WebExtStorageStore {
177183
///
178184
/// Note that `filename` isn't normalized or canonicalized.
179185
pub fn migrate(&self, filename: impl AsRef<Path>) -> Result<()> {
180-
let db = self.db.lock();
181-
let tx = db.unchecked_transaction()?;
186+
let db = &self.db.lock();
187+
let conn = db.get_connection()?;
188+
let tx = conn.unchecked_transaction()?;
182189
let result = migrate(&tx, filename.as_ref())?;
183190
tx.commit()?;
184191
// Failing to store this information should not cause migration failure.
185-
if let Err(e) = result.store(&db) {
192+
if let Err(e) = result.store(conn) {
186193
debug_assert!(false, "Migration error: {:?}", e);
187194
log::warn!("Failed to record migration telmetry: {}", e);
188195
}
@@ -192,8 +199,9 @@ impl WebExtStorageStore {
192199
/// Read-and-delete (e.g. `take` in rust parlance, see Option::take)
193200
/// operation for any MigrationInfo stored in this database.
194201
pub fn take_migration_info(&self) -> Result<Option<MigrationInfo>> {
195-
let db = self.db.lock();
196-
let tx = db.unchecked_transaction()?;
202+
let db = &self.db.lock();
203+
let conn = db.get_connection()?;
204+
let tx = conn.unchecked_transaction()?;
197205
let result = MigrationInfo::take(&tx)?;
198206
tx.commit()?;
199207
Ok(result)

0 commit comments

Comments
 (0)