• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / edge-runtime / 16259269683

14 Jul 2025 06:04AM UTC coverage: 51.235% (-0.5%) from 51.764%
16259269683

push

github

web-flow
feat: add v2.2.0 `node:sqlite` support (#557)

* chore: add dependencies

* chore: update `Cargo.lock`

* feat: v2.2.0 `node:sqlite` support

* stamp: perm check

0 of 362 new or added lines in 2 files covered. (0.0%)

3 existing lines in 3 files now uncovered.

18235 of 35591 relevant lines covered (51.23%)

5490.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/ext/node/ops/sqlite/statement.rs
1
// Copyright 2018-2025 the Deno authors. MIT license.
2

3
use std::cell::Cell;
4
use std::cell::RefCell;
5
use std::rc::Rc;
6

7
use deno_core::anyhow;
8
use deno_core::anyhow::anyhow;
9
use deno_core::op2;
10
use deno_core::v8;
11
use deno_core::GarbageCollected;
12
use libsqlite3_sys as ffi;
13
use serde::Serialize;
14

15
// use super::SqliteError;
16

17
#[derive(Serialize)]
18
#[serde(rename_all = "camelCase")]
19
pub struct RunStatementResult {
20
  last_insert_rowid: i64,
21
  changes: u64,
22
}
23

24
pub struct StatementSync {
25
  pub inner: *mut ffi::sqlite3_stmt,
26
  pub db: Rc<RefCell<Option<rusqlite::Connection>>>,
27

28
  pub use_big_ints: Cell<bool>,
29
}
30

31
impl Drop for StatementSync {
NEW
32
  fn drop(&mut self) {
×
NEW
33
    // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
34
    // no other references to this pointer exist.
×
NEW
35
    unsafe {
×
NEW
36
      ffi::sqlite3_finalize(self.inner);
×
NEW
37
    }
×
NEW
38
  }
×
39
}
40

41
struct ColumnIterator<'a> {
42
  stmt: &'a StatementSync,
43
  index: i32,
44
  count: i32,
45
}
46

47
impl<'a> ColumnIterator<'a> {
NEW
48
  fn new(stmt: &'a StatementSync) -> Self {
×
NEW
49
    let count = stmt.column_count();
×
NEW
50
    ColumnIterator {
×
NEW
51
      stmt,
×
NEW
52
      index: 0,
×
NEW
53
      count,
×
NEW
54
    }
×
NEW
55
  }
×
56

NEW
57
  fn column_count(&self) -> usize {
×
NEW
58
    self.count as usize
×
NEW
59
  }
×
60
}
61

62
impl<'a> Iterator for ColumnIterator<'a> {
63
  type Item = (i32, &'a [u8]);
64

NEW
65
  fn next(&mut self) -> Option<Self::Item> {
×
NEW
66
    if self.index >= self.count {
×
NEW
67
      return None;
×
NEW
68
    }
×
NEW
69

×
NEW
70
    let index = self.index;
×
NEW
71
    let name = self.stmt.column_name(self.index);
×
NEW
72

×
NEW
73
    self.index += 1;
×
NEW
74
    Some((index, name))
×
NEW
75
  }
×
76
}
77

78
impl GarbageCollected for StatementSync {}
79

80
impl StatementSync {
81
  // Clear the prepared statement back to its initial state.
NEW
82
  fn reset(&self) {
×
NEW
83
    // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
84
    // as it lives as long as the StatementSync instance.
×
NEW
85
    unsafe {
×
NEW
86
      ffi::sqlite3_reset(self.inner);
×
NEW
87
    }
×
NEW
88
  }
×
89

90
  // Evaluate the prepared statement.
NEW
91
  fn step(&self) -> Result<bool, anyhow::Error> {
×
NEW
92
    let raw = self.inner;
×
NEW
93
    // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
94
    // as it lives as long as the StatementSync instance.
×
NEW
95
    unsafe {
×
NEW
96
      let r = ffi::sqlite3_step(raw);
×
NEW
97
      if r == ffi::SQLITE_DONE {
×
NEW
98
        return Ok(true);
×
NEW
99
      }
×
NEW
100
      if r != ffi::SQLITE_ROW {
×
NEW
101
        return Err(anyhow!("Failed to step statement"));
×
NEW
102
      }
×
NEW
103
    }
×
NEW
104

×
NEW
105
    Ok(false)
×
NEW
106
  }
×
107

NEW
108
  fn column_count(&self) -> i32 {
×
NEW
109
    // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
110
    // as it lives as long as the StatementSync instance.
×
NEW
111
    unsafe { ffi::sqlite3_column_count(self.inner) }
×
NEW
112
  }
×
113

NEW
114
  fn column_name(&self, index: i32) -> &[u8] {
×
NEW
115
    // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
116
    // as it lives as long as the StatementSync instance.
×
NEW
117
    unsafe {
×
NEW
118
      let name = ffi::sqlite3_column_name(self.inner, index);
×
NEW
119
      std::ffi::CStr::from_ptr(name as _).to_bytes()
×
NEW
120
    }
×
NEW
121
  }
×
122

NEW
123
  fn column_value<'a>(
×
NEW
124
    &self,
×
NEW
125
    index: i32,
×
NEW
126
    scope: &mut v8::HandleScope<'a>,
×
NEW
127
  ) -> v8::Local<'a, v8::Value> {
×
NEW
128
    // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
129
    // as it lives as long as the StatementSync instance.
×
NEW
130
    unsafe {
×
NEW
131
      match ffi::sqlite3_column_type(self.inner, index) {
×
132
        ffi::SQLITE_INTEGER => {
NEW
133
          let value = ffi::sqlite3_column_int64(self.inner, index);
×
NEW
134
          if self.use_big_ints.get() {
×
NEW
135
            v8::BigInt::new_from_i64(scope, value).into()
×
136
          } else {
NEW
137
            v8::Integer::new(scope, value as _).into()
×
138
          }
139
        }
140
        ffi::SQLITE_FLOAT => {
NEW
141
          let value = ffi::sqlite3_column_double(self.inner, index);
×
NEW
142
          v8::Number::new(scope, value).into()
×
143
        }
144
        ffi::SQLITE_TEXT => {
NEW
145
          let value = ffi::sqlite3_column_text(self.inner, index);
×
NEW
146
          let value = std::ffi::CStr::from_ptr(value as _);
×
NEW
147
          v8::String::new_from_utf8(
×
NEW
148
            scope,
×
NEW
149
            value.to_bytes(),
×
NEW
150
            v8::NewStringType::Normal,
×
NEW
151
          )
×
NEW
152
          .unwrap()
×
NEW
153
          .into()
×
154
        }
155
        ffi::SQLITE_BLOB => {
NEW
156
          let value = ffi::sqlite3_column_blob(self.inner, index);
×
NEW
157
          let size = ffi::sqlite3_column_bytes(self.inner, index);
×
NEW
158
          let value =
×
NEW
159
            std::slice::from_raw_parts(value as *const u8, size as usize);
×
NEW
160
          let value =
×
NEW
161
            v8::ArrayBuffer::new_backing_store_from_vec(value.to_vec())
×
NEW
162
              .make_shared();
×
NEW
163
          v8::ArrayBuffer::with_backing_store(scope, &value).into()
×
164
        }
NEW
165
        ffi::SQLITE_NULL => v8::null(scope).into(),
×
NEW
166
        _ => v8::undefined(scope).into(),
×
167
      }
168
    }
NEW
169
  }
×
170

171
  // Read the current row of the prepared statement.
NEW
172
  fn read_row<'a>(
×
NEW
173
    &self,
×
NEW
174
    scope: &mut v8::HandleScope<'a>,
×
NEW
175
  ) -> Result<Option<v8::Local<'a, v8::Object>>, anyhow::Error> {
×
NEW
176
    if self.step()? {
×
NEW
177
      return Ok(None);
×
NEW
178
    }
×
NEW
179

×
NEW
180
    let iter = ColumnIterator::new(self);
×
NEW
181

×
NEW
182
    let num_cols = iter.column_count();
×
NEW
183

×
NEW
184
    let mut names = Vec::with_capacity(num_cols);
×
NEW
185
    let mut values = Vec::with_capacity(num_cols);
×
186

NEW
187
    for (index, name) in iter {
×
NEW
188
      let value = self.column_value(index, scope);
×
NEW
189
      let name =
×
NEW
190
        v8::String::new_from_utf8(scope, name, v8::NewStringType::Normal)
×
NEW
191
          .unwrap()
×
NEW
192
          .into();
×
NEW
193

×
NEW
194
      names.push(name);
×
NEW
195
      values.push(value);
×
NEW
196
    }
×
197

NEW
198
    let null = v8::null(scope).into();
×
NEW
199
    let result =
×
NEW
200
      v8::Object::with_prototype_and_properties(scope, null, &names, &values);
×
NEW
201

×
NEW
202
    Ok(Some(result))
×
NEW
203
  }
×
204

205
  // Bind the parameters to the prepared statement.
NEW
206
  fn bind_params(
×
NEW
207
    &self,
×
NEW
208
    scope: &mut v8::HandleScope,
×
NEW
209
    params: Option<&v8::FunctionCallbackArguments>,
×
NEW
210
  ) -> Result<(), anyhow::Error> {
×
NEW
211
    let raw = self.inner;
×
212

NEW
213
    if let Some(params) = params {
×
NEW
214
      let len = params.length();
×
NEW
215
      for i in 0..len {
×
NEW
216
        let value = params.get(i);
×
NEW
217

×
NEW
218
        if value.is_number() {
×
NEW
219
          let value = value.number_value(scope).unwrap();
×
NEW
220

×
NEW
221
          // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
222
          // as it lives as long as the StatementSync instance.
×
NEW
223
          unsafe {
×
NEW
224
            ffi::sqlite3_bind_double(raw, i + 1, value);
×
NEW
225
          }
×
NEW
226
        } else if value.is_string() {
×
NEW
227
          let value = value.to_rust_string_lossy(scope);
×
NEW
228

×
NEW
229
          // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
230
          // as it lives as long as the StatementSync instance.
×
NEW
231
          //
×
NEW
232
          // SQLITE_TRANSIENT is used to indicate that SQLite should make a copy of the data.
×
NEW
233
          unsafe {
×
NEW
234
            ffi::sqlite3_bind_text(
×
NEW
235
              raw,
×
NEW
236
              i + 1,
×
NEW
237
              value.as_ptr() as *const _,
×
NEW
238
              value.len() as i32,
×
NEW
239
              ffi::SQLITE_TRANSIENT(),
×
NEW
240
            );
×
NEW
241
          }
×
NEW
242
        } else if value.is_null() {
×
243
          // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
244
          // as it lives as long as the StatementSync instance.
NEW
245
          unsafe {
×
NEW
246
            ffi::sqlite3_bind_null(raw, i + 1);
×
NEW
247
          }
×
NEW
248
        } else if value.is_array_buffer_view() {
×
NEW
249
          let value: v8::Local<v8::ArrayBufferView> = value.try_into().unwrap();
×
NEW
250
          let data = value.data();
×
NEW
251
          let size = value.byte_length();
×
NEW
252

×
NEW
253
          // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
×
NEW
254
          // as it lives as long as the StatementSync instance.
×
NEW
255
          //
×
NEW
256
          // SQLITE_TRANSIENT is used to indicate that SQLite should make a copy of the data.
×
NEW
257
          unsafe {
×
NEW
258
            ffi::sqlite3_bind_blob(
×
NEW
259
              raw,
×
NEW
260
              i + 1,
×
NEW
261
              data,
×
NEW
262
              size as i32,
×
NEW
263
              ffi::SQLITE_TRANSIENT(),
×
NEW
264
            );
×
NEW
265
          }
×
266
        } else {
NEW
267
          return Err(anyhow!(
×
NEW
268
            "Failed to bind parameter {}",
×
NEW
269
            "Unsupported Type"
×
NEW
270
          ));
×
271
        }
272
      }
NEW
273
    }
×
274

NEW
275
    Ok(())
×
NEW
276
  }
×
277
}
278

279
// Represents a single prepared statement. Cannot be initialized directly via constructor.
280
// Instances are created using `DatabaseSync#prepare`.
281
//
282
// A prepared statement is an efficient binary representation of the SQL used to create it.
NEW
283
#[op2]
×
284
impl StatementSync {
285
  #[constructor]
286
  #[cppgc]
NEW
287
  fn new(_: bool) -> Result<StatementSync, anyhow::Error> {
×
NEW
288
    Err(anyhow!("Invalid constructor"))
×
NEW
289
  }
×
290

291
  // Executes a prepared statement and returns the first result as an object.
292
  //
293
  // The prepared statement does not return any results, this method returns undefined.
294
  // Optionally, parameters can be bound to the prepared statement.
NEW
295
  fn get<'a>(
×
NEW
296
    &self,
×
NEW
297
    scope: &mut v8::HandleScope<'a>,
×
NEW
298
    #[varargs] params: Option<&v8::FunctionCallbackArguments>,
×
NEW
299
  ) -> Result<v8::Local<'a, v8::Value>, anyhow::Error> {
×
NEW
300
    self.reset();
×
NEW
301

×
NEW
302
    self.bind_params(scope, params)?;
×
303

NEW
304
    let entry = self.read_row(scope)?;
×
NEW
305
    let result = entry
×
NEW
306
      .map(|r| r.into())
×
NEW
307
      .unwrap_or_else(|| v8::undefined(scope).into());
×
NEW
308

×
NEW
309
    Ok(result)
×
NEW
310
  }
×
311

312
  // Executes a prepared statement and returns an object summarizing the resulting
313
  // changes.
314
  //
315
  // Optionally, parameters can be bound to the prepared statement.
316
  #[serde]
NEW
317
  fn run(
×
NEW
318
    &self,
×
NEW
319
    scope: &mut v8::HandleScope,
×
NEW
320
    #[varargs] params: Option<&v8::FunctionCallbackArguments>,
×
NEW
321
  ) -> Result<RunStatementResult, anyhow::Error> {
×
NEW
322
    let db = self.db.borrow();
×
NEW
323
    let db = db.as_ref().ok_or(anyhow!("Database is already in use"))?;
×
324

NEW
325
    self.bind_params(scope, params)?;
×
NEW
326
    self.step()?;
×
327

NEW
328
    self.reset();
×
NEW
329

×
NEW
330
    Ok(RunStatementResult {
×
NEW
331
      last_insert_rowid: db.last_insert_rowid(),
×
NEW
332
      changes: db.changes(),
×
NEW
333
    })
×
NEW
334
  }
×
335

336
  // Executes a prepared statement and returns all results as an array of objects.
337
  //
338
  // If the prepared statement does not return any results, this method returns an empty array.
339
  // Optionally, parameters can be bound to the prepared statement.
NEW
340
  fn all<'a>(
×
NEW
341
    &self,
×
NEW
342
    scope: &mut v8::HandleScope<'a>,
×
NEW
343
    #[varargs] params: Option<&v8::FunctionCallbackArguments>,
×
NEW
344
  ) -> Result<v8::Local<'a, v8::Array>, anyhow::Error> {
×
NEW
345
    let mut arr = vec![];
×
NEW
346

×
NEW
347
    self.bind_params(scope, params)?;
×
NEW
348
    while let Some(result) = self.read_row(scope)? {
×
NEW
349
      arr.push(result.into());
×
NEW
350
    }
×
351

NEW
352
    self.reset();
×
NEW
353

×
NEW
354
    let arr = v8::Array::new_with_elements(scope, &arr);
×
NEW
355
    Ok(arr)
×
NEW
356
  }
×
357

358
  #[fast]
NEW
359
  fn set_read_big_ints(&self, enabled: bool) {
×
NEW
360
    self.use_big_ints.set(enabled);
×
NEW
361
  }
×
362
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc