Skip to content

Commit 7d19668

Browse files
authored
fix(ext/node): throw RangeError when sqlite INTEGER is too large (denoland#27907)
Signed-off-by: Divy Srivastava <[email protected]>
1 parent 9da6a20 commit 7d19668

File tree

3 files changed

+23
-5
lines changed

3 files changed

+23
-5
lines changed

ext/node/ops/sqlite/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,7 @@ pub enum SqliteError {
3838
#[class(generic)]
3939
#[error("Invalid constructor")]
4040
InvalidConstructor,
41+
#[class(range)]
42+
#[error("The value of column {0} is too large to be represented as a JavaScript number: {1}")]
43+
NumberTooLarge(i32, i64),
4144
}

ext/node/ops/sqlite/statement.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use serde::Serialize;
1212

1313
use super::SqliteError;
1414

15+
// ECMA-262, 15th edition, 21.1.2.6. Number.MAX_SAFE_INTEGER (2^53-1)
16+
const MAX_SAFE_JS_INTEGER: i64 = 9007199254740991;
17+
1518
#[derive(Serialize)]
1619
#[serde(rename_all = "camelCase")]
1720
pub struct RunStatementResult {
@@ -122,17 +125,19 @@ impl StatementSync {
122125
&self,
123126
index: i32,
124127
scope: &mut v8::HandleScope<'a>,
125-
) -> v8::Local<'a, v8::Value> {
128+
) -> Result<v8::Local<'a, v8::Value>, SqliteError> {
126129
// SAFETY: `self.inner` is a valid pointer to a sqlite3_stmt
127130
// as it lives as long as the StatementSync instance.
128131
unsafe {
129-
match ffi::sqlite3_column_type(self.inner, index) {
132+
Ok(match ffi::sqlite3_column_type(self.inner, index) {
130133
ffi::SQLITE_INTEGER => {
131134
let value = ffi::sqlite3_column_int64(self.inner, index);
132135
if self.use_big_ints.get() {
133136
v8::BigInt::new_from_i64(scope, value).into()
134-
} else {
137+
} else if value.abs() <= MAX_SAFE_JS_INTEGER {
135138
v8::Integer::new(scope, value as _).into()
139+
} else {
140+
return Err(SqliteError::NumberTooLarge(index, value));
136141
}
137142
}
138143
ffi::SQLITE_FLOAT => {
@@ -162,7 +167,7 @@ impl StatementSync {
162167
}
163168
ffi::SQLITE_NULL => v8::null(scope).into(),
164169
_ => v8::undefined(scope).into(),
165-
}
170+
})
166171
}
167172
}
168173

@@ -183,7 +188,7 @@ impl StatementSync {
183188
let mut values = Vec::with_capacity(num_cols);
184189

185190
for (index, name) in iter {
186-
let value = self.column_value(index, scope);
191+
let value = self.column_value(index, scope)?;
187192
let name =
188193
v8::String::new_from_utf8(scope, name, v8::NewStringType::Normal)
189194
.unwrap()

tests/unit_node/sqlite_test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ Deno.test("[node/sqlite] StatementSync read bigints are supported", () => {
7373
assertEquals(stmt.get(), { key: 1n, __proto__: null });
7474
});
7575

76+
Deno.test("[node/sqlite] StatementSync integer too large", () => {
77+
const db = new DatabaseSync(":memory:");
78+
db.exec("CREATE TABLE data(key INTEGER PRIMARY KEY);");
79+
db.prepare("INSERT INTO data (key) VALUES (?)").run(
80+
Number.MAX_SAFE_INTEGER + 1,
81+
);
82+
83+
assertThrows(() => db.prepare("SELECT * FROM data").get());
84+
});
85+
7686
Deno.test("[node/sqlite] StatementSync blob are Uint8Array", () => {
7787
const db = new DatabaseSync(":memory:");
7888
const obj = db.prepare("select cast('test' as blob)").all();

0 commit comments

Comments
 (0)