1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00
This commit is contained in:
Divy Srivastava 2024-12-12 16:34:09 +05:30
parent 3279935647
commit bf6825e9fa
3 changed files with 212 additions and 151 deletions

36
cli/bench/sqlite.js Normal file
View file

@ -0,0 +1,36 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console
import { DatabaseSync } from "node:sqlite";
import fs from "node:fs";
function bench(name, fun, count = 10000) {
const start = Date.now();
for (let i = 0; i < count; i++) fun();
const elapsed = Date.now() - start;
const rate = Math.floor(count / (elapsed / 1000));
console.log(` ${name}: time ${elapsed} ms rate ${rate}`);
}
for (const name of [":memory:", "test.db"]) {
console.log(`Benchmarking ${name}`);
try {
fs.unlinkSync(name);
} catch (e) {
// Ignore
}
const db = new DatabaseSync(name);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
bench("prepare", () => db.prepare("SELECT * FROM test"));
bench("exec", () => db.exec("INSERT INTO test (name) VALUES ('foo')"));
const stmt = db.prepare("SELECT * FROM test");
bench("get", () => stmt.get());
const stmt2 = db.prepare("SELECT * FROM test WHERE id = ?");
bench("get (integer bind)", () => stmt2.get(1));
bench("all", () => stmt.all(), 1000);
}

View file

@ -24,6 +24,8 @@ pub enum SqliteError {
AlreadyOpen, AlreadyOpen,
#[error("Failed to prepare statement")] #[error("Failed to prepare statement")]
PrepareFailed, PrepareFailed,
#[error("Invalid constructor")]
InvalidConstructor,
#[error(transparent)] #[error(transparent)]
Other(deno_core::error::AnyError), Other(deno_core::error::AnyError),
} }

View file

@ -6,6 +6,7 @@ use std::rc::Rc;
use deno_core::op2; use deno_core::op2;
use deno_core::v8; use deno_core::v8;
use deno_core::GarbageCollected; use deno_core::GarbageCollected;
use libsqlite3_sys as ffi;
use serde::Serialize; use serde::Serialize;
use super::SqliteError; use super::SqliteError;
@ -18,150 +19,194 @@ pub struct RunStatementResult {
} }
pub struct StatementSync { pub struct StatementSync {
pub inner: *mut libsqlite3_sys::sqlite3_stmt, pub inner: *mut ffi::sqlite3_stmt,
pub db: Rc<RefCell<Option<rusqlite::Connection>>>, pub db: Rc<RefCell<Option<rusqlite::Connection>>>,
} }
impl Drop for StatementSync {
fn drop(&mut self) {
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// no other references to this pointer exist.
unsafe {
ffi::sqlite3_finalize(self.inner);
}
}
}
impl GarbageCollected for StatementSync {} impl GarbageCollected for StatementSync {}
fn read_entry<'a>( impl StatementSync {
raw: *mut libsqlite3_sys::sqlite3_stmt, // Clear the prepared statement back to its initial state.
scope: &mut v8::HandleScope<'a>, fn reset(&self) {
) -> Result<Option<v8::Local<'a, v8::Object>>, SqliteError> { // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
let result = v8::Object::new(scope); unsafe {
unsafe { ffi::sqlite3_reset(self.inner);
let r = libsqlite3_sys::sqlite3_step(raw);
if r == libsqlite3_sys::SQLITE_DONE {
return Ok(None);
}
if r != libsqlite3_sys::SQLITE_ROW {
return Err(SqliteError::FailedStep);
}
let columns = libsqlite3_sys::sqlite3_column_count(raw);
for i in 0..columns {
let name = libsqlite3_sys::sqlite3_column_name(raw, i);
let name = std::ffi::CStr::from_ptr(name).to_string_lossy().to_string();
let value = match libsqlite3_sys::sqlite3_column_type(raw, i) {
libsqlite3_sys::SQLITE_INTEGER => {
let value = libsqlite3_sys::sqlite3_column_int64(raw, i);
v8::Integer::new(scope, value as _).into()
}
libsqlite3_sys::SQLITE_FLOAT => {
let value = libsqlite3_sys::sqlite3_column_double(raw, i);
v8::Number::new(scope, value).into()
}
libsqlite3_sys::SQLITE_TEXT => {
let value = libsqlite3_sys::sqlite3_column_text(raw, i);
let value = std::ffi::CStr::from_ptr(value as _)
.to_string_lossy()
.to_string();
v8::String::new_from_utf8(
scope,
value.as_bytes(),
v8::NewStringType::Normal,
)
.unwrap()
.into()
}
libsqlite3_sys::SQLITE_BLOB => {
let value = libsqlite3_sys::sqlite3_column_blob(raw, i);
let size = libsqlite3_sys::sqlite3_column_bytes(raw, i);
let value =
std::slice::from_raw_parts(value as *const u8, size as usize);
let value =
v8::ArrayBuffer::new_backing_store_from_vec(value.to_vec())
.make_shared();
v8::ArrayBuffer::with_backing_store(scope, &value).into()
}
libsqlite3_sys::SQLITE_NULL => v8::null(scope).into(),
_ => {
return Err(SqliteError::UnknownColumnType);
}
};
let name = v8::String::new_from_utf8(
scope,
name.as_bytes(),
v8::NewStringType::Normal,
)
.unwrap()
.into();
result.set(scope, name, value);
} }
} }
Ok(Some(result)) // Evaluate the prepared statement.
} fn step(&self) -> Result<bool, SqliteError> {
let raw = self.inner;
fn bind_params( // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
scope: &mut v8::HandleScope, unsafe {
raw: *mut libsqlite3_sys::sqlite3_stmt, let r = ffi::sqlite3_step(raw);
params: Option<&v8::FunctionCallbackArguments>, if r == ffi::SQLITE_DONE {
) -> Result<(), SqliteError> { return Ok(true);
if let Some(params) = params { }
let len = params.length(); if r != ffi::SQLITE_ROW {
for i in 0..len { return Err(SqliteError::FailedStep);
let value = params.get(i);
if value.is_number() {
let value = value.number_value(scope).unwrap();
unsafe {
libsqlite3_sys::sqlite3_bind_double(raw, i as i32 + 1, value);
}
} else if value.is_string() {
let value = value.to_rust_string_lossy(scope);
unsafe {
libsqlite3_sys::sqlite3_bind_text(
raw,
i as i32 + 1,
value.as_ptr() as *const i8,
value.len() as i32,
libsqlite3_sys::SQLITE_TRANSIENT(),
);
}
} else if value.is_null() {
unsafe {
libsqlite3_sys::sqlite3_bind_null(raw, i as i32 + 1);
}
} else {
return Err(SqliteError::FailedBind("Unsupported type"));
} }
} }
Ok(false)
} }
Ok(()) // Read the current row of the prepared statement.
fn read_row<'a>(
&self,
scope: &mut v8::HandleScope<'a>,
) -> Result<Option<v8::Local<'a, v8::Object>>, SqliteError> {
let result = v8::Object::new(scope);
let raw = self.inner;
unsafe {
if self.step()? {
return Ok(None);
}
let columns = ffi::sqlite3_column_count(raw);
for i in 0..columns {
let name = ffi::sqlite3_column_name(raw, i);
let name = std::ffi::CStr::from_ptr(name).to_string_lossy();
let value = match ffi::sqlite3_column_type(raw, i) {
ffi::SQLITE_INTEGER => {
let value = ffi::sqlite3_column_int64(raw, i);
v8::Integer::new(scope, value as _).into()
}
ffi::SQLITE_FLOAT => {
let value = ffi::sqlite3_column_double(raw, i);
v8::Number::new(scope, value).into()
}
ffi::SQLITE_TEXT => {
let value = ffi::sqlite3_column_text(raw, i);
let value = std::ffi::CStr::from_ptr(value as _)
.to_string_lossy()
.to_string();
v8::String::new_from_utf8(
scope,
value.as_bytes(),
v8::NewStringType::Normal,
)
.unwrap()
.into()
}
ffi::SQLITE_BLOB => {
let value = ffi::sqlite3_column_blob(raw, i);
let size = ffi::sqlite3_column_bytes(raw, i);
let value =
std::slice::from_raw_parts(value as *const u8, size as usize);
let value =
v8::ArrayBuffer::new_backing_store_from_vec(value.to_vec())
.make_shared();
v8::ArrayBuffer::with_backing_store(scope, &value).into()
}
ffi::SQLITE_NULL => v8::null(scope).into(),
_ => {
return Err(SqliteError::UnknownColumnType);
}
};
let name = v8::String::new_from_utf8(
scope,
name.as_bytes(),
v8::NewStringType::Normal,
)
.unwrap()
.into();
result.set(scope, name, value);
}
}
Ok(Some(result))
}
fn bind_params(
&self,
scope: &mut v8::HandleScope,
params: Option<&v8::FunctionCallbackArguments>,
) -> Result<(), SqliteError> {
let raw = self.inner;
if let Some(params) = params {
let len = params.length();
for i in 0..len {
let value = params.get(i);
if value.is_number() {
let value = value.number_value(scope).unwrap();
unsafe {
ffi::sqlite3_bind_double(raw, i as i32 + 1, value);
}
} else if value.is_string() {
let value = value.to_rust_string_lossy(scope);
unsafe {
ffi::sqlite3_bind_text(
raw,
i as i32 + 1,
value.as_ptr() as *const i8,
value.len() as i32,
ffi::SQLITE_TRANSIENT(),
);
}
} else if value.is_null() {
unsafe {
ffi::sqlite3_bind_null(raw, i as i32 + 1);
}
} else if value.is_array_buffer_view() {
let value: v8::Local<v8::ArrayBufferView> = value.try_into().unwrap();
let data = value.data();
let size = value.byte_length();
unsafe {
ffi::sqlite3_bind_blob(
raw,
i as i32 + 1,
data,
size as i32,
ffi::SQLITE_TRANSIENT(),
);
}
} else {
return Err(SqliteError::FailedBind("Unsupported type"));
}
}
}
Ok(())
}
} }
#[op2] #[op2]
impl StatementSync { impl StatementSync {
#[constructor] #[constructor]
#[cppgc] #[cppgc]
fn new(_: bool) -> StatementSync { fn new(_: bool) -> Result<StatementSync, SqliteError> {
unimplemented!() Err(SqliteError::InvalidConstructor)
} }
fn get<'a>( fn get<'a>(
&self, &self,
scope: &mut v8::HandleScope<'a>, scope: &mut v8::HandleScope<'a>,
#[varargs] params: Option<&v8::FunctionCallbackArguments>, #[varargs] params: Option<&v8::FunctionCallbackArguments>,
) -> Result<v8::Local<'a, v8::Object>, SqliteError> { ) -> Result<v8::Local<'a, v8::Value>, SqliteError> {
let raw = self.inner; self.bind_params(scope, params)?;
unsafe { let entry = self.read_row(scope)?;
libsqlite3_sys::sqlite3_reset(raw); let result = entry
} .map(|r| r.into())
.unwrap_or_else(|| v8::undefined(scope).into());
bind_params(scope, raw, params)?; self.reset();
let result = read_entry(raw, scope)
.map(|r| r.unwrap_or_else(|| v8::Object::new(scope)))?;
unsafe {
libsqlite3_sys::sqlite3_reset(raw);
}
Ok(result) Ok(result)
} }
@ -172,34 +217,17 @@ impl StatementSync {
scope: &mut v8::HandleScope, scope: &mut v8::HandleScope,
#[varargs] params: Option<&v8::FunctionCallbackArguments>, #[varargs] params: Option<&v8::FunctionCallbackArguments>,
) -> Result<RunStatementResult, SqliteError> { ) -> Result<RunStatementResult, SqliteError> {
let raw = self.inner;
let db = self.db.borrow(); let db = self.db.borrow();
let db = db.as_ref().unwrap(); let db = db.as_ref().ok_or(SqliteError::InUse)?;
let last_insert_rowid; self.bind_params(scope, params)?;
let changes; self.step()?;
unsafe { self.reset();
libsqlite3_sys::sqlite3_reset(raw);
bind_params(scope, raw, params)?;
loop {
let r = libsqlite3_sys::sqlite3_step(raw);
if r == libsqlite3_sys::SQLITE_DONE {
break;
}
if r != libsqlite3_sys::SQLITE_ROW {
return Err(SqliteError::FailedStep);
}
}
last_insert_rowid = db.last_insert_rowid();
changes = db.changes();
}
Ok(RunStatementResult { Ok(RunStatementResult {
last_insert_rowid, last_insert_rowid: db.last_insert_rowid(),
changes, changes: db.changes(),
}) })
} }
@ -208,20 +236,15 @@ impl StatementSync {
scope: &mut v8::HandleScope<'a>, scope: &mut v8::HandleScope<'a>,
#[varargs] params: Option<&v8::FunctionCallbackArguments>, #[varargs] params: Option<&v8::FunctionCallbackArguments>,
) -> Result<v8::Local<'a, v8::Array>, SqliteError> { ) -> Result<v8::Local<'a, v8::Array>, SqliteError> {
let raw = self.inner;
let mut arr = vec![]; let mut arr = vec![];
unsafe {
libsqlite3_sys::sqlite3_reset(raw);
bind_params(scope, raw, params)?; self.bind_params(scope, params)?;
while let Some(result) = read_entry(raw, scope)? { while let Some(result) = self.read_row(scope)? {
arr.push(result.into()); arr.push(result.into());
}
libsqlite3_sys::sqlite3_reset(raw);
} }
self.reset();
let arr = v8::Array::new_with_elements(scope, &arr); let arr = v8::Array::new_with_elements(scope, &arr);
Ok(arr) Ok(arr)
} }