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("The \"options\" argument must be an object.")
39 })?;
40
41 let mut options = SessionOptions::default();
42
43 v8_static_strings! {
44 TABLE_STRING = "table",
45 DB_STRING = "db",
46 }
47
48 let table_string = TABLE_STRING.v8_string(scope).unwrap();
49 if let Some(table_value) = obj.get(scope, table_string.into())
50 && !table_value.is_undefined()
51 {
52 if !table_value.is_string() {
53 return Err(Error::InvalidArgType(
54 "The \"options.table\" argument must be a string.",
55 ));
56 }
57 let table =
58 v8::Local::<v8::String>::try_from(table_value).map_err(|_| {
59 Error::InvalidArgType(
60 "The \"options.table\" argument must be a string.",
61 )
62 })?;
63 options.table = Some(table.to_rust_string_lossy(scope).to_string());
64 }
65
66 let db_string = DB_STRING.v8_string(scope).unwrap();
67 if let Some(db_value) = obj.get(scope, db_string.into())
68 && !db_value.is_undefined()
69 {
70 if !db_value.is_string() {
71 return Err(Error::InvalidArgType(
72 "The \"options.db\" argument must be a string.",
73 ));
74 }
75 let db = v8::Local::<v8::String>::try_from(db_value).map_err(|_| {
76 Error::InvalidArgType("The \"options.db\" argument must be a string.")
77 })?;
78 options.db = Some(db.to_rust_string_lossy(scope).to_string());
79 }
80
81 Ok(options)
82 }
83}
84
85pub struct Session {
86 pub(crate) inner: *mut ffi::sqlite3_session,
87 pub(crate) freed: Cell<bool>,
88
89 pub(crate) db: Rc<RefCell<Option<rusqlite::Connection>>>,
91}
92
93unsafe impl GarbageCollected for Session {
95 fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
96
97 fn get_name(&self) -> &'static std::ffi::CStr {
98 c"Session"
99 }
100}
101
102impl Drop for Session {
103 fn drop(&mut self) {
104 let _ = self.delete();
105 }
106}
107
108impl Session {
109 fn delete(&self) -> Result<(), SqliteError> {
110 if self.freed.get() {
111 return Err(SqliteError::SessionClosed);
112 }
113
114 self.freed.set(true);
115 if self.db.borrow().is_none() {
116 return Ok(());
117 }
118 unsafe {
121 ffi::sqlite3session_delete(self.inner);
122 }
123
124 Ok(())
125 }
126}
127
128#[op2]
129impl Session {
130 #[constructor]
131 #[cppgc]
132 fn create(_: bool) -> Session {
133 unreachable!()
134 }
135
136 #[fast]
138 #[undefined]
139 fn close(&self) -> Result<(), SqliteError> {
140 if self.db.borrow().is_none() {
141 return Err(SqliteError::AlreadyClosed);
142 }
143
144 self.delete()
145 }
146
147 #[buffer]
152 fn changeset(&self) -> Result<Box<[u8]>, SqliteError> {
153 if self.db.borrow().is_none() {
154 return Err(SqliteError::AlreadyClosed);
155 }
156 if self.freed.get() {
157 return Err(SqliteError::SessionClosed);
158 }
159
160 session_buffer_op(self.inner, ffi::sqlite3session_changeset)
161 }
162
163 #[buffer]
167 fn patchset(&self) -> Result<Box<[u8]>, SqliteError> {
168 if self.db.borrow().is_none() {
169 return Err(SqliteError::AlreadyClosed);
170 }
171 if self.freed.get() {
172 return Err(SqliteError::SessionClosed);
173 }
174
175 session_buffer_op(self.inner, ffi::sqlite3session_patchset)
176 }
177}
178
179fn session_buffer_op(
180 s: *mut ffi::sqlite3_session,
181 f: unsafe extern "C" fn(
182 *mut ffi::sqlite3_session,
183 *mut i32,
184 *mut *mut c_void,
185 ) -> i32,
186) -> Result<Box<[u8]>, SqliteError> {
187 let mut n_buffer = 0;
188 let mut p_buffer = std::ptr::null_mut();
189
190 let r = unsafe { f(s, &mut n_buffer, &mut p_buffer) };
193 if r != ffi::SQLITE_OK {
194 return Err(SqliteError::SessionChangesetFailed);
195 }
196
197 if n_buffer == 0 {
198 return Ok(Default::default());
199 }
200
201 let buffer = unsafe {
203 std::slice::from_raw_parts(p_buffer as *const u8, n_buffer as usize)
204 }
205 .to_vec()
206 .into_boxed_slice();
207
208 unsafe {
210 ffi::sqlite3_free(p_buffer);
211 }
212
213 Ok(buffer)
214}