ic_sqlite_features/
vfs.rs1use 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; #[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 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 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 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 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}