Skip to content

Commit 2ed41fe

Browse files
committed
chore(upstream): sync node to 4251b8b
v26.x-staging now carries nodejs/node#66042 and #66209, which 3.0.0 ported from main; their new upstream tests pass unchanged. #65902 is a refactor with nothing to port.
1 parent c2ffeb6 commit 2ed41fe

7 files changed

Lines changed: 150 additions & 33 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
"typescript-eslint": "8.70.1"
188188
},
189189
"versions": {
190-
"nodejs": "v26.x-staging@6c924ee",
190+
"nodejs": "v26.x-staging@4251b8b",
191191
"sqlite": "3.53.4"
192192
},
193193
"overrides": {

‎src/upstream/node_sqlite.cc‎

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ using v8::Value;
7373

7474
inline MaybeLocal<String> Utf8StringMaybeOneByte(Isolate* isolate,
7575
std::string_view input) {
76+
// SQLITE_MAX_LENGTH exceeds String::kMaxLength, and V8 returns an empty
77+
// handle without throwing. Raise the error here or the value is dropped.
78+
if (input.size() > static_cast<size_t>(String::kMaxLength)) [[unlikely]] {
79+
isolate->ThrowException(node::ERR_STRING_TOO_LONG(isolate));
80+
return MaybeLocal<String>();
81+
}
82+
7683
const int len = static_cast<int>(input.size());
7784
if (simdutf::validate_ascii(input.data(), input.size())) {
7885
return String::NewFromOneByte(
@@ -351,7 +358,11 @@ class Database;
351358
inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, Database* db) {
352359
if (db->ShouldIgnoreSQLiteError()) {
353360
db->SetIgnoreNextSQLiteError(false);
354-
return;
361+
// Suppression that swallows no pending exception would also swallow the
362+
// SQLite error, reporting a failed statement as a success.
363+
if (isolate->HasPendingException()) {
364+
return;
365+
}
355366
}
356367

357368
Local<Object> e;
@@ -971,8 +982,7 @@ Intercepted DatabaseLimits::LimitsSetter(
971982
}
972983
}
973984

974-
sqlite3_limit(
975-
limits->database_->Connection(), limit_info->sqlite_limit_id, new_value);
985+
limits->database_->SetLimit(limit_info->sqlite_limit_id, new_value);
976986
return Intercepted::kYes;
977987
}
978988

@@ -1190,6 +1200,7 @@ int VirtualTableModule::xBestIndex(sqlite3_vtab* pVTab,
11901200
for (int hidden_idx = 0; hidden_idx < num_hidden; hidden_idx++) {
11911201
int col = mod->hidden_col_indices_[hidden_idx];
11921202

1203+
int args_before = argv_index;
11931204
for (int i = 0; i < pInfo->nConstraint; i++) {
11941205
if (pInfo->aConstraint[i].iColumn == col &&
11951206
pInfo->aConstraint[i].usable &&
@@ -1204,6 +1215,20 @@ int VirtualTableModule::xBestIndex(sqlite3_vtab* pVTab,
12041215
break;
12051216
}
12061217
}
1218+
1219+
// No usable constraint means the query supplied a parameter whose value is
1220+
// not available at this point in the plan. Accepting it would pass null to
1221+
// rows() and silently return empty results, so reject the plan and let
1222+
// SQLite pick a different ordering instead.
1223+
// https://www.sqlite.org/vtab.html#enforcing_required_parameters_on_table_valued_functions
1224+
if (argv_index == args_before) {
1225+
for (int i = 0; i < pInfo->nConstraint; i++) {
1226+
if (pInfo->aConstraint[i].iColumn == col &&
1227+
pInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ) {
1228+
return SQLITE_CONSTRAINT;
1229+
}
1230+
}
1231+
}
12071232
}
12081233

12091234
if (!idx_str.empty()) {
@@ -1650,15 +1675,14 @@ bool Database::Open() {
16501675

16511676
sqlite3_busy_timeout(connection_.get(), open_config_.get_timeout());
16521677

1653-
// Apply initial limits
16541678
for (const auto& [js_name, sqlite_limit_id] : kLimitMapping) {
1655-
const auto& limit_value = open_config_.initial_limits()[sqlite_limit_id];
1679+
const auto& limit_value = open_config_.limits()[sqlite_limit_id];
16561680
if (limit_value.has_value()) {
16571681
sqlite3_limit(connection_.get(), sqlite_limit_id, *limit_value);
16581682
}
16591683
}
16601684

1661-
if (allow_load_extension_) {
1685+
if (enable_load_extension_) {
16621686
if (env()->permission()->enabled()) [[unlikely]] {
16631687
THROW_ERR_LOAD_SQLITE_EXTENSION(env(),
16641688
"Cannot load SQLite extensions when the "
@@ -1677,6 +1701,15 @@ bool Database::Open() {
16771701
connection_.get(), SQLITE_TRACE_PROFILE, TraceCallback, this);
16781702
}
16791703

1704+
// The authorizer outlives the connection, so reopening must reinstall it.
1705+
Local<Value> authorizer =
1706+
object()->GetInternalField(kAuthorizerCallback).template As<Value>();
1707+
if (authorizer->IsFunction()) {
1708+
r = sqlite3_set_authorizer(
1709+
connection_.get(), Database::AuthorizerCallback, this);
1710+
CHECK_ERROR_OR_THROW(env()->isolate(), this, r, SQLITE_OK, false);
1711+
}
1712+
16801713
opened = true;
16811714
return true;
16821715
}
@@ -1724,6 +1757,11 @@ inline sqlite3* Database::Connection() {
17241757
return connection_.get();
17251758
}
17261759

1760+
void Database::SetLimit(int sqlite_limit_id, int value) {
1761+
sqlite3_limit(connection_.get(), sqlite_limit_id, value);
1762+
open_config_.set_limit(sqlite_limit_id, value);
1763+
}
1764+
17271765
void Database::SetIgnoreNextSQLiteError(bool ignore) {
17281766
ignore_next_sqlite_error_ = ignore;
17291767
}
@@ -2069,7 +2107,7 @@ void Database::New(const FunctionCallbackInfo<Value>& args) {
20692107
return;
20702108
}
20712109

2072-
open_config.set_initial_limit(sqlite_limit_id, limit_val);
2110+
open_config.set_limit(sqlite_limit_id, limit_val);
20732111
}
20742112
}
20752113
}
@@ -2171,9 +2209,7 @@ void Database::Prepare(const FunctionCallbackInfo<Value>& args) {
21712209
Local<Object> options = args[1].As<Object>();
21722210

21732211
Local<Value> return_arrays_v;
2174-
if (!options
2175-
->Get(env->context(),
2176-
FIXED_ONE_BYTE_STRING(env->isolate(), "returnArrays"))
2212+
if (!options->Get(env->context(), env->return_arrays_string())
21772213
.ToLocal(&return_arrays_v)) {
21782214
return;
21792215
}
@@ -2188,9 +2224,7 @@ void Database::Prepare(const FunctionCallbackInfo<Value>& args) {
21882224
}
21892225

21902226
Local<Value> read_big_ints_v;
2191-
if (!options
2192-
->Get(env->context(),
2193-
FIXED_ONE_BYTE_STRING(env->isolate(), "readBigInts"))
2227+
if (!options->Get(env->context(), env->read_bigints_string())
21942228
.ToLocal(&read_big_ints_v)) {
21952229
return;
21962230
}
@@ -2205,10 +2239,7 @@ void Database::Prepare(const FunctionCallbackInfo<Value>& args) {
22052239
}
22062240

22072241
Local<Value> allow_bare_named_params_v;
2208-
if (!options
2209-
->Get(env->context(),
2210-
FIXED_ONE_BYTE_STRING(env->isolate(),
2211-
"allowBareNamedParameters"))
2242+
if (!options->Get(env->context(), env->allow_bare_named_params_string())
22122243
.ToLocal(&allow_bare_named_params_v)) {
22132244
return;
22142245
}
@@ -2224,10 +2255,7 @@ void Database::Prepare(const FunctionCallbackInfo<Value>& args) {
22242255
}
22252256

22262257
Local<Value> allow_unknown_named_params_v;
2227-
if (!options
2228-
->Get(env->context(),
2229-
FIXED_ONE_BYTE_STRING(env->isolate(),
2230-
"allowUnknownNamedParameters"))
2258+
if (!options->Get(env->context(), env->allow_unknown_named_params_string())
22312259
.ToLocal(&allow_unknown_named_params_v)) {
22322260
return;
22332261
}
@@ -5338,10 +5366,8 @@ static void Initialize(Local<Object> target,
53385366
db_tmpl,
53395367
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
53405368
Database::IsTransactionGetter);
5341-
SetSideEffectFreeGetter(isolate,
5342-
db_tmpl,
5343-
FIXED_ONE_BYTE_STRING(isolate, "limits"),
5344-
Database::LimitsGetter);
5369+
SetSideEffectFreeGetter(
5370+
isolate, db_tmpl, env->limits_string(), Database::LimitsGetter);
53455371
Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
53465372
Local<v8::Symbol> sqlite_type_symbol =
53475373
v8::Symbol::For(isolate, sqlite_type_key);

‎src/upstream/node_sqlite.h‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,13 @@ class DatabaseOpenConfiguration {
139139

140140
inline bool get_enable_defensive() const { return defensive_; }
141141

142-
inline void set_initial_limit(int sqlite_limit_id, int value) {
143-
initial_limits_.at(sqlite_limit_id) = value;
142+
inline void set_limit(int sqlite_limit_id, int value) {
143+
limits_.at(sqlite_limit_id) = value;
144144
}
145145

146-
inline const std::array<std::optional<int>, kLimitMapping.size()>&
147-
initial_limits() const {
148-
return initial_limits_;
146+
inline const std::array<std::optional<int>, kLimitMapping.size()>& limits()
147+
const {
148+
return limits_;
149149
}
150150

151151
private:
@@ -159,7 +159,7 @@ class DatabaseOpenConfiguration {
159159
bool allow_bare_named_params_ = true;
160160
bool allow_unknown_named_params_ = false;
161161
bool defensive_ = true;
162-
std::array<std::optional<int>, kLimitMapping.size()> initial_limits_{};
162+
std::array<std::optional<int>, kLimitMapping.size()> limits_{};
163163
};
164164

165165
class Database;
@@ -277,6 +277,7 @@ class Database : public BaseObject {
277277
return open_config_.get_allow_unknown_named_params();
278278
}
279279
sqlite3* Connection();
280+
void SetLimit(int sqlite_limit_id, int value);
280281

281282
// In some situations, such as when using custom functions, it is possible
282283
// that SQLite reports an error while JavaScript already has a pending

‎test/node-compat/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ node --test test/node-compat/test-sqlite-statement-columns.test.js
2020
```
2121

2222
Source: https://github.com/nodejs/node/tree/v26.x-staging/test/parallel
23-
Commit: 6c924ee1b073996c5203ee6adbf6f6ad8981c093
23+
Commit: 4251b8b1f9426672642a46c9a43538c72c37ccbd

‎test/node-compat/test-sqlite-authz.test.js‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,43 @@ suite("Database.prototype.setAuthorizer()", () => {
330330
},
331331
);
332332
});
333+
334+
it("remains installed after close() and open()", (t) => {
335+
const db = new Database(":memory:");
336+
const authorizer = t.mock.fn(() => constants.SQLITE_DENY);
337+
db.setAuthorizer(authorizer);
338+
339+
assert.throws(
340+
() => {
341+
db.exec("CREATE TABLE x (a)");
342+
},
343+
{ code: "ERR_SQLITE_ERROR" },
344+
);
345+
const callsBefore = authorizer.mock.callCount();
346+
assert.ok(callsBefore > 0);
347+
348+
db.close();
349+
db.open();
350+
351+
assert.throws(
352+
() => {
353+
db.exec("CREATE TABLE x (a)");
354+
},
355+
{ code: "ERR_SQLITE_ERROR" },
356+
);
357+
assert.ok(authorizer.mock.callCount() > callsBefore);
358+
});
359+
360+
it("stays cleared after close() and open()", () => {
361+
const db = new Database(":memory:");
362+
db.setAuthorizer(() => constants.SQLITE_DENY);
363+
db.setAuthorizer(null);
364+
365+
db.close();
366+
db.open();
367+
368+
db.exec("CREATE TABLE x (a)");
369+
});
333370
});
334371

335372
// SQLite forbids an authorizer callback from modifying the connection that

‎test/node-compat/test-sqlite-limits.test.js‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,22 @@ suite("Database limits", () => {
361361
},
362362
);
363363
});
364+
365+
test("limits set at runtime survive close() and open()", (t) => {
366+
const db = new Database(":memory:");
367+
368+
db.limits.attach = 0;
369+
db.close();
370+
db.open();
371+
372+
t.assert.strictEqual(db.limits.attach, 0);
373+
t.assert.throws(
374+
() => {
375+
db.exec("ATTACH DATABASE ':memory:' AS db1");
376+
},
377+
{
378+
message: /too many attached databases/,
379+
},
380+
);
381+
});
364382
});

‎test/node-compat/test-sqlite-statement.test.js‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
// Flags: --expose-gc
1212
"use strict";
13+
const { enoughTestMem } = require("../common/test-utils.cjs");
1314
const { Database, Statement } = require("@photostructure/sqlite");
15+
const { constants } = require("node:buffer");
1416
const { suite, test } = require("node:test");
1517

1618
suite("Statement() constructor", () => {
@@ -1613,3 +1615,36 @@ suite("options.persistent", () => {
16131615
t.assert.deepStrictEqual(stmt.get(), { __proto__: null, val: 42n });
16141616
});
16151617
});
1618+
1619+
suite(
1620+
"values larger than the maximum string length",
1621+
{ skip: !enoughTestMem },
1622+
() => {
1623+
// hex() doubles its input, so this is the smallest blob whose text form
1624+
// exceeds what V8 can hold in a string.
1625+
const blobSize = (constants.MAX_STRING_LENGTH >>> 1) + 1;
1626+
const tooLong = { code: "ERR_STRING_TOO_LONG", name: "Error" };
1627+
1628+
test("get() throws instead of returning undefined", (t) => {
1629+
using db = new Database(":memory:");
1630+
using stmt = db.prepare("SELECT hex(zeroblob(?))");
1631+
t.assert.throws(() => {
1632+
stmt.get(blobSize);
1633+
}, tooLong);
1634+
});
1635+
1636+
test("exec() surfaces the error from a user-defined function", (t) => {
1637+
using db = new Database(":memory:");
1638+
db.exec("CREATE TABLE data(val TEXT)");
1639+
db.function("identity", (val) => val);
1640+
1641+
t.assert.throws(() => {
1642+
db.exec(
1643+
`INSERT INTO data (val) VALUES (identity(hex(zeroblob(${blobSize}))))`,
1644+
);
1645+
}, tooLong);
1646+
using stmt = db.prepare("SELECT count(*) AS count FROM data");
1647+
t.assert.deepStrictEqual(stmt.get(), { __proto__: null, count: 0 });
1648+
});
1649+
},
1650+
);

0 commit comments

Comments
 (0)