Skip to content

Release the authorizer callback that RegisterAuthorizer replaces - #6

Merged
laodouya merged 1 commit into
developfrom
fix/release-replaced-authorizer
Nov 6, 2026
Merged

laodouya merged 1 commit into
developfrom
fix/release-replaced-authorizer

Conversation

@laodouya

@laodouya laodouya commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

RegisterAuthorizer creates a callback handle on every call and never releases the one it replaces, so every replaced or removed authorizer stays in handleVals, together with whatever its closure references, until the connection closes. With the test of this branch, develop fails with connection 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_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. 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 only RegisterAuthorizer and Close, not how statements run, and query results stay byte-identical (see Testing).

Left out:

Testing

On linux/amd64 (Debian trixie container cql-ci:go1.27 plus libsqlite3-dev), Go 1.27.3, --cpus 4.

  • The test file of this branch on the code of develop: go test -race -count=1 -run '^TestAuthorizerHandleRelease$' . fails with sqlite3_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" . and go test -race -count=1 -tags "$TAGS" . pass for each $TAGS of .github/workflows/go.yaml (empty, libsqlite3, the full set, sqlite_vacuum_full) and for CovenantSQL's sqlite_omit_load_extension sqlite_omit_localtime and miner tags sqlite_omit_load_extension sqlite_omit_localtime sqlite_vtable sqlite_fts5 sqlite_icu. Only go vet with the full set fails, on call to (*testing.T).Fatal from a non-test goroutine in sqlite3_opt_unlock_notify_test.go, identically on develop.
  • Query results: a program runs about 60 statements over an encrypted WAL database opened with vfs=nocurrenttime and a Go function registered in a ConnectHook, as CovenantSQL's xenomint/sqlite does: types and conversions, numeric and text formatting, ICU case folding, LIKE, REGEXP and collation, date functions with and without the current time, JSON, window functions, FTS5 ranking, errors, EXPLAIN QUERY PLAN and the SQLCipher pragmas. It prints every value with its Go type, every error, rows affected and last insert IDs. Its output is byte-identical with develop and with all five open PRs of this repository merged (tree 74119820), for each tag set above and for the full set plus CovenantSQL's tags; with sqlite_os_trace, the VFS trace lines are identical once the temporary directory and the process ID are masked.

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
laodouya merged commit 9095436 into develop Nov 6, 2026
18 checks passed
@laodouya
laodouya deleted the fix/release-replaced-authorizer branch November 6, 2026 02:05
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants