deno_node_sqlite/
session.rs1use std::cell::Cell;
4use std::cell::RefCell;
5use std::ffi::c_void;
6use std::rc::Rc;
7
8use deno_core::FromV8;
9use deno_core::GarbageCollected;
10use deno_core::op2;
11use deno_core::v8;
12use deno_core::v8_static_strings;
13use rusqlite::ffi;
14
15use super::SqliteError;
16use super::validators;
17
18#[derive(Default)]
19pub struct SessionOptions {
20 pub table: Option<String>,
21 pub db: Option<String>,
22}
23
24impl FromV8<'_> for SessionOptions {
25 type Error = validators::Error;
26
27 fn from_v8(
28 scope: &mut v8::PinScope<'_, '_>,
29 value: v8::Local<v8::Value>,
30 ) -> Result<Self, validators::Error> {
31 use validators::Error;
32
33 if value.is_undefined() {
34 return Ok(SessionOptions::default());
35 }
36
37 let obj = v8::Local::<v8::Object>::try_from(value).map_err(|_| {
38 Error::InvalidArgType(
39 "The \"options\" argument must be an object.".into(),
40 )
41 })?;
42
43 let mut options = SessionOptions::default();
44
45 v8_static_strings! {
46 TABLE_STRING = "table",
47 DB_STRING = "db",
48 }
49
50 let table_string = TABLE_STRING.v8_string(scope).unwrap();
51 if let Some(table_value) = obj.get(scope, table_string.into())
52 && !table_value.is_undefined()
53 {
54 if !table_value.is_string() {
55 return Err(Error::InvalidArgType(
56 "The \"options.table\" argument must be a string.".into(),
57 ));
58 }
59 let table =
60 v8::Local::<v8::String>::try_from(table_value).map_err(|_| {
61 Error::InvalidArgType(
62 "The \"options.table\" argument must be a string.".into(),
63 )
64 })?;
65 options.table = Some(table.to_rust_string_lossy(scope).to_string());
66 }
67
68 let db_string = DB_STRING.v8_string(scope).unwrap();
69 if let Some(db_value) = obj.get(scope, db_string.into())
70 && !db_value.is_undefined()
71 {
72 if !db_value.is_string() {
73 return Err(Error::InvalidArgType(
74 "The \"options.db\" argument must be a string.".into(),
75 ));
76 }
77 let db = v8::Local::<v8::String>::try_from(db_value).map_err(|_| {
78 Error::InvalidArgType(
79 "The \"options.db\" argument must be a string.".into(),
80 )
81 })?;
82 options.db = Some(db.to_rust_string_lossy(scope).to_string());
83 }
84
85 Ok(options)
86 }
87}
88
89pub struct Session {
90 pub(crate) inner: *mut ffi::sqlite3_session,
91 pub(crate) freed: Cell<bool>,
92
93 pub(crate) db: Rc<RefCell<Option<rusqlite::Connection>>>,
95}
96
97unsafe impl GarbageCollected for Session {
99 fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
100
101 fn get_name(&self) -> &'static std::ffi::CStr {
102 c"Session"
103 }
104}
105
106impl Drop for Session {
107 fn drop(&mut self) {
108 let _ = self.delete();
109 }
110}
111
112impl Session {
113 fn delete(&self) -> Result<(), SqliteError> {
114 if self.freed.get() {
115 return Err(SqliteError::SessionClosed);
116 }
117
118 self.freed.set(true);
119 if self.db.borrow().is_none() {
120 return Ok(());
121 }
122 unsafe {
125 ffi::sqlite3session_delete(self.inner);
126 }
127
128 Ok(())
129 }
130}
131
132#[op2]
133impl Session {
134 #[constructor]
135 #[cppgc]
136 fn create(_: bool) -> Session {
137 unreachable!()
138 }
139
140 #[fast]
142 #[undefined]
143 fn close(&self) -> Result<(), SqliteError> {
144 if self.db.borrow().is_none() {
145 return Err(SqliteError::AlreadyClosed);
146 }
147
148 self.delete()
149 }
150
151 #[buffer]
156 fn changeset(&self) -> Result<Box<[u8]>, SqliteError> {
157 if self.db.borrow().is_none() {
158 return Err(SqliteError::AlreadyClosed);
159 }
160 if self.freed.get() {
161 return Err(SqliteError::SessionClosed);
162 }
163
164 session_buffer_op(self.inner, ffi::sqlite3session_changeset)
165 }
166
167 #[buffer]
171 fn patchset(&self) -> Result<Box<[u8]>, SqliteError> {
172 if self.db.borrow().is_none() {
173 return Err(SqliteError::AlreadyClosed);
174 }
175 if self.freed.get() {
176 return Err(SqliteError::SessionClosed);
177 }
178
179 session_buffer_op(self.inner, ffi::sqlite3session_patchset)
180 }
181}
182
183fn session_buffer_op(
184 s: *mut ffi::sqlite3_session,
185 f: unsafe extern "C" fn(
186 *mut ffi::sqlite3_session,
187 *mut i32,
188 *mut *mut c_void,
189 ) -> i32,
190) -> Result<Box<[u8]>, SqliteError> {
191 let mut n_buffer = 0;
192 let mut p_buffer = std::ptr::null_mut();
193
194 let r = unsafe { f(s, &mut n_buffer, &mut p_buffer) };
197 if r != ffi::SQLITE_OK {
198 return Err(SqliteError::SessionChangesetFailed);
199 }
200
201 if n_buffer == 0 {
202 return Ok(Default::default());
203 }
204
205 let buffer = unsafe {
207 std::slice::from_raw_parts(p_buffer as *const u8, n_buffer as usize)
208 }
209 .to_vec()
210 .into_boxed_slice();
211
212 unsafe {
214 ffi::sqlite3_free(p_buffer);
215 }
216
217 Ok(buffer)
218}