diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 9ebc0cb9731..ab96385ad04 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -71,8 +71,10 @@ using v8::TryCatch; using v8::Uint8Array; using v8::Value; -inline MaybeLocal Utf8StringMaybeOneByte(Isolate* isolate, - std::string_view input) { +inline MaybeLocal Utf8StringMaybeOneByte( + Isolate* isolate, + std::string_view input, + NewStringType type = NewStringType::kNormal) { // SQLITE_MAX_LENGTH exceeds String::kMaxLength, and V8 returns an empty // handle without throwing. Raise the error here or the value is dropped. if (input.size() > static_cast(String::kMaxLength)) [[unlikely]] { @@ -83,13 +85,9 @@ inline MaybeLocal Utf8StringMaybeOneByte(Isolate* isolate, const int len = static_cast(input.size()); if (simdutf::validate_ascii(input.data(), input.size())) { return String::NewFromOneByte( - isolate, - reinterpret_cast(input.data()), - NewStringType::kNormal, - len); + isolate, reinterpret_cast(input.data()), type, len); } - return String::NewFromUtf8( - isolate, input.data(), NewStringType::kNormal, len); + return String::NewFromUtf8(isolate, input.data(), type, len); } BindingData::BindingData(Realm* realm, Local wrap) @@ -3713,7 +3711,11 @@ int Database::TraceCallback(unsigned int type, char* expanded = sqlite3_expanded_sql(static_cast(p)); Local sql_string; if (expanded != nullptr) { - bool ok = String::NewFromUtf8(isolate, expanded).ToLocal(&sql_string); + bool ok = String::NewFromUtf8(isolate, + expanded, + NewStringType::kNormal, + static_cast(strlen(expanded))) + .ToLocal(&sql_string); sqlite3_free(expanded); if (!ok) { return 0; @@ -3722,7 +3724,11 @@ int Database::TraceCallback(unsigned int type, // Fallback to source SQL if expanded is unavailable const char* source = sqlite3_sql(static_cast(p)); if (source == nullptr || - !String::NewFromUtf8(isolate, source).ToLocal(&sql_string)) { + !String::NewFromUtf8(isolate, + source, + NewStringType::kNormal, + static_cast(strlen(source))) + .ToLocal(&sql_string)) { return 0; } } @@ -4004,7 +4010,7 @@ MaybeLocal Statement::ColumnNameToName(const int column) { return MaybeLocal(); } - return String::NewFromUtf8( + return Utf8StringMaybeOneByte( env()->isolate(), col_name, NewStringType::kInternalized) .As(); } @@ -4414,7 +4420,8 @@ void Statement::SourceSQLGetter(const FunctionCallbackInfo& args) { THROW_AND_RETURN_ON_BAD_STATE( env, stmt->IsFinalized(), "statement has been finalized"); Local sql; - if (!String::NewFromUtf8(env->isolate(), sqlite3_sql(stmt->statement_.get())) + if (!Utf8StringMaybeOneByte(env->isolate(), + sqlite3_sql(stmt->statement_.get())) .ToLocal(&sql)) { return; } @@ -4434,7 +4441,7 @@ void Statement::ExpandedSQLGetter(const FunctionCallbackInfo& args) { return THROW_ERR_SQLITE_ERROR( env->isolate(), "Expanded SQL text would exceed configured limits"); } - auto maybe_expanded = String::NewFromUtf8(env->isolate(), expanded); + auto maybe_expanded = Utf8StringMaybeOneByte(env->isolate(), expanded); sqlite3_free(expanded); Local result; if (!maybe_expanded.ToLocal(&result)) { diff --git a/test/parallel/test-sqlite-statement.js b/test/parallel/test-sqlite-statement.js index d06b9018bf8..2cd54ea2c2e 100644 --- a/test/parallel/test-sqlite-statement.js +++ b/test/parallel/test-sqlite-statement.js @@ -4,6 +4,7 @@ const { enoughTestMem, skipIfSQLiteMissing } = require('../common'); skipIfSQLiteMissing(); const { Database, Statement } = require('node:sqlite'); const { constants } = require('node:buffer'); +const dc = require('node:diagnostics_channel'); const { suite, test } = require('node:test'); suite('Statement() constructor', () => { @@ -1469,4 +1470,24 @@ suite('values larger than the maximum string length', { skip: !enoughTestMem }, using stmt = db.prepare('SELECT count(*) AS count FROM data'); t.assert.deepStrictEqual(stmt.get(), { __proto__: null, count: 0 }); }); + + test('expandedSQL throws instead of aborting', (t) => { + using db = new Database(':memory:'); + using stmt = db.prepare('SELECT ?'); + stmt.run(Buffer.alloc(blobSize)); + t.assert.throws(() => stmt.expandedSQL, tooLong); + }); + + test('sqlite.db.query subscribers do not affect the statement', (t) => { + const handler = t.mock.fn(); + dc.subscribe('sqlite.db.query', handler); + t.after(() => dc.unsubscribe('sqlite.db.query', handler)); + using db = new Database(':memory:'); + using stmt = db.prepare('SELECT length(?) AS len'); + t.assert.deepStrictEqual( + stmt.get(Buffer.alloc(blobSize)), + { __proto__: null, len: blobSize }, + ); + t.assert.strictEqual(handler.mock.callCount(), 0); + }); });