mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
docs
This commit is contained in:
parent
509bd954da
commit
11c4d61b86
2 changed files with 142 additions and 61 deletions
|
@ -122,14 +122,22 @@ impl DatabaseSync {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compiles an SQL statement into a prepared statement.
|
||||||
|
//
|
||||||
|
// This method is a wrapper around `sqlite3_prepare_v2()`.
|
||||||
#[cppgc]
|
#[cppgc]
|
||||||
fn prepare(&self, #[string] sql: &str) -> Result<StatementSync, SqliteError> {
|
fn prepare(&self, #[string] sql: &str) -> Result<StatementSync, SqliteError> {
|
||||||
let db = self.conn.borrow();
|
let db = self.conn.borrow();
|
||||||
let db = db.as_ref().ok_or(SqliteError::InUse)?;
|
let db = db.as_ref().ok_or(SqliteError::InUse)?;
|
||||||
|
|
||||||
|
// SAFETY: lifetime of the connection is guaranteed by reference
|
||||||
|
// counting.
|
||||||
let raw_handle = unsafe { db.handle() };
|
let raw_handle = unsafe { db.handle() };
|
||||||
|
|
||||||
let mut raw_stmt = std::ptr::null_mut();
|
let mut raw_stmt = std::ptr::null_mut();
|
||||||
|
|
||||||
|
// SAFETY: `sql` points to a valid memory location and its length
|
||||||
|
// is correct.
|
||||||
let r = unsafe {
|
let r = unsafe {
|
||||||
libsqlite3_sys::sqlite3_prepare_v2(
|
libsqlite3_sys::sqlite3_prepare_v2(
|
||||||
raw_handle,
|
raw_handle,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -33,6 +34,39 @@ impl Drop for StatementSync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ColumnIterator<'a> {
|
||||||
|
stmt: &'a StatementSync,
|
||||||
|
index: i32,
|
||||||
|
count: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ColumnIterator<'a> {
|
||||||
|
fn new(stmt: &'a StatementSync) -> Self {
|
||||||
|
let count = stmt.column_count();
|
||||||
|
ColumnIterator {
|
||||||
|
stmt,
|
||||||
|
index: 0,
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ColumnIterator<'a> {
|
||||||
|
type Item = (i32, Cow<'a, str>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.index >= self.count {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = self.index;
|
||||||
|
let name = self.stmt.column_name(self.index);
|
||||||
|
|
||||||
|
self.index += 1;
|
||||||
|
Some((index, name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GarbageCollected for StatementSync {}
|
impl GarbageCollected for StatementSync {}
|
||||||
|
|
||||||
impl StatementSync {
|
impl StatementSync {
|
||||||
|
@ -61,75 +95,90 @@ impl StatementSync {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn column_count(&self) -> i32 {
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
|
unsafe { ffi::sqlite3_column_count(self.inner) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn column_name(&self, index: i32) -> Cow<str> {
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
|
unsafe {
|
||||||
|
let name = ffi::sqlite3_column_name(self.inner, index);
|
||||||
|
std::ffi::CStr::from_ptr(name as _).to_string_lossy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn column_value<'a>(
|
||||||
|
&self,
|
||||||
|
index: i32,
|
||||||
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
) -> v8::Local<'a, v8::Value> {
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
|
unsafe {
|
||||||
|
match ffi::sqlite3_column_type(self.inner, index) {
|
||||||
|
ffi::SQLITE_INTEGER => {
|
||||||
|
let value = ffi::sqlite3_column_int64(self.inner, index);
|
||||||
|
v8::Integer::new(scope, value as _).into()
|
||||||
|
}
|
||||||
|
ffi::SQLITE_FLOAT => {
|
||||||
|
let value = ffi::sqlite3_column_double(self.inner, index);
|
||||||
|
v8::Number::new(scope, value).into()
|
||||||
|
}
|
||||||
|
ffi::SQLITE_TEXT => {
|
||||||
|
let value = ffi::sqlite3_column_text(self.inner, index);
|
||||||
|
let value = std::ffi::CStr::from_ptr(value as _).to_string_lossy();
|
||||||
|
v8::String::new_from_utf8(
|
||||||
|
scope,
|
||||||
|
value.as_bytes(),
|
||||||
|
v8::NewStringType::Normal,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
ffi::SQLITE_BLOB => {
|
||||||
|
let value = ffi::sqlite3_column_blob(self.inner, index);
|
||||||
|
let size = ffi::sqlite3_column_bytes(self.inner, index);
|
||||||
|
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(),
|
||||||
|
_ => v8::undefined(scope).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read the current row of the prepared statement.
|
// Read the current row of the prepared statement.
|
||||||
fn read_row<'a>(
|
fn read_row<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut v8::HandleScope<'a>,
|
scope: &mut v8::HandleScope<'a>,
|
||||||
) -> Result<Option<v8::Local<'a, v8::Object>>, SqliteError> {
|
) -> Result<Option<v8::Local<'a, v8::Object>>, SqliteError> {
|
||||||
|
if self.step()? {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
let result = v8::Object::new(scope);
|
let result = v8::Object::new(scope);
|
||||||
let raw = self.inner;
|
let iter = ColumnIterator::new(self);
|
||||||
unsafe {
|
|
||||||
if self.step()? {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let columns = ffi::sqlite3_column_count(raw);
|
for (index, name) in iter {
|
||||||
|
let value = self.column_value(index, scope);
|
||||||
for i in 0..columns {
|
let name = v8::String::new_from_utf8(
|
||||||
let name = ffi::sqlite3_column_name(raw, i);
|
scope,
|
||||||
let name = std::ffi::CStr::from_ptr(name).to_string_lossy();
|
name.as_bytes(),
|
||||||
let value = match ffi::sqlite3_column_type(raw, i) {
|
v8::NewStringType::Normal,
|
||||||
ffi::SQLITE_INTEGER => {
|
)
|
||||||
let value = ffi::sqlite3_column_int64(raw, i);
|
.unwrap()
|
||||||
v8::Integer::new(scope, value as _).into()
|
.into();
|
||||||
}
|
result.set(scope, name, value);
|
||||||
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))
|
Ok(Some(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind the parameters to the prepared statement.
|
||||||
fn bind_params(
|
fn bind_params(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut v8::HandleScope,
|
scope: &mut v8::HandleScope,
|
||||||
|
@ -144,33 +193,41 @@ impl StatementSync {
|
||||||
|
|
||||||
if value.is_number() {
|
if value.is_number() {
|
||||||
let value = value.number_value(scope).unwrap();
|
let value = value.number_value(scope).unwrap();
|
||||||
|
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::sqlite3_bind_double(raw, i as i32 + 1, value);
|
ffi::sqlite3_bind_double(raw, i + 1, value);
|
||||||
}
|
}
|
||||||
} else if value.is_string() {
|
} else if value.is_string() {
|
||||||
let value = value.to_rust_string_lossy(scope);
|
let value = value.to_rust_string_lossy(scope);
|
||||||
|
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
|
// SQLITE_TRANSIENT is used to indicate that SQLite should make a copy of the data.
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::sqlite3_bind_text(
|
ffi::sqlite3_bind_text(
|
||||||
raw,
|
raw,
|
||||||
i as i32 + 1,
|
i + 1,
|
||||||
value.as_ptr() as *const i8,
|
value.as_ptr() as *const i8,
|
||||||
value.len() as i32,
|
value.len() as i32,
|
||||||
ffi::SQLITE_TRANSIENT(),
|
ffi::SQLITE_TRANSIENT(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if value.is_null() {
|
} else if value.is_null() {
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::sqlite3_bind_null(raw, i as i32 + 1);
|
ffi::sqlite3_bind_null(raw, i + 1);
|
||||||
}
|
}
|
||||||
} else if value.is_array_buffer_view() {
|
} else if value.is_array_buffer_view() {
|
||||||
let value: v8::Local<v8::ArrayBufferView> = value.try_into().unwrap();
|
let value: v8::Local<v8::ArrayBufferView> = value.try_into().unwrap();
|
||||||
let data = value.data();
|
let data = value.data();
|
||||||
let size = value.byte_length();
|
let size = value.byte_length();
|
||||||
|
|
||||||
|
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt.
|
||||||
|
// SQLITE_TRANSIENT is used to indicate that SQLite should make a copy of the data.
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::sqlite3_bind_blob(
|
ffi::sqlite3_bind_blob(
|
||||||
raw,
|
raw,
|
||||||
i as i32 + 1,
|
i + 1,
|
||||||
data,
|
data,
|
||||||
size as i32,
|
size as i32,
|
||||||
ffi::SQLITE_TRANSIENT(),
|
ffi::SQLITE_TRANSIENT(),
|
||||||
|
@ -186,6 +243,10 @@ impl StatementSync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents a single prepared statement. Cannot be initialized directly via constructor.
|
||||||
|
// Instances are created using `DatabaseSync#prepare`.
|
||||||
|
//
|
||||||
|
// A prepared statement is an efficient binary representation of the SQL used to create it.
|
||||||
#[op2]
|
#[op2]
|
||||||
impl StatementSync {
|
impl StatementSync {
|
||||||
#[constructor]
|
#[constructor]
|
||||||
|
@ -194,6 +255,10 @@ impl StatementSync {
|
||||||
Err(SqliteError::InvalidConstructor)
|
Err(SqliteError::InvalidConstructor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes a prepared statement and returns the first result as an object.
|
||||||
|
//
|
||||||
|
// The prepared statement does not return any results, this method returns undefined.
|
||||||
|
// Optionally, parameters can be bound to the prepared statement.
|
||||||
fn get<'a>(
|
fn get<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut v8::HandleScope<'a>,
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
@ -211,6 +276,10 @@ impl StatementSync {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes a prepared statement and returns an object summarizing the resulting
|
||||||
|
// changes.
|
||||||
|
//
|
||||||
|
// Optionally, parameters can be bound to the prepared statement.
|
||||||
#[serde]
|
#[serde]
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
|
@ -231,6 +300,10 @@ impl StatementSync {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes a prepared statement and returns all results as an array of objects.
|
||||||
|
//
|
||||||
|
// If the prepared statement does not return any results, this method returns an empty array.
|
||||||
|
// Optionally, parameters can be bound to the prepared statement.
|
||||||
fn all<'a>(
|
fn all<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut v8::HandleScope<'a>,
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
|
Loading…
Add table
Reference in a new issue