0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-01 12:16:11 -05:00

fix(ext/node): throw RangeError when sqlite INTEGER is too large (#27907)

Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
This commit is contained in:
Divy Srivastava 2025-02-01 13:19:53 +05:30 committed by GitHub
parent 9da6a20e57
commit 7d19668255
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 23 additions and 5 deletions

View file

@ -38,4 +38,7 @@ pub enum SqliteError {
#[class(generic)] #[class(generic)]
#[error("Invalid constructor")] #[error("Invalid constructor")]
InvalidConstructor, InvalidConstructor,
#[class(range)]
#[error("The value of column {0} is too large to be represented as a JavaScript number: {1}")]
NumberTooLarge(i32, i64),
} }

View file

@ -12,6 +12,9 @@ use serde::Serialize;
use super::SqliteError; use super::SqliteError;
// ECMA-262, 15th edition, 21.1.2.6. Number.MAX_SAFE_INTEGER (2^53-1)
const MAX_SAFE_JS_INTEGER: i64 = 9007199254740991;
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RunStatementResult { pub struct RunStatementResult {
@ -122,17 +125,19 @@ impl StatementSync {
&self, &self,
index: i32, index: i32,
scope: &mut v8::HandleScope<'a>, scope: &mut v8::HandleScope<'a>,
) -> v8::Local<'a, v8::Value> { ) -> Result<v8::Local<'a, v8::Value>, SqliteError> {
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt // SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
// as it lives as long as the StatementSync instance. // as it lives as long as the StatementSync instance.
unsafe { unsafe {
match ffi::sqlite3_column_type(self.inner, index) { Ok(match ffi::sqlite3_column_type(self.inner, index) {
ffi::SQLITE_INTEGER => { ffi::SQLITE_INTEGER => {
let value = ffi::sqlite3_column_int64(self.inner, index); let value = ffi::sqlite3_column_int64(self.inner, index);
if self.use_big_ints.get() { if self.use_big_ints.get() {
v8::BigInt::new_from_i64(scope, value).into() v8::BigInt::new_from_i64(scope, value).into()
} else { } else if value.abs() <= MAX_SAFE_JS_INTEGER {
v8::Integer::new(scope, value as _).into() v8::Integer::new(scope, value as _).into()
} else {
return Err(SqliteError::NumberTooLarge(index, value));
} }
} }
ffi::SQLITE_FLOAT => { ffi::SQLITE_FLOAT => {
@ -162,7 +167,7 @@ impl StatementSync {
} }
ffi::SQLITE_NULL => v8::null(scope).into(), ffi::SQLITE_NULL => v8::null(scope).into(),
_ => v8::undefined(scope).into(), _ => v8::undefined(scope).into(),
} })
} }
} }
@ -183,7 +188,7 @@ impl StatementSync {
let mut values = Vec::with_capacity(num_cols); let mut values = Vec::with_capacity(num_cols);
for (index, name) in iter { for (index, name) in iter {
let value = self.column_value(index, scope); let value = self.column_value(index, scope)?;
let name = let name =
v8::String::new_from_utf8(scope, name, v8::NewStringType::Normal) v8::String::new_from_utf8(scope, name, v8::NewStringType::Normal)
.unwrap() .unwrap()

View file

@ -73,6 +73,16 @@ Deno.test("[node/sqlite] StatementSync read bigints are supported", () => {
assertEquals(stmt.get(), { key: 1n, __proto__: null }); assertEquals(stmt.get(), { key: 1n, __proto__: null });
}); });
Deno.test("[node/sqlite] StatementSync integer too large", () => {
const db = new DatabaseSync(":memory:");
db.exec("CREATE TABLE data(key INTEGER PRIMARY KEY);");
db.prepare("INSERT INTO data (key) VALUES (?)").run(
Number.MAX_SAFE_INTEGER + 1,
);
assertThrows(() => db.prepare("SELECT * FROM data").get());
});
Deno.test("[node/sqlite] StatementSync blob are Uint8Array", () => { Deno.test("[node/sqlite] StatementSync blob are Uint8Array", () => {
const db = new DatabaseSync(":memory:"); const db = new DatabaseSync(":memory:");
const obj = db.prepare("select cast('test' as blob)").all(); const obj = db.prepare("select cast('test' as blob)").all();