更新履歴
[1.5.7dev1] - 2026-05-24
パフォーマンス改善
- PERF-30: フック無し
__setitem__の不要な旧値取得を省略(core.py)on_write_success用のold_valueはフックが登録されている場合だけ取得するようにしました。- デフォルト構成の単体書き込みで、余分なキャッシュ/DB 確認を避けます。
- PERF-31 / PERF-32:
batch_get()の一括取得ホットパスを軽量化(core.py)- DB 未取得キー判定を、結果全体ではなく DB に問い合わせたキーだけに限定しました。
- フルチャンク用の
IN (...)プレースホルダー文字列を事前計算し、大量キー取得時の文字列生成を減らしました。
- PERF-33:
AsyncNanaSQLite.abatch_get()の全件キャッシュ既知パスを高速化(async_core.py)- デフォルトの unbounded キャッシュかつフック無しで、要求キーがすべてキャッシュ済みまたは既知の未存在キーの場合、executor 往復なしで返します。
- PERF-34:
load_all()のデフォルトキャッシュ投入を一括化(core.py)- サイズ制限なし unbounded キャッシュでは、1件ずつ
cache.set()せず背後の dict をまとめて更新します。
- サイズ制限なし unbounded キャッシュでは、1件ずつ
- PERF-35:
memory_first=Trueを追加(core.py)- KVS CRUD を全件ロード済みメモリ上で完結させ、差分は v2 engine の time flush でバックグラウンド永続化します。
- デフォルトでは
flush_interval=5.0相当で、close()時は v2 engine の最終 flush により永続化されます。 - 既存挙動には影響しない明示オプションです。全件メモリ保持が前提のため、LRU/TTL や
cache_size付きキャッシュとは併用できません。 - v2 engine の time flush は、変更が無い場合に空 flush をワーカーへ投げないようにしました。
- pytest-benchmark と Actions のベンチ集計に memory-first CRUD ベンチを追加しました。
[1.5.6b1] - 2026-05-24
セキュリティ修正
- SEC-01:
query(..., columns=[...])のサブクエリ拒否を強化(core.py)ORDER BY/GROUP BYと同様に、列式でもSELECT/FROMなどのサブクエリ系キーワードを strict モードで拒否するようにしました。COUNT(*) AS totalなどの通常の集計式は引き続き利用できます。
- SEC-02:
create_table()の列型定義でトップレベルのカンマ注入を拒否(core.py)DECIMAL(10,2)のような括弧内カンマは許可しつつ、TEXT, injected INTEGERのような列定義割り込みを拒否します。
- BUG-02: V2 デッドレターキュー (DLQ) の無制限成長を抑制(
v2_engine.py)- DLQ にデフォルト上限
1000を追加し、上限到達時は最古エントリを破棄して新しい失敗を保持します。V2Config(max_dlq_size=...)またはv2_max_dlq_size=...で調整でき、Noneを指定すると従来通り無制限にできます。
- DLQ にデフォルト上限
- SEC-03:
V2Engineの KVS / DLQ 復旧経路で unsafe table_name を拒否(v2_engine.py)V2Engineを直接利用した場合でも、kvs_set()/kvs_delete()/kvs_get_staging()と DLQ 復旧処理でテーブル名を検証し、SQL 断片へ unsafe な名前を連結しないようにしました。
- SEC-04:
pragma()の書き込み可能 PRAGMA を制限(core.py)schema_versionやtable_infoなどの情報取得系 / 危険な PRAGMA は読み取りのみ許可し、設定できる PRAGMA を明示的な allowlist に分離しました。
バグ修正
- QUAL-01:
AsyncNanaSQLiteのlock_timeout転送漏れを修正(async_core.py)AsyncNanaSQLite(..., lock_timeout=...)で指定した値が内部のNanaSQLiteインスタンスに渡されず、非同期 API ではロック取得タイムアウトが実質的に無効になっていた問題を修正しました。AsyncNanaSQLite.table()で作成したサブテーブルも、親インスタンスのlock_timeoutを継承することを回帰テストで確認しました。
パフォーマンス改善
- PERF-01:
AsyncNanaSQLite.aget()/acontains()のキャッシュ済みホットパスを高速化(async_core.py)- デフォルトの unbounded キャッシュで、キャッシュ済みキーおよび既知の未存在キーを executor 往復なしで返すようにしました。キャッシュミス、LRU/TTL、フック付き読み取りは従来通り同期 DB 側へ委譲します。
- 非同期ベンチマークにキャッシュ済み読み取りと既知未存在キー読み取りのケースを追加しました。
ドキュメント
- 非同期 API ドキュメントに
lock_timeoutの説明を追加しました。
[1.5.5] - 2026-04-30
セキュリティ修正 (Security Remediation)
- F-002: UniqueHook における状態不整合の解消(
core.py,hooks.py,protocols.py)- DB 書き込み失敗時にメモリ上のインデックスのみが更新される不整合を修正しました。
NanaHookにon_write_success/on_delete_successコールバックを追加し、DB への書き込みが確定したタイミングでインデックスを更新する「確定後反映モデル」に移行しました。
- DB 書き込み失敗時にメモリ上のインデックスのみが更新される不整合を修正しました。
- F-003: ORDER BY / GROUP BY 句への SQL 注入脆弱性の強化(
core.py)- プレースホルダが使用できない
ORDER BY/GROUP BY句に対して、英数字・アンダースコア・スペース・カンマ・ドット・括弧・比較演算子・引用符類(',",`,[,])などの安全な文字のみを許可するホワイトリスト検証を導入しました。また、サブクエリキーワード(SELECT、FROM等)の検出と構造的インジェクションパターン(--、/*、;)の事前チェックを追加しました。
- プレースホルダが使用できない
- F-004: デッドレターキュー (DLQ) における機密情報露出の防止(
v2_engine.py)- 背景フラッシュ失敗時のログ出力において、ペイロードデータがログに露出しないことを確認・強化しました。
get_dlq()の docstring にセキュリティ警告を追加しました。
- 背景フラッシュ失敗時のログ出力において、ペイロードデータがログに露出しないことを確認・強化しました。
- F-005: ExpiringDict (TTL キャッシュ) におけるレースコンディションの修正(
utils.py)- キャッシュ期限切れ処理中にキーが更新された場合、新しい値が誤って削除されるレースコンディションを修正しました。Compare-and-Delete (CAS) パターンを導入し、削除実行時に検出時の expiry タイムスタンプを照合するようにしました。
ドキュメントの改善
- F-001 (atexit) の制限事項に関する明文化(
README.md,v2_architecture.md)- v2 モードにおいて、OS による強制終了 (
SIGKILL) 時にデータが消失するリスクと、重要データに対するflush(wait=True)またはimmediateモードの利用推奨を明記しました。
- v2 モードにおいて、OS による強制終了 (
[1.5.4] - 2026-04-19
バグ修正
BUG-01 (pop hook lock):
pop()のbefore_deleteフックをロック内へ移動(core.py)pop()の非 v2 モードでbefore_deleteフックがロック外で呼ばれていたため、__delitem__の SEC-05 修正との一貫性が失われていました。- 非 v2 パスで
before_deleteの呼び出しを_acquire_lock()ブロック内に移動し、フック実行と DB 削除をアトミックに実行するよう修正しました。self._lockはthreading.RLockのため同一スレッドからの再入呼び出しもデッドロックしません。 - また、フックが例外を送出した場合に DB 削除が実行されず、キーが保持されることを確認しました。
BUG-02 (batch_update hook result):
batch_update()でフック変換値を常に適用(core.py)batch_update()の非 coerce パスにおいてbefore_writeフックの返り値が無視されていたため、PydanticHookなどの変換フックが__setitem__では機能するのにbatch_update()では機能しないサイレントな不整合が発生していました。if self._coerce:の 2 分岐構造を廃止し、統一された copy-on-write パターンに変更しました。フックが値を変更しない場合は新しい dict は生成されません(メモリ効率を維持)。ValidkitHookはcoerce=False時に自身でフック内部で変換を行わないため、既存の coerce=False テストとの後方互換性は維持されています。
BUG-03 (batch_delete hook lock):
batch_delete()のbefore_deleteフックをロック内へ移動(core.py)batch_delete()の非 v2 モードでbefore_deleteフックがロック外で呼ばれていました。- v2 モードではフックはロック外(
__delitem__v2 パスと同様)、非 v2 モードではロック内で実行するよう修正しました。
セキュリティ修正
- SEC-05:
UniqueHookTOCTOU 競合状態の修正(core.py,hooks.py)__setitem__においてbefore_writeフックの呼び出しをロックの外側で行っていたため、マルチスレッド環境で UniqueHook の一意性チェックと DB 書き込みの間に別スレッドが割り込む TOCTOU 競合が発生していました。- 非 v2 モードでは
__setitem__のbefore_writeおよび__delitem__のbefore_deleteの呼び出しを_acquire_lock()の内側に移動し、一意性チェック・削除前チェックと DB 更新をアトミックに実行するよう修正しました。self._lockはthreading.RLockのためフックからの再入呼び出しでもデッドロックは発生しません。 - UniqueHook の docstring を更新し「WARNING」を削除して修正済みの動作を記載しました(SEC-03 → SEC-05)。
- v2 モードでは非同期フラッシュ構造上の制約があるため、v2 モードでの厳格な一意制約には SQLite UNIQUE 制約の使用を推奨します。
セキュリティ強化
- SEC-06 opt-in:
google-re2による ReDoS 対策強化(compat.py,hooks.py,pyproject.toml)pip install nanasqlite[re2]でgoogle-re2(RE2 エンジン)をインストールすると、BaseHookのすべての正規表現コンパイルおよびマッチングに RE2 エンジンが使用されます。- RE2 エンジンは線形時間計算量を保証するため、どんなパターンでも ReDoS 攻撃が不可能になります。
- RE2 利用時は
logging.debugでメッセージを出力します(nanasqlite.compatロガー)。 - RE2 非インストール時は従来通りの危険パターンブラックリスト検証が機能します。
pyproject.tomlにre2 = ["google-re2>=1.1"]オプション依存とallextras への追加。devextras にもgoogle-re2>=1.1を追加し、CI テストで実際の RE2 エンジンを使用するよう変更。- エラーメッセージを更新し
pip install nanasqlite[re2]への案内を追記。 re_fallbackパラメータをBaseHookに追加: RE2 が対応していないパターン(後方参照(\w)\1、先読み(?=...)等)を使用した場合のフォールバック動作を制御。re_fallback=False(デフォルト): RE2 のre2.Errorをそのまま伝播させ、ReDoS 保護を維持。re_fallback=True:warnings.warnを出力した上で標準reエンジンにフォールバック。このパターンでは ReDoS 保護が無効になります。
パフォーマンス改善
PERF-01:
UniqueHook—use_index=Trueopt-in 逆引きインデックス(hooks.py)- 従来
before_writeのたびにdb.items()で全件スキャン(O(N))していたため、大規模テーブルで著しいボトルネックになっていました。 UniqueHook("email", use_index=True)を指定すると、最初の書き込み時にのみ O(N) の逆引きインデックスを構築し、以降の一意性チェックを O(1) で実行します。- インデックスは
before_write・before_deleteコールバックで自動更新されます。フックライフサイクル外でDBを変更した場合はhook.invalidate_index()でインデックスを再構築できます。 - 後方互換:
use_index=False(デフォルト)では従来の O(N) 動作が維持されます。
- 従来
PERF-02:
BaseHook.__init__— コンパイル済みPattern型の再コンパイル省略(hooks.py)- 非 RE2 モードで既コンパイル済みの
re.Patternオブジェクトを渡した場合、re.compile()による再コンパイルを省略してコンパイル済みオブジェクトをそのまま利用します。 - セキュリティ上の要件として、
pattern.patternテキストに対する_validate_regex_patternの ReDoS バリデーションは引き続き実行されます(コンパイル済み Pattern を経由してブラックリストを迂回できないよう保証)。 - これにより安全性を維持したままフック初期化時のオーバーヘッドを削減しました。
- 非 RE2 モードで既コンパイル済みの
セキュリティ強化(前倒し)
- SEC-01: DLQ ペイロード漏洩リスクのドキュメント化(
v2_engine.py)- DLQ エントリには KVS の
op["value"](シリアライズ済み値)が含まれるため、非暗号化DBではget_dlq()経由でプレーンテキスト値が外部に漏洩するリスクがあります。 DLQEntrydataclass・_add_to_dlq()・get_dlq()の各 docstring に SEC-01 セキュリティ注記を追加し、本番環境でのロギング・モニタリング連携時の注意点を明記しました。
- DLQ エントリには KVS の
コード品質改善(前倒し)
QUAL-01:
compat.py—re2_module型アノテーション改善(compat.py)re2_module = None # type: ignore[assignment]をre2_module: types.ModuleType | None = Noneに変更し、mypy が使用箇所で型を追跡できるようにしました。
QUAL-02:
v2_engine.py—DLQEntrydataclass 導入(v2_engine.py)- DLQ の内部表現を
list[tuple[str, Any, float]]からlist[DLQEntry]に変更しました。DLQEntryはdataclassで定義された明示的な型です。get_dlq()の戻り値(list[dict])は後方互換を維持します。
- DLQ の内部表現を
[1.5.3rc3] - 2026-04-07
パフォーマンス改善
PERF-21:
execute_many()— Python ループ →cursor.executemany()に変更(core.py)execute_many()の実装がバインドパラメータのリストを Python のforループで一件ずつcursor.execute()していました。APSW 組み込みのcursor.executemany()を使うことで Python 側の関数呼び出しオーバーヘッドを排除しました。- 影響テスト:
test_execute_many・test_import_from_dict_listで約 15% 高速化。
PERF-22:
batch_delete()— フック未登録時の事前チェックループを省略(core.py)batch_delete()は削除前に全キーに対して_ensure_cached()を呼び出していましたが、その唯一の目的はbefore_deleteフックの呼び出しでした。_has_hooksがFalseの場合(デフォルト)にはこのループ全体を完全にスキップするよう変更しました。- 影響テスト:
test_batch_deleteでフックなし時のオーバーヘッドを削減。
PERF-23:
batch_update()— シリアライズをロック外へ移動・dict.update()使用・_absent_keysガード追加(core.py)- シリアライズ(
_serialize()呼び出し)は SQLite 接続に触れない純粋な Python/JSON 処理であるため、_acquire_lock()の外に移動しました(__setitem__と同様の方針)。 - Unbounded モードのキャッシュ更新を per-key 代入ループから
dict.update()に変更しました。dict.update()は C レベルで実装されており、Python ループの約 6 倍高速です。 - v2 パスも同様に
dict.update()+ ガードを適用。 _absent_keys.discard()の per-key 呼び出しをif self._absent_keys: self._absent_keys.difference_update(mapping.keys())に置き換え、空セット時のハッシュ計算を完全に排除しました。- 影響テスト:
test_batch_write_100・test_batch_update_partial_100で約 9% 高速化。
- シリアライズ(
PERF-24:
batch_update_partial()—dict.update()+_absent_keysガード適用(core.py)batch_update()(PERF-23) と同様の最適化をbatch_update_partial()の v1 / v2 両パスに適用しました。
PERF-25:
batch_delete()—_absent_keys.add()per-key →_absent_keys.update(keys)に変更(core.py)- 削除後のキャッシュ更新で、キーごとに
_absent_keys.add(key)を呼び出していたものを_absent_keys.update(keys)の一括呼び出しに変更しました。ハッシュ計算コストとセット内部の再割り当て回数を削減します。 - v2 パスも同様に変更。
- 削除後のキャッシュ更新で、キーごとに
PERF-26:
begin_transaction()/commit()/rollback()—execute()オーバーヘッドをバイパス(core.py)- これらのメソッドは内部で
self.execute("BEGIN IMMEDIATE")等を呼び出していましたが、execute()には v2 モード判定・SQL 文字列のstrip().upper()処理・追加の_check_connection()呼び出しが含まれます。with self._acquire_lock(): self._connection.execute(...)を直接使うことでこれらのオーバーヘッドを排除しました。 - 影響テスト:
test_context_manager_transaction・test_begin_commit・test_begin_rollbackで高速化。
- これらのメソッドは内部で
PERF-29:
_serialize()— 暗号化なし時の早期リターン(core.py)__init__時に_no_encrypt: boolフラグを事前計算し、暗号化が無効な場合(デフォルト)はif self._fernet:/if self._aead:の 2 回の属性ルックアップをスキップして即座に返すようにしました(PERF-20 と同様の手法)。- 影響テスト:
test_nested_write・test_write_encryption[plaintext]・その他全書き込みパスで微小な高速化。
[1.5.3rc2] - 2026-04-07
バグ修正
- [Medium] BUG-01:
setdefault()+before_write変換フック組み合わせ時の返値誤り(core.py)- PERF-18 最適化で
self[key] = default後にself[key]を再読み込みせず直接defaultをafter_readフックに渡す実装としましたが、ValidkitHook(coerce=True)やPydanticHookのようにbefore_writeフックが値を変換する場合に誤った値を返す問題がありました(例:"hello"→"HELLO"と変換されるフックがあっても"hello"を返してしまう)。 - 修正:
self[key] = defaultの後、_has_hooksが True の場合はキャッシュから実際に格納された値を読み直してからafter_readフックを適用するよう変更しました。フックがない場合は従来どおりdefaultを直接返します(パフォーマンス最適化を維持)。 - 対応する POC:
etc/poc/poc_bug01_setdefault_coerce_hook.py
- PERF-18 最適化で
パフォーマンス修正(v1.5.3rc2 ベンチマーク低下対応)
[High] PERF-14: Unbounded モード
__getitem__の try/except 高速パス(core.py)- キャッシュヒット時のホットパスで
.get(key, _NOT_FOUND)+ センチネル同一性比較を使用していました。Python の辞書直接アクセスd[key]はtry/except KeyErrorパターンで呼び出すと、センチネルオブジェクト生成・キーワード引数処理・同一性比較が不要になり約 1.9 倍高速になります。test_single_read_cachedで -15% の改善を確認。 _ensure_cached()・__getitem__・get()のすべての Unbounded ホットパスに適用しました。
- キャッシュヒット時のホットパスで
[High] PERF-15: Unbounded モード
get()の try/except 高速パス(core.py)- PERF-14 と同様の最適化を
get()メソッドに適用しました。test_read_encryption[fernet]で -11.6% の改善を確認。
- PERF-14 と同様の最適化を
[High] PERF-16: Unbounded モード
__contains__の try/except 高速パス(core.py)- PERF-14 と同様の最適化を
__contains__メソッドに適用しました。
- PERF-14 と同様の最適化を
[Medium] PERF-17:
_update_cacheでの空セットdiscard()呼び出し省略(core.py)- v1.5.2 の
_absent_keys導入以降、_update_cache()は毎回self._absent_keys.discard(key)を呼んでいました。書き込み中心のワークロードでは_absent_keysは空のままのため、無駄なハッシュ計算が発生していました。if self._absent_keys:ガードを追加し、空セットの場合はスキップするよう変更しました。
- v1.5.2 の
[Medium] PERF-18:
setdefault()の冗長なself[key]呼び出し省略(core.py)setdefault()はキーが存在しない場合にself[key] = defaultで書き込み後、さらにself[key]で読み戻していました。書き込み後の値はすでに既知であるため、再度の__getitem__呼び出しを省略してデフォルト値を直接返すよう変更しました。また Unbounded モードでは_cache.get()のポリモーフィックな呼び出しを_data[key]直接アクセスに置き換えました。
[Medium] PERF-19:
pop()の Unbounded モードでの直接_dataアクセス(core.py)pop()は値の取得にself._cache.get(key)を使用していました。Unbounded モードでは_dataに実値が直接格納されているため、self._data[key]で取得することでポリモーフィックなメソッドディスパッチ(LRU ではmove_to_end()を伴う)を回避できます。
[Medium] PERF-20:
_has_hooks事前計算フラグによる全ホットパスの高速化(core.py)__getitem__・__setitem__・__delitem__・get()・get_fresh()・batch_get()・pop()・setdefault()・batch_update_partial()・batch_delete()のすべてのホットパスでif self._hooks:を使用していました。これは毎回リストの__len__を呼び出すオーバーヘッドがあります。__init__時にself._has_hooks: bool = bool(self._hooks)を事前計算し、add_hook()時も更新するよう変更しました。
テスト
tests/test_v153_perf_fixes.pyに以下を追加(PERF-14〜20 の動作検証 および BUG-01 回帰テスト):- キャッシュヒット時の
__getitem__/get()/__contains__の正確性 _update_cacheの空_absent_keys時のスキップ動作setdefault()の新キー・既存キーの返値(フックあり・なし)pop()の Unbounded モードでの正確性_has_hooksの初期化・add_hook()後の更新確認- BUG-01:
before_write変換フック付きsetdefault()の正確な返値検証
- キャッシュヒット時の
[1.5.3rc1] - 2026-04-07
パフォーマンス修正(v1.5.3 プレリリース監査)
[High] PERF-07: 共通 SQL 文字列の
__init__時事前計算(core.py)__setitem__・__delitem__・__contains__・__len__・_write_to_db・_read_from_db・_delete_from_db・load_all・batch_update・batch_deleteの全ホットパスで、毎呼び出し f-string によって同一の SQL 文字列(テーブル名を含む INSERT / DELETE / SELECT 等)を再構築していました。__init__時に 6 種類の SQL テンプレートを事前計算してインスタンス変数に保持し、ホットパスでは直接参照するよう変更しました。- 効果: 書き込み・読み込み・削除・カウント等の全 KV 操作で文字列構築コストを排除。
test_single_write/test_execute_raw/test_sql_insert_singleの改善に寄与。
[Medium] PERF-08: Unbounded モードでの
to_dict()/copy()MISSING フィルタ省略(core.py)- Unbounded モードでは
_dataに MISSING センチネルが格納されることはないため、毎回{k: v for k, v in _data.items() if v is not MISSING}で全要素の同一性比較を行う必要はありませんでした。Unbounded モードではdict(self._data)を直接返すよう変更し、LRU/TTL モードでのみフィルタを適用します。 - 効果:
test_to_dict_1000/test_copyの改善に寄与。
- Unbounded モードでは
[Medium] PERF-09: LRU
__getitem__での二重キャッシュルックアップ排除(core.py)- LRU/TTL モードの
__getitem__は_ensure_cached()内部でself._cache.get()(LRU ではmove_to_end()を伴う)を 1 回呼び、さらに戻り値取得のために同じキーでself._cache.get()を再度呼んでいました。キャッシュヒット時の_data在籍確認を先行させ、self._cache.get()を 1 回の呼び出しで完結するよう変更しました。 - 効果: LRU/TTL キャッシュヒット時の
move_to_end()冗長呼び出しを排除。test_cache_hit[lru]/test_cache_hit[ttl]の改善に寄与。
- LRU/TTL モードの
[Medium] PERF-10:
_validate_expression()の正規表現最適化と関数スキャン早期スキップ(core.py)_validate_expression()が毎呼び出し 4 つの危険パターン文字列をre.search()で個別にスキャンしていました。4 パターンをモジュールレベルで事前コンパイルした単一正規表現_DANGEROUS_SQL_REに統合し、1 回のスキャンで検出するよう変更しました。また、式に(が含まれない場合(典型的なid = ?等)は高コストなsanitize_sql_for_function_scan()+re.findall()の実行を完全にスキップします。- 注意: 非 strict モードでは、複数の危険パターンが同時にマッチしても警告は 1 件のみ発行されます(以前は複数件)。strict モード(例外発生)の挙動は変わりません。
- 効果:
exists()/sql_update()/sql_delete()のホットパスで複数のシングルパターン regex 走査を排除。test_sql_update_single/test_exists_check/test_execute_rawの改善に寄与。
[Medium] PERF-11:
ExpiringDict._check_expiry()のロックフリー早期リターン最適化(utils.py)_check_expiry()は毎呼び出しthreading.RLockを取得していました。TTL キャッシュのヒットパスでは 1 回のキーアクセスにつき複数回_check_expiry()が呼ばれるため、このロック取得コストが積み重なっていました。CPython の GIL 下ではdict.get()はアトミックであるため、ロックなしで_exptimes.get(key)を読み取り、期限切れでない場合は即座にFalseを返す楽観的プレチェックを追加しました。- 効果: TTL キャッシュのキャッシュヒット時のロック取得回数を削減。
test_cache_hit[ttl]/test_ttl_expiry_checkの改善に寄与。
[High] PERF-12: LRU/TTL モードの
get()における二重キャッシュルックアップ排除(core.py)(v1.5.3 監査で発見)- PERF-09 で
__getitem__を最適化した際、同じ二重ルックアップ問題がget()メソッドに残存していました。get()はto_dict()/items()のホットパスでも利用されるため影響が大きい問題です。__getitem__と同じパターン(_data在籍確認 →cache.get()1 回)を適用しました。 - 効果: LRU/TTL キャッシュヒット時の
move_to_end()冗長呼び出しをget()でも排除。test_cache_hit[lru]/test_cache_hit[ttl]のさらなる改善に寄与。
- PERF-09 で
[Medium] PERF-13: Unbounded モードの
values()/items()における MISSING フィルタ省略(core.py)(v1.5.3 監査で発見)- PERF-08 で
to_dict()を最適化した際、同じ最適化がvalues()とitems()に適用されていませんでした。Unbounded モードではこれらもlist(self._data.values())/list(self._data.items())を直接返すよう変更しました。 - 効果:
test_to_dict_1000/ 全データ取得系ホットパスのさらなる改善に寄与。
- PERF-08 で
新規ベンチマークテスト追加
tests/test_benchmark.pyにtest_cache_hit[lru]/test_cache_hit[ttl]を追加。単一キーへの繰り返しアクセスによるキャッシュヒットパスのオーバーヘッドを計測する。
テスト
tests/test_v153_perf_fixes.pyを追加し、以下を検証:- PERF-07:
_sql_kv_*事前計算属性の存在と正確性 - PERF-08:
to_dict()/copy()が Unbounded / LRU 両モードで MISSING を含まないこと - PERF-09: LRU / TTL
__getitem__がキャッシュヒット・不在キーで正しく動作すること - PERF-10: 単純な WHERE 句・関数付き WHERE 句・危険パターンの検出が正しく機能すること
- PERF-11:
ExpiringDict._check_expiry()の有効期限前後の挙動が正しいこと
- PERF-07:
tests/test_audit_poc.pyにTestPerf12GetDoubleLookup/TestPerf13ValuesItemsFilterを追加:- PERF-12: LRU/TTL
get()がキャッシュヒット・不在キーで正しく動作すること - PERF-13:
values()/items()が Unbounded / LRU 両モードで MISSING を含まないこと
- PERF-12: LRU/TTL
[1.5.2] - 2026-04-06
パフォーマンス修正(v1.5.0dev1 以降の性能低下 継続対応)
- [High] PERF-06: Unbounded キャッシュ読み取りホットパスの分岐最適化(
core.py)__getitem__/get/__contains__/_ensure_cachedの Unbounded モードで、内部メタデータ参照を優先してから_dataを確認する経路が残っており、正のキャッシュヒット時にも不要な membership 判定が追加され、キャッシュ済み読み取りで無視できないオーバーヘッドになっていました。_dataを先に確認する fast-path に変更(ヒット時は即 return)
- known-absent の早期 return は
_absent_keysに限定
- known-absent の早期 return は
__getitem__/getでも同様の fast-path を適用し、不要な_ensure_cached()呼び出しを回避
- 効果: キャッシュ済み読み取り・存在確認の追加オーバーヘッドを削減(既存 API/挙動は維持)。
破壊的変更(許可済み対応)
- Unbounded モードの内部メタデータを
_cached_keysから_absent_keys(known-absent 専用)へ分離しました。- 公開 API への影響はありませんが、内部属性
_cached_keysに依存するコードは互換性がありません。 - 移行:
in/get/is_cached等の公開 API を利用してください。
- 公開 API への影響はありませんが、内部属性
テスト
tests/test_v152_perf_fastpath.pyを追加し、以下を検証:- Unbounded モードで
_data優先 fast-path が機能すること - 既知の不在キー(negative cache)挙動が維持されること
- Unbounded モードで
監査(etc/audit/audit_prompt.md 準拠)
- フェーズ1〜6の観点で差分監査を実施し、今回の修正範囲(read/contains ホットパス)において:
- 後方互換性を壊す変更なし
- 新規セキュリティ問題の導入なし
- 既存の negative cache セマンティクス維持を確認
[1.5.1] - 2026-04-05
セキュリティ修正(v1.5.1 プレリリース監査)
[Medium] SEC-01:
exists()の WHERE 句に_validate_expression()未適用を修正(core.py)query()/count()/query_with_pagination()は WHERE 句を_validate_expression()で検証してforbidden_sql_functionsなどのポリシーを適用していましたが、exists()はこの検証を行っていませんでした。アプリケーションがforbidden_sql_functionsを設定していてもexists()のみポリシーが無視されるという不整合を修正しました。exists()を呼び出す前に_validate_expression(where, context="where")を実行するよう変更しました。
[Medium] SEC-02:
sql_update()/sql_delete()の WHERE 句に_validate_expression()未適用を修正(core.py)sql_update()とsql_delete()の WHERE 句も同様に_validate_expression()が呼ばれていませんでした。strict_sql_validation=True/forbidden_sql_functionsの設定がこれらのメソッドでは機能しない不整合を修正しました。
バグ修正(v1.5.1 プレリリース監査)
[High] BUG-01:
pop()が v2 モードで v2 エンジンをバイパスする問題を修正(core.py)- v2 モードで
pop()を呼ぶと_delete_from_db()が直接 DB へ DELETE を発行し、v2 staging buffer を完全にバイパスしていました。staging buffer に SET 操作が残留している状態でこの直接 DELETE が実行されると、その後のflush()で staging の SET が DB に書き込まれてキーが「復活」するデータ整合性バグが発生していました。__delitem__と同様に v2 モードではv2_engine.kvs_delete()を経由するよう修正しました。
- v2 モードで
[Medium] BUG-02:
batch_get()が_cached_keysの「存在しない」ステータスを尊重しない問題を修正(core.py)__delitem__実行後、キーは_cached_keysに「存在しない」として記録されますが、batch_get()は_dataのみを確認して_cached_keysを参照しないため、v2 non-immediate モードでは staging の DELETE がまだ DB に反映されていない状態でbatch_get()が DB の旧値を返してしまう不整合がありました。batch_get()でも_cached_keysを確認して「既知の不在キー」をスキップするよう修正しました。
[Low] BUG-03:
to_dict()が LRU/TTL モードでMISSINGセンチネル値を含む問題を修正(core.py)- LRU/TTL キャッシュモードでは、存在しないキーへのアクセス時に
MISSINGセンチネルがキャッシュに書き込まれます(負キャッシュ)。to_dict()がこれをフィルタリングせずに返していたため、結果に{key: <MISSING sentinel>}が混入する可能性がありました。items()と同様にMISSINGをフィルタリングするよう修正しました。
- LRU/TTL キャッシュモードでは、存在しないキーへのアクセス時に
パフォーマンス修正(v1.5.1 プレリリース監査)
- [Low] PERF-05:
fast_validate_sql_chars()のホットパス最適化(sql_utils.py)fast_validate_sql_chars()が呼び出しごとにset(...)で文字セットオブジェクトを新規生成していました。この関数は全てのクエリメソッドのバリデーション経路(ホットパス)から呼ばれるため、モジュールレベルのfrozenset定数_SAFE_SQL_CHARSとして事前計算するよう変更しました。約 200–300 ns / 呼び出しのオーバーヘッドを削減します。
パフォーマンス修正(v1.5.0dev1 以降の性能低下対応)
ベンチマーク(RPI 実機)で確認された v1.5.0dev1 以降の性能低下を修正しました。
[Critical] PERF-01: フックホットパスのオーバーヘッド除去(
core.py)__getitem__・__setitem__・__delitem__・get・batch_get・setdefault・pop・batch_update_partial・batch_deleteの全ての読み書き操作で、毎呼び出しgetattr(self, "_hooks", [])を実行していたため、フックが未設定の場合でも無視できないオーバーヘッドが発生していました。self._hooks(常に初期化済み)への直接アクセスとif self._hooks:による早期スキップに変更しました。- 効果: キャッシュ済みキーの読み込み速度が約 30% 向上(実機 RPI: ~1.74M → ~2.3M ops/sec 相当)。
[Critical] PERF-02: v2 モードにおける共有ロック競合の解消(
core.py)__setitem__・__delitem__・batch_update・batch_deleteの v2 モードパスで、インメモリキャッシュの更新(_data[key] = value等)に対して DB フラッシュスレッドと共有するロックを使用していました。このため、バックグラウンドフラッシュスレッドが DB トランザクションのためにロックを保持している間、メインスレッドのキャッシュ更新がブロックされ、特に低速 CPU(Raspberry Pi 等)で深刻なスループット低下を引き起こしていました。- v2 モードにおいてインメモリのみの更新操作は Python の GIL によるアトミック性が保証されており、バックグラウンドフラッシュスレッドは
_data/_cached_keysに直接アクセスしないため、これらの操作に対する明示的なロック取得は不要です。 - 効果: v2 immediate モードの書き込みスループットが約 3.7 倍向上(実機 RPI: ~169 → ~600+ calls/sec 相当)。
[Medium] PERF-03:
_update_cache内のhasattr()呼び出しを事前計算に変更(core.py)_update_cache()が毎呼び出しhasattr(self._cache, "_max_size")を実行していたため、書き込みパスで不要なオーバーヘッドが発生していました。__init__時に_use_cache_setフラグとして事前計算するように変更し、ホットパスからhasattr()呼び出しを除去しました。- 効果: 書き込みパスで約 2-3% の速度改善。
[Medium] PERF-04:
_acquire_lock()を@contextmanagerジェネレータから直接 RLock 返却に変更(core.py)_acquire_lock()が@contextmanagerデコレータを使用していたため、毎呼び出しでジェネレータオブジェクトの生成・next()呼び出し・contextlibのオーバーヘッドが発生していました。タイムアウト未設定(共通ケース)の場合はthreading.RLockオブジェクトをそのまま返し、タイムアウト設定時のみ新設の_TimedLockContextを返すよう変更しました。RLockは C レベルの__enter__/__exit__を持つため、ジェネレータより大幅に高速です。- 効果: 非 v2 書き込みで約 7% の速度改善。
[1.5.0] - 2026-04-04
セキュリティ修正(v1.5.0 プレリリース監査)
- [Critical] SEC-03:
UniqueHookにおける TOCTOU (Time-of-check/Time-of-use) 競合状態を文書化・警告追加。ユニーク制約チェックが DB 書き込みの外側で行われるため、マルチスレッド環境では制約をバイパスされる可能性があることをクラス docstring に明記しました。本質的な修正には SQLite ネイティブ制約 (UNIQUE) または排他ロックの適用を推奨します。 - [Critical] SEC-04:
ForeignKeyHookにおける TOCTOU 競合状態を同様に文書化・警告追加。参照整合性制約チェックとDB書き込みの間に参照先キーが削除される可能性を docstring に明記。本質的な修正にはPRAGMA foreign_keys=ONの使用を推奨します。 - [High] SEC-05:
BaseHookのkey_pattern正規表現パターンに ReDoS (正規表現によるサービス拒否) 脆弱性が存在しました。悪意ある正規表現パターンにより CPU 負荷を引き起こす可能性があったため、コンストラクト時にパターン検証を行うよう修正しました。 - [High] SEC-06: フック制約違反時のエラーメッセージに詳細なフィールド名・値が含まれ、情報漏洩の恐れがありました。エラーメッセージを汎用化し、詳細情報はサーバーサイドのログのみに記録するように修正しました。
バグ修正(v1.5.0 プレリリース監査)
- [Critical] BUG-05:
PydanticHookが全ての例外をValidationErrorとして一律に変換・抑制していた問題を修正。ConnectionError,MemoryError等のシステムエラーは正しく再送出されるようになりました。 - [High] BUG-06: フック処理において値が変更されない場合も不要な辞書コピーを行っていた問題を修正。変更検出ロジックを導入し、実際に値が変更された場合のみ新しい辞書を生成するようにしました(バッチ処理でのメモリ効率改善)。
コード品質修正(PR レビュー指摘対応)
- [Low] BANDIT-B110:
v2_engine.pyにて Bandit が指摘したtry/except/passパターン(atexit.unregisterの空キャッチ)をcontextlib.suppress(Exception)に置き換え。 - [Low] POC クリーンアップ: CodeQL・Bandit が指摘した POC スクリプト内の問題(未使用インポート、未使用変数、bare
except、ReDoS パターンのリテラル埋め込み)をすべて修正。テストファイル内の重複import sqlite3も解消。
パッケージングとIDE支援の改善
- [High] PEP 561 準拠と型補完の修正:
pyproject.tomlのtool.setuptools設定を標準的なsrc-layout用に刷新。これまで PyPI 配布版でimport nanasqliteした際に型補完(IntelliSense)が効かなかった問題を修正しました。include-package-data = trueを有効化し、MANIFEST.inを追加することで、ビルドされたパッケージ (.whl, sdist) に確実にpy.typedファイルが含まれるようにしました。- これにより、VS Code (Pylance) や PyCharm 等の主要な IDE で、インストール直後から
NanaSQLiteやPydanticHookなどの完全な型補完が利用可能になりました。
リリース品質監査 (Release Audit) による改善
- [Critical] BUG-01:
batch_update,batch_update_partial,batch_deleteメソッドにおいて V2 モードをバイパスして直接 DB を書き換えていた不具合を修正。V2 エンジンのステージングバッファを経由するようにルーティングし、データの整合性と順序を保証しました。 - [Critical] BUG-02:
clear()およびload_all()メソッドにおいて、V2 エンジンのflush()が完了する前に DB 操作が実行され、古いデータが再挿入される「幽霊書き込み(Ghost Re-inserts)」が発生する問題を修正。flush(wait=True)による同期的待機を導入しました。 - [High] QUAL-01:
AsyncNanaSQLite.add_hook()の実装を整理。ベース DB 初期化前後のフック登録処理を堅牢化し、非同期実行時の安定性を向上させました。 - [Non-Breaking] API 拡張:
flush()(同期) およびaflush()(非同期) にwait引数を追加。バックグラウンド処理の完了を待機できるようになりました。 - [High] Python 3.9 互換性の完全復旧:
- 全てのソースファイルに
from __future__ import annotationsを追加し、Python 3.10+ の|(Union) 演算子を型ヒントで使用していても Python 3.9 で動作するように修正しました。 compat.pyにEllipsisTypeの互換レイヤーを導入し、Python 3.9 環境でのmypyチェックと実行時の型検証の安定性を向上させました。pyproject.tomlのmypy設定を3.9に更新し、継続的な互換性を保証しました。
- 全てのソースファイルに
新機能: Ultimate Hooks (汎用フック&制約アーキテクチャ)
- 強力なフック機構の導入:
NanaHookプロトコルを新設し、before_write,after_read,before_deleteの3つのライフサイクルイベントをフック可能にしました。- カスタムフックを自作することで、データの検証、暗号化の拡張、ロギング、他システムへの通知などを自由に実装できます。
- 標準制約(Standard Constraints)の組み込み:
CheckHook: SQLite のCHECK制約のような関数ベースの検証を提供。UniqueHook: 指定したキー(またはフィールド)の値の一意性を保証(TOCTOU 警告あり、詳細は SEC-03 参照)。ForeignKeyHook: 他のNanaSQLiteテーブルのキーに対する参照整合性を保証(TOCTOU 警告あり、詳細は SEC-04 参照)。
- 外部ライブラリ統合の透過的サポート:
ValidkitHook: 従来のvalidator引数と互換性を持ち、validkit-pyによる高性能バリデーションを提供。PydanticHook:Pydanticモデルを直接フックに登録することで、読み書き時の自動シリアライズ/デシリアライズおよび厳格な型検証を実現。
- メソッドの拡張:
NanaSQLite.add_hook()およびAsyncNanaSQLite.add_hook()を追加しました。
アーキテクチャ強化と後方互換性
- 従来の
validatorパラメータは内部的にValidkitHookへと自動変換されるようになり、後方互換性が100%維持されています。 batch_update,get,batch_get,setdefault,popなど、あらゆるアクセス経路でフックが等しく適用されるように内部ロジックを統合・堅牢化しました。
監査・テスト
- プレリリース監査レポート (
audit.md) を更新 — v1.5.0 向け 12 件の発見事項を文書化。 - POC スクリプト 5 件を
etc/poc/に追加。 - POC 検証テスト 14 件を
tests/test_audit_poc.pyに追加。
[1.5.0dev2] - 2026-03-28 (リリース済みバージョン — v1.5.0 に統合)
パッケージングとIDE支援の改善
- [High] PEP 561 準拠と型補完の修正:
pyproject.tomlのtool.setuptools設定を標準的なsrc-layout用に刷新。これまで PyPI 配布版でimport nanasqliteした際に型補完(IntelliSense)が効かなかった問題を修正しました。include-package-data = trueを有効化し、MANIFEST.inを追加することで、ビルドされたパッケージ (.whl, sdist) に確実にpy.typedファイルが含まれるようにしました。- これにより、VS Code (Pylance) や PyCharm 等の主要な IDE で、インストール直後から
NanaSQLiteやPydanticHookなどの完全な型補完が利用可能になりました。
[1.5.0dev1] - 2026-03-28
リリース品質監査 (Release Audit) による改善
- [Critical] BUG-01:
batch_update,batch_update_partial,batch_deleteメソッドにおいて V2 モードをバイパスして直接 DB を書き換えていた不具合を修正。V2 エンジンのステージングバッファを経由するようにルーティングし、データの整合性と順序を保証しました。 - [Critical] BUG-02:
clear()およびload_all()メソッドにおいて、V2 エンジンのflush()が完了する前に DB 操作が実行され、古いデータが再挿入される「幽霊書き込み(Ghost Re-inserts)」が発生する問題を修正。flush(wait=True)による同期的待機を導入しました。 - [High] QUAL-01:
AsyncNanaSQLite.add_hook()の実装を整理。ベース DB 初期化前後のフック登録処理を堅牢化し、非同期実行時の安定性を向上させました。 - [Non-Breaking] API 拡張:
flush()(同期) およびaflush()(非同期) にwait引数を追加。バックグラウンド処理の完了を待機できるようになりました。 - [High] Python 3.9 互換性の完全復旧:
- 全てのソースファイルに
from __future__ import annotationsを追加し、Python 3.10+ の|(Union) 演算子を型ヒントで使用していても Python 3.9 で動作するように修正しました。 compat.pyにEllipsisTypeの互換レイヤーを導入し、Python 3.9 環境でのmypyチェックと実行時の型検証の安定性を向上させました。pyproject.tomlのmypy設定を3.9に更新し、継続的な互換性を保証しました。
- 全てのソースファイルに
新機能: Ultimate Hooks (汎用フック&制約アーキテクチャ)
- 強力なフック機構の導入:
NanaHookプロトコルを新設し、before_write,after_read,before_deleteの3つのライフサイクルイベントをフック可能にしました。- カスタムフックを自作することで、データの検証、暗号化の拡張、ロギング、他システムへの通知などを自由に実装できます。
- 標準制約(Standard Constraints)の組み込み:
CheckHook: SQLite のCHECK制約のような関数ベースの検証を提供。UniqueHook: 指定したキー(またはフィールド)の値の一意性を保証。ForeignKeyHook: 他のNanaSQLiteテーブルのキーに対する参照整合性を保証。
- 外部ライブラリ統合の透過的サポート:
ValidkitHook: 従来のvalidator引数と互換性を持ち、validkit-pyによる高性能バリデーションを提供。PydanticHook:Pydanticモデルを直接フックに登録することで、読み書き時の自動シリアライズ/デシリアライズおよび厳格な型検証を実現。
- メソッドの拡張:
NanaSQLite.add_hook()およびAsyncNanaSQLite.add_hook()を追加しました。
アーキテクチャ強化と後方互換性
- 従来の
validatorパラメータは内部的にValidkitHookへと自動変換されるようになり、後方互換性が100%維持されています。 batch_update,get,batch_get,setdefault,popなど、あらゆるアクセス経路でフックが等しく適用されるように内部ロジックを統合・堅牢化しました。
[1.4.1] - 2026-03-27
セキュリティ修正
- QUAL-07 [High] 同期版
NanaSQLiteクラスに V2 エンジンの管理メソッドを追加し、完全な機能パリティを実現しました。 - CORE [Critical]
clear()、load_all()、restore()メソッドにおける V2 エンジンの整合性を強化し、データの不整合や「幽霊書き込み」を防止しました。 - SEC-01/02 [Critical]
column_typeバリデーションに ReDoS 対策を施したホワイトリスト方式を導入し、セキュリティを強化しました。 - CONC-01/02 [High] V2 エンジンと
ExpiringDictにおけるマルチスレッド実行時のレースコンディションおよびデッドロックを修正しました。 - [Critical] PERF-02:
table()メソッドで作成された子インスタンスが親のV2Engineを共有するように改善。これにより、テーブルごとにスレッドやatexitハンドラが生成されるリソースリーク(およびプロセス終了時のハングアップ)を解消しました。 - [Critical] DEADLOCK-01:
V2EngineにおいてStrictTaskの処理中にデッドロックが発生し、pytest等の並列実行中にプロセスがハングアップする問題を修正しました。タスク処理のトランザクション分離と、shutdown時の確実なイベント解放を実装しました。 - [Critical] MULTI-TENANT-01:
V2Engineが単一のテーブル名に依存していた不具合を修正。複数のテーブルインスタンスが一つのエンジンを共有しても、データが混同されないマルチテナント(テーブル単位の分離)に対応しました。 - [High] QUAL-08:
V2Engine.shutdown()の堅牢性を強化。二重実行の防止、atexitハンドラの確実な解除、およびシャットダウン時のフラッシュ処理の安全性を向上させました。 - QUAL-05 [Medium] V2 モードでの明示的な
begin_transaction()呼び出しに対するガードを追加し、バックグラウンドフラッシュとの衝突を防止しました。 - [Medium] SEC-02:
core.pyにおけるcolumn_typeバリデーションの正規表現を脆弱性パターン([\w ]*)から安全なパターンに修正し、SonarQube が警告していた ReDoS(正規表現によるサービス拒否)の脆弱性を完全に解消しました。
バグ修正
- [High] BUG-01:
upsert()およびaupsert()において、データ辞書を第1引数に渡しつつconflict_columnsを指定した場合にAttributeErrorが発生する問題を修正。解決済みのtarget_dataのキーを参照するよう内部ロジックを改善しました。(1.4.1rc1) - [High] Qual-02:
AsyncNanaSQLiteの初期化時において、複数の非同期タスクが同時にアクセスした場合に発生する可能性があった競合状態(Race Condition)を修正。asyncio.Lockを導入し、スレッドプールの二重初期化を防止しました。 AsyncNanaSQLite.table()において、docstring の分断や引数伝搬の不備により発生していた構文エラーおよび初期化の不具合を修正しました。(1.4.1dev3)AsyncNanaSQLiteの一部メソッドにおいて、機能適用時に重複して定義されていた箇所を整理しました。(1.4.1dev3)
最終監査(Deep Audit)による重要修正
- [Critical] BUG-02: V2モードにおいて、書き込み直後に
get()や__getitem__でデータを取得すると古い値が返る「不整合(Stale Read)」バグを修正。バックグラウンドのステージングバッファを優先的に参照するように改善しました。 - [Critical] QUAL-04:
AsyncNanaSQLiteの__init__内でasyncio.Lock()を初期化していたため、イベントループ外でインスタンス化した際にエラーが発生する問題を修正。ロックを遅延初期化(Lazy Initialization)するように変更しました。 - [Critical] LOCK-01:
ExpiringDictの TTL 失効処理(on_expire)が DB ロックを保持したまま呼び出され、通常の書き込み処理と競合してデッドロックが発生する問題を修正。コールバックをロックの外側で実行するように改善しました。 - [Critical] CONC-01:
NanaSQLiteの内部キャッシュ更新処理が DB ロックの外側で行われていたため、マルチスレッド環境(AsyncNanaSQLite等)でRuntimeErrorやキャッシュ破損、TOCTOU 競合が発生する問題を修正。キャッシュ操作を DB ロックの保護下に移動しました。 - [Critical] CONC-02: V2モードで
table()を使用して子インスタンスを作成した際、同じ SQLite 接続に対して複数のスレッドが同時にトランザクションを開始しようとしてクラッシュする問題を修正。親子の V2Engine 間でshared_lockを共有し、排他制御を強化しました。 - [Critical] ASYNC-01:
AsyncNanaSQLiteにおいて V2 モード用のメソッド(aflush,aget_dlq等)が未実装であった問題を修正。同期版と同等のすべての管理機能を非同期 API として追加しました。 - [High] QUAL-07: 同期版
NanaSQLiteにも V2 管理メソッドを追加し、非同期版との完全な機能パリティを実現。 - [High] QUAL-05: V2モードにおいて
begin_transaction()等の明示的なトランザクション操作を行うと V2 エンジンのバックグラウンド処理と衝突するため、V2モード時は明示的なトランザクションを禁止(例外送出)するようにガードを追加しました。 - [High] QUAL-06:
AsyncNanaSQLite.table()においてv2_enable_metrics設定が子インスタンスに継承されない不具合を修正しました。 - [Medium] SEC-01 (強化):
create_table()のカラム型バリデーションをブラックリスト方式からホワイトリスト方式(正規表現による記号制限)へ移行し、検知パターンを強化しました。
パフォーマンス改善
- [Low] PERF-01: LRU および TTL キャッシュ戦略において、データベースに存在しないキーの検索結果を記憶する「ネガティブキャッシュ」を導入し、繰り返しアクセス時の I/O 負荷を削減しました。(同時に、本機能によってセンチネルが混入する破壊的バグを早期発見し修正済みです)(1.4.1rc1)
コード品質改善
- [Low] QUAL-01:
ExpiringDictのスケジューラスレッド停止処理を改善し、インスタンス破棄時やクリア時のクリーンアップをより堅牢にしました。(1.4.1rc1) - [Low] QUAL-03: ソースコード内のマジックリテラル(
"BEGIN IMMEDIATE"等)の共通定数化を行い、保守性を向上させました。 - [Low] CI-01: SonarQube Cloud の「Quality Gate」における誤検知(ドキュメントやスクリプトがカバレッジに含まれる問題)を解消し、認知複雑度などの非本質的な警告を抑制する設定を導入しました。
- [Low] QUAL-09:
utils.pyのlist(dict.keys())をlist(dict)に変更し、不要な.keys()呼び出しを削除しました(SonarCloud指摘対応)。 - [Low] QUAL-10 (新機能):
V2Configデータクラスを追加し、v2関連パラメータ(flush_mode,flush_interval,flush_count,chunk_size,enable_metrics)をひとまとめにして渡せるようにしました。既存の個別引数は後方互換のためすべて維持されます。SonarCloud の「パラメータが多すぎる(brain-overload)」警告への対応です。pythonfrom nanasqlite import NanaSQLite, V2Config cfg = V2Config(flush_mode="time", flush_interval=5.0, enable_metrics=True) db = NanaSQLite("mydata.db", v2_mode=True, v2_config=cfg) - [Low] CI-02:
bench-rpi.ymlにおいて、docker run実行前にdocker rm -fを追加し、キャンセル後の再実行時にコンテナ名が競合するエラー("Conflict. The container name is already in use")を解消しました。
新機能: V2エンジンの利便性と観測性の向上 (オプトイン)
- デッドレターキュー (DLQ) の可視化:
get_dlq(),retry_dlq(),clear_dlq()メソッドを同期・非同期(a*)両方に追加しました。- バックグラウンドで発生したエラー内容を直接確認し、必要に応じて手動でリトライや消去が可能です。
- メトリクス収集機能:
v2_enable_metrics=Trueを指定することで、エンジンの詳細な統計情報を収集可能になりました。get_v2_metrics()により、総フラッシュ件数、処理時間、DLQ発生数などのメトリクスを取得できます。
- 設定の継承:
table()メソッドで子インスタンスを作成する際、v2_enable_metricsなどの V2 固有設定が正しく引き継がれるようになりました。
ドキュメント
- APIリファレンス自動生成の刷新:
scripts/gen_api_docs.pyを大幅に改修し、VitePress のコールアウトやテーブルを活用した、より美しく使いやすい API ドキュメントの自動生成を実現しました。 - 全ドキュメントのモダン化: 既存の Markdown ドキュメント内の太字警告等を、VitePress 標準のカスタムコンテナ(
::: warning等)へ一括変換し、サイト全体のデザインを統一しました。
[1.4.0] - 2026-03-12
セキュリティ修正
- [Critical] SEC-01:
create_table()のカラム型に対するSQLインジェクション脆弱性を修正。APSW はセミコロン区切りの複数文を一度に実行するため、カラム型定義を通じて任意のSQLを実行可能でした。セミコロン (;)、ラインコメント (--)、ブロックコメント (/*) を含む文字列を拒否するバリデーションを追加しました。
バグ修正
- [High] BUG-01: V2Engine の
_process_strict_queue()でon_successコールバックがトランザクション COMMIT 前に呼ばれ、後続タスク失敗時のロールバックで不整合が発生する問題を修正。コールバックを COMMIT 成功後に遅延実行するよう変更しました。 - [Medium] BUG-02:
AsyncNanaSQLite.table()で子インスタンスに_v2_mode,_cache_strategy,_encryption_key等の属性が設定されずAttributeErrorが発生する問題を修正。親インスタンスの全設定を正しく継承するよう変更しました。 - [Medium] BUG-03: v2モードで
execute()経由の SELECT/PRAGMA/EXPLAIN クエリが常に空結果を返す問題を修正。読み取りクエリをバックグラウンドキューからバイパスして直接実行するよう変更しました。
コード品質改善
- [Low] BUG-04:
async_core.pyの_shared_query_impl()内で重複していたエイリアス抽出ロジックをNanaSQLite._extract_column_aliases()の呼び出しに置き換えました。 - [Low] QUAL-01:
update()メソッドの型アノテーションをdictからdict | Noneに修正しました。
[1.4.0dev2] - 2026-03-12
改善: 非同期 API の完全化
AsyncNanaSQLiteにおいて、同期版と同等の全主要メソッドを非同期版(abackup,arestore,apragma,aget_table_schema,alist_indexes,aalter_table_add_column,aupsert,aget_dlq,aretry_dlq等)として実装・公開しました。
変更: upsert() メソッドの統合と強化
upsert()メソッドのシグネチャを統合し、(table_name, data_dict, conflict_columns)パターンと(key, value)パターンの両方を単一のメソッドでサポートするように強化しました。- v2 モード有効時に
(key, value)パターンで呼び出すと、内部的にバックグラウンドキューへルーティングされます。
テスト: ベンチマーク・カバレッジの拡充
pytest-benchmarkによる計測対象を 158 から 177 に拡大。- これまで未カバーだった
backup,restore,pragma,DDL (alter table/index),export/import等の主要全操作を計測対象に追加しました。 - 非同期ベンチマーク (
tests/test_async_benchmark.py) を大幅に強化。
修正
get_table_schemaメソッドがtableプロパティが存在しない場合にエラーになる不具合を修正し、table_name引数を省略可能(デフォルトで現在のテーブルを使用)に変更しました。- プロジェクト全体の
rufflint エラー(31件)およびmypy型チェックエラーを解消。
[1.4.0dev1] - 2026-03-12
新機能: v2 アーキテクチャ (オプション)
- ノンブロッキング・バックグラウンド永続化:
NanaSQLite(db_path, v2_mode=True)を指定することで、v2アーキテクチャが有効になります。- すべての書き込み操作(KVS操作およびSQL実行)が一時的にメモリ上のバッファまたはキューに格納され、バックグラウンドスレッドで非同期にSQLiteへフラッシュされます。
- これにより、書き込みによるメインスレッドのI/Oブロックが完全にゼロになり、書き込みレイテンシが劇的に改善します。
- 読み込みレイテンシは従来通り(メモリキャッシュから直接取得するため)ゼロコストです。
- フラッシュモード:
flush_modeパラメータで最適なタイミング(immediate,count,time,manual)を選択できます。 - デッドレターキュー (DLQ): バックグラウンドでのSQL実行失敗時に、問題のあるタスクだけを隔離し、他のデータ永続化を継続・保護します。
get_dlq()で内容確認、retry_dlq()で再試行が可能です。 - チャンク処理: 大量データの書き込み時にSQLiteのロックを長時間占有しないよう、バッチを分割(デフォルト 1000件ごと)して少しずつ書き込みます。
- 注意: v2アーキテクチャは「単一プロセス」システム専用です。マルチプロセス環境(FastAPI/Gunicornの複数ワーカーなど)ではデータ破損の原因となるため警告が出力されます。
変更
NanaSQLiteおよびAsyncNanaSQLiteの__init__にv2_mode,flush_mode,flush_interval,flush_count,v2_chunk_sizeパラメータを追加。- 手動フラッシュ用の
flush()(同期) およびaflush()(非同期) メソッドを追加。 V2Engineに DLQ 管理用のget_dlq(),retry_dlq()メソッドを追加。
修正
- v2 エンジンにおけるデッドレターキュー (DLQ) への同時アクセスによる競合状態 (Race Condition) を修正。
- v2 エンジンにおいて Staging Buffer が空の場合に Strict Queue が処理されない不具合を修正。
[1.3.4] - 2026-03-10
セキュリティ修正
- SEC-01 [High]:
alter_table_add_column()のcolumn_typeバリデーションをブラックリスト方式からホワイトリスト正規表現に変更。TEXT; DROP TABLEのようなインジェクションペイロードを確実にブロック。 - SEC-02 [High]:
sanitize_sql_for_function_scan()を修正し、ダブルクォート付き SQL 識別子の内容を保持するよう変更。"LOAD_EXTENSION"()のようなクォート付き関数名バイパスを_validate_expression()が正しく検出可能に。
バグ修正
- BUG-01 [Critical]:
items()メソッドに_check_connection()チェックを追加。クローズ済みインスタンスで呼び出した際に APSW 低レベル例外ではなくNanaSQLiteClosedErrorが発生するよう修正。 - BUG-02 [High]: AEAD 暗号化有効時に非 bytes 値を受け取った場合、サイレントに平文 JSON フォールバックするのではなく警告ログを出力するよう変更。
- BUG-03 [High]: AEAD 復号前に nonce+認証タグを含む最小長の検証(≥28 バイト = nonce 12 + auth tag 16)を追加。短すぎるデータに対して明確な
NanaSQLiteDatabaseErrorを送出。InvalidTag など低レベル例外も同エラーにラップ。 - BUG-04 [High]:
AsyncNanaSQLite.acontains()の冗長な二重_ensure_initialized()呼び出しを削除。 - BUG-05 [Medium]: 非同期
_shared_query_impl()にoffsetパラメータの型・非負チェックを追加。 - BUG-06 [Medium]:
async_core.pyのparameters: tuple = Noneをtuple | None = Noneに修正(mypy strict 対応)。 - BUG-07 [Medium]:
ExpiringDictスケジューラが 1 反復で期限切れキーをすべて処理するよう改善(従来は 1 キーずつ)。 - BUG-09 [Medium]:
batch_get()が値Noneを明示的に格納したキーを結果に含めるよう修正。 - BUG-10 [Low]:
_sanitize_identifier()でコンパイル済みIDENTIFIER_PATTERNを再利用。 - BUG-12 [Low]:
NanaSQLiteDatabaseError.__init__のoriginal_error型アノテーションをException | Noneに修正。
パフォーマンス改善
- PERF-03 [Medium]: カラム名エイリアス抽出ロジックを
_extract_column_aliases()ヘルパーに共通化(3 箇所の重複排除)。
コード品質改善
- QUAL-01 [Medium]:
_get_all_keys_from_db()の戻り値型をlist[str]に修正。 - QUAL-03 [Medium]:
query()とquery_with_pagination()間のカラム名クォート除去ロジックを統一。
監査・テスト
- プレリリース監査レポート (
audit.md) を追加 — 35 件の発見事項を文書化。 - POC スクリプト 6 件を
etc/poc/に追加。 - POC 検証テスト 20 件を
tests/test_audit_poc.pyに追加。 audit_prompt.mdを 6 フェーズ構成に改正(監査 → POC → パッチ → pytest → CI 検証 → リリース準備)。
[1.3.4rc4] - 2026-03-08
CI 修正
- provenance ジョブの権限を最小権限に変更 (PR #127):
provenanceジョブのcontents: writeをcontents: readに降格。upload-assetsを使わないためwrite権限は不要だった。- 無効だった
upload-assets: trueを削除(タグトリガーのないワークフローでは常にスキップされていたデッドコード)。 - プロベナンスの GitHub Release への添付は
releaseジョブが引き続き担当。 - CI アノテーション(
go.sum not foundワーニング・PyPI アテステーション通知)の原因をコメントで説明し、誤解を防止。 CHANGELOG.mdを main ブランチの最新版に同期。
[1.3.4rc3] - 2026-03-08
CI 修正
- SLSA3 provenance リリースフローを復旧・安定化 (PR #123):
- GitHub Actions の provenance 検証ジョブに
actions: read/contents: read権限を追加。 provenance-name出力から期待する provenance ファイル名を明示的に組み立て、存在確認に失敗した場合は早期終了するよう改善。- GitHub Release へ添付する provenance アーティファクトをワイルドカードではなく生成済みファイル名で指定し、リリース時の取り違えを防止。
- GitHub Actions の provenance 検証ジョブに
[1.3.4rc2] - 2026-03-08
セキュリティ修正
- SQLインジェクション保護を実装 (PR #121, #122):
- テーブル名を SQL クエリ内で直接展開していたため、細工されたテーブル名でインジェクションが可能でした。
self._safe_tableにサニタイズ済み(クォート済み)のテーブル名をキャッシュし、すべての SQL 実行箇所でこちらを使用するよう変更。self._tableは従来どおり生の名前を保持し、__repr__や後方互換のために使用。- SECURITY.md を更新し脆弱性の経緯と修正内容を記載。
- PoC スクリプト (
etc/poc/poc_sqli.py,etc/poc/poc_none.py) を追加してリスクを文書化。
バグ修正・コード品質改善
_NOT_FOUNDセンチネルをget_fresh()および__contains__に適用 (PR #121):get_fresh()が DB ミス時にNoneを返すため、実際にNoneを格納したキーと区別できなかった問題を修正。_NOT_FOUND = object()センチネルを使用し、DB ミスと格納値Noneを正確に識別できるよう改善。__contains__を軽量な実装に戻し、不要な DB 読み込みを削減。
CI 修正
- validkit-py の CI テストガードを修正 (PR #119):
- CI で
validationエクストラをインストールするよう修正し、validkit 関連テストが正しく実行されるようになった。
- CI で
ドキュメント
- validkit-py バリデーションガイドを追加 (PR #117):
- 日英両方のドキュメントサイトに validkit-py の使い方・バリデーションガイドを追加。
- ガイドのレッスン順序を整理 (PR #116):
- JA/EN サイトドキュメントのガイドレッスンを再整理・分類。
- ドキュメントの不整合・リンク切れ・誤記を修正 (PR #115):
- 英語・日本語ドキュメント間の不整合、壊れたリンク、誤った記述、および欠落していた文書を修正。
[1.3.4rc1] - 2026-03-07
新機能
batch_update_partial()メソッドを追加(同期・非同期):validatorが設定されている場合にバッチ書き込みを「部分成功モード」で実行する新メソッド。- 各エントリを個別にバリデーションし、成功したものだけ DB に書き込む。
- 失敗したエントリは
{key: エラーメッセージ}のdictとして返却し、例外は送出しない。 coerce=Trueの場合は変換済みの値で書き込む。- 既存の
batch_update()はアトミック動作(全件成功 or 全件拒否)のまま維持。 - 非同期版は
AsyncNanaSQLite.abatch_update_partial()として追加。
バグ修正・コード品質改善
core.pymypy エラーを修正:_serialize()のjson_strがHAS_ORJSON=Falseパスでstr確定だが mypy がstr | Noneと推論していたためtype: ignoreを付与。
- examples の ruff 違反を修正:
examples/test_examples.py: import ソート(I001)、assert False→raise AssertionError()(B011)、クラス名を CapWords に変更(N801)。examples/validkit_batch_demo.py: import ソート(I001)。
サンプル追加
examples/validkit_batch_demo.pyを追加:batch_update()のアトミック動作とbatch_update_partial()の部分成功モードを実演。coerce=Trueとの組み合わせ例を含む。
examples/test_examples.pyに validkit バッチ操作の検証を追加:batch_update()のロールバック確認、batch_update_partial()の部分書き込み確認、coerce=Trueの変換確認。
[1.3.4b3] - 2026-03-05
バグ修正・安定性改善
Python 3.9 でのテスト不安定問題を修正 (
tests/test_tdd_cycle_6.py) (PR #113):test_ellipsis_type_is_availableはtypes.EllipsisType(Python 3.10 で追加)の有無を確認するテストですが、 Python 3.9 環境では無条件に失敗していました。@pytest.mark.skipif(sys.version_info < (3, 10), ...)デコレータを追加し、Python 3.9 では このテストをスキップするよう修正。Python 3.10 以降では引き続き実行されます。from __future__ import annotationsが有効なため、types.EllipsisTypeを使った型注釈は ランタイムに評価されず、Python 3.9 でも本体コードは正常に動作します(テストのみの問題でした)。- ライブラリの動作・公開 API への影響はありません。
table()のキャッシュ設定継承を修正 (PR #112):table()で子インスタンスを生成する際、cache_ttl/cache_persistence_ttlが親から引き継がれず、 TTL キャッシュ戦略を使用している場合にValueErrorが発生する問題を修正。_cache_strategy_raw/_cache_size_raw/_cache_ttl_raw/_cache_persistence_ttl_rawを内部に保持し、table()が全キャッシュ設定を正しく継承するよう修正。
AsyncNanaSQLiteでの validkit-py 未インストール時ImportErrorの即時送出 (PR #112):- 従来は操作実行時まで
ImportErrorが遅延されていたが、AsyncNanaSQLite.__init__でvalidatorを指定した時点で 即座に送出するよう修正(NanaSQLiteとの挙動を統一)。 HAS_VALIDKITフラグをasync_core.pyに追加。
- 従来は操作実行時まで
例外の絞り込み (
core.py):- オプション依存(orjson / validkit-py)のインポートで
except Exception:を使用していた箇所をexcept ImportError:に変更。
- オプション依存(orjson / validkit-py)のインポートで
型アノテーション修正:
table()のcache_strategy引数のLiteral型に"ttl"を追加。_UNSETセンチネルの型注釈をtypes.EllipsisTypeに変更し、型安全性を向上。
mypy 設定更新 (
pyproject.toml):python_versionを3.9→3.10に更新し、types.EllipsisTypeを型チェック時に認識させるよう修正。
API ドキュメント修正 (PR #112)
NanaSQLite.table()およびAsyncNanaSQLite.table()の API ドキュメント(日・英)で、validator/coerceの既定値が= ...(親から継承)であることを明記。
テスト・品質改善 (PR #112)
- 包括的テストスイートを追加:
tests/test_table_inheritance_comprehensive.py:table()の全継承パターンを 75 ケースで検証。tests/test_validkit_integration.py: validkit-py 統合テスト(同期・非同期)。tests/test_tdd_review_fixes.py: レビューコメント対応の検証テスト。tests/test_tdd_cycle_2.py〜tests/test_tdd_cycle_10.py: TDD サイクルごとの回帰テスト。
- validkit インストール確認の方法を改善:
importlib.util.find_specからtry/except import方式に変更し、破損インストールも正しく検出。
[1.3.4b2] - 2026-03-04
新機能
validatorパラメータの追加(オプション依存: validkit-py):NanaSQLite.__init__およびAsyncNanaSQLite.__init__にvalidatorパラメータを追加。- validkit-py のスキーマ(辞書または
Schemaオブジェクト)を渡すと、値の書き込み時に自動バリデーションを実行します。 - スキーマ違反時は
NanaSQLiteValidationErrorを送出。 - validkit-py をインストールせずに
validatorを指定した場合はImportErrorを送出し、インストール手順を案内します。 pip install nanasqlite[validation]でインストール可能。HAS_VALIDKITフラグをnanasqliteパッケージ(およびcoreモジュール)から公開。
table()のvalidator引数対応:NanaSQLite.table()およびAsyncNanaSQLite.table()にvalidatorパラメータを追加。- テーブルごとに異なるスキーマを適用可能。
validatorを省略した場合は親インスタンスのスキーマを自動継承。
coerceパラメータの追加(自動変換オプション):NanaSQLite.__init__、NanaSQLite.table()、AsyncNanaSQLite.__init__、AsyncNanaSQLite.table()にcoerce: bool = Falseパラメータを追加。Trueを指定すると、validkit-py のバリデーション後に変換済みの値(例:"42"→42)をDBに保存します。- 注意: 自動変換を機能させるには、スキーマの各フィールドバリデーターにも
.coerce()を呼び出す必要があります(例:v.int().coerce())。フィールドに.coerce()がない場合、型が一致しない値はバリデーションエラーになります(NanaSQLite のcoerce=Trueだけでは変換されません)。 validatorと組み合わせて使用します。validatorが設定されていない場合は無効。table()で省略した場合は親インスタンスの設定を引き継ぐ。
batch_update()バリデーション対応:validatorを設定している場合、batch_update()はすべての値を DB 書き込み前に一括バリデーションするようになりました。- 1件でもスキーマ違反があった場合、何も書き込まれません(アトミックな失敗保証)。
coerce=Trueを設定している場合、変換済みの値を一括書き込みします。
バグ修正
table()でvalidatorが子インスタンスに引き継がれない問題を修正:- b1 では
table()で生成した子インスタンスに親の_validatorが渡されておらず、 サブテーブルへの書き込み時にバリデーションが実行されませんでした。 AsyncNanaSQLite.table()でも同様に_validatorがasync_sub_dbに設定されていなかった問題を修正。
- b1 では
[1.3.4b1] - 2026-03-04
新機能
lock_timeoutパラメータの追加 (P2-1):NanaSQLite.__init__にlock_timeout: float | None = Noneパラメータを追加。- 設定すると、ロック取得時に指定秒数以内に取得できない場合は
NanaSQLiteLockErrorを送出。 - デフォルト
Noneは従来通り無制限待機。後方互換性への影響はありません。 - 内部に
_acquire_lock()コンテキストマネージャを新設し、ユーザー操作に伴う排他制御ではロックタイムアウトが反映されます(一部の内部処理〈期限切れ削除など〉は従来通りブロッキング取得のままです)。
backup()/restore()メソッドの追加 (P2-3):NanaSQLite.backup(dest_path): APSW の SQLite オンラインバックアップ API を使用して、現在の DB をdest_pathにバックアップします。NanaSQLite.restore(src_path):src_pathのバックアップファイルから DB を復元し、接続を再確立してキャッシュをクリアします。リストア時に WAL/SHM/journal サイドカーファイル(-wal/-shm/-journal)を明示的に削除し、stale な WAL 内容の再生による不整合を防止します。- 両メソッドとも新規 public メソッドの追加のみ。後方互換性への影響はありません。
スレッドセーフティ改善
table()の子インスタンス生成をロック保護:table()での子インスタンス生成〜WeakSet追加を_acquire_lock()で保護。restore()の接続差し替えとの競合を防止し、子インスタンスが閉じた接続を参照するリスクを排除。
バグ修正
__delitem__に_check_connection()を追加:del db[key]でクローズ済み接続を使用した際にNanaSQLiteClosedErrorを送出するよう修正。__setitem__・pop()・clear()と例外挙動を統一。
[1.3.4b0] - 2026-03-04
コード品質改善
- 非同期プールクリーンアップのログレベル修正:
AsyncNanaSQLite.close()内の読み取り専用プールドレイン処理で、AttributeError発生時のログレベルをERRORからWARNINGに変更。- あわせてコメントの文言を「Programming error」から実態に即した「Unexpected AttributeError - log and continue cleanup for resilience」に修正。
- ログ出力のみの変更であり、動作・後方互換性への影響はありません。
ドキュメント・計画
- v1.3.x 計画レビュードキュメントの追加 (
etc/in_progress/v1.3.x_plan_review.md):etc/配下の全計画書を横断的にレビューし、v1.3.x で実施すべき残タスクを整理・優先順位付け。- ロードマップ残項目(ロックタイムアウト、バリデーション基盤、バックアップ/リストア)の対応優先度を明記。
- v1.3.4b0 〜 v1.4.0 のリリース計画案を記載。
etc/README.md更新: 新規レビュードキュメントをin_progress/一覧に追記。etc/ディレクトリの再編(PR #109):etc/を実装状況別(implemented/・in_progress/・planned/)のサブディレクトリ構造に再編。フラットなfuture_plans/フォルダを廃止。- v1.3.0 キャッシュ機能(
ExpiringDict・UnboundedCache・TTLCache等)がすべて実装済みであることを確認。
依存関係更新(docs/site メンテナンス)
- docs/site 依存ライブラリの更新(Renovate):
[1.3.4dev0] - 2026-03-02
CI / 開発環境
- SLSA プロバナンスキャッシュ警告への対応・撤退:
その他
- バージョンを
1.3.4dev0に引き上げ(1.3.3リリース後の開発スナップショット)。
[1.3.3] - 2026-03-02
セキュリティ
- docs/site の依存関係脆弱性対応:
CI / 開発環境
- GitHub Actions の更新:
依存関係更新(メンテナンス)
- リリース自動化アクション更新:
softprops/action-gh-releaseを v2 に更新。(#96)
備考
- このリリースは主にメンテナンス(セキュリティ/CI/依存更新)を目的としたもので、ライブラリの公開API互換性に影響する変更は含みません。
[1.3.2] - 2026-01-17
パフォーマンス最適化
- orjson 統合の最適化:
_serialize()メソッドの不要な変数割り当てを削除し、コード可読性と保守性を向上。- orjson による JSON エンコード/デコードが全暗号化パス(Fernet, AES-GCM, ChaCha20)で効果的に活用されることを確認・検証。
- 標準
jsonモジュールと比較して 3~5倍の高速化 を期待。 - 非同期処理(
AsyncNanaSQLite)では ThreadPoolExecutor 経由で自動的に orjson の恩恵を受けるアーキテクチャを確認。
コード品質改善
- 本体コードの最適化:
- コード可読性を向上させ、変数スコープを明確化。
テスト・検証
- orjson テストの実行確認:
tests/test_json_backends.pyの全テストが正常に動作することを確認。- orjson 有無時の互換性テストが両環境で正常に実行。
- JSON バックエンドの自動切り替え機能(HAS_ORJSON フラグ)が正常に動作。
[1.3.1] - 2025-12-28
新機能: オプションのデータ暗号化
- マルチモード暗号化:
cryptographyを使用した透過的な暗号化を実装。- AES-GCM (デフォルト): 安全かつ高速。ハードウェア加速(AES-NI)対応環境で最適。
- ChaCha20-Poly1305: ハードウェア加速がない環境(ARM等)でも高速なソフトウェア実装。
- Fernet: 従来通りの使いやすさと互換性重視のオプション。
NanaSQLiteおよびAsyncNanaSQLiteにencryption_keyおよびencryption_mode引数を追加。- SQLite への保存前に暗号化し、取得時に自動復号。
- ハイブリッド設計: メモリキャッシュ内は平文で保持されるため、暗号化有効時も高速なリードパフォーマンスを維持。
- 拡張インストール:
pip install nanasqlite[encryption]で必要な依存関係を含めてインストール可能に。
新機能: 柔軟なキャッシュ戦略と TTL サポート (v1.3.1-alpha.0)
- TTL (Time-To-Live) キャッシュ:
cache_strategy=CacheType.TTL, cache_ttl=secondsでデータの有効期限を設定可能に。 - Persistence TTL (自動削除):
cache_persistence_ttl=Trueで失効時に SQLite からも自動削除。 - FIFO 制限付き Unbounded: 無制限キャッシュでも
cache_size指定で FIFO 方式のメモリ制限が可能。 - キャッシュクリア API:
db.clear_cache()および非同期版aclear_cache()を追加。
改良と修正
- 最適化された
ExpiringDict: 低オーバーヘッドかつ高精度な有効期限管理ユーティリティを内部実装。 - 後方互換性の維持: デフォルトの
UNBOUNDEDモードでは従来通りの高速パスを維持しつつ、制限設定時のみインターセプトを適用。 - 型安全性の向上:
mypyとruffによる厳格なチェックを通過し、型ヒントを強化。 - ベンチマークの統合:
tests/test_benchmark.py(Sync) とtests/test_async_benchmark.py(Async) に暗号化・キャッシュ戦略のベンチマークを集約し、可視性を向上。 - テストカバレッジ: 非同期環境におけるキャッシュ挙動(LRU退避、TTL失効)の検証テスト
tests/test_async_cache.pyを追加。
[1.3.0dev0] - 2025-12-27
新機能: 柔軟なキャッシュ戦略
CacheType列挙型の追加:UNBOUNDED(無制限、従来動作) とLRU(追い出し型) を選択可能に。- LRUキャッシュの実装:
cache_strategy=CacheType.LRU, cache_size=Nでメモリ使用量を制限可能。 - テーブル別設定:
db.table("logs", cache_strategy=CacheType.LRU, cache_size=100)で個別設定。 - 高速化オプション:
pip install nanasqlite[speed]で C拡張lru-dictを導入し、最大2倍の速度向上。 - 自動フォールバック:
lru-dict未インストール時は標準ライブラリOrderedDictを使用。
新規テスト
tests/test_cache.py: キャッシュ戦略の包括的テストスイート(追い出し、永続化、テーブル別設定)。
[1.2.2b1] - 2025-12-27
ドキュメントとブランドの刷新
- 超モダンなドキュメントサイトの構築:
- VitePress + Tailwind CSS を採用し、デザイン性とブラウジング体験を大幅に向上させた公式サイトを
docs/siteに構築。 - 公式SVGロゴの制作: 辞書
{ }とデータスタックを融合させた独自シンボルを開発。100%透過、ダークモード自動対応(反転フィルタ)、無限解像度のベクター形式。 - 多言語APIリファレンスの真の分離: docstringからターゲット言語(日・英)のみを抽出・整形するインテリジェントな生成エンジンを実装。
- VitePress + Tailwind CSS を採用し、デザイン性とブラウジング体験を大幅に向上させた公式サイトを
- 自動化と公開:
- GitHub Actions による docs 自動ビルド・公開ワークフロー (
deploy-docs.yml) を導入。 - 公開時に
gh-pagesブランチの過去のベンチマーク履歴を自動取得・マージし、履歴を保護するロジックを実装。
- GitHub Actions による docs 自動ビルド・公開ワークフロー (
セキュリティと安定性の向上
- SQL検証の適正化:
||(文字列連結) 演算子を高速検証の許可リストに追加し、複雑なエイリアスを含むクエリでの誤検知を解消。
- CI/CDの安定化:
ruffの最新ルールに基づき、core.pyおよびgen_api_docs.pyのインポート順序を厳密に整理。- 依存関係の欠落を防ぐため、docsビルド時の依存管理を強化。
[1.2.2a1] - 2025-12-26
開発ツール (ベンチマーク・CI/CD)
- ベンチマークの性能比較ロジックを修正:
- 比較計算を Ops/sec ベースに統一し、速度向上時に正しく
+(🚀/✅)が表示されるように改善。 - サマリーテーブルに Ops/sec の絶対値の差分(例:
+2.1M ops)を追加。 - Ops/sec 計算の正確性向上: 平均時間からの逆算(近似値)ではなく、ベンチマークツールの生データ (
ops) を直接使用するように修正。これにより、OS別詳細表示で(0.0)と表示されるバグも解消。 - 0.001ms 未満の微小な時間計測結果に対して
ns(nanoseconds) 単位を正しく表示。 - 絵文字(🚀, ✅, ➖, ⚠️, 🔴)による直感的なパフォーマンス評価を追加。
- 比較計算を Ops/sec ベースに統一し、速度向上時に正しく
- CI/CDワークフローの最適化:
benchmark.yml: GitHub Actions ランナーの性能ばらつき(10-60%)を考慮し、ベンチマークを「情報提供のみ」に変更。性能低下による CI 失敗を防止。ci.yml: トリガーを最適化し、pushによる自動実行をmainブランチのみに限定。他ブランチはworkflow_dispatchで手動実行可能に。should-runジョブの判定ロジックを簡略化。
[1.2.1b2] - 2025-12-25
開発ツール
- CI/CDワークフローの統合:
lint.yml,test.yml,publish.yml,quality-gate.ymlを一つのci.ymlに統合。- リリースサマリーにPyPIとGitHub Releaseへの直接リンク、詳細なジョブステータス(Cancelled/Skipped対応)を追加。
- テスト環境の最適化:
- CIテストマトリックスを調整。Ubuntuは全バージョン、Windows/macOSは利用率の高い Python 3.11および3.13に絞り込み、実行時間を短縮。
- dev依存関係に
pytest-xdistを追加し、並列テストをサポート。
- 型チェックの改善:
- mypy設定の緩和と整理により、156件の型エラーを解消(
--no-strict-optionalの導入とエラーコードの個別制御)。
- mypy設定の緩和と整理により、156件の型エラーを解消(
開発ツール
- リント・CI環境の追加:
tox.iniを追加し、tox -e lint(ruff),tox -e type(mypy),tox -e format,tox -e fixの環境を構築。pyproject.tomlにruff設定を追加(E/W/F/I/B/UP/N/ASYNCルール、Python 3.9+対応、line-length: 120)。pyproject.tomlにmypy設定を追加(--no-strict-optionalフラグ使用、実用的な型チェック)。.github/workflows/lint.ymlを追加:PyPA/twineスタイルのCIワークフロー(tox統合、FORCE_COLOR対応、サマリー出力)。.github/workflows/quality-gate.ymlを追加:all-greenゲートでmainブランチ判定とpublish準備確認。- dev依存関係に
tox>=4.0.0,ruff>=0.8.0,mypy>=1.13.0を追加。
- コード品質改善:
- ruff auto-fixで1373件のリントエラーを修正(import順序、未使用import削除、pyupgrade、whitespace等)。
- B904 (raise without from), B017 (assert raises Exception) をignore listに追加。
- mypy設定を実用的に調整(156エラー → 0エラー)。
[1.2.0b1] - 2025-12-24
セキュリティと堅牢性
ORDER BY解析の強化:NanaSQLiteに専用のパーサー_parse_order_by_clauseを実装し、複雑なORDER BY句を安全に処理・検証できるようにしました。- 正当なソートパターンをサポートしつつ、SQLインジェクションに対する保護を強化しました。
- 厳格な検証の修正:
- 危険なパターン(
;,--,/*)に対するエラーメッセージをInvalid [label]: [message]の形式に統一しました。 - すべての検証エラーに対して統一されたメッセージ形式を適用することで、レガシーテストと新しいセキュリティテスト間の一貫した動作を保証しました。
- 危険なパターン(
リファクタリング
- コード構成:
_sanitize_sql_for_function_scanロジックを新しいnanasqlite.sql_utilsモジュールに抽出・移動し、保守性を向上させました。AsyncNanaSQLiteのqueryとquery_with_paginationメソッドから重複コードを削除し、共通ロジックを_shared_query_implヘルパーメソッドに統合しました(約150行の削減)。
- 型安全性:
contextパラメータにLiteral型ヒントを追加し、IDEサポートと型チェックを強化しました (PR #36)。
修正と改善
- 非同期ロギング:
- 読み取り専用プールのクリーンアップ中に発生するエラーのログレベルを DEBUG から WARNING に引き上げ、リソースの問題を検知しやすくしました。
- エラーメッセージに接続コンテキスト情報を追加しました。
- 非同期プールクリーンアップの堅牢性向上:
AsyncNanaSQLite.close()メソッドにおいて、プール内の一部の接続でエラーが発生しても、残りの接続を確実にクリーンアップするように改善しました。AttributeError発生時にbreakしていた処理を継続するように変更し、リソースリークを防止します。
- テスト:
- インスタンスがクローズされている場合に
__eq__が正しくNanaSQLiteClosedErrorを送出するように修正しました (PR #44)。 - セキュリティテストにおける例外ハンドリングの具体性を向上させました (PR #43)。
- セキュリティテストのコメントを明確化し、検証タイミングの説明を追加しました (PR #35)。
- 重複していた
pytestインポートを削除し、一時的なテストファイル(temp_test_parser.py)を整理しました。
- インスタンスがクローズされている場合に
[1.2.0a2] - 2025-12-23
- 非同期セキュリティ機能の強化:
AsyncNanaSQLite.queryおよびquery_with_paginationにおいて、allowed_sql_functions,forbidden_sql_functions,override_allowedが正しく_validate_expressionに渡されるように修正。AsyncNanaSQLiteの非同期セキュリティテスト (tests/test_security_async_v120.py) を追加。
- 非同期接続管理の改善:
AsyncNanaSQLiteにクローズ状態を追跡する_closedフラグを追加。- 親インスタンスがクローズされた際に、
table()で作成された子インスタンスも即座にクローズ状態となるように改善。 - 未初期化のインスタンスをクローズした場合でも、その後の操作で正しく
NanaSQLiteClosedErrorが発生するように修正。
[1.2.0a1] - 2025-12-23
- 非同期読み取り専用接続プール:
AsyncNanaSQLiteにread_pool_size引数を追加。query,query_with_pagination,fetch_all,fetch_oneメソッドで読み取り専用プールを使用可能に。- 安全性のため、プール接続は常に
read-onlyモードで動作。
- バグ修正:
queryおよびquery_with_paginationで結果が0件の場合に発生していたapsw.ExecutionCompleteErrorを修正。- カラム名の取得方法を
cursor.description依存から同期版と同様のPRAGMA table_infoおよび手動パース方式に変更。
[1.2.0dev1] - 2025-12-23
修正
- 非同期APIの一貫性向上:
AsyncNanaSQLiteに全てのメソッドのaプレフィックス付きエイリアス(abatch_update,ato_dict等)を追加。- ベンチマークテスト (
test_async_benchmark.py) でのメソッド未定義エラーを解消。
- 後方互換性の修正:
- SQLインジェクション検知時のエラーメッセージを
test_security.py等の既存テストが期待する形式("Invalid order_by clause" 等)に再調整。 test_enhancements.pyにおいてNanaSQLiteClosedErrorを許容するように修正し、例外クラス名チェックとの整合性を確保。
- SQLインジェクション検知時のエラーメッセージを
- Windows環境の安定性向上:
test_security_v120.pyでpytestのtmp_pathフィクスチャを使用するように変更し、WindowsでのBusyErrorやIOErrorを回避。
query/query_with_paginationのバグ修正:limit=0およびoffset=0が無視されていた問題を修正。if limit:からif limit is not None:に変更。- ⚠️ 後方互換性: 以前は
limit=0を渡すと全件取得していましたが、今後は正しく0件を返します。limit=0を「制限なし」の意味で使用していた場合はlimit=Noneに変更してください。
- エッジケーステストの追加:
tests/test_edge_cases_v120.pyを新規作成。空リストでのbatch_*操作やページネーションの境界値テストを追加。
[1.2.0dev0] - 2025-12-22
追加
- セキュリティ強化 (Phase 1):
strict_sql_validationフラグの導入(未許可関数の使用時に例外または警告)。max_clause_lengthによる動的SQLの長さ制限(ReDoS対策)。- 文字列ベースの危険なSQLパターン(
;,--,/*)およびSQLキーワード(DROP,DELETE等)の検知ロジックの強化。
- 接続管理の厳格化:
NanaSQLiteClosedErrorの導入。- 親インスタンス・クローズ時に子インスタンス(
table()で作成)を自動的に無効化する追跡機構の実装。
- メンテナンス性向上:
DEVELOPMENT_GUIDE.mdの作成(日英)。pip install -e . -Uによる環境同期ルールの明文化。
[1.1.0] - 2025-12-19
追加
カスタム例外クラスの導入:
NanaSQLiteError(基底クラス)NanaSQLiteValidationError(バリデーションエラー)NanaSQLiteDatabaseError(データベース操作エラー)NanaSQLiteTransactionError(トランザクション関連エラー)NanaSQLiteConnectionError(接続エラー)NanaSQLiteLockError(ロックエラー、将来用)NanaSQLiteCacheError(キャッシュエラー、将来用)
バッチ取得機能 (
batch_get):batch_get(keys: List[str])による効率的な複数キーの一括ロードAsyncNanaSQLite.abatch_get(keys)による非同期サポート- 1回のクエリで複数データを取得しキャッシュを最適化
トランザクション管理の強化:
- トランザクション状態の追跡(
_in_transaction,_transaction_depth) - ネストしたトランザクションの検出とエラー発生
in_transaction()メソッドの追加- トランザクション中の接続クローズを防止
- トランザクション外でのcommit/rollbackを検出
- トランザクション状態の追跡(
非同期版トランザクション対応:
AsyncNanaSQLite.begin_transaction()AsyncNanaSQLite.commit()AsyncNanaSQLite.rollback()AsyncNanaSQLite.in_transaction()AsyncNanaSQLite.transaction()(コンテキストマネージャ)_AsyncTransactionContextクラスの実装
リソースリーク対策:
- 親インスタンスが子インスタンスを弱参照で追跡
- 親が閉じられた際、子インスタンスに通知
- 孤立した子インスタンスの使用を防止
_check_connection()メソッドの追加_mark_parent_closed()メソッドの追加
改善
エラーハンドリングの強化:
execute()メソッドにエラーハンドリングを追加- APSWの例外を
NanaSQLiteDatabaseErrorでラップ - 元のエラー情報を保持(
original_error属性) - 接続状態のチェックを各メソッドに追加
_sanitize_identifier()でNanaSQLiteValidationErrorを使用
__setitem__メソッドに接続チェックを追加
ドキュメント
新規ドキュメント:
docs/ja/error_handling.md- エラーハンドリングガイドdocs/ja/transaction_guide.md- トランザクションガイドdocs/ja/implementation_status.md- 実装状況と今後の計画tests/test_enhancements.py- 強化機能のテスト(21件)
README更新:
- トランザクションサポートのセクションを追加
- カスタム例外のサンプルコードを追加
- 非同期版のトランザクションサンプルを追加
テスト
- 新規テスト(21件):
- カスタム例外クラスのテスト(5件)
- トランザクション機能の強化テスト(6件)
- リソース管理のテスト(3件)
- エラーハンドリングのテスト(2件)
- トランザクションと例外の組み合わせテスト(2件)
- 非同期版トランザクションのテスト(3件)
修正
- セキュリティテストで
NanaSQLiteValidationErrorを期待するように修正
[1.1.0a3] - 2025-12-17
ドキュメント改善
table()メソッドの使用上の注意を追加:- README.mdに重要な使用上の注意セクションを追加(英語・日本語)
- 同じテーブルへの複数インスタンス作成に関する警告
- コンテキストマネージャ使用の推奨
- ベストプラクティスの明記
- docstring改善:
NanaSQLite.table()のdocstringに詳細な注意事項を追加AsyncNanaSQLite.table()のdocstringに詳細な注意事項を追加- 非推奨パターンと推奨パターンの具体例を追加
- 将来的な改善計画:
etc/future_plans/ディレクトリに改善提案を文書化- 重複インスタンス検出警告機能(提案B)
- 接続状態チェック機能(提案B)
- 共有キャッシュ機構(提案C - 保留)
分析・調査
- table()機能の包括的な調査を実施:
- ストレステスト: 7件すべて合格
- エッジケーステスト: 10件実施
- 並行処理テスト: 5件すべて合格
- 発見された問題: 2件(軽微な設計上の制限)
- 同一テーブルへの複数インスタンスでキャッシュ不整合(ドキュメント化で対応)
- close後のサブインスタンスアクセス(ドキュメント化で対応)
- 結論: 本番環境で使用可能、パフォーマンス問題なし
[1.1.0dev2] - 2025-12-16
現在の開発状況
- 開発中のバージョン
- テスト実施中(
test_concurrent_table_writes.pyで15個のテスト全てパス)
[1.1.0dev1] - 2025-12-15
追加
- マルチテーブルサポート(
table()メソッド): 同一データベース内の複数テーブルを安全に操作db.table(table_name)で別テーブル用のインスタンスを取得- 接続とロックの共有: 複数のテーブルインスタンスが同じSQLite接続とスレッドロックを共有
- スレッドセーフ: 複数スレッドから異なるテーブルへの同時書き込みが安全に動作
- メモリ効率: 接続を再利用することでリソースを節約
- 同期版:
NanaSQLite.table(table_name)→NanaSQLiteインスタンス - 非同期版:
await AsyncNanaSQLite.table(table_name)→AsyncNanaSQLiteインスタンス - キャッシュ分離: 各テーブルインスタンスは独立したメモリキャッシュを保持
内部実装の改善
- スレッドセーフティの強化: 全データベース操作に
threading.RLockを追加- 読み込み(
_read_from_db)、書き込み(_write_to_db)、削除(_delete_from_db) - クエリ実行(
execute,execute_many) - トランザクション操作
- 読み込み(
- 接続管理の改善:
_shared_connectionパラメータで接続の共有をサポート_shared_lockパラメータでロックの共有をサポート_is_connection_ownerフラグで接続の所有権を管理close()メソッドは接続の所有者のみが実行
テスト
- 15の包括的なテストケース(全てパス):
- 同期版マルチテーブル並行書き込みテスト(2テーブル、複数テーブル)
- 非同期版マルチテーブル並行書き込みテスト(2テーブル、複数テーブル)
- ストレステスト(1000件の並行書き込み)
- キャッシュ分離テスト
- テーブル切り替えテスト
- エッジケーステスト
互換性
- 完全な後方互換性: 既存のコードに影響なし
- 新しいパラメータはすべてオプショナル(内部使用)
[1.0.3rc7] - 2025-12-10
追加
- 非同期サポート(AsyncNanaSQLite): 非同期アプリケーション向けの完全な非同期インターフェース
AsyncNanaSQLiteクラス: 全操作の非同期版を提供- 専用スレッドプールエグゼキューター: 設定可能なmax_workers(デフォルト5)で最適化
ThreadPoolExecutorによる高性能な並行処理- FastAPI、aiohttp等の非同期フレームワークで安全に使用可能
- 非同期dict風インターフェース:
await db.aget(),await db.aset(),await db.adelete() - 非同期バッチ操作:
await db.batch_update(),await db.batch_delete() - 非同期SQL実行:
await db.execute(),await db.query() - 非同期コンテキストマネージャ:
async with AsyncNanaSQLite(...) as db: - 並行処理サポート: 複数の非同期操作を並行実行可能
- 自動リソース管理: スレッドプールの自動クリーンアップ
- 包括的なテストスイート: 100以上の非同期テストケース
- 基本操作、並行処理、エラーハンドリング、パフォーマンステスト
- 全テストが合格
- 完全な後方互換性: 既存の
NanaSQLiteクラスは変更なし
パフォーマンス改善
- 非同期アプリでのブロッキング防止により、イベントループの応答性が向上
- 専用スレッドプールによる高効率な並行処理(設定可能なワーカー数)
- APSW + スレッドプールの組み合わせで最適なパフォーマンス
- 高負荷環境向けにmax_workersを調整可能(5~50)
[1.0.3rc6] - 2025-12-10
追加
get_fresh(key, default=None)メソッド: DBから直接読み込み、キャッシュを更新して値を返すexecute()でDBを直接変更した後のキャッシュ同期に便利_read_from_dbを直接使用してオーバーヘッドを最小化
[1.0.3rc5] - 2025-12-10
パフォーマンス改善
batch_update()の最適化:executemany使用で大量データ処理が10-30%高速化batch_delete()の最適化:executemany使用で一括削除が高速化__contains__()の最適化: 軽量なEXISTSクエリ使用で存在確認が高速化(大きなvalueの場合に効果大)
IDE/型サポート強化
from __future__ import annotations追加Dict[str, Any]、Set[str]等の具体的な型アノテーションOptional[Tuple]等のより明確な引数型
ドキュメント
execute()メソッドにキャッシュ一貫性に関する警告を追加- docstringの改善(Returns、警告セクション追加)
バグ修正
- Gitマージコンフリクトの解消(order_by検証の正規表現)
- ReDoS脆弱性の修正(カンマ分割方式に変更)
[1.0.3rc4] - 2025-12-09
追加
- 22の新しいSQLiteラッパー関数
- スキーマ管理:
drop_table(),drop_index(),alter_table_add_column(),get_table_schema(),list_indexes() - データ操作:
sql_insert(),sql_update(),sql_delete(),upsert(),count(),exists() - クエリ拡張:
query_with_pagination()(offset/group_by対応) - ユーティリティ:
vacuum(),get_db_size(),export_table_to_dict(),import_from_dict_list(),get_last_insert_rowid(),pragma() - トランザクション:
begin_transaction(),commit(),rollback(),transaction()コンテキストマネージャ
- スキーマ管理:
- 35の新しいテストケース(全て合格)
- 完全な後方互換性維持
[1.0.3rc3] - 2025-12-09
追加
- Pydantic互換性
set_model(),get_model()メソッド- ネストされたモデルとオプショナルフィールドのサポート
- 直接SQL実行機能
execute(),execute_many(),fetch_one(),fetch_all()メソッド- パラメータバインディングによるSQLインジェクション対策
- SQLiteラッパー関数
create_table(),create_index(),query()メソッドtable_exists(),list_tables()ヘルパー関数
- 32の新しいテストケース
- 英語・日本語ドキュメントの更新
- 非同期対応に関する相談文書
[1.0.0] - 2025-12-09
追加
- 初回リリース
- dict風インターフェース(
db["key"] = value) - APSWによるSQLite即時永続化
- 遅延ロード(アクセス時にキャッシュ)
- 一括ロード(
bulk_load=True) - ネスト構造サポート(30階層テスト済み)
- パフォーマンス最適化(WAL、mmap、cache_size)
- バッチ操作(
batch_update、batch_delete) - コンテキストマネージャ対応
- 完全なdictメソッド互換性
- 型ヒント(PEP 561)
- バイリンガルドキュメント(英語/日本語)
- GitHub Actions CI(Python 3.9-3.13、Ubuntu/Windows/macOS)