mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 02:48:24 +00:00
fix(ext/node): implement StatementSync#iterate (#28168)
Fixes https://github.com/denoland/deno/issues/28130
This commit is contained in:
parent
2f4527562c
commit
4ab380e0a7
3 changed files with 160 additions and 0 deletions
|
@ -205,6 +205,7 @@ impl DatabaseSync {
|
|||
inner: raw_stmt,
|
||||
db: self.conn.clone(),
|
||||
use_big_ints: Cell::new(false),
|
||||
is_iter_finished: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,13 @@ pub struct RunStatementResult {
|
|||
changes: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StatementSync {
|
||||
pub inner: *mut ffi::sqlite3_stmt,
|
||||
pub db: Rc<RefCell<Option<rusqlite::Connection>>>,
|
||||
|
||||
pub use_big_ints: Cell<bool>,
|
||||
pub is_iter_finished: bool,
|
||||
}
|
||||
|
||||
impl Drop for StatementSync {
|
||||
|
@ -369,6 +371,143 @@ impl StatementSync {
|
|||
Ok(arr)
|
||||
}
|
||||
|
||||
fn iterate<'a>(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
#[varargs] params: Option<&v8::FunctionCallbackArguments>,
|
||||
) -> Result<v8::Local<'a, v8::Object>, SqliteError> {
|
||||
macro_rules! v8_static_strings {
|
||||
($($ident:ident = $str:literal),* $(,)?) => {
|
||||
$(
|
||||
pub static $ident: deno_core::FastStaticString = deno_core::ascii_str!($str);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
v8_static_strings! {
|
||||
ITERATOR = "Iterator",
|
||||
PROTOTYPE = "prototype",
|
||||
NEXT = "next",
|
||||
RETURN = "return",
|
||||
DONE = "done",
|
||||
VALUE = "value",
|
||||
}
|
||||
|
||||
self.reset();
|
||||
|
||||
self.bind_params(scope, params)?;
|
||||
|
||||
let iterate_next = |scope: &mut v8::HandleScope,
|
||||
args: v8::FunctionCallbackArguments,
|
||||
mut rv: v8::ReturnValue| {
|
||||
let context = v8::Local::<v8::External>::try_from(args.data())
|
||||
.expect("Iterator#next expected external data");
|
||||
// SAFETY: `context` is a valid pointer to a StatementSync instance
|
||||
let statement = unsafe { &mut *(context.value() as *mut StatementSync) };
|
||||
|
||||
let names = &[
|
||||
DONE.v8_string(scope).unwrap().into(),
|
||||
VALUE.v8_string(scope).unwrap().into(),
|
||||
];
|
||||
|
||||
if statement.is_iter_finished {
|
||||
let values = &[
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
v8::undefined(scope).into(),
|
||||
];
|
||||
let null = v8::null(scope).into();
|
||||
let result =
|
||||
v8::Object::with_prototype_and_properties(scope, null, names, values);
|
||||
rv.set(result.into());
|
||||
return;
|
||||
}
|
||||
|
||||
let Ok(Some(row)) = statement.read_row(scope) else {
|
||||
statement.reset();
|
||||
statement.is_iter_finished = true;
|
||||
|
||||
let values = &[
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
v8::undefined(scope).into(),
|
||||
];
|
||||
let null = v8::null(scope).into();
|
||||
let result =
|
||||
v8::Object::with_prototype_and_properties(scope, null, names, values);
|
||||
rv.set(result.into());
|
||||
return;
|
||||
};
|
||||
|
||||
let values = &[v8::Boolean::new(scope, false).into(), row.into()];
|
||||
let null = v8::null(scope).into();
|
||||
let result =
|
||||
v8::Object::with_prototype_and_properties(scope, null, names, values);
|
||||
rv.set(result.into());
|
||||
};
|
||||
|
||||
let iterate_return = |scope: &mut v8::HandleScope,
|
||||
args: v8::FunctionCallbackArguments,
|
||||
mut rv: v8::ReturnValue| {
|
||||
let context = v8::Local::<v8::External>::try_from(args.data())
|
||||
.expect("Iterator#return expected external data");
|
||||
// SAFETY: `context` is a valid pointer to a StatementSync instance
|
||||
let statement = unsafe { &mut *(context.value() as *mut StatementSync) };
|
||||
|
||||
statement.is_iter_finished = true;
|
||||
statement.reset();
|
||||
|
||||
let names = &[
|
||||
DONE.v8_string(scope).unwrap().into(),
|
||||
VALUE.v8_string(scope).unwrap().into(),
|
||||
];
|
||||
let values = &[
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
v8::undefined(scope).into(),
|
||||
];
|
||||
|
||||
let null = v8::null(scope).into();
|
||||
let result =
|
||||
v8::Object::with_prototype_and_properties(scope, null, names, values);
|
||||
rv.set(result.into());
|
||||
};
|
||||
|
||||
let external = v8::External::new(scope, self as *const _ as _);
|
||||
let next_func = v8::Function::builder(iterate_next)
|
||||
.data(external.into())
|
||||
.build(scope)
|
||||
.expect("Failed to create Iterator#next function");
|
||||
let return_func = v8::Function::builder(iterate_return)
|
||||
.data(external.into())
|
||||
.build(scope)
|
||||
.expect("Failed to create Iterator#return function");
|
||||
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let iter_str = ITERATOR.v8_string(scope).unwrap();
|
||||
let js_iterator: v8::Local<v8::Object> = {
|
||||
global
|
||||
.get(scope, iter_str.into())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let proto_str = PROTOTYPE.v8_string(scope).unwrap();
|
||||
let js_iterator_proto = js_iterator.get(scope, proto_str.into()).unwrap();
|
||||
|
||||
let names = &[
|
||||
NEXT.v8_string(scope).unwrap().into(),
|
||||
RETURN.v8_string(scope).unwrap().into(),
|
||||
];
|
||||
let values = &[next_func.into(), return_func.into()];
|
||||
let iterator = v8::Object::with_prototype_and_properties(
|
||||
scope,
|
||||
js_iterator_proto,
|
||||
names,
|
||||
values,
|
||||
);
|
||||
|
||||
Ok(iterator)
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn set_read_big_ints(&self, enabled: bool) {
|
||||
self.use_big_ints.set(enabled);
|
||||
|
|
|
@ -197,3 +197,23 @@ CREATE TABLE two(id int PRIMARY KEY) STRICT;`);
|
|||
|
||||
db.close();
|
||||
});
|
||||
|
||||
Deno.test("[node/sqlite] StatementSync#iterate", () => {
|
||||
const db = new DatabaseSync(":memory:");
|
||||
const stmt = db.prepare("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3");
|
||||
// @ts-ignore: types are not up to date
|
||||
const iter = stmt.iterate();
|
||||
|
||||
const result = [];
|
||||
for (const row of iter) {
|
||||
result.push(row);
|
||||
}
|
||||
|
||||
assertEquals(result, stmt.all());
|
||||
|
||||
const { done, value } = iter.next();
|
||||
assertEquals(done, true);
|
||||
assertEquals(value, undefined);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue