1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-20 20:42:19 -05:00

Compare commits

...

31 commits

Author SHA1 Message Date
Divy Srivastava
998f5cb5a4
Merge fa2a41ef27 into 0d3d4f5466 2025-01-20 23:27:15 +01:00
David Sherret
0d3d4f5466
fix(install/global): remove importMap field from specified config file (#27744)
Closes https://github.com/denoland/deno/issues/27734
2025-01-20 16:50:34 -05:00
Luca Casonato
5e9b3712de
feat(unstable): add basic support for otel trace links (#27727)
Currently only links with no attributes.
2025-01-20 15:39:59 +01:00
Bartek Iwańczuk
395628026f
fix(ext/os): pass SignalState to web worker (#27741)
Closes https://github.com/denoland/deno/issues/27717

Made a mistake in https://github.com/denoland/deno/pull/27655 and
didn't add the `SignalStore` for web worker.
2025-01-20 19:43:15 +05:30
Divy Srivastava
4f27d7cdc0
fix(ext/node): GCM auth tag check on DechiperIv#final (#27733) 2025-01-20 18:16:44 +05:30
Divy Srivastava
fa2a41ef27
Merge branch 'main' into sqlite_node_cppgc 2025-01-17 19:26:40 +05:30
Divy Srivastava
8b030f3247 Merge branch 'main' of github.com:denoland/deno into sqlite_node_cppgc 2024-12-31 22:36:10 +05:30
Divy Srivastava
f9654b35d6 huh 2024-12-16 21:18:32 +05:30
Divy Srivastava
83c6bfde85 Merge branch 'main' of github.com:denoland/deno into sqlite_node_cppgc 2024-12-16 21:16:41 +05:30
Divy Srivastava
58f348b496
Merge branch 'main' into sqlite_node_cppgc 2024-12-16 05:39:54 -08:00
Divy Srivastava
0c0a3e6505 add tests 2024-12-16 11:29:43 +05:30
Divy Srivastava
a43ea7ae19 update comments 2024-12-16 09:47:42 +05:30
Divy Srivastava
636a6c3265 deno_core 0.326.0 2024-12-15 18:22:25 +05:30
Divy Srivastava
f0409a6122 lint 2024-12-15 12:07:19 +05:30
Divy Srivastava
574892aa02 update test 2024-12-15 12:02:50 +05:30
Divy Srivastava
e21982d159 perf 2024-12-15 12:02:03 +05:30
Divy Srivastava
a9f85a70e2 aarch64 ptr type 2024-12-15 09:12:02 +05:30
Divy Srivastava
d104b0b8b3 copyright header 2024-12-15 09:04:17 +05:30
Divy Srivastava
9e37da438c lint ks 2024-12-15 08:58:30 +05:30
Divy Srivastava
9e7306ac7f core 2024-12-15 08:50:49 +05:30
Divy Srivastava
008738058f setReadBigints 2024-12-14 13:16:15 +05:30
Divy Srivastava
95763d3f43
Merge branch 'main' into sqlite_node_cppgc
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
2024-12-13 21:07:43 -08:00
Divy Srivastava
57c29c326e use git 2024-12-14 10:36:38 +05:30
Divy Srivastava
11c4d61b86 docs 2024-12-14 10:35:34 +05:30
Divy Srivastava
509bd954da 2024-12-14 09:59:52 +05:30
Divy Srivastava
bf6825e9fa bench 2024-12-12 16:34:09 +05:30
Divy Srivastava
3279935647 in the mirrorrrr tsh tshh 2024-12-11 22:55:37 +05:30
Divy Srivastava
87574d241e changes 2024-12-11 19:17:37 +05:30
Divy Srivastava
72c96bf7d3 more 2024-12-10 20:38:06 +05:30
Divy Srivastava
87de65525a lets see 2024-12-10 09:32:37 +05:30
Divy Srivastava
0ab878cda8 init 2024-12-09 12:03:31 +05:30
30 changed files with 966 additions and 53 deletions

12
Cargo.lock generated
View file

@ -720,13 +720,13 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.106" version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
"once_cell", "shlex",
] ]
[[package]] [[package]]
@ -2065,6 +2065,7 @@ dependencies = [
"k256", "k256",
"lazy-regex", "lazy-regex",
"libc", "libc",
"libsqlite3-sys",
"libz-sys", "libz-sys",
"md-5", "md-5",
"md4", "md4",
@ -2086,6 +2087,7 @@ dependencies = [
"ring", "ring",
"ripemd", "ripemd",
"rsa", "rsa",
"rusqlite",
"scrypt", "scrypt",
"sec1", "sec1",
"serde", "serde",
@ -4841,9 +4843,9 @@ dependencies = [
[[package]] [[package]]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.30.0" version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b694a822684ddb75df4d657029161431bcb4a85c1856952f845b76912bc6fec" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [ dependencies = [
"cc", "cc",
"pkg-config", "pkg-config",

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 {
// 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

@ -115,10 +115,16 @@ exec deno {} "$@"
Ok(()) Ok(())
} }
fn get_installer_root() -> Result<PathBuf, io::Error> { fn get_installer_root() -> Result<PathBuf, AnyError> {
if let Ok(env_dir) = env::var("DENO_INSTALL_ROOT") { if let Some(env_dir) = env::var_os("DENO_INSTALL_ROOT") {
if !env_dir.is_empty() { if !env_dir.is_empty() {
return canonicalize_path_maybe_not_exists(&PathBuf::from(env_dir)); let env_dir = PathBuf::from(env_dir);
return canonicalize_path_maybe_not_exists(&env_dir).with_context(|| {
format!(
"Canonicalizing DENO_INSTALL_ROOT ('{}').",
env_dir.display()
)
});
} }
} }
// Note: on Windows, the $HOME environment variable may be set by users or by // Note: on Windows, the $HOME environment variable may be set by users or by
@ -587,11 +593,22 @@ async fn resolve_shim_data(
let copy_path = get_hidden_file_with_ext(&file_path, "deno.json"); let copy_path = get_hidden_file_with_ext(&file_path, "deno.json");
executable_args.push("--config".to_string()); executable_args.push("--config".to_string());
executable_args.push(copy_path.to_str().unwrap().to_string()); executable_args.push(copy_path.to_str().unwrap().to_string());
extra_files.push(( let mut config_text = fs::read_to_string(config_path)
copy_path, .with_context(|| format!("error reading {config_path}"))?;
fs::read_to_string(config_path) // always remove the import map field because when someone specifies `--import-map` we
.with_context(|| format!("error reading {config_path}"))?, // don't want that file to be attempted to be loaded and when they don't specify that
)); // (which is just something we haven't implemented yet)
if let Some(new_text) = remove_import_map_field_from_text(&config_text) {
if flags.import_map_path.is_none() {
log::warn!(
"{} \"importMap\" field in the specified config file we be ignored. Use the --import-map flag instead.",
crate::colors::yellow("Warning"),
);
}
config_text = new_text;
}
extra_files.push((copy_path, config_text));
} else { } else {
executable_args.push("--no-config".to_string()); executable_args.push("--no-config".to_string());
} }
@ -631,6 +648,16 @@ async fn resolve_shim_data(
}) })
} }
fn remove_import_map_field_from_text(config_text: &str) -> Option<String> {
let value =
jsonc_parser::cst::CstRootNode::parse(config_text, &Default::default())
.ok()?;
let root_value = value.object_value()?;
let import_map_value = root_value.get("importMap")?;
import_map_value.remove();
Some(value.to_string())
}
fn get_hidden_file_with_ext(file_path: &Path, ext: &str) -> PathBuf { fn get_hidden_file_with_ext(file_path: &Path, ext: &str) -> PathBuf {
// use a dot file to prevent the file from showing up in some // use a dot file to prevent the file from showing up in some
// users shell auto-complete since this directory is on the PATH // users shell auto-complete since this directory is on the PATH
@ -1585,4 +1612,17 @@ mod tests {
assert!(!file_path.exists()); assert!(!file_path.exists());
} }
} }
#[test]
fn test_remove_import_map_field_from_text() {
assert_eq!(
remove_import_map_field_from_text(
r#"{
"importMap": "./value.json"
}"#,
)
.unwrap(),
"{}"
);
}
} }

View file

@ -59,6 +59,7 @@ ipnetwork = "0.20.0"
k256 = "0.13.1" k256 = "0.13.1"
lazy-regex.workspace = true lazy-regex.workspace = true
libc.workspace = true libc.workspace = true
libsqlite3-sys = "0.30.1"
libz-sys.workspace = true libz-sys.workspace = true
md-5 = { version = "0.10.5", features = ["oid"] } md-5 = { version = "0.10.5", features = ["oid"] }
md4 = "0.10.2" md4 = "0.10.2"
@ -80,6 +81,7 @@ regex.workspace = true
ring.workspace = true ring.workspace = true
ripemd = { version = "0.1.3", features = ["oid"] } ripemd = { version = "0.1.3", features = ["oid"] }
rsa.workspace = true rsa.workspace = true
rusqlite.workspace = true
scrypt = "0.11.0" scrypt = "0.11.0"
sec1.workspace = true sec1.workspace = true
serde = "1.0.149" serde = "1.0.149"

View file

@ -226,7 +226,6 @@ deno_core::extension!(deno_node,
ops::crypto::op_node_decipheriv_decrypt, ops::crypto::op_node_decipheriv_decrypt,
ops::crypto::op_node_decipheriv_final, ops::crypto::op_node_decipheriv_final,
ops::crypto::op_node_decipheriv_set_aad, ops::crypto::op_node_decipheriv_set_aad,
ops::crypto::op_node_decipheriv_take,
ops::crypto::op_node_dh_compute_secret, ops::crypto::op_node_dh_compute_secret,
ops::crypto::op_node_diffie_hellman, ops::crypto::op_node_diffie_hellman,
ops::crypto::op_node_ecdh_compute_public_key, ops::crypto::op_node_ecdh_compute_public_key,
@ -438,7 +437,9 @@ deno_core::extension!(deno_node,
ops::inspector::op_inspector_enabled, ops::inspector::op_inspector_enabled,
], ],
objects = [ objects = [
ops::perf_hooks::EldHistogram ops::perf_hooks::EldHistogram,
ops::sqlite::DatabaseSync,
ops::sqlite::StatementSync
], ],
esm_entry_point = "ext:deno_node/02_init.js", esm_entry_point = "ext:deno_node/02_init.js",
esm = [ esm = [
@ -662,6 +663,7 @@ deno_core::extension!(deno_node,
"node:readline" = "readline.ts", "node:readline" = "readline.ts",
"node:readline/promises" = "readline/promises.ts", "node:readline/promises" = "readline/promises.ts",
"node:repl" = "repl.ts", "node:repl" = "repl.ts",
"node:sqlite" = "sqlite.ts",
"node:stream" = "stream.ts", "node:stream" = "stream.ts",
"node:stream/consumers" = "stream/consumers.mjs", "node:stream/consumers" = "stream/consumers.mjs",
"node:stream/promises" = "stream/promises.mjs", "node:stream/promises" = "stream/promises.mjs",

View file

@ -500,6 +500,11 @@ impl Decipher {
auth_tag: &[u8], auth_tag: &[u8],
) -> Result<(), DecipherError> { ) -> Result<(), DecipherError> {
use Decipher::*; use Decipher::*;
if input.is_empty() && !matches!(self, Aes128Gcm(_) | Aes256Gcm(_)) {
return Ok(());
}
match (self, auto_pad) { match (self, auto_pad) {
(Aes128Cbc(decryptor), true) => { (Aes128Cbc(decryptor), true) => {
assert!(input.len() == 16); assert!(input.len() == 16);

View file

@ -332,17 +332,6 @@ pub fn op_node_decipheriv_decrypt(
true true
} }
#[op2(fast)]
pub fn op_node_decipheriv_take(
state: &mut OpState,
#[smi] rid: u32,
) -> Result<(), cipher::DecipherContextError> {
let context = state.resource_table.take::<cipher::DecipherContext>(rid)?;
Rc::try_unwrap(context)
.map_err(|_| cipher::DecipherContextError::ContextInUse)?;
Ok(())
}
#[op2] #[op2]
pub fn op_node_decipheriv_final( pub fn op_node_decipheriv_final(
state: &mut OpState, state: &mut OpState,

View file

@ -13,6 +13,7 @@ pub mod os;
pub mod perf_hooks; pub mod perf_hooks;
pub mod process; pub mod process;
pub mod require; pub mod require;
pub mod sqlite;
pub mod tls; pub mod tls;
pub mod util; pub mod util;
pub mod v8; pub mod v8;

View file

@ -0,0 +1,162 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
use deno_core::op2;
use deno_core::GarbageCollected;
use serde::Deserialize;
use super::SqliteError;
use super::StatementSync;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct DatabaseSyncOptions {
#[serde(default = "true_fn")]
open: bool,
#[serde(default = "true_fn")]
enable_foreign_key_constraints: bool,
}
fn true_fn() -> bool {
true
}
impl Default for DatabaseSyncOptions {
fn default() -> Self {
DatabaseSyncOptions {
open: true,
enable_foreign_key_constraints: true,
}
}
}
pub struct DatabaseSync {
conn: Rc<RefCell<Option<rusqlite::Connection>>>,
options: DatabaseSyncOptions,
location: String,
}
impl GarbageCollected for DatabaseSync {}
// Represents a single connection to a SQLite database.
#[op2]
impl DatabaseSync {
// Constructs a new `DatabaseSync` instance.
//
// A SQLite database can be stored in a file or in memory. To
// use a file-backed database, the `location` should be a path.
// To use an in-memory database, the `location` should be special
// name ":memory:".
#[constructor]
#[cppgc]
fn new(
#[string] location: String,
#[serde] options: Option<DatabaseSyncOptions>,
) -> Result<DatabaseSync, SqliteError> {
let options = options.unwrap_or_default();
let db = if options.open {
let db = rusqlite::Connection::open(&location)?;
if options.enable_foreign_key_constraints {
db.execute("PRAGMA foreign_keys = ON", [])?;
}
Some(db)
} else {
None
};
Ok(DatabaseSync {
conn: Rc::new(RefCell::new(db)),
location,
options,
})
}
// Opens the database specified by `location` of this instance.
//
// This method should only be used when the database is not opened
// via the constructor. An exception is thrown if the database is
// already opened.
#[fast]
fn open(&self) -> Result<(), SqliteError> {
if self.conn.borrow().is_some() {
return Err(SqliteError::AlreadyOpen);
}
let db = rusqlite::Connection::open(&self.location)?;
if self.options.enable_foreign_key_constraints {
db.execute("PRAGMA foreign_keys = ON", [])?;
}
*self.conn.borrow_mut() = Some(db);
Ok(())
}
// Closes the database connection. An exception is thrown if the
// database is not open.
#[fast]
fn close(&self) -> Result<(), SqliteError> {
if self.conn.borrow().is_none() {
return Err(SqliteError::AlreadyClosed);
}
*self.conn.borrow_mut() = None;
Ok(())
}
// This method allows one or more SQL statements to be executed
// without returning any results.
//
// This method is a wrapper around sqlite3_exec().
#[fast]
fn exec(&self, #[string] sql: &str) -> Result<(), SqliteError> {
let db = self.conn.borrow();
let db = db.as_ref().ok_or(SqliteError::InUse)?;
let mut stmt = db.prepare_cached(sql)?;
stmt.raw_execute()?;
Ok(())
}
// Compiles an SQL statement into a prepared statement.
//
// This method is a wrapper around `sqlite3_prepare_v2()`.
#[cppgc]
fn prepare(&self, #[string] sql: &str) -> Result<StatementSync, SqliteError> {
let db = self.conn.borrow();
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 mut raw_stmt = std::ptr::null_mut();
// SAFETY: `sql` points to a valid memory location and its length
// is correct.
let r = unsafe {
libsqlite3_sys::sqlite3_prepare_v2(
raw_handle,
sql.as_ptr() as *const _,
sql.len() as i32,
&mut raw_stmt,
std::ptr::null_mut(),
)
};
if r != libsqlite3_sys::SQLITE_OK {
return Err(SqliteError::PrepareFailed);
}
Ok(StatementSync {
inner: raw_stmt,
db: self.conn.clone(),
use_big_ints: Cell::new(false),
})
}
}

View file

@ -0,0 +1,33 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
mod database;
mod statement;
pub use database::DatabaseSync;
pub use statement::StatementSync;
#[derive(Debug, thiserror::Error)]
pub enum SqliteError {
#[error(transparent)]
SqliteError(#[from] rusqlite::Error),
#[error("Database is already in use")]
InUse,
#[error("Failed to step statement")]
FailedStep,
#[error("Failed to bind parameter. {0}")]
FailedBind(&'static str),
#[error("Unknown column type")]
UnknownColumnType,
#[error("Failed to get SQL")]
GetSqlFailed,
#[error("Database is already closed")]
AlreadyClosed,
#[error("Database is already open")]
AlreadyOpen,
#[error("Failed to prepare statement")]
PrepareFailed,
#[error("Invalid constructor")]
InvalidConstructor,
#[error(transparent)]
Other(deno_core::error::AnyError),
}

View file

@ -0,0 +1,357 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
use deno_core::op2;
use deno_core::v8;
use deno_core::GarbageCollected;
use libsqlite3_sys as ffi;
use serde::Serialize;
use super::SqliteError;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RunStatementResult {
last_insert_rowid: i64,
changes: u64,
}
pub struct StatementSync {
pub inner: *mut ffi::sqlite3_stmt,
pub db: Rc<RefCell<Option<rusqlite::Connection>>>,
pub use_big_ints: Cell<bool>,
}
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);
}
}
}
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,
}
}
fn column_count(&self) -> usize {
self.count as usize
}
}
impl<'a> Iterator for ColumnIterator<'a> {
type Item = (i32, &'a [u8]);
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 StatementSync {
// Clear the prepared statement back to its initial state.
fn reset(&self) {
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
unsafe {
ffi::sqlite3_reset(self.inner);
}
}
// Evaluate the prepared statement.
fn step(&self) -> Result<bool, SqliteError> {
let raw = self.inner;
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
unsafe {
let r = ffi::sqlite3_step(raw);
if r == ffi::SQLITE_DONE {
return Ok(true);
}
if r != ffi::SQLITE_ROW {
return Err(SqliteError::FailedStep);
}
}
Ok(false)
}
fn column_count(&self) -> i32 {
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
unsafe { ffi::sqlite3_column_count(self.inner) }
}
fn column_name(&self, index: i32) -> &[u8] {
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
unsafe {
let name = ffi::sqlite3_column_name(self.inner, index);
std::ffi::CStr::from_ptr(name as _).to_bytes()
}
}
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
// as it lives as long as the StatementSync instance.
unsafe {
match ffi::sqlite3_column_type(self.inner, index) {
ffi::SQLITE_INTEGER => {
let value = ffi::sqlite3_column_int64(self.inner, index);
if self.use_big_ints.get() {
v8::BigInt::new_from_i64(scope, value).into()
} else {
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 _);
v8::String::new_from_utf8(
scope,
value.to_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.
fn read_row<'a>(
&self,
scope: &mut v8::HandleScope<'a>,
) -> Result<Option<v8::Local<'a, v8::Object>>, SqliteError> {
if self.step()? {
return Ok(None);
}
let iter = ColumnIterator::new(self);
let num_cols = iter.column_count();
let mut names = Vec::with_capacity(num_cols);
let mut values = Vec::with_capacity(num_cols);
for (index, name) in iter {
let value = self.column_value(index, scope);
let name =
v8::String::new_from_utf8(scope, name, v8::NewStringType::Normal)
.unwrap()
.into();
names.push(name);
values.push(value);
}
let null = v8::null(scope).into();
let result =
v8::Object::with_prototype_and_properties(scope, null, &names, &values);
Ok(Some(result))
}
// Bind the parameters to the prepared statement.
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();
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
unsafe {
ffi::sqlite3_bind_double(raw, i + 1, value);
}
} else if value.is_string() {
let value = value.to_rust_string_lossy(scope);
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
//
// SQLITE_TRANSIENT is used to indicate that SQLite should make a copy of the data.
unsafe {
ffi::sqlite3_bind_text(
raw,
i + 1,
value.as_ptr() as *const _,
value.len() as i32,
ffi::SQLITE_TRANSIENT(),
);
}
} else if value.is_null() {
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
unsafe {
ffi::sqlite3_bind_null(raw, i + 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();
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance.
//
// SQLITE_TRANSIENT is used to indicate that SQLite should make a copy of the data.
unsafe {
ffi::sqlite3_bind_blob(
raw,
i + 1,
data,
size as i32,
ffi::SQLITE_TRANSIENT(),
);
}
} else {
return Err(SqliteError::FailedBind("Unsupported type"));
}
}
}
Ok(())
}
}
// 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]
impl StatementSync {
#[constructor]
#[cppgc]
fn new(_: bool) -> Result<StatementSync, SqliteError> {
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>(
&self,
scope: &mut v8::HandleScope<'a>,
#[varargs] params: Option<&v8::FunctionCallbackArguments>,
) -> Result<v8::Local<'a, v8::Value>, SqliteError> {
self.reset();
self.bind_params(scope, params)?;
let entry = self.read_row(scope)?;
let result = entry
.map(|r| r.into())
.unwrap_or_else(|| v8::undefined(scope).into());
Ok(result)
}
// Executes a prepared statement and returns an object summarizing the resulting
// changes.
//
// Optionally, parameters can be bound to the prepared statement.
#[serde]
fn run(
&self,
scope: &mut v8::HandleScope,
#[varargs] params: Option<&v8::FunctionCallbackArguments>,
) -> Result<RunStatementResult, SqliteError> {
let db = self.db.borrow();
let db = db.as_ref().ok_or(SqliteError::InUse)?;
self.bind_params(scope, params)?;
self.step()?;
self.reset();
Ok(RunStatementResult {
last_insert_rowid: db.last_insert_rowid(),
changes: db.changes(),
})
}
// 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>(
&self,
scope: &mut v8::HandleScope<'a>,
#[varargs] params: Option<&v8::FunctionCallbackArguments>,
) -> Result<v8::Local<'a, v8::Array>, SqliteError> {
let mut arr = vec![];
self.bind_params(scope, params)?;
while let Some(result) = self.read_row(scope)? {
arr.push(result.into());
}
self.reset();
let arr = v8::Array::new_with_elements(scope, &arr);
Ok(arr)
}
#[fast]
fn set_read_big_ints(&self, enabled: bool) {
self.use_big_ints.set(enabled);
}
}

View file

@ -71,6 +71,7 @@ generate_builtin_node_module_lists! {
"readline", "readline",
"readline/promises", "readline/promises",
"repl", "repl",
"sqlite",
"stream", "stream",
"stream/consumers", "stream/consumers",
"stream/promises", "stream/promises",

View file

@ -142,6 +142,7 @@ import querystring from "node:querystring";
import readline from "node:readline"; import readline from "node:readline";
import readlinePromises from "node:readline/promises"; import readlinePromises from "node:readline/promises";
import repl from "node:repl"; import repl from "node:repl";
import sqlite from "node:sqlite";
import stream from "node:stream"; import stream from "node:stream";
import streamConsumers from "node:stream/consumers"; import streamConsumers from "node:stream/consumers";
import streamPromises from "node:stream/promises"; import streamPromises from "node:stream/promises";
@ -253,6 +254,7 @@ function setupBuiltinModules() {
readline, readline,
"readline/promises": readlinePromises, "readline/promises": readlinePromises,
repl, repl,
sqlite,
stream, stream,
"stream/consumers": streamConsumers, "stream/consumers": streamConsumers,
"stream/promises": streamPromises, "stream/promises": streamPromises,

View file

@ -18,7 +18,6 @@ import {
op_node_decipheriv_decrypt, op_node_decipheriv_decrypt,
op_node_decipheriv_final, op_node_decipheriv_final,
op_node_decipheriv_set_aad, op_node_decipheriv_set_aad,
op_node_decipheriv_take,
op_node_private_decrypt, op_node_private_decrypt,
op_node_private_encrypt, op_node_private_encrypt,
op_node_public_encrypt, op_node_public_encrypt,
@ -352,14 +351,6 @@ export class Decipheriv extends Transform implements Cipher {
} }
final(encoding: string = getDefaultEncoding()): Buffer | string { final(encoding: string = getDefaultEncoding()): Buffer | string {
if (!this.#needsBlockCache || this.#cache.cache.byteLength === 0) {
op_node_decipheriv_take(this.#context);
return encoding === "buffer" ? Buffer.from([]) : "";
}
if (this.#cache.cache.byteLength != 16) {
throw new Error("Invalid final block size");
}
let buf = new Buffer(16); let buf = new Buffer(16);
op_node_decipheriv_final( op_node_decipheriv_final(
this.#context, this.#context,
@ -369,6 +360,13 @@ export class Decipheriv extends Transform implements Cipher {
this.#authTag || NO_TAG, this.#authTag || NO_TAG,
); );
if (!this.#needsBlockCache || this.#cache.cache.byteLength === 0) {
return encoding === "buffer" ? Buffer.from([]) : "";
}
if (this.#cache.cache.byteLength != 16) {
throw new Error("Invalid final block size");
}
buf = buf.subarray(0, 16 - buf.at(-1)); // Padded in Pkcs7 mode buf = buf.subarray(0, 16 - buf.at(-1)); // Padded in Pkcs7 mode
return encoding === "buffer" ? buf : buf.toString(encoding); return encoding === "buffer" ? buf : buf.toString(encoding);
} }

View file

@ -0,0 +1,9 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { DatabaseSync } from "ext:core/ops";
export { DatabaseSync };
export default {
DatabaseSync,
};

View file

@ -116,6 +116,12 @@ deno_core::extension!(
"op_exit" | "op_set_exit_code" | "op_get_exit_code" => "op_exit" | "op_set_exit_code" | "op_get_exit_code" =>
op.with_implementation_from(&deno_core::op_void_sync()), op.with_implementation_from(&deno_core::op_void_sync()),
_ => op, _ => op,
},
state = |state| {
#[cfg(unix)]
{
state.put(ops::signal::SignalState::default());
}
} }
); );

View file

@ -42,6 +42,7 @@ use opentelemetry::metrics::InstrumentBuilder;
use opentelemetry::metrics::MeterProvider as _; use opentelemetry::metrics::MeterProvider as _;
use opentelemetry::otel_debug; use opentelemetry::otel_debug;
use opentelemetry::otel_error; use opentelemetry::otel_error;
use opentelemetry::trace::Link;
use opentelemetry::trace::SpanContext; use opentelemetry::trace::SpanContext;
use opentelemetry::trace::SpanId; use opentelemetry::trace::SpanId;
use opentelemetry::trace::SpanKind; use opentelemetry::trace::SpanKind;
@ -94,6 +95,7 @@ deno_core::extension!(
op_otel_span_attribute1, op_otel_span_attribute1,
op_otel_span_attribute2, op_otel_span_attribute2,
op_otel_span_attribute3, op_otel_span_attribute3,
op_otel_span_add_link,
op_otel_span_update_name, op_otel_span_update_name,
op_otel_metric_attribute3, op_otel_metric_attribute3,
op_otel_metric_record0, op_otel_metric_record0,
@ -1324,17 +1326,6 @@ impl OtelSpan {
} }
} }
#[fast]
fn drop_link(&self) {
let mut state = self.0.borrow_mut();
match &mut **state {
OtelSpanState::Recording(span) => {
span.links.dropped_count += 1;
}
OtelSpanState::Done(_) => {}
}
}
#[fast] #[fast]
fn end(&self, end_time: f64) { fn end(&self, end_time: f64) {
let end_time = if end_time.is_nan() { let end_time = if end_time.is_nan() {
@ -1448,6 +1439,48 @@ fn op_otel_span_update_name<'s>(
} }
} }
#[op2(fast)]
fn op_otel_span_add_link<'s>(
scope: &mut v8::HandleScope<'s>,
span: v8::Local<'s, v8::Value>,
trace_id: v8::Local<'s, v8::Value>,
span_id: v8::Local<'s, v8::Value>,
#[smi] trace_flags: u8,
is_remote: bool,
#[smi] dropped_attributes_count: u32,
) -> bool {
let trace_id = parse_trace_id(scope, trace_id);
if trace_id == TraceId::INVALID {
return false;
};
let span_id = parse_span_id(scope, span_id);
if span_id == SpanId::INVALID {
return false;
};
let span_context = SpanContext::new(
trace_id,
span_id,
TraceFlags::new(trace_flags),
is_remote,
TraceState::NONE,
);
let Some(span) =
deno_core::_ops::try_unwrap_cppgc_object::<OtelSpan>(scope, span)
else {
return true;
};
let mut state = span.0.borrow_mut();
if let OtelSpanState::Recording(span) = &mut **state {
span.links.links.push(Link::new(
span_context,
vec![],
dropped_attributes_count,
));
}
true
}
struct OtelMeter(opentelemetry::metrics::Meter); struct OtelMeter(opentelemetry::metrics::Meter);
impl deno_core::GarbageCollected for OtelMeter {} impl deno_core::GarbageCollected for OtelMeter {}

View file

@ -15,6 +15,7 @@ import {
op_otel_metric_record2, op_otel_metric_record2,
op_otel_metric_record3, op_otel_metric_record3,
op_otel_metric_wait_to_observe, op_otel_metric_wait_to_observe,
op_otel_span_add_link,
op_otel_span_attribute1, op_otel_span_attribute1,
op_otel_span_attribute2, op_otel_span_attribute2,
op_otel_span_attribute3, op_otel_span_attribute3,
@ -186,7 +187,6 @@ interface OtelSpan {
spanContext(): SpanContext; spanContext(): SpanContext;
setStatus(status: SpanStatusCode, errorDescription: string): void; setStatus(status: SpanStatusCode, errorDescription: string): void;
dropEvent(): void; dropEvent(): void;
dropLink(): void;
end(endTime: number): void; end(endTime: number): void;
} }
@ -359,14 +359,24 @@ class Span {
return this; return this;
} }
addLink(_link: Link): Span { addLink(link: Link): Span {
this.#otelSpan?.dropLink(); const droppedAttributeCount = (link.droppedAttributesCount ?? 0) +
(link.attributes ? ObjectKeys(link.attributes).length : 0);
const valid = op_otel_span_add_link(
this.#otelSpan,
link.context.traceId,
link.context.spanId,
link.context.traceFlags,
link.context.isRemote ?? false,
droppedAttributeCount,
);
if (!valid) return this;
return this; return this;
} }
addLinks(links: Link[]): Span { addLinks(links: Link[]): Span {
for (let i = 0; i < links.length; i++) { for (let i = 0; i < links.length; i++) {
this.#otelSpan?.dropLink(); this.addLink(links[i]);
} }
return this; return this;
} }

View file

@ -8725,6 +8725,7 @@ fn lsp_completions_node_specifier() {
"node:readline", "node:readline",
"node:readline/promises", "node:readline/promises",
"node:repl", "node:repl",
"node:sqlite",
"node:stream", "node:stream",
"node:stream/consumers", "node:stream/consumers",
"node:stream/promises", "node:stream/promises",

View file

@ -90,6 +90,7 @@ util::unit_test_factory!(
querystring_test, querystring_test,
readline_test, readline_test,
repl_test, repl_test,
sqlite_test,
stream_test, stream_test,
string_decoder_test, string_decoder_test,
timers_test, timers_test,

View file

@ -22,6 +22,10 @@
}, },
"args": "run -A main.ts metric.ts", "args": "run -A main.ts metric.ts",
"output": "metric.out" "output": "metric.out"
},
"links": {
"args": "run -A main.ts links.ts",
"output": "links.out"
} }
} }
} }

View file

@ -0,0 +1,96 @@
{
"spans": [
{
"traceId": "00000000000000000000000000000001",
"spanId": "0000000000000001",
"traceState": "",
"parentSpanId": "",
"flags": 1,
"name": "example span",
"kind": 1,
"startTimeUnixNano": "[WILDCARD]",
"endTimeUnixNano": "[WILDCARD]",
"attributes": [],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"links": [
{
"traceId": "1234567890abcdef1234567890abcdef",
"spanId": "1234567890abcdef",
"traceState": "",
"attributes": [],
"droppedAttributesCount": 0,
"flags": 1
}
],
"droppedLinksCount": 0,
"status": {
"message": "",
"code": 0
}
},
{
"traceId": "00000000000000000000000000000002",
"spanId": "0000000000000002",
"traceState": "",
"parentSpanId": "",
"flags": 1,
"name": "example span",
"kind": 1,
"startTimeUnixNano": "[WILDCARD]",
"endTimeUnixNano": "[WILDCARD]",
"attributes": [],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"links": [
{
"traceId": "1234567890abcdef1234567890abcdef",
"spanId": "1234567890abcdef",
"traceState": "",
"attributes": [],
"droppedAttributesCount": 0,
"flags": 1
}
],
"droppedLinksCount": 0,
"status": {
"message": "",
"code": 0
}
},
{
"traceId": "00000000000000000000000000000003",
"spanId": "0000000000000003",
"traceState": "",
"parentSpanId": "",
"flags": 1,
"name": "example span",
"kind": 1,
"startTimeUnixNano": "[WILDCARD]",
"endTimeUnixNano": "[WILDCARD]",
"attributes": [],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"links": [
{
"traceId": "1234567890abcdef1234567890abcdef",
"spanId": "1234567890abcdef",
"traceState": "",
"attributes": [],
"droppedAttributesCount": 2,
"flags": 1
}
],
"droppedLinksCount": 0,
"status": {
"message": "",
"code": 0
}
}
],
"logs": [],
"metrics": []
}

View file

@ -0,0 +1,40 @@
// Copyright 2018-2025 the Deno authors. MIT license.
import { trace } from "npm:@opentelemetry/api@1.9.0";
const tracer = trace.getTracer("example-tracer");
const span1 = tracer.startSpan("example span", {
links: [{
context: {
traceId: "1234567890abcdef1234567890abcdef",
spanId: "1234567890abcdef",
traceFlags: 1,
},
}],
});
span1.end();
const span2 = tracer.startSpan("example span");
span2.addLink({
context: {
traceId: "1234567890abcdef1234567890abcdef",
spanId: "1234567890abcdef",
traceFlags: 1,
},
});
span2.end();
const span3 = tracer.startSpan("example span");
span3.addLink({
context: {
traceId: "1234567890abcdef1234567890abcdef",
spanId: "1234567890abcdef",
traceFlags: 1,
},
attributes: {
key: "value",
},
droppedAttributesCount: 1,
});
span3.end();

View file

@ -0,0 +1,5 @@
{
"tempDir": true,
"args": "install -g --root ./folder --config deno.json main.ts --name my-cli",
"output": "install.out"
}

View file

@ -0,0 +1,3 @@
{
"importMap": "./import_map.json"
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,3 @@
Warning "importMap" field in the specified config file we be ignored. Use the --import-map flag instead.
✅ Successfully installed my-cli
[WILDCARD]

View file

@ -0,0 +1 @@
console.log(1);

View file

@ -4,7 +4,7 @@ import crypto from "node:crypto";
import { Buffer } from "node:buffer"; import { Buffer } from "node:buffer";
import testVectors128 from "./gcmEncryptExtIV128.json" with { type: "json" }; import testVectors128 from "./gcmEncryptExtIV128.json" with { type: "json" };
import testVectors256 from "./gcmEncryptExtIV256.json" with { type: "json" }; import testVectors256 from "./gcmEncryptExtIV256.json" with { type: "json" };
import { assertEquals } from "@std/assert"; import { assertEquals, assertThrows } from "@std/assert";
const aesGcm = (bits: string, key: Uint8Array) => { const aesGcm = (bits: string, key: Uint8Array) => {
const ALGO = bits == "128" ? `aes-128-gcm` : `aes-256-gcm`; const ALGO = bits == "128" ? `aes-128-gcm` : `aes-256-gcm`;
@ -123,7 +123,7 @@ Deno.test({
// Issue #27441 // Issue #27441
// https://github.com/denoland/deno/issues/27441 // https://github.com/denoland/deno/issues/27441
Deno.test({ Deno.test({
name: "aes-256-gcm supports IV of non standard length", name: "aes-256-gcm supports IV of non standard length and auth tag check",
fn() { fn() {
const decipher = crypto.createDecipheriv( const decipher = crypto.createDecipheriv(
"aes-256-gcm", "aes-256-gcm",
@ -136,6 +136,10 @@ Deno.test({
"utf-8", "utf-8",
); );
assertEquals(decrypted, "this is a secret"); assertEquals(decrypted, "this is a secret");
decipher.final(); assertThrows(
() => decipher.final(),
TypeError,
"Failed to authenticate data",
);
}, },
}); });

View file

@ -0,0 +1,65 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { DatabaseSync } from "node:sqlite";
import { assertEquals, assertThrows } from "@std/assert";
Deno.test("[node/sqlite] in-memory databases", () => {
const db1 = new DatabaseSync(":memory:");
const db2 = new DatabaseSync(":memory:");
db1.exec("CREATE TABLE data(key INTEGER PRIMARY KEY);");
db1.exec("INSERT INTO data (key) VALUES (1);");
db2.exec("CREATE TABLE data(key INTEGER PRIMARY KEY);");
db2.exec("INSERT INTO data (key) VALUES (1);");
assertEquals(db1.prepare("SELECT * FROM data").all(), [{
key: 1,
__proto__: null,
}]);
assertEquals(db2.prepare("SELECT * FROM data").all(), [{
key: 1,
__proto__: null,
}]);
});
Deno.test("[node/sqlite] Errors originating from SQLite should be thrown", () => {
const db = new DatabaseSync(":memory:");
db.exec(`
CREATE TABLE test(
key INTEGER PRIMARY KEY
) STRICT;
`);
const stmt = db.prepare("INSERT INTO test(key) VALUES(?)");
assertEquals(stmt.run(1), { lastInsertRowid: 1, changes: 1 });
assertThrows(() => stmt.run(1), Error);
});
Deno.test(
{
permissions: { read: true, write: true },
name: "[node/sqlite] PRAGMAs are supported",
},
() => {
const tempDir = Deno.makeTempDirSync();
const db = new DatabaseSync(`${tempDir}/test.db`);
assertEquals(db.prepare("PRAGMA journal_mode = WAL").get(), {
journal_mode: "wal",
__proto__: null,
});
db.close();
},
);
Deno.test("[node/sqlite] StatementSync read bigints are supported", () => {
const db = new DatabaseSync(":memory:");
db.exec("CREATE TABLE data(key INTEGER PRIMARY KEY);");
db.exec("INSERT INTO data (key) VALUES (1);");
const stmt = db.prepare("SELECT * FROM data");
assertEquals(stmt.get(), { key: 1, __proto__: null });
stmt.setReadBigInts(true);
assertEquals(stmt.get(), { key: 1n, __proto__: null });
});