Skip to content

feature: Add random rowid support (libSQL extension)#39

Merged
ocean merged 2 commits intomainfrom
random-rowid-support
Dec 27, 2025
Merged

feature: Add random rowid support (libSQL extension)#39
ocean merged 2 commits intomainfrom
random-rowid-support

Conversation

@ocean
Copy link
Copy Markdown
Owner

@ocean ocean commented Dec 26, 2025

  • Added support for libSQL's RANDOM ROWID table option to generate pseudorandom rowid values instead of consecutive integers
    • Usage: Pass options: [random_rowid: true] to create table() in migrations
    • Example:
      create table(:sessions, options: [random_rowid: true]) do
        add :token, :string
        add :user_id, :integer
        timestamps()
      end
    • Compatibility: Works with all table configurations (single PK, composite PK, IF NOT EXISTS)
    • Restrictions: Mutually exclusive with WITHOUT ROWID and AUTOINCREMENT (per libSQL specification)
    • SQL output: CREATE TABLE sessions (...) RANDOM ROWID

Summary by CodeRabbit

  • New Features

    • Documented RANDOM ROWID support for libSQL migrations and clarified ALTER COLUMN behaviour.
  • Documentation

    • Expanded migration guides and changelog with examples, SQL output and notes on libSQL vs standard SQLite, including recommended workarounds.
  • Tests

    • Added tests covering RANDOM ROWID scenarios (single/composite keys, IF NOT EXISTS) and related error cases.
  • Bug Fixes/Validation

    • Added validation to surface errors when incompatible table options are combined.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 26, 2025

Walkthrough

Adds LibSQL extension support for RANDOM ROWID and ALTER COLUMN, refactors CREATE TABLE DDL to return {table_constraints, table_suffix}, enforces mutual-exclusivity rules for RANDOM ROWID, and adds tests and documentation for these features.

Changes

Cohort / File(s) Summary
Documentation & Release Notes
AGENTS.md, CHANGELOG.md
Add docs and changelog entries describing RANDOM ROWID and ALTER COLUMN as libSQL extensions, usage examples, restrictions, SQL examples and migration notes.
Core DDL Implementation
lib/ecto/adapters/libsql/connection.ex
Refactor table_options to return {table_constraints, table_suffix}; compose CREATE TABLE using constraints inside parentheses and suffix after; add validation to forbid RANDOM ROWID with WITHOUT ROWID or AUTOINCREMENT; handle composite primary keys placement.
Tests
test/ecto_connection_test.exs
Add tests for RANDOM ROWID: single PK, composite PK, absent option, IF NOT EXISTS cases, and error cases for incompatible options.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I nibble lines of SQL bright,

constraints tucked in, suffixes light.
RANDOM ROWID hops into place,
ALTER COLUMN winks with grace.
A tiny rabbit cheers the race 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feature: Add random rowid support (libSQL extension)' directly and accurately summarizes the main change—adding RANDOM ROWID support as a libSQL extension feature, which is confirmed by the substantial documentation, code, and test additions across multiple files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch random-rowid-support

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 545e637 and d97ff56.

📒 Files selected for processing (4)
  • AGENTS.md
  • CHANGELOG.md
  • lib/ecto/adapters/libsql/connection.ex
  • test/ecto_connection_test.exs
🧰 Additional context used
📓 Path-based instructions (7)
AGENTS.md

📄 CodeRabbit inference engine (AGENT.md)

Document agent architecture and design patterns in AGENTS.md

Update documentation in AGENTS.md API Reference section and CHANGELOG.md when adding or modifying public APIs

Files:

  • AGENTS.md
**/*.{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:

  • AGENTS.md
  • test/ecto_connection_test.exs
  • lib/ecto/adapters/libsql/connection.ex
  • CHANGELOG.md
**/*.{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/ecto_connection_test.exs
  • lib/ecto/adapters/libsql/connection.ex
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/ecto_connection_test.exs
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, prefer with clauses for chaining multiple operations that return Result tuples, providing clear error handling with an else clause
In Ecto changesets, use the cast function 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: :deferred for mixed workloads, :immediate for write-heavy operations, :exclusive for critical operations requiring exclusive locks, :read_only for 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 with EctoLibSql.Native.vector() function, insert vectors with vector(?) SQL syntax, and query with vector distance functions like vector_distance_cos

Files:

  • lib/ecto/adapters/libsql/connection.ex
lib/ecto/adapters/libsql/connection.ex

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • lib/ecto/adapters/libsql/connection.ex
**/*.ex

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ex: Use prepared statements via EctoLibSql.Native.prepare/2 and EctoLibSql.Native.query_stmt/3 for repeated queries to improve performance by ~10-15x
Use batch operations with EctoLibSql.Native.batch_transactional/2 for 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 with EctoLibSql.handle_begin/2, EctoLibSql.handle_commit/2, and EctoLibSql.handle_rollback/2 for multi-step database operations
Use cursor streaming with DBConnection.stream/3 for memory-efficient processing of large result sets
Use vector search with EctoLibSql.Native.vector/1, EctoLibSql.Native.vector_type/2, and EctoLibSql.Native.vector_distance_cos/2 for AI/ML features
Use EctoLibSql.Pragma.* functions to configure SQLite parameters such as foreign keys, journal mode, cache size, and synchronous level
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
Use Ecto changesets for data validation before inserting or updating records
Use Ecto query composition with Ecto.Query for building complex queries incrementally
Preload associations with Repo.preload/2 to avoid N+1 query problems in Ecto applications
For remote replicas in production, use EctoLibSql.Native.sync/1, EctoLibSql.Native.get_frame_number_for_replica/1, EctoLibSql.Native.sync_until_frame/2, and EctoLibSql.Native.flush_and_get_frame/1 for replication management
Use busy timeout configuration with EctoLibSql.Native.busy_timeout/2 to 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/adapters/libsql/connection.ex
🧠 Learnings (24)
📓 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 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
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
📚 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:

  • AGENTS.md
  • test/ecto_connection_test.exs
  • lib/ecto/adapters/libsql/connection.ex
  • CHANGELOG.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 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:

  • AGENTS.md
  • test/ecto_connection_test.exs
  • lib/ecto/adapters/libsql/connection.ex
  • CHANGELOG.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 AGENTS.md : Update documentation in AGENTS.md API Reference section and CHANGELOG.md when adding or modifying public APIs

Applied to files:

  • AGENTS.md
📚 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:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.ex
  • CHANGELOG.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 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:

  • AGENTS.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:

  • AGENTS.md
  • test/ecto_connection_test.exs
  • CHANGELOG.md
📚 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 priv/repo/migrations/*.exs : Use Ecto migrations with `Ecto.Migration` for all database schema changes

Applied to files:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.ex
  • CHANGELOG.md
📚 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:

  • AGENTS.md
📚 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:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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 batch operations with `EctoLibSql.Native.batch_transactional/2` for bulk inserts/updates instead of individual statements

Applied to files:

  • AGENTS.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 lib/**/*.ex : Use transactions with appropriate isolation behaviours when executing database operations: `:deferred` for mixed workloads, `:immediate` for write-heavy operations, `:exclusive` for critical operations requiring exclusive locks, `:read_only` for read-only queries

Applied to files:

  • AGENTS.md
📚 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:

  • AGENTS.md
📚 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 : For remote replicas in production, use `EctoLibSql.Native.sync/1`, `EctoLibSql.Native.get_frame_number_for_replica/1`, `EctoLibSql.Native.sync_until_frame/2`, and `EctoLibSql.Native.flush_and_get_frame/1` for replication management

Applied to files:

  • AGENTS.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 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:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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 **/repo.ex : Define custom Ecto repositories using `Ecto.Repo` with `Ecto.Adapters.LibSql` adapter for ecto_libsql projects

Applied to files:

  • AGENTS.md
📚 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 **/*_schema.{ex,exs} : Use Ecto schemas with proper type definitions for database models in Elixir applications

Applied to files:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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/ecto_connection_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 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:

  • test/ecto_connection_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:

  • test/ecto_connection_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 : 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:

  • test/ecto_connection_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 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:

  • lib/ecto/adapters/libsql/connection.ex
🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md

132-132: Bare URL used

(MD034, no-bare-urls)

⏰ 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 (14)
test/ecto_connection_test.exs (6)

49-63: LGTM! Comprehensive test for RANDOM ROWID basic usage.

The test correctly validates RANDOM ROWID placement after the closing parenthesis and verifies the SQL structure.


65-80: LGTM! Validates composite PK with RANDOM ROWID correctly.

The test ensures both composite PRIMARY KEY and RANDOM ROWID are present in the correct positions.


82-106: LGTM! Negative test cases validate opt-in behaviour.

These tests ensure RANDOM ROWID is only added when explicitly requested, confirming the feature is opt-in.


108-120: LGTM! Validates RANDOM ROWID with IF NOT EXISTS.

Test correctly verifies compatibility between RANDOM ROWID and IF NOT EXISTS, as documented in the PR objectives.


122-139: LGTM! Validates mutual exclusivity with WITHOUT ROWID.

Test correctly enforces libSQL specification requiring RANDOM ROWID and WITHOUT ROWID to be mutually exclusive.


141-154: LGTM! Validates mutual exclusivity with AUTOINCREMENT.

Test correctly enforces libSQL specification and includes the column name in the error message for better debugging.

lib/ecto/adapters/libsql/connection.ex (2)

187-191: LGTM! DDL construction properly refactored for libSQL extensions.

The change to return {table_constraints, table_suffix} correctly separates composite PRIMARY KEY (inside parentheses) from table options like RANDOM ROWID (outside parentheses), aligning with libSQL syntax.


386-432: LGTM! Validation and DDL generation logic is sound.

The implementation correctly:

  • Validates mutual exclusivity constraints per libSQL specification
  • Raises ArgumentError with clear messages for violations
  • Separates composite PK constraints (inside parentheses) from table suffix options (outside)
  • Returns the two-part structure needed for proper SQL construction

The validation order is correct: RANDOM ROWID checks are performed before DDL construction, preventing runtime errors.

CHANGELOG.md (3)

12-32: Well-documented RANDOM ROWID support.

The documentation is comprehensive, covering usage, benefits, restrictions, and validation. Security benefits are clearly articulated.


99-116: LGTM! Clear clarification of ALTER COLUMN support.

The documentation correctly identifies this as an existing feature (v0.6.0) and emphasises the important caveat that changes only apply to new/updated rows, not existing data.


117-133: LGTM! Transparent documentation of unsupported features.

Excellent transparency about limitations, with clear technical explanations and alternative approaches. The reference to Rustler issue #293 provides context for the architectural constraints.

AGENTS.md (3)

1301-1357: LGTM! Comprehensive RANDOM ROWID migration guide.

The documentation provides clear examples for basic usage, composite primary keys, and IF NOT EXISTS scenarios. Security benefits and restrictions are well-articulated.


1359-1401: LGTM! Clear ALTER COLUMN documentation.

The documentation correctly demonstrates libSQL's ALTER COLUMN capabilities and emphasises the critical caveat that changes only apply to new/updated rows.


1824-1881: Migration notes clearly distinguish libSQL extensions from standard SQLite.

The documentation effectively separates:

  • Fully supported operations (standard across SQLite/libSQL)
  • libSQL-specific extensions (ALTER COLUMN, RANDOM ROWID)
  • Standard SQLite workarounds (table recreation pattern)

The table recreation example at lines 1864-1879 correctly includes the email column at line 1868 (add :email, :string) and preserves email data in the INSERT SELECT statement at line 1873, contrary to a previous review comment that flagged this as an issue.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
CHANGELOG.md (1)

10-65: Duplicate ### Added sections under the same version.

There are two ### Added headings under the ## [Unreleased] section (lines 10 and 65). Consider merging them into a single ### Added section for consistency with the Keep a Changelog format.

lib/ecto/adapters/libsql/connection.ex (1)

401-407: Consider adding validation for mutually exclusive options.

Per the libSQL specification documented in the PR, RANDOM ROWID is mutually exclusive with WITHOUT ROWID and AUTOINCREMENT. Currently, the implementation doesn't validate these constraints—libSQL will reject invalid combinations at runtime, but earlier validation could provide clearer error messages.

🔎 Example validation (optional)
# In table_options/2, before constructing the suffix:
if table.options && Keyword.get(table.options, :random_rowid, false) do
  if Keyword.get(table.options, :without_rowid, false) do
    raise ArgumentError, "RANDOM ROWID cannot be combined with WITHOUT ROWID"
  end
  
  # Check for AUTOINCREMENT in columns
  has_autoincrement = Enum.any?(columns, fn {:add, _name, _type, opts} ->
    Keyword.get(opts, :autoincrement, false)
  end)
  
  if has_autoincrement do
    raise ArgumentError, "RANDOM ROWID cannot be combined with AUTOINCREMENT"
  end
  
  " RANDOM ROWID"
else
  ""
end
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88a46cb and 545e637.

📒 Files selected for processing (4)
  • AGENTS.md
  • CHANGELOG.md
  • lib/ecto/adapters/libsql/connection.ex
  • test/ecto_connection_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:

  • CHANGELOG.md
  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.ex
  • test/ecto_connection_test.exs
AGENTS.md

📄 CodeRabbit inference engine (AGENT.md)

Document agent architecture and design patterns in AGENTS.md

Update documentation in AGENTS.md API Reference section and CHANGELOG.md when adding or modifying public APIs

Files:

  • AGENTS.md
**/*.{ex,exs}

📄 CodeRabbit inference engine (CLAUDE.md)

ALWAYS run the Elixir formatter (mix format --check-formatted) before committing changes and fix any issues

Files:

  • lib/ecto/adapters/libsql/connection.ex
  • test/ecto_connection_test.exs
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, prefer with clauses for chaining multiple operations that return Result tuples, providing clear error handling with an else clause
In Ecto changesets, use the cast function 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: :deferred for mixed workloads, :immediate for write-heavy operations, :exclusive for critical operations requiring exclusive locks, :read_only for 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 with EctoLibSql.Native.vector() function, insert vectors with vector(?) SQL syntax, and query with vector distance functions like vector_distance_cos

Files:

  • lib/ecto/adapters/libsql/connection.ex
lib/ecto/adapters/libsql/connection.ex

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • lib/ecto/adapters/libsql/connection.ex
**/*.ex

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ex: Use prepared statements via EctoLibSql.Native.prepare/2 and EctoLibSql.Native.query_stmt/3 for repeated queries to improve performance by ~10-15x
Use batch operations with EctoLibSql.Native.batch_transactional/2 for 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 with EctoLibSql.handle_begin/2, EctoLibSql.handle_commit/2, and EctoLibSql.handle_rollback/2 for multi-step database operations
Use cursor streaming with DBConnection.stream/3 for memory-efficient processing of large result sets
Use vector search with EctoLibSql.Native.vector/1, EctoLibSql.Native.vector_type/2, and EctoLibSql.Native.vector_distance_cos/2 for AI/ML features
Use EctoLibSql.Pragma.* functions to configure SQLite parameters such as foreign keys, journal mode, cache size, and synchronous level
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
Use Ecto changesets for data validation before inserting or updating records
Use Ecto query composition with Ecto.Query for building complex queries incrementally
Preload associations with Repo.preload/2 to avoid N+1 query problems in Ecto applications
For remote replicas in production, use EctoLibSql.Native.sync/1, EctoLibSql.Native.get_frame_number_for_replica/1, EctoLibSql.Native.sync_until_frame/2, and EctoLibSql.Native.flush_and_get_frame/1 for replication management
Use busy timeout configuration with EctoLibSql.Native.busy_timeout/2 to 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/adapters/libsql/connection.ex
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/ecto_connection_test.exs
🧠 Learnings (23)
📚 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:

  • CHANGELOG.md
  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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.md
  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.ex
  • test/ecto_connection_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/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:

  • CHANGELOG.md
  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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.md
  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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.md
  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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/**/*.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.md
  • AGENTS.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 AGENTS.md : Update documentation in AGENTS.md API Reference section and CHANGELOG.md when adding or modifying public APIs

Applied to files:

  • AGENTS.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:

  • AGENTS.md
  • test/ecto_connection_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 priv/repo/migrations/*.exs : Use Ecto migrations with `Ecto.Migration` for all database schema changes

Applied to files:

  • AGENTS.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 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:

  • AGENTS.md
  • lib/ecto/adapters/libsql/connection.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 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:

  • AGENTS.md
📚 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:

  • AGENTS.md
📚 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:

  • AGENTS.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 lib/**/*.ex : Use transactions with appropriate isolation behaviours when executing database operations: `:deferred` for mixed workloads, `:immediate` for write-heavy operations, `:exclusive` for critical operations requiring exclusive locks, `:read_only` for read-only queries

Applied to files:

  • AGENTS.md
📚 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:

  • AGENTS.md
📚 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 : For remote replicas in production, use `EctoLibSql.Native.sync/1`, `EctoLibSql.Native.get_frame_number_for_replica/1`, `EctoLibSql.Native.sync_until_frame/2`, and `EctoLibSql.Native.flush_and_get_frame/1` for replication management

Applied to files:

  • AGENTS.md
📚 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 **/repo.ex : Define custom Ecto repositories using `Ecto.Repo` with `Ecto.Adapters.LibSql` adapter for ecto_libsql projects

Applied to files:

  • AGENTS.md
📚 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 **/*_schema.{ex,exs} : Use Ecto schemas with proper type definitions for database models in Elixir applications

Applied to files:

  • AGENTS.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 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/ecto_connection_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 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:

  • test/ecto_connection_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 : 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:

  • test/ecto_connection_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_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:

  • test/ecto_connection_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 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:

  • test/ecto_connection_test.exs
⏰ 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 (5)
AGENTS.md (2)

1301-1357: LGTM! Comprehensive documentation for RANDOM ROWID feature.

The documentation clearly covers:

  • Usage patterns with code examples
  • Security benefits (preventing ID enumeration attacks)
  • Compatibility with composite primary keys and IF NOT EXISTS
  • Restrictions (mutual exclusivity with WITHOUT ROWID and AUTOINCREMENT)
  • SQL output examples

1359-1401: LGTM! Clear documentation for ALTER COLUMN support.

The documentation appropriately:

  • Lists supported modification types
  • Warns that changes only apply to new/updated rows
  • Notes this is a libSQL extension not available in standard SQLite
test/ecto_connection_test.exs (1)

49-120: LGTM! Comprehensive test coverage for RANDOM ROWID feature.

The tests thoroughly cover:

  • Single primary key with RANDOM ROWID
  • Composite primary key with RANDOM ROWID (verifying both constraints are present)
  • Negative case when random_rowid: false
  • Default behaviour when no options specified
  • Combination with IF NOT EXISTS

The regex assertions correctly verify RANDOM ROWID placement after the closing parenthesis.

lib/ecto/adapters/libsql/connection.ex (2)

186-191: LGTM! Clean refactoring of DDL generation.

The two-part tuple approach {table_constraints, table_suffix} elegantly separates:

  • Internal table constraints (composite primary keys) placed inside the parentheses
  • External table suffixes (RANDOM ROWID) placed after the closing parenthesis

The string interpolation correctly positions both components in the final SQL.


386-410: Well-structured implementation with clear separation of concerns.

The function correctly:

  • Separates composite PK constraints (inside parentheses) from table suffixes (after parentheses)
  • Handles nil options safely with table.options && Keyword.get(...)
  • Returns a descriptive tuple for the caller to use

This aligns well with the libSQL specification where RANDOM ROWID must appear after the table definition.

Comment thread AGENTS.md
@ocean ocean merged commit 068bb89 into main Dec 27, 2025
13 checks passed
@ocean ocean deleted the random-rowid-support branch December 27, 2025 06:52
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.

1 participant