ic_sqlite_features/
vfs.rs

1use std::io::{self, ErrorKind};
2use std::sync::{Arc, Mutex};
3use std::time::{Duration, Instant};
4use ic_cdk::api::stable::{stable64_read, stable64_size, stable64_write};
5
6use sqlite_vfs::{LockKind, OpenKind, OpenOptions, Vfs};
7use crate::{stable_capacity, stable_grow_bytes};
8
9const SQLITE_SIZE_IN_BYTES: u64 = 8; // 8 byte
10
11#[derive(Default)]
12pub struct PagesVfs {
13    lock_state: Arc<Mutex<LockState>>,
14}
15
16#[derive(Debug, Default)]
17struct LockState {
18    read: usize,
19    write: Option<bool>,
20}
21
22pub struct Connection {
23    lock_state: Arc<Mutex<LockState>>,
24    lock: LockKind,
25}
26
27impl Vfs for PagesVfs {
28    type Handle = Connection;
29
30    fn open(&self, db: &str, opts: OpenOptions) -> Result<Self::Handle, io::Error> {
31        // Always open the same database for now.
32        if db != "main.db" {
33            return Err(io::Error::new(
34                ErrorKind::NotFound,
35                format!("unexpected database name `{}`; expected `main.db`", db),
36            ));
37        }
38        // Only main databases supported right now (no journal, wal, temporary, ...)
39        if opts.kind != OpenKind::MainDb {
40            return Err(io::Error::new(
41                ErrorKind::PermissionDenied,
42                "only main database supported right now (no journal, wal, ...)",
43            ));
44        }
45
46        Ok(Connection {
47            lock_state: self.lock_state.clone(),
48            lock: LockKind::None,
49        })
50    }
51
52    fn delete(&self, _db: &str) -> Result<(), io::Error> {
53        Ok(())
54    }
55
56    fn exists(&self, db: &str) -> Result<bool, io::Error> {
57        Ok(db == "main.db" && Connection::size() > 0)
58    }
59
60    fn temporary_name(&self) -> String {
61        String::from("main.db")
62    }
63
64    fn random(&self, buffer: &mut [i8]) {
65        let mut rng = rusqlite::ffi::Rand::new();
66        rng.fill_i8(buffer);
67    }
68
69    fn sleep(&self, duration: Duration) -> Duration {
70        let now = Instant::now();
71        conn_sleep((duration.as_millis() as u32).max(1));
72        now.elapsed()
73    }
74}
75
76impl sqlite_vfs::DatabaseHandle for Connection {
77    type WalIndex = sqlite_vfs::WalDisabled;
78
79    fn size(&self) -> Result<u64, io::Error> {
80        Ok(Self::size())
81    }
82
83    fn read_exact_at(&mut self, buf: &mut [u8], offset: u64) -> Result<(), io::Error> {
84        if stable64_size() > 0 {
85            stable64_read(offset + SQLITE_SIZE_IN_BYTES, buf);
86        }
87        Ok(())
88    }
89
90    fn write_all_at(&mut self, buf: &[u8], offset: u64) -> Result<(), io::Error> {
91        let size = offset + buf.len() as u64;
92        if size > Self::size() {
93            stable64_write(0, &size.to_be_bytes());
94        }
95        stable64_write(offset + SQLITE_SIZE_IN_BYTES, buf);
96        Ok(())
97    }
98
99    fn sync(&mut self, _data_only: bool) -> Result<(), io::Error> {
100        // Everything is directly written to storage, so no extra steps necessary to sync.
101        Ok(())
102    }
103
104    fn set_len(&mut self, size: u64) -> Result<(), io::Error> {
105        let capacity = if stable64_size() == 0 { 0 } else { stable_capacity() - SQLITE_SIZE_IN_BYTES };
106        if size > capacity {
107            stable_grow_bytes(size - capacity).map_err(|err| {
108                io::Error::new(
109                    ErrorKind::OutOfMemory,
110                    err,
111                )
112            })?;
113            stable64_write(0, &size.to_be_bytes());
114        }
115        Ok(())
116    }
117
118    fn lock(&mut self, lock: LockKind) -> Result<bool, io::Error> {
119        let ok = Self::lock(self, lock);
120        Ok(ok)
121    }
122
123    fn reserved(&mut self) -> Result<bool, io::Error> {
124        Ok(Self::reserved(self))
125    }
126
127    fn current_lock(&self) -> Result<LockKind, io::Error> {
128        Ok(self.lock)
129    }
130
131    fn wal_index(&self, _readonly: bool) -> Result<Self::WalIndex, io::Error> {
132        Ok(sqlite_vfs::WalDisabled::default())
133    }
134}
135
136impl Connection {
137    fn size() -> u64 {
138        if stable64_size() == 0 {
139            return 0;
140        }
141        let mut buf = [0u8; SQLITE_SIZE_IN_BYTES as usize];
142        stable64_read(0, &mut buf);
143        u64::from_be_bytes(buf)
144    }
145
146    fn lock(&mut self, to: LockKind) -> bool {
147        if self.lock == to {
148            return true;
149        }
150
151        let mut lock_state = self.lock_state.lock().unwrap();
152
153        match to {
154            LockKind::None => {
155                if self.lock == LockKind::Shared {
156                    lock_state.read -= 1;
157                } else if self.lock > LockKind::Shared {
158                    lock_state.write = None;
159                }
160                self.lock = LockKind::None;
161                true
162            }
163
164            LockKind::Shared => {
165                if lock_state.write == Some(true) && self.lock <= LockKind::Shared {
166                    return false;
167                }
168
169                lock_state.read += 1;
170                if self.lock > LockKind::Shared {
171                    lock_state.write = None;
172                }
173                self.lock = LockKind::Shared;
174                true
175            }
176
177            LockKind::Reserved => {
178                if lock_state.write.is_some() || self.lock != LockKind::Shared {
179                    return false;
180                }
181
182                if self.lock == LockKind::Shared {
183                    lock_state.read -= 1;
184                }
185                lock_state.write = Some(false);
186                self.lock = LockKind::Reserved;
187                true
188            }
189
190            LockKind::Pending => {
191                // cannot be requested directly
192                false
193            }
194
195            LockKind::Exclusive => {
196                if lock_state.write.is_some() && self.lock <= LockKind::Shared {
197                    return false;
198                }
199
200                if self.lock == LockKind::Shared {
201                    lock_state.read -= 1;
202                }
203
204                lock_state.write = Some(true);
205                if lock_state.read == 0 {
206                    self.lock = LockKind::Exclusive;
207                    true
208                } else {
209                    self.lock = LockKind::Pending;
210                    false
211                }
212            }
213        }
214    }
215
216    fn reserved(&self) -> bool {
217        if self.lock > LockKind::Shared {
218            return true;
219        }
220
221        let lock_state = self.lock_state.lock().unwrap();
222        lock_state.write.is_some()
223    }
224}
225
226impl Drop for Connection {
227    fn drop(&mut self) {
228        if self.lock != LockKind::None {
229            self.lock(LockKind::None);
230        }
231    }
232}
233
234fn conn_sleep(ms: u32) {
235    std::thread::sleep(Duration::from_secs(ms.into()));
236}