commits
- Add Expr.Some constructor to lift values to option type
- Add Expr.some function to create Some expressions
- Add Expr.int_opt, int32_opt, int64_opt, float_opt, string_opt, bool_opt
helpers that convert OCaml option values to option expressions
- Bump version to 0.3.2
Replace Expr.raw examples with Expr.column throughout documentation to
demonstrate proper schema integration and type-safe query building.
- queries.md: Complete rewrite showing Expr.column usage, added schema
integration section, updated all examples
- getting-started.md: Updated Query DSL section to use Expr.column
- README.md: Updated quick example to use Expr.column
- docs/README.md: Same updates for consistency
Add tuple-based values1-values30 and values1_multi-values30_multi functions
that provide compile-time verification:
- Column count matches value count
- Each value type matches its corresponding field type
- All fields belong to the same record type
This solves OCaml's limitation where list elements must be homogeneous,
allowing type-safe INSERT statements with mixed column types (int, string, etc).
Includes documentation updates to README, getting-started, and queries docs.
The SQL generator was concatenating query parts with empty string,
causing malformed SQL like 'FROM usersINNER JOIN' instead of
'FROM users INNER JOIN'.
Added regression test to prevent future occurrences.
- Replace fragile docker-compose with proper podman-compose setup
- Add replication slots for reliable WAL streaming
- Add proper replica bootstrap with pg_basebackup and su-exec
- Add lag-aware health checks for replicas
- Add run_cluster.sh orchestration script (start/stop/status/clean/test)
- Update test ports to 15432-15434 to avoid conflicts
- Fix concurrent load test to handle pool exhaustion gracefully
- Add Pool.Make(Driver).Multi submodule with lock-free round-robin load
balancing using kcas, health tracking, and full pool API
- Add Cqrs.Make(Driver) functor for transparent read/write splitting:
- Automatic routing: reads to replicas, writes to primary
- Three replica selection strategies: RoundRobin, Random, LeastConnections
- Transaction pinning to primary connection
- Automatic fallback when all replicas unhealthy
- Add comprehensive unit tests (test_multi_pool.ml, test_cqrs.ml)
- Add Podman-based integration tests with PostgreSQL replication
- Add documentation (docs/cqrs.md, docs/pool.md MultiPool section)
The PostgreSQL driver was converting Value.Null to empty string which
caused NULL parameters to be treated as empty strings instead of SQL
NULL values.
Also adds comprehensive integration tests for Pdate (date type) in both
SQLite and PostgreSQL drivers, plus unit tests for date conversions.
New in this release:
- Scheduler-agnostic connection pool using kcas
- Works with Eio, Lwt, or direct-style OCaml
- Lock-free, thread-safe pool operations
- Connection validation and graceful shutdown
- Comprehensive pool documentation
- Create docs/pool.md with comprehensive pool usage guide
- Cover setup, basic usage, concurrency patterns (Eio, Lwt, multi-domain)
- Include pool statistics, validation, lifecycle management
- Add best practices and SQLite considerations
- Update READMEs with Pool feature and documentation link
- Add lib/pool.ml using kcas for lock-free concurrency
- Works with any OCaml scheduler (Eio, Lwt, direct-style)
- Features: acquire/release, blocking acquire with timeout,
connection validation, pool stats, graceful shutdown
- Fix thread-safety in lib/stream.ml (use Atomic counter)
- Add comprehensive tests for Lwt and Eio integration
- Add pool integration tests to SQLite and PostgreSQL drivers
- All 181 unit tests + integration tests pass
- Add root README.md with links to docs/
- Fix Error.to_string -> Error.show_db_error in documentation
- Remove placeholder bin/ directory
- Set version 0.1.0 in dune-project and opam files
- Remove unused placeholder_* functions from driver.ml
- Have multi.ml reuse SQL builder functions from repo.ml
- Remove duplicate top-level types from embedded.ml (keep only Make functor)
Add complete documentation for all repodb modules:
- schemas.md: Schema/Field definitions and types
- changesets.md: Validation and change tracking
- queries.md: Query DSL with expressions
- repo.md: Repo operations and preloading
- associations.md: Relationships and N+1 prevention
- transactions.md: Transactions and Multi
- migrations.md: Database migrations
- Fix README.md Changeset.create to use a value not a type
Add comprehensive memory benchmarking suite using OCaml's Gc module:
bench/mem_bench.ml - Benchmark infrastructure:
- Allocation tracking (minor/major words, GC counts)
- Leak detection via repeated runs with heap monitoring
- Pretty-printed benchmark results
bench/bench_sqlite.ml - SQLite memory benchmarks
bench/bench_postgresql.ml - PostgreSQL memory benchmarks
Benchmarks cover:
- CRUD operations (insert, query, update, delete)
- Transactions
- Query DSL operations
- Complex queries (joins, grouping)
Leak detection runs 50 rounds of 100 operations each,
monitoring heap and live word growth between rounds.
Results (no leaks detected):
- SQLite: 0 word growth across all operations
- PostgreSQL: 0 word growth across all operations
Run with:
dune exec bench/bench_sqlite.exe
dune exec bench/bench_postgresql.exe
Add 10 integration tests covering Query-Repo execution against real PostgreSQL:
- all_query: Fetch all rows with Query DSL
- where: Filter with Expr.raw conditions
- one_query: Get single result or Not_found
- one_query_not_found: Verify Not_found error
- one_query_opt: Get optional result
- order_limit: ORDER BY and LIMIT via Query
- delete_query: DELETE via Query DSL
- complex: Multiple WHERE, ORDER, LIMIT, OFFSET
- insert_returning: PostgreSQL RETURNING clause
- update_returning: UPDATE with RETURNING
Tests verified against real PostgreSQL 16 container.
Test counts:
- Unit tests: 168
- SQLite integration: 89
- PostgreSQL integration: 92
Connect the Query DSL module to Repo for direct query execution:
- all_query: Execute SELECT, return list of decoded results
- one_query: Execute SELECT, return first row or Not_found error
- one_query_opt: Execute SELECT, return first row as option
- insert_query: Execute INSERT query
- update_query: Execute UPDATE query
- delete_query: Execute DELETE query
- insert/update/delete_query_returning: Variants with RETURNING
Add 10 integration tests covering:
- Basic all_query usage
- WHERE clause filtering with Expr.raw
- one_query with and without results
- one_query_opt behavior
- ORDER BY and LIMIT
- INSERT/UPDATE/DELETE via Query
- Complex query with multiple conditions
Tests: 168 unit + 89 SQLite integration
- Add Ecto-like preloading to solve N+1 queries:
- preload_has_many, preload_has_one, preload_belongs_to
- preload_many_to_many for junction tables
- preload_chunked for streaming large datasets
- Rewrite Multi module for composable database transactions:
- Named steps with typed results
- Dependent operations (access previous results)
- Automatic transaction wrapping with rollback on failure
- insert/update/delete with _fn and _returning variants
- Add comprehensive error tests:
- SQLite: 17 error scenarios
- PostgreSQL: 13 error scenarios
- Covers constraint violations, syntax errors, connection failures
- Add driver abstraction layer (lib/driver.ml)
- Split into 3 opam packages: repodb, repodb-sqlite, repodb-postgresql
Tests: 168 unit + 79 SQLite integration + 82 PostgreSQL integration
Allocation optimizations:
- Switch changeset.changes from assoc list to StringMap (O(log n) lookups)
- Add Buffer-based SQL generation (to_sql_buf) in expr.ml
- Replace List.rev + List.map with List.rev_map in query.ml
- Add named operator functions (eq, lt, add, etc.) to avoid shadowing stdlib
Streaming API (lib/stream.ml):
- fold/iter for memory-efficient result processing
- with_cursor for PostgreSQL cursor-based streaming
- Sync helpers: fold_list, iter_list, to_seq, filter_map, take, drop, chunks
- Configurable batch_size and max_rows
167 tests passing (11 new stream tests)
- Rename all lib/mlecto_*.ml to simple names (types.ml, schema.ml, etc.)
- Add assoc.ml for associations (has_many, belongs_to, has_one, many_to_many)
- Add embedded.ml for embedded schemas with pluggable JSON
- Refactor changeset.ml for pipe-friendly API (t argument last)
- Enhanced repo.ml with transactions and SQL builders
- Comprehensive test suite with 156 passing tests
Core modules:
- Type: GADT type witnesses with Caqti integration
- Schema: Table/column DSL with constraints and SQL generation
- Expr: Full expression GADT (operators, aggregates, functions)
- Query: SELECT/INSERT/UPDATE/DELETE with JOINs, upserts
- Changeset: Type-safe validations and constraints
- Repo: SQL builders, transaction state machine with savepoints
- Migration: DSL + runner with version tracking, plan/rollback
- Multi: Operation chaining with named results, atomic execution
- Error: Comprehensive error types
All modules compile, tests pass.
Replace Expr.raw examples with Expr.column throughout documentation to
demonstrate proper schema integration and type-safe query building.
- queries.md: Complete rewrite showing Expr.column usage, added schema
integration section, updated all examples
- getting-started.md: Updated Query DSL section to use Expr.column
- README.md: Updated quick example to use Expr.column
- docs/README.md: Same updates for consistency
Add tuple-based values1-values30 and values1_multi-values30_multi functions
that provide compile-time verification:
- Column count matches value count
- Each value type matches its corresponding field type
- All fields belong to the same record type
This solves OCaml's limitation where list elements must be homogeneous,
allowing type-safe INSERT statements with mixed column types (int, string, etc).
Includes documentation updates to README, getting-started, and queries docs.
- Replace fragile docker-compose with proper podman-compose setup
- Add replication slots for reliable WAL streaming
- Add proper replica bootstrap with pg_basebackup and su-exec
- Add lag-aware health checks for replicas
- Add run_cluster.sh orchestration script (start/stop/status/clean/test)
- Update test ports to 15432-15434 to avoid conflicts
- Fix concurrent load test to handle pool exhaustion gracefully
- Add Pool.Make(Driver).Multi submodule with lock-free round-robin load
balancing using kcas, health tracking, and full pool API
- Add Cqrs.Make(Driver) functor for transparent read/write splitting:
- Automatic routing: reads to replicas, writes to primary
- Three replica selection strategies: RoundRobin, Random, LeastConnections
- Transaction pinning to primary connection
- Automatic fallback when all replicas unhealthy
- Add comprehensive unit tests (test_multi_pool.ml, test_cqrs.ml)
- Add Podman-based integration tests with PostgreSQL replication
- Add documentation (docs/cqrs.md, docs/pool.md MultiPool section)
New in this release:
- Scheduler-agnostic connection pool using kcas
- Works with Eio, Lwt, or direct-style OCaml
- Lock-free, thread-safe pool operations
- Connection validation and graceful shutdown
- Comprehensive pool documentation
- Add lib/pool.ml using kcas for lock-free concurrency
- Works with any OCaml scheduler (Eio, Lwt, direct-style)
- Features: acquire/release, blocking acquire with timeout,
connection validation, pool stats, graceful shutdown
- Fix thread-safety in lib/stream.ml (use Atomic counter)
- Add comprehensive tests for Lwt and Eio integration
- Add pool integration tests to SQLite and PostgreSQL drivers
- All 181 unit tests + integration tests pass
Add complete documentation for all repodb modules:
- schemas.md: Schema/Field definitions and types
- changesets.md: Validation and change tracking
- queries.md: Query DSL with expressions
- repo.md: Repo operations and preloading
- associations.md: Relationships and N+1 prevention
- transactions.md: Transactions and Multi
- migrations.md: Database migrations
- Fix README.md Changeset.create to use a value not a type
Add comprehensive memory benchmarking suite using OCaml's Gc module:
bench/mem_bench.ml - Benchmark infrastructure:
- Allocation tracking (minor/major words, GC counts)
- Leak detection via repeated runs with heap monitoring
- Pretty-printed benchmark results
bench/bench_sqlite.ml - SQLite memory benchmarks
bench/bench_postgresql.ml - PostgreSQL memory benchmarks
Benchmarks cover:
- CRUD operations (insert, query, update, delete)
- Transactions
- Query DSL operations
- Complex queries (joins, grouping)
Leak detection runs 50 rounds of 100 operations each,
monitoring heap and live word growth between rounds.
Results (no leaks detected):
- SQLite: 0 word growth across all operations
- PostgreSQL: 0 word growth across all operations
Run with:
dune exec bench/bench_sqlite.exe
dune exec bench/bench_postgresql.exe
Add 10 integration tests covering Query-Repo execution against real PostgreSQL:
- all_query: Fetch all rows with Query DSL
- where: Filter with Expr.raw conditions
- one_query: Get single result or Not_found
- one_query_not_found: Verify Not_found error
- one_query_opt: Get optional result
- order_limit: ORDER BY and LIMIT via Query
- delete_query: DELETE via Query DSL
- complex: Multiple WHERE, ORDER, LIMIT, OFFSET
- insert_returning: PostgreSQL RETURNING clause
- update_returning: UPDATE with RETURNING
Tests verified against real PostgreSQL 16 container.
Test counts:
- Unit tests: 168
- SQLite integration: 89
- PostgreSQL integration: 92
Connect the Query DSL module to Repo for direct query execution:
- all_query: Execute SELECT, return list of decoded results
- one_query: Execute SELECT, return first row or Not_found error
- one_query_opt: Execute SELECT, return first row as option
- insert_query: Execute INSERT query
- update_query: Execute UPDATE query
- delete_query: Execute DELETE query
- insert/update/delete_query_returning: Variants with RETURNING
Add 10 integration tests covering:
- Basic all_query usage
- WHERE clause filtering with Expr.raw
- one_query with and without results
- one_query_opt behavior
- ORDER BY and LIMIT
- INSERT/UPDATE/DELETE via Query
- Complex query with multiple conditions
Tests: 168 unit + 89 SQLite integration
- Add Ecto-like preloading to solve N+1 queries:
- preload_has_many, preload_has_one, preload_belongs_to
- preload_many_to_many for junction tables
- preload_chunked for streaming large datasets
- Rewrite Multi module for composable database transactions:
- Named steps with typed results
- Dependent operations (access previous results)
- Automatic transaction wrapping with rollback on failure
- insert/update/delete with _fn and _returning variants
- Add comprehensive error tests:
- SQLite: 17 error scenarios
- PostgreSQL: 13 error scenarios
- Covers constraint violations, syntax errors, connection failures
- Add driver abstraction layer (lib/driver.ml)
- Split into 3 opam packages: repodb, repodb-sqlite, repodb-postgresql
Tests: 168 unit + 79 SQLite integration + 82 PostgreSQL integration
Allocation optimizations:
- Switch changeset.changes from assoc list to StringMap (O(log n) lookups)
- Add Buffer-based SQL generation (to_sql_buf) in expr.ml
- Replace List.rev + List.map with List.rev_map in query.ml
- Add named operator functions (eq, lt, add, etc.) to avoid shadowing stdlib
Streaming API (lib/stream.ml):
- fold/iter for memory-efficient result processing
- with_cursor for PostgreSQL cursor-based streaming
- Sync helpers: fold_list, iter_list, to_seq, filter_map, take, drop, chunks
- Configurable batch_size and max_rows
167 tests passing (11 new stream tests)
- Rename all lib/mlecto_*.ml to simple names (types.ml, schema.ml, etc.)
- Add assoc.ml for associations (has_many, belongs_to, has_one, many_to_many)
- Add embedded.ml for embedded schemas with pluggable JSON
- Refactor changeset.ml for pipe-friendly API (t argument last)
- Enhanced repo.ml with transactions and SQL builders
- Comprehensive test suite with 156 passing tests
Core modules:
- Type: GADT type witnesses with Caqti integration
- Schema: Table/column DSL with constraints and SQL generation
- Expr: Full expression GADT (operators, aggregates, functions)
- Query: SELECT/INSERT/UPDATE/DELETE with JOINs, upserts
- Changeset: Type-safe validations and constraints
- Repo: SQL builders, transaction state machine with savepoints
- Migration: DSL + runner with version tracking, plan/rollback
- Multi: Operation chaining with named results, atomic execution
- Error: Comprehensive error types
All modules compile, tests pass.