-
Notifications
You must be signed in to change notification settings - Fork 0
feature: Add support for SQLite extensions #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds SQLite extension loading and statement introspection NIFs/wrappers, introduces hooks NIFs that return Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used📓 Path-based instructions (7)**/*.{ex,exs,rs,md}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
native/**/*.rs📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ex,exs}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
lib/ecto_libsql/native.ex📄 CodeRabbit inference engine (CLAUDE.md)
Files:
lib/**/*.ex📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.ex📄 CodeRabbit inference engine (AGENTS.md)
Files:
test/**/*.exs📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (29)📓 Common learnings📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:26.477ZApplied to files:
📚 Learning: 2025-12-17T11:58:46.019ZApplied to files:
🧬 Code graph analysis (2)lib/ecto_libsql/native.ex (3)
test/hooks_test.exs (1)
🪛 LanguageToolENHANCEMENTS.md[style] ~160-~160: Consider using a different verb for a more formal wording. (FIX_RESOLVE) [uncategorized] ~166-~166: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short). (COMMA_COMPOUND_SENTENCE) [typographical] ~269-~269: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [grammar] ~454-~454: A determiner may be missing. (THE_SUPERLATIVE) [grammar] ~455-~455: A determiner may be missing. (THE_SUPERLATIVE) [grammar] ~457-~457: A determiner may be missing. (THE_SUPERLATIVE) [grammar] ~458-~458: A determiner may be missing. (THE_SUPERLATIVE) [misspelling] ~475-~475: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’. (EN_A_VS_AN) [uncategorized] ~476-~476: When ‘Look-up’ is used as a noun or modifier, it needs to be hyphenated. (VERB_NOUN_CONFUSION) [typographical] ~481-~481: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~493-~493: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~497-~497: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~509-~509: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~549-~549: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~558-~558: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~568-~568: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~705-~705: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~723-~723: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [typographical] ~736-~736: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN) [uncategorized] ~882-~882: If this is a compound adjective that modifies the following noun, use a hyphen. (EN_COMPOUND_ADJECTIVE_INTERNAL) [uncategorized] ~884-~884: If this is a compound adjective that modifies the following noun, use a hyphen. (EN_COMPOUND_ADJECTIVE_INTERNAL) [uncategorized] ~886-~886: If this is a compound adjective that modifies the following noun, use a hyphen. (EN_COMPOUND_ADJECTIVE_INTERNAL) ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
🔇 Additional comments (14)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
ENHANCEMENTS.md (2)
380-381: Inconsistent spelling: use British English throughout.The document mixes "authorization" (US) and "authorisation" (British). Per coding guidelines, use British/Australian English spelling consistently.
🔎 Suggested fix
-For **Row-Level Security / Authorization**: -- Application-level authorization checks before queries +For **Row-Level Security / Authorisation**: +- Application-level authorisation checks before queries
448-458: Inconsistent spelling: "Optimization" should be "Optimisation".Per coding guidelines, use British/Australian English. Change "Optimization" to "Optimisation" for consistency with the rest of the document.
🔎 Suggested fix
-3. **Index Optimization**: Add spatial indexes for performance on large datasets +3. **Index Optimisation**: Add spatial indexes for performance on large datasetsAlso update line 454:
-- **Delivery optimization**: Locate nearest warehouse to customer location +- **Delivery optimisation**: Locate the nearest warehouse to customer location
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
CHANGELOG.mdENHANCEMENTS.mdlib/ecto_libsql/native.exnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/lib.rsnative/ecto_libsql/src/statement.rstest/hooks_test.exstest/statement_features_test.exs
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ex,exs,rs,md}
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS use British/Australian English spelling and grammar for code, comments, and documentation, except where required for function calls, SQL keywords, error messages, or compatibility with external systems that may use US English
Files:
native/ecto_libsql/src/constants.rsENHANCEMENTS.mdnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/statement.rsnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/lib.rsCHANGELOG.mdtest/statement_features_test.exslib/ecto_libsql/native.extest/hooks_test.exs
native/**/*.rs
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS run the Rust Cargo formatter (
cargo fmt) before committing changes and fix any issues
Files:
native/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/statement.rsnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/lib.rs
**/*.{ex,exs}
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS run the Elixir formatter (
mix format --check-formatted) before committing changes and fix any issues
Files:
test/statement_features_test.exslib/ecto_libsql/native.extest/hooks_test.exs
test/**/*.exs
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.exs: Add tests for all new features in appropriate Elixir test files (test/ecto_*_test.exs) and Rust test modules (native/ecto_libsql/src/tests/), covering happy path, error cases, edge cases, and type conversions
Add comprehensive tests for unsupported functions asserting that they always return {:error, :unsupported} and that they don't modify database state or corrupt connections
Files:
test/statement_features_test.exstest/hooks_test.exs
lib/ecto_libsql/native.ex
📄 CodeRabbit inference engine (CLAUDE.md)
lib/ecto_libsql/native.ex: For each new Rust NIF function, add corresponding Elixir NIF stubs and safe wrapper functions in lib/ecto_libsql/native.ex, with proper state management via EctoLibSql.State
For explicitly unsupported functions, update the Elixir wrapper with comprehensive documentation explaining why the feature is unsupported, what architectural constraints prevent it, and what alternative approaches users can take
Files:
lib/ecto_libsql/native.ex
lib/**/*.ex
📄 CodeRabbit inference engine (CLAUDE.md)
lib/**/*.ex: When updating Elixir code, follow the pattern of case matching on NIF results:{:ok, _, result, new_state}for successful query execution or{:error, reason}for failures
In Elixir error handling, preferwithclauses for chaining multiple operations that return Result tuples, providing clear error handling with anelseclause
In Ecto changesets, use thecastfunction to automatically attempt type conversions for fields, which will raise ChangeError if types don't match schema expectations
Use transactions with appropriate isolation behaviours when executing database operations::deferredfor mixed workloads,:immediatefor write-heavy operations,:exclusivefor critical operations requiring exclusive locks,:read_onlyfor read-only queries
When working with database locks, use transactions with proper timeout configuration, ensure connections are properly closed in try-after blocks, and use immediate/exclusive transaction behaviours for write-heavy workloads to minimise lock contention
When handling vector search operations, verify LibSQL version supports vectors, use correct vector syntax withEctoLibSql.Native.vector()function, insert vectors withvector(?)SQL syntax, and query with vector distance functions likevector_distance_cos
Files:
lib/ecto_libsql/native.ex
**/*.ex
📄 CodeRabbit inference engine (AGENTS.md)
**/*.ex: Use prepared statements viaEctoLibSql.Native.prepare/2andEctoLibSql.Native.query_stmt/3for repeated queries to improve performance by ~10-15x
Use batch operations withEctoLibSql.Native.batch_transactional/2for bulk inserts/updates instead of individual statements
Implement proper error handling by pattern matching on{:ok, ...}and{:error, ...}tuples from all EctoLibSql operations
Use transactions withEctoLibSql.handle_begin/2,EctoLibSql.handle_commit/2, andEctoLibSql.handle_rollback/2for multi-step database operations
Use cursor streaming withDBConnection.stream/3for memory-efficient processing of large result sets
Use vector search withEctoLibSql.Native.vector/1,EctoLibSql.Native.vector_type/2, andEctoLibSql.Native.vector_distance_cos/2for AI/ML features
UseEctoLibSql.Pragma.*functions to configure SQLite parameters such as foreign keys, journal mode, cache size, and synchronous level
Use savepoints withEctoLibSql.Native.create_savepoint/2,EctoLibSql.Native.release_savepoint_by_name/2, andEctoLibSql.Native.rollback_to_savepoint_by_name/2for nested transactions
Use Ecto changesets for data validation before inserting or updating records
Use Ecto query composition withEcto.Queryfor building complex queries incrementally
Preload associations withRepo.preload/2to avoid N+1 query problems in Ecto applications
For remote replicas in production, useEctoLibSql.Native.sync/1,EctoLibSql.Native.get_frame_number_for_replica/1,EctoLibSql.Native.sync_until_frame/2, andEctoLibSql.Native.flush_and_get_frame/1for replication management
Use busy timeout configuration withEctoLibSql.Native.busy_timeout/2to handle database locking conflicts gracefully
Use statement introspection functions (EctoLibSql.Native.stmt_parameter_count/2,EctoLibSql.Native.stmt_column_count/2,EctoLibSql.Native.stmt_column_name/3) to validate prepared statement structure
Handle connection errors gracefully by ch...
Files:
lib/ecto_libsql/native.ex
🧠 Learnings (31)
📓 Common learnings
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto/adapters/libsql/connection.ex : In lib/ecto/adapters/libsql/connection.ex, implement SQL expression handlers for SQLite functions by pattern matching on function names and converting them to SQL fragments, respecting SQLite syntax and limitations
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto_libsql/native.ex : For explicitly unsupported functions, update the Elixir wrapper with comprehensive documentation explaining why the feature is unsupported, what architectural constraints prevent it, and what alternative approaches users can take
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use `EctoLibSql.Pragma.*` functions to configure SQLite parameters such as foreign keys, journal mode, cache size, and synchronous level
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto_libsql/native.ex : For each new Rust NIF function, add corresponding Elixir NIF stubs and safe wrapper functions in lib/ecto_libsql/native.ex, with proper state management via EctoLibSql.State
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto/adapters/libsql.ex : In type loaders/dumpers in lib/ecto/adapters/libsql.ex, ensure bidirectional conversion: loaders convert SQLite values to Elixir types on retrieval, dumpers convert Elixir types to SQLite on storage
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use statement introspection functions (`EctoLibSql.Native.stmt_parameter_count/2`, `EctoLibSql.Native.stmt_column_count/2`, `EctoLibSql.Native.stmt_column_name/3`) to validate prepared statement structure
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use prepared statements via `EctoLibSql.Native.prepare/2` and `EctoLibSql.Native.query_stmt/3` for repeated queries to improve performance by ~10-15x
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : Include comprehensive Rust documentation comments (/// comments) on all public NIF functions explaining purpose, arguments, return values (success and error cases), and usage examples
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : When adding a new NIF function in Rust, place it in the appropriate module based on functionality: connection.rs (lifecycle), query.rs (execution), transaction.rs (transactions), batch.rs (batch operations), statement.rs (prepared statements), cursor.rs (streaming), replication.rs (replica sync), metadata.rs (metadata access), or savepoint.rs (nested transactions)
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/**/*.ex : When handling vector search operations, verify LibSQL version supports vectors, use correct vector syntax with `EctoLibSql.Native.vector()` function, insert vectors with `vector(?)` SQL syntax, and query with vector distance functions like `vector_distance_cos`
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto_libsql/native.ex : For explicitly unsupported functions, update the Elixir wrapper with comprehensive documentation explaining why the feature is unsupported, what architectural constraints prevent it, and what alternative approaches users can take
Applied to files:
native/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/connection.rsCHANGELOG.mdtest/statement_features_test.exslib/ecto_libsql/native.extest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: When a function cannot be implemented due to architectural constraints, explicitly mark it as unsupported by returning an `:unsupported` atom error rather than returning vague errors or silently failing
Applied to files:
native/ecto_libsql/src/constants.rs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto_libsql/native.ex : For each new Rust NIF function, add corresponding Elixir NIF stubs and safe wrapper functions in lib/ecto_libsql/native.ex, with proper state management via EctoLibSql.State
Applied to files:
native/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/statement.rsnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/lib.rsCHANGELOG.mdlib/ecto_libsql/native.extest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : In Rust, all non-test code must have no `.unwrap()` calls; this was a critical refactoring in v0.5.0 to eliminate 146 unwrap calls and prevent BEAM VM crashes from panics in native code
Applied to files:
native/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/lib.rsCHANGELOG.md
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : Include comprehensive Rust documentation comments (/// comments) on all public NIF functions explaining purpose, arguments, return values (success and error cases), and usage examples
Applied to files:
native/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/statement.rsnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/lib.rsCHANGELOG.mdlib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to test/**/*.exs : Add comprehensive tests for unsupported functions asserting that they always return {:error, :unsupported} and that they don't modify database state or corrupt connections
Applied to files:
native/ecto_libsql/src/constants.rstest/statement_features_test.exstest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : In Rust NIF signatures, convert async/await Result<T, E> errors to String via `.map_err(|e| format!("{:?}", e))` before returning from async blocks within `block_on`
Applied to files:
native/ecto_libsql/src/constants.rsnative/ecto_libsql/src/hooks.rs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : In Rust NIFs marked with `#[rustler::nif]`, always use the `?` operator for error propagation to automatically convert errors to NifResult and return them to Elixir callers
Applied to files:
native/ecto_libsql/src/constants.rs
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use prepared statements via `EctoLibSql.Native.prepare/2` and `EctoLibSql.Native.query_stmt/3` for repeated queries to improve performance by ~10-15x
Applied to files:
ENHANCEMENTS.mdCHANGELOG.mdtest/statement_features_test.exslib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : When adding a new NIF function in Rust, place it in the appropriate module based on functionality: connection.rs (lifecycle), query.rs (execution), transaction.rs (transactions), batch.rs (batch operations), statement.rs (prepared statements), cursor.rs (streaming), replication.rs (replica sync), metadata.rs (metadata access), or savepoint.rs (nested transactions)
Applied to files:
native/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/statement.rsnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/lib.rsCHANGELOG.mdlib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : Modern Rustler (used in this project) automatically detects all NIFs annotated with `#[rustler::nif]`; the `rustler::init!` macro in `lib.rs` automatically discovers and registers these functions without manual registration
Applied to files:
native/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/connection.rsnative/ecto_libsql/src/lib.rs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : In Rust async blocks within NIFs, drop registry locks before entering the `TOKIO_RUNTIME.block_on(async { ... })` call to prevent deadlocks, then re-acquire locks if needed after the async block completes
Applied to files:
native/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/connection.rstest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : In Rust, use the `safe_lock(®ISTRY, "context_description")` helper function instead of directly calling `.lock().unwrap()` on Mutex types to safely acquire locks with proper error handling
Applied to files:
native/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/connection.rs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/!(tests)/**/*.rs : In Rust, use the `safe_lock_arc(&arc_mutex, "context_description")` helper function instead of directly calling `.lock().unwrap()` on Arc<Mutex<T>> types to safely acquire locks with proper error handling
Applied to files:
native/ecto_libsql/src/hooks.rsnative/ecto_libsql/src/connection.rs
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use statement introspection functions (`EctoLibSql.Native.stmt_parameter_count/2`, `EctoLibSql.Native.stmt_column_count/2`, `EctoLibSql.Native.stmt_column_name/3`) to validate prepared statement structure
Applied to files:
native/ecto_libsql/src/statement.rsCHANGELOG.mdtest/statement_features_test.exslib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto/adapters/libsql/connection.ex : In lib/ecto/adapters/libsql/connection.ex, implement SQL expression handlers for SQLite functions by pattern matching on function names and converting them to SQL fragments, respecting SQLite syntax and limitations
Applied to files:
native/ecto_libsql/src/statement.rsCHANGELOG.mdtest/statement_features_test.exslib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/savepoint.rs : In savepoint operations in native/ecto_libsql/src/savepoint.rs, validate that the transaction belongs to the connection by comparing entry.conn_id with the provided conn_id parameter
Applied to files:
native/ecto_libsql/src/statement.rsnative/ecto_libsql/src/connection.rslib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to native/ecto_libsql/src/transaction.rs : Use the TransactionEntry struct in Rust transaction operations to track which connection owns each transaction, validating ownership in savepoint and transaction operations to prevent cross-connection misuse
Applied to files:
native/ecto_libsql/src/connection.rs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/**/*.ex : When handling vector search operations, verify LibSQL version supports vectors, use correct vector syntax with `EctoLibSql.Native.vector()` function, insert vectors with `vector(?)` SQL syntax, and query with vector distance functions like `vector_distance_cos`
Applied to files:
CHANGELOG.mdlib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto/adapters/libsql.ex : When implementing Ecto storage operations (create, drop, status) in lib/ecto/adapters/libsql.ex, ensure they properly handle both local SQLite and remote Turso databases via the connection mode in EctoLibSql.State
Applied to files:
CHANGELOG.mdlib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use `EctoLibSql.Pragma.*` functions to configure SQLite parameters such as foreign keys, journal mode, cache size, and synchronous level
Applied to files:
CHANGELOG.mdlib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to test/**/*migration*test.exs : For SQLite migrations that require column type changes or drops (which SQLite doesn't support via ALTER), use the table recreation pattern: create new table, copy data with transformations, swap tables, and recreate indexes
Applied to files:
CHANGELOG.mdtest/statement_features_test.exstest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/ecto/adapters/libsql.ex : In type loaders/dumpers in lib/ecto/adapters/libsql.ex, ensure bidirectional conversion: loaders convert SQLite values to Elixir types on retrieval, dumpers convert Elixir types to SQLite on storage
Applied to files:
CHANGELOG.mdlib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to test/**/*.exs : Add tests for all new features in appropriate Elixir test files (test/ecto_*_test.exs) and Rust test modules (native/ecto_libsql/src/tests/), covering happy path, error cases, edge cases, and type conversions
Applied to files:
test/statement_features_test.exstest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use savepoints with `EctoLibSql.Native.create_savepoint/2`, `EctoLibSql.Native.release_savepoint_by_name/2`, and `EctoLibSql.Native.rollback_to_savepoint_by_name/2` for nested transactions
Applied to files:
lib/ecto_libsql/native.extest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: Applies to lib/**/*.ex : When updating Elixir code, follow the pattern of case matching on NIF results: `{:ok, _, result, new_state}` for successful query execution or `{:error, reason}` for failures
Applied to files:
lib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use transactions with `EctoLibSql.handle_begin/2`, `EctoLibSql.handle_commit/2`, and `EctoLibSql.handle_rollback/2` for multi-step database operations
Applied to files:
lib/ecto_libsql/native.extest/hooks_test.exs
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Use batch operations with `EctoLibSql.Native.batch_transactional/2` for bulk inserts/updates instead of individual statements
Applied to files:
lib/ecto_libsql/native.ex
📚 Learning: 2025-12-17T11:58:46.019Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T11:58:46.019Z
Learning: Applies to **/*.ex : Implement proper error handling by pattern matching on `{:ok, ...}` and `{:error, ...}` tuples from all EctoLibSql operations
Applied to files:
test/hooks_test.exs
📚 Learning: 2025-12-17T11:58:26.477Z
Learnt from: CR
Repo: ocean/ecto_libsql PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T11:58:26.477Z
Learning: When debugging failing tests, run with trace (`mix test file.exs --trace`), verify NIF compilation (`File.exists?("priv/native/ecto_libsql.so")`), check Rust output with `cargo test -- --nocapture`, and run Rust tests independently to isolate issues
Applied to files:
test/hooks_test.exs
🧬 Code graph analysis (5)
native/ecto_libsql/src/statement.rs (2)
native/ecto_libsql/src/utils.rs (2)
safe_lock(16-23)safe_lock_arc(28-38)native/ecto_libsql/src/decode.rs (1)
verify_statement_ownership(47-54)
native/ecto_libsql/src/connection.rs (2)
native/ecto_libsql/src/utils.rs (2)
safe_lock(16-23)safe_lock_arc(28-38)native/ecto_libsql/src/models.rs (1)
LibSQLConn(14-19)
test/statement_features_test.exs (1)
lib/ecto_libsql/native.ex (2)
prepare(496-504)close_stmt(580-582)
lib/ecto_libsql/native.ex (3)
native/ecto_libsql/src/connection.rs (2)
enable_load_extension(371-402)load_extension(427-450)native/ecto_libsql/src/hooks.rs (3)
set_update_hook(65-67)clear_update_hook(79-81)set_authorizer(151-153)native/ecto_libsql/src/statement.rs (1)
statement_parameter_name(349-379)
test/hooks_test.exs (2)
lib/ecto_libsql.ex (2)
disconnect(124-127)handle_execute(134-151)lib/ecto_libsql/native.ex (3)
add_update_hook(995-997)remove_update_hook(1008-1010)add_authorizer(1072-1074)
🪛 LanguageTool
ENHANCEMENTS.md
[style] ~160-~160: Consider using a different verb for a more formal wording.
Context: ...GH - Significant performance issue (NOW FIXED) Implementation: `reset_statement/2...
(FIX_RESOLVE)
[uncategorized] ~166-~166: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ements - Ecto caches prepared statements but we ignore the cache - Significant perfo...
(COMMA_COMPOUND_SENTENCE)
[typographical] ~269-~269: If specifying a range, consider using an en dash instead of a hyphen.
Context: ... read performance Estimated Effort: 2-3 days Why Important: - Multi-Versio...
(HYPHEN_TO_EN)
[uncategorized] ~380-~380: Do not mix variants of the same word (‘authorization’ and ‘authorisation’) within a single text.
Context: ...te tracking For Row-Level Security / Authorization: - Application-level authorization ch...
(EN_WORD_COHERENCY)
[uncategorized] ~381-~381: Do not mix variants of the same word (‘authorization’ and ‘authorisation’) within a single text.
Context: ... / Authorization**: - Application-level authorization checks before queries - Database views ...
(EN_WORD_COHERENCY)
[uncategorized] ~448-~448: Do not mix variants of the same word (‘optimization’ and ‘optimisation’) within a single text.
Context: ...data (elevation, time, etc.) 3. Index Optimization: Add spatial indexes for performance ...
(EN_WORD_COHERENCY)
[uncategorized] ~454-~454: Do not mix variants of the same word (‘optimization’ and ‘optimisation’) within a single text.
Context: ...ants, hotels, gas stations - Delivery optimization: Locate nearest warehouse to customer...
(EN_WORD_COHERENCY)
[grammar] ~454-~454: A determiner may be missing.
Context: ...ons - Delivery optimization: Locate nearest warehouse to customer location - **Regi...
(THE_SUPERLATIVE)
[grammar] ~455-~455: A determiner may be missing.
Context: ...location - Regional analytics: Find closest office/branch in each region - **Social...
(THE_SUPERLATIVE)
[grammar] ~457-~457: A determiner may be missing.
Context: ...tup groups - Asset tracking: Locate nearest available equipment or resources - **Em...
(THE_SUPERLATIVE)
[grammar] ~458-~458: A determiner may be missing.
Context: ...esources - Emergency services: Find nearest hospital, fire station, or police - **R...
(THE_SUPERLATIVE)
[misspelling] ~475-~475: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...Entryto hold theTransactionbehind anArc<Mutex>` - Look up and...
(EN_A_VS_AN)
[uncategorized] ~476-~476: When ‘Look-up’ is used as a noun or modifier, it needs to be hyphenated.
Context: ...behind anArc<Mutex>` - Look up and ownership-check under TXN_REGISTRY ...
(VERB_NOUN_CONFUSION)
[typographical] ~481-~481: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...cy, reduced lock contention Effort: 3-4 days ### 2. Complete Phase 2-5 Refacto...
(HYPHEN_TO_EN)
[typographical] ~493-~493: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...de organisation - Reduced module sizes (150-350 lines vs 2500+) - Improved maintainabil...
(HYPHEN_TO_EN)
[typographical] ~497-~497: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...rer separation of concerns Effort: 10-15 days total ### 3. Add Structured Error...
(HYPHEN_TO_EN)
[typographical] ~509-~509: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...asier to document and test Effort: 5-7 days ### 4. Transaction & Concurrency ...
(HYPHEN_TO_EN)
[typographical] ~549-~549: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...e - Replication edge cases Effort: 5-10 days ### 2. Performance Benchmark Test...
(HYPHEN_TO_EN)
[typographical] ~558-~558: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...vs immediate vs exclusive) Effort: 3-5 days ### 3. Documentation Improvements...
(HYPHEN_TO_EN)
[typographical] ~568-~568: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...eate troubleshooting guide Effort: 5-7 days ### 4. Test Infrastructure Enhanc...
(HYPHEN_TO_EN)
[typographical] ~705-~705: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...iling identifies bottleneck Effort: 3-5 days #### 5b. Lookup Table Pre-computa...
(HYPHEN_TO_EN)
[typographical] ~723-~723: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...it Effort: 1 day Total Effort: 5-7 days (investigate via profiling first) ...
(HYPHEN_TO_EN)
[typographical] ~736-~736: If specifying a range, consider using an en dash instead of a hyphen.
Context: ...ackoff) - Mutex contention Effort: 3-5 days ### 2. Telemetry Events **Add Tel...
(HYPHEN_TO_EN)
[uncategorized] ~882-~882: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...visibility, NIF pattern review) 5. P1 High Priority Features (query_row, named parameters...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[uncategorized] ~884-~884: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...benchmarks, coverage reporting) 7. P2 Medium Priority Features 8. **Ecto Integration Improv...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[uncategorized] ~886-~886: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...Ecto Integration Improvements** 9. P3 Low Priority Features This prioritization ensures...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Elixir 1.18.0 / OTP 27.0 / macos-latest
- GitHub Check: Elixir 1.18.0 / OTP 27.0 / ubuntu-latest
- GitHub Check: Analyze (rust)
🔇 Additional comments (22)
native/ecto_libsql/src/connection.rs (2)
353-402: Well-structured extension loading toggle implementation.The
enable_load_extensionNIF follows the established patterns in this codebase:
- Proper lock acquisition and release before operations
- Comprehensive documentation with security warnings
- Descriptive error messages
- No
.unwrap()calls, using safe error propagation
404-450: Extension loading implementation looks solid with appropriate security warnings.The
load_extensionNIF correctly:
- Validates the connection exists before proceeding
- Uses
PathBuffor cross-platform path handling- Supports optional entry point parameter
- Includes comprehensive security documentation with clear warnings about trusted sources
The error handling appropriately delegates to
libsql::Connection::load_extension, which will provide informative error messages for missing files or other issues. No additional path validation is necessary.CHANGELOG.md (2)
10-25: Excellent documentation of unsupported hooks with clear reasoning.The changelog entry thoroughly explains:
- Why hooks cannot be implemented (Rustler threading limitations)
- The specific technical constraints (managed vs unmanaged threads, synchronous callback requirements)
- Links to the relevant Rustler issue for further context
- Practical alternatives for users (CDC patterns, RLS approaches)
This level of documentation helps users understand the limitation and find suitable workarounds.
29-60: Comprehensive feature documentation for extension loading and parameter introspection.The changelog entries clearly document:
- Security-first design with extension loading disabled by default
- API workflow and platform support
- Parameter indexing convention (1-based following SQLite)
- Test coverage details
native/ecto_libsql/src/constants.rs (1)
78-79: Appropriate atom addition for signalling unsupported features.Adding the
unsupportedatom follows the project's established pattern for explicit error signalling. As per learnings, returning an:unsupportedatom is the correct approach for features that cannot be implemented due to architectural constraints.native/ecto_libsql/src/lib.rs (1)
10-10: Module export follows established pattern.The hooks module is correctly declared as public, allowing Rustler to automatically discover and register the NIFs annotated with
#[rustler::nif]in that module.native/ecto_libsql/src/statement.rs (1)
328-379: Well-implemented parameter name introspection with correct indexing semantics.The implementation correctly:
- Documents the 1-based indexing convention (following SQLite)
- Returns
Option<String>to distinguish named vs positional parameters- Follows the established lock-clone-drop-operate pattern
- Verifies statement ownership before accessing
The use of
i32for the index parameter aligns with SQLite's C API which usesintfor parameter indices. The documentation clearly explains the indexing behaviour.ENHANCEMENTS.md (1)
1-17: Comprehensive enhancement tracking document.This document provides excellent visibility into:
- Feature prioritisation and status tracking
- Estimated effort for planning purposes
- Clear categorisation by impact and use case
- Implementation notes and security considerations
This will be valuable for contributors and users understanding the project's roadmap.
test/statement_features_test.exs (3)
464-539: Excellent parameter name introspection test coverage.The tests comprehensively cover:
- All three SQLite named parameter styles (
:name,$name,@name)- Positional parameters returning
nil- Mixed positional and named parameters
- 1-based indexing convention
This aligns with the coding guidelines requiring tests for happy path, edge cases, and type conversions. Based on learnings, the statement introspection functions are correctly validated.
546-686: Thorough column introspection edge case coverage.The tests cover important edge cases:
SELECT *column count- INSERT/UPDATE/DELETE without RETURNING (0 columns)
- Aggregate functions with aliases
- Multi-table JOINs
- Subqueries
- Computed expressions
This provides good confidence that the column introspection features work correctly across various SQL constructs.
400-406: Good edge case test for zero parameters.Testing the zero-parameter case ensures the function handles the boundary condition correctly.
test/hooks_test.exs (2)
24-45: Good test verifying database operations remain unaffected.This test correctly validates that calling unsupported hook functions does not corrupt the connection state or affect subsequent database operations. This aligns with the coding guidelines requiring tests to verify unsupported functions "don't modify database state or corrupt connections".
53-56: Good idempotency test for remove_update_hook.Testing that the function can be called multiple times safely ensures robust handling of the unsupported case without side effects.
native/ecto_libsql/src/hooks.rs (4)
1-8: Excellent documentation of unsupported features.The module-level documentation clearly explains the current status and fundamental limitations. This aligns with the learnings requirement to provide comprehensive documentation for explicitly unsupported functions, including architectural constraints and alternatives.
Based on learnings, this follows the documented pattern for unsupported features.
10-67: LGTM! Comprehensive documentation with practical alternatives.The function documentation thoroughly explains the threading limitations and provides four practical alternatives (application-level events, database triggers, polling-based CDC, and Phoenix.Tracker). The implementation correctly returns an error for the unsupported feature.
Based on learnings, this follows the pattern for explicitly unsupported functions with comprehensive documentation.
69-81: LGTM! Consistent error handling.The function correctly returns
unsupportedand references the main documentation inset_update_hookfor details.
83-153: LGTM! Thorough explanation of synchronous communication limitations.The documentation clearly explains why authoriser hooks require synchronous bidirectional communication, which isn't feasible with Rustler's threading model. The four alternatives (application-level authorisation, database views, query rewriting, connection-level restrictions) provide comprehensive guidance for implementing row-level security.
Based on learnings, this follows the documented pattern for explicitly unsupported functions.
lib/ecto_libsql/native.ex (5)
133-146: LGTM! NIF stubs follow established patterns.The NIF stub declarations are consistent with existing stubs in the file and properly marked with
@doc false.Based on learnings, these correspond to the new Rust NIF functions.
Also applies to: 166-167
856-944: LGTM! Extension loading functions are well-documented with security warnings.The
enable_extensions/2andload_ext/3functions properly extract connection IDs from state, include appropriate guards, and provide comprehensive documentation with security warnings and practical examples.Based on learnings, these wrappers correctly integrate with state management.
999-1010: LGTM! Correctly extracts connection ID from state.
1012-1074: LGTM! Comprehensive documentation and correct parameter usage.The
add_authorizer/2function correctly uses thepidparameter (unlikeadd_update_hook/2) and provides extensive documentation explaining the threading limitations and four practical alternatives for implementing row-level security.Based on learnings, this wrapper provides comprehensive documentation for the unsupported feature.
1234-1279: LGTM! Correctly implements 1-based parameter indexing and handles optional names.The function properly validates that the parameter index is 1-based (matching SQLite convention), extracts the connection ID from state, and handles the
Option<String>return type from Rust (binary name, nil for positional, or error).Based on learnings, this wrapper correctly integrates with statement introspection capabilities.
Summary by CodeRabbit
New Features
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.