From f212aee48325dcf6d85e1b76bae21d91b8bc59b8 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sat, 7 Feb 2026 11:48:53 -0500 Subject: [PATCH 1/4] build: enable temporal on GHA macOS build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61691 Refs: https://github.com/nodejs/node/pull/60942 Refs: https://github.com/nodejs/node/issues/58730 Reviewed-By: Richard Lau Reviewed-By: Michaël Zasso Reviewed-By: Yagiz Nizipli Reviewed-By: Colin Ihrig --- .github/workflows/test-macos.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index dc6283d90ccebd..3920b9a365ebae 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -68,6 +68,7 @@ env: PYTHON_VERSION: '3.14' XCODE_VERSION: '16.4' FLAKY_TESTS: keep_retrying + RUSTC_VERSION: '1.82' permissions: contents: read @@ -95,6 +96,10 @@ jobs: allow-prereleases: true - name: Set up Xcode ${{ env.XCODE_VERSION }} run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app + - name: Install Rust ${{ env.RUSTC_VERSION }} + run: | + rustup override set "$RUSTC_VERSION" + rustup --version - name: Set up sccache uses: Mozilla-Actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 with: @@ -124,7 +129,7 @@ jobs: df -h echo "::endgroup::" - name: Build - run: make -C node build-ci -j$(getconf _NPROCESSORS_ONLN) V=1 CONFIG_FLAGS="--error-on-warn" + run: make -C node build-ci -j$(getconf _NPROCESSORS_ONLN) V=1 CONFIG_FLAGS="--error-on-warn --v8-enable-temporal-support" - name: Free Space After Build run: df -h - name: Test From 346ad95c3cdf32891727b9fd81fd7a2bf9a30a69 Mon Sep 17 00:00:00 2001 From: Ali Hassan <24819103+thisalihassan@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:52:38 +0500 Subject: [PATCH 2/4] sqlite: avoid extra copy for large text binds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When binding UTF-8 strings to prepared statements, transfer ownership of malloc-backed Utf8Value buffers to SQLite to avoid an extra copy for large strings. Use sqlite3_bind_blob64() when binding BLOB parameters. PR-URL: https://github.com/nodejs/node/pull/61580 Reviewed-By: Matteo Collina Reviewed-By: Edy Silva Reviewed-By: René Reviewed-By: Zeyu "Alex" Yang --- src/node_sqlite.cc | 34 ++++++++++++++++++------ test/parallel/test-sqlite-data-types.js | 35 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 6bb5d63ddc1a59..3f4749286406e0 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -2261,20 +2261,38 @@ bool StatementSync::BindValue(const Local& value, const int index) { // Dates could be supported by converting them to numbers. However, there // would not be a good way to read the values back from SQLite with the // original type. + Isolate* isolate = env()->isolate(); int r; if (value->IsNumber()) { - double val = value.As()->Value(); + const double val = value.As()->Value(); r = sqlite3_bind_double(statement_, index, val); } else if (value->IsString()) { - Utf8Value val(env()->isolate(), value.As()); - r = sqlite3_bind_text( - statement_, index, *val, val.length(), SQLITE_TRANSIENT); + Utf8Value val(isolate, value.As()); + if (val.IsAllocated()) { + // Avoid an extra SQLite copy for large strings by transferring ownership + // of the malloc()'d buffer to SQLite. + char* data = *val; + const sqlite3_uint64 length = static_cast(val.length()); + val.Release(); + r = sqlite3_bind_text64( + statement_, index, data, length, std::free, SQLITE_UTF8); + } else { + r = sqlite3_bind_text64(statement_, + index, + *val, + static_cast(val.length()), + SQLITE_TRANSIENT, + SQLITE_UTF8); + } } else if (value->IsNull()) { r = sqlite3_bind_null(statement_, index); } else if (value->IsArrayBufferView()) { ArrayBufferViewContents buf(value); - r = sqlite3_bind_blob( - statement_, index, buf.data(), buf.length(), SQLITE_TRANSIENT); + r = sqlite3_bind_blob64(statement_, + index, + buf.data(), + static_cast(buf.length()), + SQLITE_TRANSIENT); } else if (value->IsBigInt()) { bool lossless; int64_t as_int = value.As()->Int64Value(&lossless); @@ -2285,13 +2303,13 @@ bool StatementSync::BindValue(const Local& value, const int index) { r = sqlite3_bind_int64(statement_, index, as_int); } else { THROW_ERR_INVALID_ARG_TYPE( - env()->isolate(), + isolate, "Provided value cannot be bound to SQLite parameter %d.", index); return false; } - CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false); + CHECK_ERROR_OR_THROW(isolate, db_.get(), r, SQLITE_OK, false); return true; } diff --git a/test/parallel/test-sqlite-data-types.js b/test/parallel/test-sqlite-data-types.js index 590c6d5bdc1e6e..26af15a777d234 100644 --- a/test/parallel/test-sqlite-data-types.js +++ b/test/parallel/test-sqlite-data-types.js @@ -82,6 +82,41 @@ suite('data binding and mapping', () => { }); }); + test('large strings are bound correctly', (t) => { + const db = new DatabaseSync(nextDb()); + t.after(() => { db.close(); }); + const setup = db.exec( + 'CREATE TABLE data(key INTEGER PRIMARY KEY, text TEXT) STRICT;' + ); + t.assert.strictEqual(setup, undefined); + + t.assert.deepStrictEqual( + db.prepare('INSERT INTO data (key, text) VALUES (?, ?)').run(1, ''), + { changes: 1, lastInsertRowid: 1 }, + ); + + const update = db.prepare('UPDATE data SET text = ? WHERE key = 1'); + + // > 1024 bytes so `Utf8Value` uses heap storage internally. + const largeAscii = 'a'.repeat(8 * 1024); + // Force a non-one-byte string path through UTF-8 conversion. + const largeUnicode = '\u2603'.repeat(2048); + + const res = update.run(largeAscii); + t.assert.strictEqual(res.changes, 1); + + t.assert.strictEqual( + db.prepare('SELECT text FROM data WHERE key = 1').get().text, + largeAscii, + ); + + t.assert.strictEqual(update.run(largeUnicode).changes, 1); + t.assert.strictEqual( + db.prepare('SELECT text FROM data WHERE key = 1').get().text, + largeUnicode, + ); + }); + test('unsupported data types', (t) => { const db = new DatabaseSync(nextDb()); t.after(() => { db.close(); }); From 28979fb5951ac0fb4a975e9ee7e0b2ed8db3fe4c Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Fri, 6 Feb 2026 20:35:08 +0300 Subject: [PATCH 3/4] src: use simdutf for one-byte string UTF-8 write in stringBytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61696 Reviewed-By: Yagiz Nizipli Reviewed-By: Stephen Belanger Reviewed-By: Daniel Lemire Reviewed-By: Vinícius Lourenço Claro Cardoso Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Gürgün Dayıoğlu Reviewed-By: Anna Henningsen Reviewed-By: René --- src/string_bytes.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 6341346da07bec..28432a82ad8835 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -266,8 +266,17 @@ size_t StringBytes::Write(Isolate* isolate, case BUFFER: case UTF8: - nbytes = str->WriteUtf8V2( - isolate, buf, buflen, String::WriteFlags::kReplaceInvalidUtf8); + if (input_view.is_one_byte()) { + // Use simdutf for one-byte strings instead of V8's WriteUtf8V2. + nbytes = simdutf::convert_latin1_to_utf8_safe( + reinterpret_cast(input_view.data8()), + input_view.length(), + buf, + buflen); + } else { + nbytes = str->WriteUtf8V2( + isolate, buf, buflen, String::WriteFlags::kReplaceInvalidUtf8); + } break; case UCS2: { From 88760f9e8b917ae31454e45856e349489ccf9868 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 7 Feb 2026 15:50:17 -0500 Subject: [PATCH 4/4] doc: mention default option for test runner env PR-URL: https://github.com/nodejs/node/pull/61659 Reviewed-By: Ethan Arrowood Reviewed-By: Jacob Smith Reviewed-By: Moshe Atlow Reviewed-By: Pietro Marchini Reviewed-By: Chemi Atlow Reviewed-By: Colin Ihrig Reviewed-By: Tierney Cyren Reviewed-By: Luigi Pinca --- doc/api/test.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/api/test.md b/doc/api/test.md index 692d8686545040..40fb08d0d5b181 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -1561,6 +1561,7 @@ changes: * `env` {Object} Specify environment variables to be passed along to the test process. This options is not compatible with `isolation='none'`. These variables will override those from the main process, and are not merged with `process.env`. + **Default:** `process.env`. * Returns: {TestsStream} **Note:** `shard` is used to horizontally parallelize test running across