feat(plugin-sqlite): load SQLite extensions such as sqlite-vec and SpatiaLite on SQLite and local libSQL connections - #3064
Merged
Merged
Conversation
…atiaLite on SQLite and local libSQL connections
…t alert to its own lines and load a file once
… app and load only the exact approved file
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
… steps to the form
This branch was successfully deployed
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.
Fixes #2502
What was wrong
Two separate things stopped a SQLite extension from ever loading:
libsqlite3, which Apple builds withSQLITE_OMIT_LOAD_EXTENSION. On macOS 27 (SQLite 3.54.0),sqlite3_load_extensionandsqlite3_enable_load_extensionare neither declared in the SDK header nor exported from the dylib (dlsymreturns NULL), andsqlite3_compileoption_getlistsOMIT_LOAD_EXTENSION. No code change inside the plugin could get past that.load_extension()SQL function on purpose. It still does.What this changes
The plugins link their own SQLite
scripts/build-sqlite.shbuilds SQLite 3.53.4 from the canonical source tree.--enable-update-limit, soDELETE ... LIMITkeeps working.-fvisibility=hidden, and archives withZERO_AR_DATE, so two builds are byte-identical.THREADSAFE=2. It leaves outFTS3_TOKENIZER.libsqlite3_vendored, so it can never win a-lsqlite3lookup from a target that hasLibs/on its search path.scripts/check-sqlite-build.shis a committed probe that checks the archive itself:SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSIONopens the C API only whileload_extension()staysnot authorized;sqlite3_symbol is exported;SQLiteBuiltinNames.swiftmatches the library's functions, table-valued functions and keywords.CSQLite(headers plus adb_configshim, sincesqlite3_db_configis variadic and Swift cannot call it) andTableProSQLiteCore(the authorizer, moved, and the two C calls loading needs) are newTableProCorepackage targets. Both plugins depend on them and-force_loadthe archive in place of-lsqlite3.SQLiteDriverandLibSQLDriverPluginno longer linklibsqlite3.dylib, and theirsqlite3_*symbols are non-external.libs-v1withscripts/publish-libs.sh, andLibs/checksums.sha256gains exactly those four lines. A checkout with an olderLibs/needsscripts/download-libs.sh --forceonce.Loading
LoadableExtensionLoader(PluginKit, pure, tested) opens loading with the C API only, loads each file in list order, and closes loading again whatever happened. A handle that refuses to close fails the connect. Before SQLite sees a file, the loader checks:~expanded explicitly (NSString.expandingTildeInPathsilently truncates at 1,024 bytes);.dylibguess, so the approved file is the file that loads;When SQLite reports a failed open, the loader opens the file itself to get dyld's reason for the file actually listed:
slice is not valid mach-o file,incompatible architecture,library load disallowed by system policy. It never re-opens a library that did open, so an initializer never runs twice.The list is a
ConnectionFieldwhose newcontentis.loadableExtensions. That is an additive change and reuses the pending kit 33;check-pluginkit-abi.shreports additions only. It syncs, exports and imports withadditionalFields. libSQL declares it for Local File mode only, in the plugin and in both curated snapshots.Trust
Nothing is approved by default:
LoadableExtensionApprovalStoreis device-local and never synced. It records approval per connection, file and entry point.DatabaseDriverFactoryrefuses to build a driver whose visible list holds an unapproved or invalid entry. That covers user connects, reconnects, pooled metadata drivers, Test Connection, MCP and AppleScript alike. No stored field exempts a list.ConnectConsentcall.SELECT BlobToFile(...)would read as a read.INSERT INTO t(id),WITH ids(id) AS,CREATE TABLE t(...)) is not read as a call.USING module(...)is checked like a call, because an extension module can read files.libSQL
Local File mode gets the same loader, and the authorizer it never had. That second part fixes a released defect:
fts3_tokenizerwas reachable from SQL there. The system library hasENABLE_FTS3_TOKENIZER, and the SQLite plugin's own comment records it as a SIGSEGV.Verification
SQLitePluginDriversources, from aswiftcharness linked against the vendored archive:vec0KNN query answered;load_extension()andfts3_tokenizercame backnot authorized;DELETE ... LIMITworked;SQLiteExtensionCallScannerTests,AdvancedPaneExtensionTests, plus additions toConnectionFailureClassifierTests,LibSQLConnectionFieldsTests,ExternalStatementGateTestsand the MCP, AI and AppleScript suites. 227 of 227 pass, plus 34 of 34 after the last scanner fix and 148 of 148 in the neighbour suites.LoadableExtensionListUITestschecks that SQLite's Options tab shows the list with a dimmed Remove, and MySQL's does not. Adding a row goes through the system file panel, which a UI test cannot drive deterministically: the panel dims anything that is not a library, and no library sits at a fixed path on every runner. The list arithmetic is unit-tested.Reviews
sqliteBackendfield could exempt a libSQL list from approval. Fixed, with a regression test./x/foowhile/x/foo.dylibloaded, and exact paths now close that.Screenshots
docs/images/sqlite-extensions.pngand-dark.pngcome from a sandboxed Debug build of this branch, pinned light and dark withTABLEPRO_SCREENSHOT_APPEARANCE: the new-connection form's Options pane with/opt/homebrew/lib/mod_spatialite.dyliband~/Downloads/vec0.dyliblisted. The SQLite page's steps now say that a new connection has the same pane, thatCmd+Shift+Greaches the hidden/optfolder in the file panel, and that a row is created only through the panel and edited in place after that.