Replace Sql Server with SQLite - #2
Merged
Merged
Conversation
A library is now a single file. New-ImageStoreDatabase creates one and opens it; Open-ImageStoreDatabase takes a path, with a connection-string parameter set kept for options a path cannot express, and refuses a path that does not exist rather than letting SQLite quietly create an empty database. The database is closed on Remove-Module and on host exit, so the -wal and -shm files are checkpointed away instead of left beside the file. Guids are stored as 16-byte BLOBs, matching uniqueidentifier. Binding a Guid directly would make the provider write 36-character TEXT and every lookup would then silently miss, so all binding goes through Parameters.AddGuid. Reader casts had to change everywhere: SQLite returns long for integers and bools, double for reals, and string or byte[] for a Guid, so the 97 direct casts all threw. They now use typed getters. Text columns are COLLATE NOCASE, keeping path comparison case-insensitive as it was under Sql Server and as the Windows file system and the in-memory OrdinalIgnoreCase comparisons already assume. LIKE escaping is rewritten. Sql Server's bracket syntax means nothing to SQLite -- it would have turned exact searches into wildcard ones -- so this uses an explicit ESCAPE clause. It also drops the quote-doubling, which was a plain bug rather than a dialect difference: those values are bound as parameters and never parsed as SQL, so searching for a name containing an apostrophe could not match. That was broken on Sql Server too. Also: TOP becomes LIMIT appended after the where and order by clauses, #temp becomes CREATE TEMP TABLE, DBCC SHRINKDATABASE becomes VACUUM. The empty database and schema script are gone; the schema lives in SqliteSchema.cs, which ImageStore.Migrator compiles by link so a migrated database and a new one cannot drift apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
A library is now a single SQLite file. No server, no instance, no connection string —
New-ImageStoreDatabase library.dband you are working.The cmdlets asked for
New-ImageStoreDatabase -Pathcreates the file, builds the schema and opens it, so it stands in for a followingOpen.-Forcereplaces an existing file along with its-wal/-shm/-journalcompanions.Open-ImageStoreDatabasetakes a path as its primary parameter set, with a-ConnectionStringset kept for what a path cannot express (Mode=ReadOnlyand similar). It refuses a path that does not exist — SQLite would otherwise create an empty database that looks fine until the first query.Close-ImageStoreDatabase, onRemove-Module(IModuleAssemblyCleanup) and on host exit (AppDomain.ProcessExit). Two hooks because neither covers the other; both funnel into one idempotent, lockedClose()that checkpoints WAL so nothing is left beside the file.What made this more than a find-and-replace
Reader casts — 97 of them. SQLite has five storage classes, so
reader[i]returnslongfor every integer and bool,doublefor every real, andstring/byte[]for a Guid. Every(Guid),(bool),(int)and(float)cast threwInvalidCastException. All now use typed getters, which convert correctly.Guid binding. Stored as 16-byte BLOB to match
uniqueidentifier. Binding aGuiddirectly makes the provider write 36-character TEXT — and the failure is silent: the write succeeds, and every subsequent lookup simply matches nothing. All binding goes throughParameters.AddGuid.Case sensitivity. SQLite defaults to binary comparison, which would have been a behaviour change: the same file at a differently-cased path becomes two records, and UNIQUE indexes stop catching
.JPGversus.jpg. Text columns areCOLLATE NOCASE. Known limit: that folds ASCII only, so accented names compare exactly.LIKE escaping, and a bug that predates this work. Sql Server's
[%]bracket syntax means nothing to SQLite — the brackets would match literally while the%inside kept its wildcard meaning, turning an exact search into a wildcard one. Now an explicitESCAPE. The rewrite also drops the quote-doubling, which was never a dialect difference but a plain bug: those values are bound as parameters and never parsed as SQL, so searching forDon't.jpgcould not match. That was broken on Sql Server too.Cascades are asymmetric on purpose.
File,IgnoredDirectoryandSameFilecascade;SimilarFiledoes not. Preserved exactly, becauseRemoveFolderCmdletrelies on the first andFileHelpercompensates for the second.Also:
TOP→LIMITappended after the where and order by clauses (not a token swap — the assembly order had to be reworked),#temp→CREATE TEMP TABLE,DBCC SHRINKDATABASE→VACUUM.Migration tool
ImageStore.Migrator, shipped as its own release asset:It compiles
SqliteSchema.csby linked compile item rather than carrying its own copy, so a migrated database and a freshly created one cannot drift apart. It reads the old database without modifying it, so the Sql Server library remains as a fallback.Removals
DataStore.mdf,DataStore_log.ldfandCreateDatabase.txtare gone, along with their solution entries and theImageStore-Database-<tag>.ziprelease asset — there is no empty database to hand out when the cmdlet creates one.Microsoft.Data.SqlClientsurvives only in the migrator.Packaging
Watch this one: SQLite ships its native engine for every platform it supports, which took the module from ~6 MB to 34 MB of Linux, macOS and wasm binaries a Windows-only module can never load. The workflow now drops every non-
win*runtime directory before packaging (verified locally: 34M → 5.8M, all three Windows architectures kept). Verification assertse_sqlite3.dllsurvives that trim, and that noMicrosoft.Data.SqlClient.dllreaches the module.Verification
Everything below was run, not assumed:
(Guid)reader[0]throws;GetGuidround-trips a BLOB;COLLATE NOCASEmatchesABC/abcbut notCAFÉ/café; Sql Server's[%]matches nothing;ESCAPE '\'works.PathandExtension, the UNIQUE index blocks a.JPG/.jpgduplicate, escaped%and apostrophe searches both match,LIMITafterorder by, temp-table flow, cascade delete,VACUUM.Not verified, and CI cannot cover it:
Import-Moduleunder a realpwsh, the WinForms cmdlets, clean shutdown leaving no-wal/-shm, and a migration against a real Sql Server library with row counts compared per table.🤖 Generated with Claude Code