1pub mod addresses;
6pub mod credit_cards;
7pub mod models;
8pub mod passports;
9pub mod schema;
10pub mod store;
11
12use crate::error::*;
13
14use error_support::error;
15use interrupt_support::{SqlInterruptHandle, SqlInterruptScope};
16use rusqlite::{Connection, OpenFlags};
17use sql_support::open_database;
18use sql_support::path::normalize_database_path;
19use std::sync::Arc;
20use std::{
21 ops::{Deref, DerefMut},
22 path::{Path, PathBuf},
23};
24
25pub struct AutofillDb {
26 pub writer: Connection,
27 interrupt_handle: Arc<SqlInterruptHandle>,
28}
29
30impl AutofillDb {
31 pub fn new(db_path: impl AsRef<Path>) -> Result<Self> {
32 let db_path = normalize_database_path(db_path)?;
33 Self::new_named(db_path)
34 }
35
36 pub fn new_memory(db_path: &str) -> Result<Self> {
37 let name = PathBuf::from(format!("file:{}?mode=memory&cache=shared", db_path));
38 Self::new_named(name)
39 }
40
41 fn new_named(db_path: PathBuf) -> Result<Self> {
42 let flags = OpenFlags::SQLITE_OPEN_NO_MUTEX
45 | OpenFlags::SQLITE_OPEN_URI
46 | OpenFlags::SQLITE_OPEN_CREATE
47 | OpenFlags::SQLITE_OPEN_READ_WRITE;
48
49 let conn = open_database::open_database_with_flags(
50 db_path,
51 flags,
52 &schema::AutofillConnectionInitializer,
53 )?;
54
55 Ok(Self {
56 interrupt_handle: Arc::new(SqlInterruptHandle::new(&conn)),
57 writer: conn,
58 })
59 }
60
61 #[inline]
62 pub fn begin_interrupt_scope(&self) -> Result<SqlInterruptScope> {
63 Ok(self.interrupt_handle.begin_interrupt_scope()?)
64 }
65
66 pub fn close(self) {
67 if let Err((_, err)) = self.writer.close() {
68 error!("Failed to close the connection: {:?}", err);
70 }
71 }
72}
73
74impl Deref for AutofillDb {
75 type Target = Connection;
76
77 fn deref(&self) -> &Self::Target {
78 &self.writer
79 }
80}
81
82impl DerefMut for AutofillDb {
83 fn deref_mut(&mut self) -> &mut Self::Target {
84 &mut self.writer
85 }
86}
87
88pub(crate) fn with_savepoint<T>(
99 tx: &rusqlite::Transaction<'_>,
100 op: impl FnOnce() -> Result<T>,
101) -> Result<std::result::Result<T, Error>> {
102 tx.execute_batch("SAVEPOINT bulk_record")?;
103 match op() {
104 Ok(value) => {
105 tx.execute_batch("RELEASE bulk_record")?;
106 Ok(Ok(value))
107 }
108 Err(e) => {
109 tx.execute_batch("ROLLBACK TO bulk_record; RELEASE bulk_record")?;
110 Ok(Err(e))
111 }
112 }
113}
114
115pub(crate) fn timestamp_from_millis(millis: i64) -> types::Timestamp {
125 types::Timestamp(types::sanitize_timestamp(millis) as u64)
126}
127
128pub(crate) enum CounterUpdate {
130 Increment,
132 Leave,
135 Set(i64),
137}
138
139impl CounterUpdate {
140 pub(crate) fn as_sql(&self) -> (&'static str, i64) {
145 match self {
146 Self::Increment => ("sync_change_counter + :counter", 1),
147 Self::Leave => ("sync_change_counter + :counter", 0),
148 Self::Set(counter) => (":counter", *counter),
149 }
150 }
151}
152
153pub(crate) mod sql_fns {
154 use rusqlite::{functions::Context, Result};
155 use sync_guid::Guid as SyncGuid;
156 use types::Timestamp;
157
158 #[inline(never)]
159 #[allow(dead_code)]
160 pub fn generate_guid(_ctx: &Context<'_>) -> Result<SyncGuid> {
161 Ok(SyncGuid::random())
162 }
163
164 #[inline(never)]
165 pub fn now(_ctx: &Context<'_>) -> Result<Timestamp> {
166 Ok(Timestamp::now())
167 }
168}
169
170#[cfg(test)]
172pub mod test {
173 use super::*;
174 use std::sync::atomic::{AtomicUsize, Ordering};
175
176 static ATOMIC_COUNTER: AtomicUsize = AtomicUsize::new(0);
178
179 pub fn new_mem_db() -> AutofillDb {
180 error_support::init_for_tests();
181 let counter = ATOMIC_COUNTER.fetch_add(1, Ordering::Relaxed);
182 AutofillDb::new_memory(&format!("test_autofill-api-{}", counter))
183 .expect("should get an API")
184 }
185}