SQLite SQL dialect and index limits 的核心責任是說明 SQLite 和 server SQL 的語意差異。SQLite 可以執行大量 SQL,也支援 transaction、index、trigger、view、window function 與 JSON;但它的 typing、constraint、file-level operation、query planner 與 extension model 會影響測試可信度、migration 成本與 production adapter。

本文的判讀錨點是:SQLite 測過代表某個 repository contract 在 SQLite 語意下成立。當 production target 是 PostgreSQL、MySQL、D1、Turso 或其他 server database 時,測試與 migration 要補上 dialect gap evidence。

Type Affinity

Type affinity 的核心責任是定義資料寫入時如何被保存與比較。SQLite 官方 Datatypes 文件說明 SQLite 使用 dynamic typing,型別關聯在 value 層與 column affinity 層共同作用;STRICT tables 則提供較嚴格的型別檢查。

議題SQLite 行為重點Production 影響
Integervalue type 可依寫入內容變化test fixture 可能放過錯誤型別
Textcollation 與比較語意需明確設定排序、大小寫、unique 判斷要對照 target DB
Date/time常以 TEXT / REAL / INTEGER 表示timezone、range query、serialization 要一致
Boolean常以 integer convention 表示adapter 要定義 true / false encoding
STRICT提供更接近 server DB 的型別 guard適合作為 fixture 預設,仍需 production test

Type affinity 的教學重點是把資料合約放在 application boundary。若 domain 說 created_at 是 timestamp,就要定義 storage format、timezone、precision、comparison query 與 serialization,而非只讓 SQLite 接受任意 value。

1CREATE TABLE orders (
2  id INTEGER PRIMARY KEY,
3  created_at TEXT NOT NULL,
4  total_cents INTEGER NOT NULL CHECK (total_cents >= 0)
5) STRICT;

這段 schema 用 STRICTNOT NULLCHECK 讓 fixture 更接近正式資料合約。Production target 仍要跑 PostgreSQL / MySQL container test,確認 timestamp、integer range 與 constraint error mapping。

Constraint Behavior

Constraint behavior 的核心責任是確保資料完整性由 database 和 application 共同維護。SQLite 支援 primary key、unique、check、foreign key 與 deferred constraint,但 foreign key enforcement 需要明確啟用,migration / test runner 也要確認連線設定。

ConstraintSQLite 審查點操作判準
Foreign keyPRAGMA foreign_keys = ON每個 connection / test setup 都要驗證
UniqueNULL、collation、expression對照 target DB 的 NULL uniqueness 與 collation
Checktype affinity 互動用 domain invalid case 驗證
Deferredtransaction boundary用 multi-step workflow 測 commit-time failure

Foreign key 是 SQLite fixture 最常漏掉的設定。每個測試連線開啟後應立刻查 PRAGMA foreign_keys;,並用一個故意違反 FK 的 fixture case 確認錯誤會出現。

1PRAGMA foreign_keys = ON;
2SELECT foreign_keys FROM pragma_foreign_keys;

Constraint error 要在 repository adapter 層被歸類。若 production target 會把 duplicate key、foreign key、check violation 映射成不同 error code,SQLite fixture 也要至少保留 domain-level classification test。

Transaction Behavior

Transaction behavior 的核心責任是定義讀寫隔離、savepoint、nested workflow 與 retry。SQLite 官方 isolation 文件說明 connection 之間的隔離語意;WAL mode 下 reader / writer behavior 也會影響 concurrent test。

行為SQLite 判讀測試影響
Single writer同一時間只有一個 writer 取得寫鎖concurrent writer test 要顯式設計
Snapshot readWAL mode 下 reader 可讀舊 snapshotfreshness 與 read-after-write 要分開測
Savepoint適合 nested workflowrepository transaction helper 要支援
Busy timeoutlock wait policyintegration test 要設定固定 timeout

Savepoint 可以讓 application 實作可組合的 transaction helper。若上層 workflow 已在 transaction 內,內層 repository 可以使用 savepoint 承接局部 rollback,而非開另一個 database transaction。

1SAVEPOINT create_order;
2INSERT INTO orders(id, created_at, total_cents) VALUES (1, '2026-05-21T00:00:00Z', 1200);
3RELEASE create_order;

Busy timeout 是測試穩定性的關鍵設定。若 fixture 會平行跑測試,應每個 temp DB 獨立,或在專門 concurrency lab 裡測 SQLITE_BUSY;一般 contract test 要追求 deterministic result。

Index Model

Index model 的核心責任是把查詢形狀與資料量變成可觀測的計畫。SQLite 支援 B-tree index、covering index、partial index、expression index 與 query planner;但 planner choice、統計資訊與 function support 會和 target DB 不同。

Index 類型適用情境審查問題
Composite index多欄位 equality / range query欄位順序是否符合主要 query pattern
Partial indexactive / pending / soft-delete rowpredicate 是否穩定、target DB 是否支援
Expression indexnormalized email、date bucketfunction deterministic 與 migration 支援
Covering indexread-mostly list pageindex size 與 write overhead

Index review 要從 query pattern 開始,而非從「常用欄位」開始。SQLite 可以用 EXPLAIN QUERY PLAN 檢查是否掃 index;production target 要用自己的 explain 工具重跑。

1EXPLAIN QUERY PLAN
2SELECT id, total_cents
3FROM orders
4WHERE created_at >= '2026-05-01T00:00:00Z'
5ORDER BY created_at DESC
6LIMIT 50;

Index drift 是 migration 的常見風險。SQLite fixture 裡的 index 可以讓測試變快,但若 production schema 缺少同等 index,正式服務會在資料量成長後出現 latency spike;因此 index 要進入 schema diff audit。

Dialect Gap

Dialect gap 的核心責任是把 SQLite 與 target database 的差異寫成 matrix。這份 matrix 應跟 repository adapter、migration plan 與 CI test suite 綁定。

面向SQLite 審查點對照路由
ALTER TABLE支援範圍、table rebuildSchema migration / versioning
JSONfunction availability、index supportproduction container test
Generated columnexpression、storage、indexmigration dry run
Window functiontarget DB 支援與 plannerquery compatibility suite
ExtensionFTS、vector、custom functionvendor extension policy

Dialect matrix 要以 query contract 為單位。每個 repository method 至少列出 SQL feature、SQLite behavior、production behavior、test layer 與 fallback strategy。

1Contract: Search active documents by tenant and prefix
2SQLite: FTS5 virtual table in fixture
3PostgreSQL: tsvector + GIN index
4Risk: ranking / tokenizer / collation differ
5Evidence: golden result set + production container explain

這種寫法讓測試負責驗證 domain contract,避免把兩個 SQL engine 的搜尋語意視為完全一致。

Test / Migration Impact

Test / migration impact 的核心責任是決定哪些東西可以用 SQLite 快速驗證,哪些東西要交給 production-like database。SQLite 很適合 repository contract、migration fixture、local development 與 file lifecycle drill;涉及 planner、extension、collation、locking、permission、role 與 HA 時,需要追加 target DB evidence。

測試層SQLite 適合度必補 evidence
Domain repositoryinvalid data、constraint、transaction case
Migration syntaxtarget DB dry run
Query performancetarget DB explain + realistic data volume
Permission / roleserver DB integration test
HA / failovervendor-specific drill

SQLite fixture 的價值在於快、穩、便宜。它應承擔「資料合約是否被 repository 保護」;production container 或 staging database 承擔「正式 engine 是否用同樣方式執行」。

下一步路由

SQL dialect and index limits 完成後,下一步要把 gap 接到實作層。測試設計讀 Test Fixture Best Practice;migration 實作讀 Schema migration / versioning;要升級到 PostgreSQL,讀 SQLite to PostgreSQL migration