Files
SnowLuma/.pnpm-store/v11/files/3c/7ff35a78b97522dfab72344d1ef04289085b98b7d346e669f91ae8a47a9bc82bc7b7f87c563cd38b6016593fd96749de668bd53acaf4e51183fc65d556c9d4
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

142 lines
4.9 KiB
Plaintext

#ifndef NODE_SQLITE3_SRC_FUNCTION_H
#define NODE_SQLITE3_SRC_FUNCTION_H
// User-defined functions, aggregates, window functions and collations
// (Deliverable 06). sqlite invokes these callbacks on whatever thread is
// executing the statement — a libuv worker here — so each invocation makes
// a blocking round trip to the JS thread through a ThreadSafeFunction:
// the worker marshals the arguments into Cells and waits on a condition
// variable; the JS thread converts, runs the user's JS, marshals the
// result back and signals.
//
// The deadlock this creates on the synchronous API (the JS thread is the
// one blocked inside sqlite) is refused with an explicit error, not
// deadlocked and not silently downgraded to inline execution — see
// Database::sync_sqlite_depth and the JsScalarFunc refusal path.
#include <string>
#include <vector>
#include <sqlite3.h>
#include <napi.h>
#include <uv.h>
#include "convert.h"
#include "database.h"
namespace node_sqlite3 {
// One registered JavaScript callback: a scalar function, an
// aggregate/window function (start/step/result/inverse), or a collation
// comparator. Owned by Database::js_functions / js_collations.
struct JsFunc {
Database* db;
std::string name;
int nArg;
bool is_collation = false;
// Scalar / collation implementation.
Napi::FunctionReference fn;
// Aggregate implementation. A non-empty `inverse` makes it a window
// function (registered via sqlite3_create_window_function).
Napi::FunctionReference start;
Napi::FunctionReference step;
Napi::FunctionReference result;
Napi::FunctionReference inverse;
// Set when the registration is replaced or removed. The JS-thread
// halves check it before touching the references; calls already in
// flight see it and fail with an error instead of using freed state.
// Exclusive scheduling makes in-flight calls impossible at removal
// time; this covers calls queued behind the round trip of another.
bool dead = false;
JsFunc(Database* db_, std::string name_, int nArg_) :
db(db_), name(std::move(name_)), nArg(nArg_) {
db->Ref();
}
};
// Per-aggregate-invocation state, pointed to by sqlite's aggregate
// context. Created, mutated and freed on the JS thread only (inside the
// round trips); the worker only carries the slot pointer across.
struct AggState {
Napi::Reference<Napi::Value> acc;
bool has_acc = false;
bool failed = false;
};
// One round trip. Allocated on the worker, handed to the JS thread via
// the channel, and freed by whichever side the `fire_and_forget` flag
// names: the worker for waited calls (it resumes after the signal), the
// JS thread for fire-and-forget cleanups (the worker never touches the
// call again after a successful enqueue).
struct FuncCall {
enum Kind {
kScalar, // fn(...args) -> result
kStep, // acc = step(acc, ...args)
kFinal, // result = result(acc); aggregate over, state freed
kValue, // result = result(acc); window frame value, state kept
kInverse, // acc = inverse(acc, ...args)
kCollation, // cmp(a, b) -> sign
kProgress, // progress() -> truthy? abort the statement
kAggCleanup // free the aggregate state without touching JS state
};
JsFunc* fn;
Database* db;
Kind kind;
std::vector<Cell> args;
Cell result;
// Aggregate-context slot for the step/final/value/inverse kinds; NULL
// for scalars, collations and cleanups (cleanups carry the state
// directly in `agg`).
AggState** agg_slot = nullptr;
AggState* agg = nullptr;
bool errored = false;
std::string error;
bool fire_and_forget = false;
uv_mutex_t mutex;
uv_cond_t cond;
bool done = false;
FuncCall(JsFunc* fn_, Kind kind_) : fn(fn_), db(fn_->db), kind(kind_) {
uv_mutex_init(&mutex);
uv_cond_init(&cond);
}
};
// Registration batons. The JS layer does option parsing; these carry the
// normalized result to the exclusive handler.
struct FunctionBaton : Database::Baton {
std::string name;
int nArg;
int flags;
bool is_collation = false;
Napi::FunctionReference fn; // scalar / collation
// Aggregate halves:
Napi::FunctionReference start;
Napi::FunctionReference step;
Napi::FunctionReference result;
Napi::FunctionReference inverse;
FunctionBaton(Database* db_, Napi::Function cb_, const char* name_,
int nArg_, int flags_) :
Baton(db_, cb_), name(name_), nArg(nArg_), flags(flags_) {}
virtual ~FunctionBaton() override = default;
};
struct RemoveFunctionBaton : Database::Baton {
std::string name;
bool collation = false;
RemoveFunctionBaton(Database* db_, Napi::Function cb_, const char* name_) :
Baton(db_, cb_), name(name_) {}
virtual ~RemoveFunctionBaton() override = default;
};
} // namespace node_sqlite3
#endif