From 2df372f1dfa65105dd6eb316ba51ca2a59f493f6 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:56:40 -0700 Subject: [PATCH] sqlite: track registered user-defined functions Track scalar and aggregate/window functions on each DatabaseSync instance. Remove registrations through SQLite destruction callbacks and clear tracking when the database closes. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex --- src/node_sqlite.cc | 20 +++++++++++++++++--- src/node_sqlite.h | 9 +++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index f52f7e4f9b9c..2280c0876b4b 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -404,7 +404,13 @@ class CustomAggregate { start_(env->isolate(), start), step_fn_(env->isolate(), step_fn), inverse_fn_(env->isolate(), inverse_fn), - result_fn_(env->isolate(), result_fn) {} + result_fn_(env->isolate(), result_fn) { + db_->user_defined_functions_.insert(this); + } + + ~CustomAggregate() { + if (db_) db_->user_defined_functions_.erase(this); + } static void xStep(sqlite3_context* ctx, int argc, sqlite3_value** argv) { xStepBase(ctx, argc, argv, &CustomAggregate::step_fn_); @@ -772,9 +778,13 @@ UserDefinedFunction::UserDefinedFunction(Environment* env, : env_(env), fn_(env->isolate(), fn), db_(std::move(db)), - use_bigint_args_(use_bigint_args) {} + use_bigint_args_(use_bigint_args) { + db_->user_defined_functions_.insert(this); +} -UserDefinedFunction::~UserDefinedFunction() {} +UserDefinedFunction::~UserDefinedFunction() { + if (db_) db_->user_defined_functions_.erase(this); +} void UserDefinedFunction::xFunc(sqlite3_context* ctx, int argc, @@ -1071,6 +1081,8 @@ DatabaseSync::~DatabaseSync() { } void DatabaseSync::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackFieldWithSize("user_defined_functions", + user_defined_functions_.size() * sizeof(void*)); // TODO(tniessen): more accurately track the size of all fields tracker->TrackFieldWithSize( "open_config", sizeof(open_config_), "DatabaseOpenConfiguration"); @@ -1615,6 +1627,8 @@ void DatabaseSync::Close(const FunctionCallbackInfo& args) { int r = sqlite3_close_v2(db->connection_.get()); CHECK_ERROR_OR_THROW(env->isolate(), db, r, SQLITE_OK, void()); db->connection_.release(); + // Backups can defer SQLite destruction until after the connection is closed. + db->user_defined_functions_.clear(); } void DatabaseSync::Dispose(const v8::FunctionCallbackInfo& args) { diff --git a/src/node_sqlite.h b/src/node_sqlite.h index 306a47f6c47f..5b91e27d5736 100644 --- a/src/node_sqlite.h +++ b/src/node_sqlite.h @@ -266,6 +266,10 @@ class DatabaseSync : public BaseObject { void FinalizeBackups(); void UntrackStatement(StatementSync* statement); bool IsOpen(); + // SQL functions are one of several paths by which SQLite can invoke JS. + size_t GetUserDefinedFunctionCount() const { + return user_defined_functions_.size(); + } bool use_big_ints() const { return open_config_.get_use_big_ints(); } bool return_arrays() const { return open_config_.get_return_arrays(); } bool allow_bare_named_params() const { @@ -340,11 +344,16 @@ class DatabaseSync : public BaseObject { int trace_suppression_depth_ = 0; std::vector stepping_statements_; + // SQLite owns these scalar and aggregate/window function registrations. Its + // destroy callbacks untrack replaced functions and failed registrations. + std::unordered_set user_defined_functions_; std::set backups_; std::unordered_set sessions_; std::unordered_set statements_; BaseObjectPtr trace_channel_; + friend class UserDefinedFunction; + friend class CustomAggregate; friend class DatabaseSyncLimits; friend class Session; friend class SQLTagStore;