Repository navigation
Release the authorizer callback that RegisterAuthorizer replaces - #6
Merged
Merged
Conversation
RegisterAuthorizer created a callback handle on every call and never released the one it replaced, so every replaced or removed authorizer stayed in handleVals, with whatever its closure references, until the connection closed. A connection has a single authorizer, and SQLite no longer calls the previous one once sqlite3_set_authorizer returns, so its handle can be released then. The connection now keeps the handle of its authorizer and releases it when SQLite accepts the replacement. A mutex of its own guards it, not c.mu: sqlite3_set_authorizer waits for the database mutex while a statement runs, and holding c.mu meanwhile would block cancelling that statement, which takes c.mu to call sqlite3_interrupt. This is go-sqlite3 commit 068ac77 (mattn#1458), unchanged, with its test.
laodouya
added a commit
that referenced
this pull request
Nov 6, 2026
…lose (#9) Backport the closed-connection guards from mattn#1462 without acquiring c.mu in new callback-accessible checks, avoiding a deadlock during Close. Preserve the authorizer handle ownership fix from #6. Add regressions for calls after Close and calls from a rollback callback during Close.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RegisterAuthorizercreates a callback handle on every call and never releases the one it replaces, so every replaced or removed authorizer stays inhandleVals, together with whatever its closure references, until the connection closes. With the test of this branch,developfails withconnection has 9 callback handles, want 8.This backports go-sqlite3 commit 068ac77 (mattn#1458), which is on go-sqlite3 master but in no release yet. The connection keeps the handle of its authorizer and releases it once SQLite has accepted the replacement. A mutex of its own guards the handle rather than
c.mu:sqlite3_set_authorizerwaits for the database mutex while a statement runs, and holdingc.mumeanwhile would block cancelling that statement, which takesc.muto callsqlite3_interrupt. Code and test are those of go-sqlite3 line for line, and the commit keeps its author.CovenantSQL does not call
RegisterAuthorizer. The change touches onlyRegisterAuthorizerandClose, not how statements run, and query results stay byte-identical (see Testing).Left out:
Execchange that Update to SQLCipher 4.20.0 (SQLite 3.54.1) on go-sqlite3 v1.14.52 #4 already made here (see Report missing arguments when Exec has no arguments mattn/go-sqlite3#1471), and the SQLite 3.54.1 amalgamation, which this repository already has through SQLCipher. They come with the next update from go-sqlite3.Close, is in Return an error instead of crashing when a connection is used after Close #9. Both PRs changeRegisterAuthorizer, so whichever merges second has a conflict there; the merged method keeps thec.db == nilcheck of Return an error instead of crashing when a connection is used after Close #9 above the body from this PR.Testing
On linux/amd64 (Debian trixie container
cql-ci:go1.27pluslibsqlite3-dev), Go 1.27.3,--cpus 4.develop:go test -race -count=1 -run '^TestAuthorizerHandleRelease$' .fails withsqlite3_test.go:2035: connection has 9 callback handles, want 8. On this branch,go test -race -count=20 -run '^TestAuthorizerHandleRelease$' .passes 20 of 20.go vet -tags "$TAGS" .andgo test -race -count=1 -tags "$TAGS" .pass for each$TAGSof.github/workflows/go.yaml(empty,libsqlite3, the full set,sqlite_vacuum_full) and for CovenantSQL'ssqlite_omit_load_extension sqlite_omit_localtimeand miner tagssqlite_omit_load_extension sqlite_omit_localtime sqlite_vtable sqlite_fts5 sqlite_icu. Onlygo vetwith the full set fails, oncall to (*testing.T).Fatal from a non-test goroutineinsqlite3_opt_unlock_notify_test.go, identically ondevelop.vfs=nocurrenttimeand a Go function registered in aConnectHook, as CovenantSQL'sxenomint/sqlitedoes: types and conversions, numeric and text formatting, ICU case folding,LIKE,REGEXPand collation, date functions with and without the current time, JSON, window functions, FTS5 ranking, errors,EXPLAIN QUERY PLANand the SQLCipher pragmas. It prints every value with its Go type, every error, rows affected and last insert IDs. Its output is byte-identical withdevelopand with all five open PRs of this repository merged (tree74119820), for each tag set above and for the full set plus CovenantSQL's tags; withsqlite_os_trace, the VFS trace lines are identical once the temporary directory and the process ID are masked.