Skip to content

fix: implement SQLite partial unique indexes to prevent session_id co… - #32

Merged
barnwell merged 2 commits into
mainfrom
fix/sqlite-partial-unique-index
Jul 31, 2026
Merged

fix: implement SQLite partial unique indexes to prevent session_id co…#32
barnwell merged 2 commits into
mainfrom
fix/sqlite-partial-unique-index

Conversation

@Tharickv75

Copy link
Copy Markdown
Contributor

Type of Change

What type of change does this PR introduce? Mark all that apply:

  • 🐛 Bug Fix
  • 🚀 Feature Request
  • 🔄 Refactor
  • 📖 Documentation Update
  • 🔧 Other (Please specify):

Summary

What does this PR address?

This PR implements SQLite support for partial unique indexes and auto-repairing existing non-partial unique indexes on session_id. It prevents INSERT OR REPLACE operations from accidentally deleting Interaction rows when they share a context.session_id with a Conversation node.


Description

Bug Fixes:

  • Description: Saving/updating nodes in SQLite caused Interaction rows to be deleted when sharing context.session_id with Conversation nodes (resulting in empty orchestrator history on SQLite).
  • Root Cause: SQLiteDB.create_index ignored Mongo-style partialFilterExpression / partial_filter_expression kwargs, creating global unique indexes across shared node collections instead of scoped/partial ones.
  • Resolution:
    1. Implemented translation of Mongo-style partial filter expressions (equality, $eq, $gt, $exists) into SQLite WHERE clauses.
    2. Added a connect-time self-healing repair step (_repair_non_partial_unique_indexes) that automatically drops legacy non-partial unique indexes on session_id missing a WHERE clause so they can be recreated properly on subsequent ensure_indexes calls.
    3. Ensured untranslatable partial filter expressions raise a ValueError rather than silently falling back to a global unique index.

Refactor Request:

  • Area Refactored: jvspatial/db/sqlite.py and jvspatial/db/_sqlite_translate.py.
  • Improvements: Changed logger level for partial-index repair from WARNING to INFO to reduce one-shot migration noise. Addressed ruff (SIM110) and mypy type-narrowing warnings. Updated .pre-commit-config.yaml hooks (pre-commit-hooks to v6.0.0).

Changes Made

High-Level Summary:

  1. Partial Index Translation: Added translate_partial_filter_expression in _sqlite_translate.py to translate supported filter shapes into SQLite WHERE clauses.
  2. Connect-time Repair: Added _repair_non_partial_unique_indexes on SQLite database initialization to automatically clean up legacy global unique indexes on context.session_id.
  3. Index Re-creation & Enforcement: Updated create_index to drop and rebuild existing indexes lacking a required WHERE clause and raise errors on untranslatable expressions.
  4. Changelog & Maintenance: Updated CHANGELOG.md with fix details and updated .pre-commit-config.yaml.

Checklist

Mark all that apply:

  • Code follows the project’s coding guidelines.
  • Tests have been added or updated for new functionality.
  • Documentation has been updated (if applicable).
  • Existing tests pass locally with these changes.
  • Any dependencies introduced are justified and documented.

Steps to Test

Provide a clear set of steps for testing the changes introduced in this PR:

  1. Run the new and updated SQLite partial index unit tests:
    pytest tests/db/test_sqlite_partial_index.py

@Tharickv75
Tharickv75 requested review from barnwell and eldonm July 31, 2026 18:12
@Tharickv75 Tharickv75 self-assigned this Jul 31, 2026
@github-actions

Copy link
Copy Markdown

Benchmark comparison

Threshold: ±25% (informational, does not block merge)

benchmark baseline (s) current (s) delta status
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_deferred_save_batched_100 0.037906 0.051476 +35.8% REGRESSION (+35.8%)
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_immediate_save_100 0.036936 0.052643 +42.5% REGRESSION (+42.5%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_batched_saves_500 0.391694 0.470958 +20.2% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_empty_query 0.831736 1.024797 +23.2% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_filtered 0.987921 1.152109 +16.6% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_find_filtered 0.757607 0.884308 +16.7% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_save_throughput 0.001448 0.001631 +12.7% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_empty 0.257901 0.352040 +36.5% REGRESSION (+36.5%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_fallback_via_regex 0.303311 0.404884 +33.5% REGRESSION (+33.5%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_pushdown 0.264607 0.372038 +40.6% REGRESSION (+40.6%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_fallback_via_regex 0.275229 0.389850 +41.6% REGRESSION (+41.6%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_pushdown 0.228907 0.346233 +51.3% REGRESSION (+51.3%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_sort_limit_pushdown 0.289700 0.414773 +43.2% REGRESSION (+43.2%)

@Tharickv75
Tharickv75 force-pushed the fix/sqlite-partial-unique-index branch from c438cf7 to f70046f Compare July 31, 2026 19:37
@github-actions

Copy link
Copy Markdown

Benchmark comparison

Threshold: ±25% (informational, does not block merge)

benchmark baseline (s) current (s) delta status
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_deferred_save_batched_100 0.038482 0.037605 -2.3% OK
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_immediate_save_100 0.036431 0.038430 +5.5% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_batched_saves_500 0.733649 0.411107 -44.0% IMPROVED (-44.0%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_empty_query 1.679002 0.854325 -49.1% IMPROVED (-49.1%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_filtered 1.366763 0.991088 -27.5% IMPROVED (-27.5%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_find_filtered 0.924655 0.753134 -18.5% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_save_throughput 0.002665 0.001424 -46.6% IMPROVED (-46.6%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_empty 0.233810 0.238306 +1.9% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_fallback_via_regex 0.276695 0.302858 +9.5% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_pushdown 0.253736 0.266389 +5.0% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_fallback_via_regex 0.275249 0.285807 +3.8% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_pushdown 0.240668 0.228285 -5.1% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_sort_limit_pushdown 0.274896 0.267216 -2.8% OK

@github-actions

Copy link
Copy Markdown

Benchmark comparison

Threshold: ±25% (informational, does not block merge)

benchmark baseline (s) current (s) delta status
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_deferred_save_batched_100 0.038482 0.037288 -3.1% OK
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_immediate_save_100 0.036431 0.037752 +3.6% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_batched_saves_500 0.733649 0.383856 -47.7% IMPROVED (-47.7%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_empty_query 1.679002 0.840647 -49.9% IMPROVED (-49.9%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_filtered 1.366763 1.022743 -25.2% IMPROVED (-25.2%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_find_filtered 0.924655 0.753649 -18.5% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_save_throughput 0.002665 0.001447 -45.7% IMPROVED (-45.7%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_empty 0.233810 0.228872 -2.1% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_fallback_via_regex 0.276695 0.286191 +3.4% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_pushdown 0.253736 0.260239 +2.6% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_fallback_via_regex 0.275249 0.281979 +2.4% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_pushdown 0.240668 0.225849 -6.2% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_sort_limit_pushdown 0.274896 0.283927 +3.3% OK

@barnwell
barnwell merged commit 4f94c8a into main Jul 31, 2026
6 checks passed
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