modernc.org/sqlite:Driver 支持注册函数与模块,vtab 行为变更
sqlite: let a caller-constructed Driver register its own functions, collation...
Go 开发者若用 modernc.org/sqlite 且通过 vtab.RegisterModule 注册模块,升级后行为会变:在自定义 Driver 上注册的模块不再全局可见,可能触发 "no such module"。建议检查代码中 vtab.RegisterModule 的调用方式,确认是否依赖旧行为。
Driver holds four categories of registration state but only connection hooks could be put on a constructed one. Functions and collations were reachable through the package-level API alone, and modules through the package-level driver only, so a constructed Driver was half-built: its modules field was written and read through the package-level instance, making it process-global state wearing a per-instance field. This is the additive half of #254, with one narrow, loud exception spelled out below: - registerFunction and registerCollation become methods on *Driver. The package-level RegisterFunction, RegisterScalarFunction, RegisterDeterministicScalarFunction and RegisterCollationUtf8 keep targeting the package-level driver, spelled explicitly now rather than by relying on the receiver named d shadowing the package variable of the same name. newDriver is renamed defaultDriver, since it read like a constructor but returns the singleton. - Driver gains RegisterFunction, RegisterScalarFunction, RegisterDeterministicScalarFunction, RegisterCollationUtf8 and RegisterModule, plus Must* variants of the first four, each registering on that Driver alone. The zero Driver stays usable: the maps are created on demand, since a constructed &Driver{} has nil maps where the package-level instance is built with make. A mutex on Driver makes concurrent registrations safe, so a *Driver can be handed out for several packages to fill from their init functions. - A connection now receives the union of the modules registered on the package-level driver and those registered on the Driver that opened it. The package-level set is applied unconditionally, which is what every connection has received since module support was added, so the isolating change #254 warns about is not made here. A name present in both resolves to the package-level implementation. - The ID handed to sqlite3_create_module_v2 as pAux is allocated per (Driver, name) pair, not per name. It is what every trampoline dispatches on, and a per-name ID would make two Drivers registering the same module name share one entry, with the last registration winning process-wide and retroactively, on connections already open. - vtab.RegisterModule honours its db argument: a non-nil db registers on the driver backing it when that driver implements the new vtab.ModuleRegisterer, and a nil db keeps targeting the driver this package registers as "sqlite". For a db opened on "sqlite" the two are the same driver, so the outcome is unchanged. The exception: vtab.RegisterModule(db, ...) where db was opened on a caller-constructed Driver used to discard the db argument and land on the "sqlite" driver, reaching every connection in the process. It now lands on the constructed driver alone, so a sql.Open("sqlite") connection that used to resolve such a module gets "no such module" instead. That same call is also the one route by which existing code can hold a module name on both a constructed Driver and the package-level one, so the collision rule above is a second, narrower part of the exception rather than a continuation. Before, the first of the two registrations won and the second was refused as already registered; now the package-level implementation wins on the constructed Driver's connections whichever order they ran in. No order-independent rule reproduces the old behavior, and reaching the case at all means the program ignored the error the older version returned. Two smaller deviations round it out: Driver.RegisterModule reports no error for such a collision, and vtab.RegisterModule validates its name and module arguments before the not-implemented check, so a call with an empty name that returned ErrNotImplemented outside this driver now returns "vtab: module name must be non-empty". Tests cover the new registrations, that they reject duplicates, that two constructed Drivers stay isolated from each other and from the package-level driver -- including when both register the same module name, exercised on a connection pinned before the second driver opens one -- that concurrent registrations survive the race detector, that globally registered modules still reach a constructed Driver, and that the db argument selects the driver. The behavior-preserving properties, the same-name case and the concurrency claim were each verified by breaking them deliberately and confirming the test fails. Updates #254
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力