Changelog

View Source

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

[2.0.0] - 2026-05-10

The 2.0 line introduces pluggable backends and multi-repo support. The intermediate 2.1-2.5 dev tags were rolled into this release; nothing shipped to Hex during that window. See MIGRATION-2.0.md for upgrade notes.

Added

  • Pluggable backends. Pick a backend package and point kura at it with {backend, ...}. Kura auto-populates dialect, pool_module, and driver_module from the aggregator at app start.

    • kura_pool behaviour - connection-pool contract.
    • kura_capabilities behaviour - backend feature flags (e.g. advisory_locks, returning, arrays).
    • kura_dialect behaviour - SQL dialect contract, with optional column_type/1 and format_default/1 callbacks for backend-specific DDL.
    • kura_driver behaviour - query/transaction driver contract.
    • kura_pool_ets - in-memory pool for tests that don't need a real DB.
  • Multi-repo configuration. Run two or more repos in the same app, each with its own backend, dialect, and pool. Configure with a map of maps under {repos, ...}:

    {kura, [
        {repos, #{
            primary => #{
                backend => kura_backend_postgres,
                host => "primary.example.com",
                database => "main",
                pool_size => 10
            },
            analytics => #{
                backend => kura_backend_sqlite,
                database => <<":memory:">>
            }
        }}
    ]}

    Kura starts each repo's pool on app start. The dialect resolves per-repo, so primary emits Postgres SQL while analytics emits SQLite SQL. The query cache is keyed per repo so dialects never share entries.

  • kura_app:pool_config/1 returns the config map for a specific repo.

  • kura_types:cast(boolean, 0|1) so SQLite (booleans stored as INTEGER) round-trips cleanly.

Changed

  • kura_query_compiler:to_sql/2, to_sql_cached/2, to_sql_from/3, insert/4, insert/5, update/5, delete/4, update_all/3, delete_all/2, insert_all/4, insert_all/5, and dialect/1 take RepoMod as the first argument so they can resolve the per-repo dialect. Internal callers (kura_repo_worker, kura_stream, kura_migrator) thread the repo through automatically.
  • kura_query_cache keys are tuples {RepoMod, QueryHash}. The cache ETS table is now owned by a supervised gen_server so it survives any caller exiting.
  • kura_db:query/3 and the rest of the query path route through kura_pool / kura_driver, so backends are swappable.
  • kura_app:pool_config/0 is dialect-aware. PG-shaped defaults (port 5432, user/database "postgres", decode_opts) only apply when dialect is kura_dialect_pg. Other dialects get a pass-through map of user-set kura env keys.
  • kura_app:configure_pg_types/0 only runs when dialect is kura_dialect_pg.
  • kura_repo:read_config/1 returns a pass-through map of all kura env keys (minus bookkeeping). Previously hardcoded the PG-shaped subset and silently dropped pool_module, driver_module, backend.
  • kura_migrator skips pg_advisory_xact_lock when the configured pool doesn't declare the advisory_locks capability. The migration transaction alone serializes runs on backends that lack advisory locks (SQLite, etc.).
  • Single-repo legacy config (flat kura env keys) and the per-app form (application:get_env(OtpApp, RepoMod)) keep working unchanged. The global application:get_env(kura, dialect) is consulted as a fallback when a repo's config map omits dialect.

Removed (BREAKING)

  • kura_pool_pgo, kura_driver_pgo no longer ship in kura. Install kura_postgres for Postgres or kura_sqlite for SQLite. pgo is no longer a runtime dependency of kura. kura_dialect_pg remains in core as the default ANSI-ish SQL emitter that other dialects delegate to.
  • kura_query_compiler:dialect/0 no longer defaults to kura_dialect_pg. Use dialect/1 and configure the dialect per repo (recommended via {backend, ...}), or set the global application:set_env(kura, dialect, Module) for legacy single-dialect setups.
  • kura_db:get_pool_module/1 and get_driver_module/1 error with {no_pool_module_configured, _} / {no_driver_module_configured, _} when not configured. Set {backend, ...} (recommended) or pool_module/driver_module explicitly.

[1.8.0] - 2026-03-06

Changed

  • Breaking: kura_repo behaviour now requires otp_app/0 callback instead of config/0 - database configuration is read from application environment via application:get_env(OtpApp, RepoModule)
  • Optional init/1 callback on kura_repo - modify config at runtime (read secrets from env vars, files, external services)
  • kura_repo:config/1 function reads config from app env, then calls init/1 if defined
  • All internal get_pool/1 calls now use kura_repo:config/1 instead of RepoMod:config()

[1.7.0] - 2026-03-06

Added

  • telemetry library integration - every query emits a [kura, repo, query] telemetry event with measurements (duration, duration_us) and metadata (query, params, repo, result, num_rows, source)
  • build_telemetry_metadata/4 and extract_source/1 exported from kura_repo_worker for testing
  • source field in telemetry metadata - table name extracted from the SQL query

Changed

  • Legacy {kura, [{log, ...}]} config still works but now runs alongside telemetry events (not instead of)

[1.6.0] - 2026-03-06

Added

  • Ecto-style index support - indexes/0 optional callback on kura_schema for declaring indexes at the schema level
  • Map-based {create_index, Table, Cols, Opts} migration operation with auto-generated index names ({table}_{cols}_index)
  • kura_migration:index_name/2 helper for generating Ecto-style index names
  • Unique indexes declared via indexes/0 auto-register changeset constraints - no manual unique_constraint/3 calls needed
  • New types: index_def(), index_opts_map() in kura_migration

[1.5.0] - 2026-03-05

Added

  • Table-level constraints in migrations - {create_table, Table, Columns, Constraints} 4-tuple variant supporting {unique, [atom()]} and {check, binary()} inline constraints
  • constraints/0 optional callback on kura_schema behaviour - declares table-level constraints on the schema
  • Auto-registration of schema constraints on kura_changeset:cast/4 - duplicate inserts get friendly error messages without manual unique_constraint/3 calls

[1.4.0] - 2026-03-05

Changed

  • Breaking: Minimum OTP version is now OTP 28 (sigil syntax, ELP fixes)
  • Binary string literals converted to sigil syntax (~"...") across changeset modules
  • Eliminated unnecessary intermediate list and tuple allocations in association casting

[1.3.2] - 2026-03-05

Fixed

  • Dialyzer warnings in get_field/2,3 - maps:find/2 returns {ok, V} | error, not map patterns

  • Atom exhaustion risk in enum load/2 - now validates against declared values list instead of binary_to_atom
  • Atom exhaustion risk in kura_repo_worker error handlers - NOT NULL and constraint errors fall back to base atom for unknown columns
  • CI running resilience tests that require docker compose control - explicitly list test modules

Added

  • Production readiness test suites: kura_stress_tests (7 concurrency tests), kura_bench_tests (6 throughput benchmarks), kura_migration_stress_tests (4 migration safety tests), kura_resilience_tests (4 failure/recovery tests)
  • Makefile targets: test-stress, test-bench, test-migration-stress, test-resilience, test-production

[1.3.1] - 2026-03-04

Fixed

  • Advisory lock query wrapped to avoid pgo void type decode error (pg_advisory_xact_lock returns void)

[1.3.0] - 2026-03-03

Changed

  • Primary key derived from field flag instead of separate callback

[1.2.0] - 2026-03-01

Added

  • Changeset validators: validate_exclusion, validate_subset, traverse_errors, prepare_changes, optimistic_lock
  • Query extensions: subqueries in WHERE, CTEs, UNION/INTERSECT/EXCEPT, window functions (select_expr), query scopes
  • kura_sandbox - test transaction rollback for isolated concurrent tests
  • kura_stream - server-side cursor batching for large result sets
  • insert_all with RETURNING support

Changed

  • to_sql_from/2 refactored as keystone for all query compilation

[1.1.0] - 2026-02-27

Added

  • Foreign key constraints in migrations - references, on_delete, on_update fields on #kura_column{} generate inline REFERENCES "table"("col") ON DELETE CASCADE etc.
  • kura_changeset:validate_confirmation/2,3 - validates a field has a matching <field>_confirmation in params (for password/email confirmation flows)
  • kura_repo_worker:exists/2 - checks if any record matches a query, returns {ok, true | false}

  • kura_repo_worker:reload/3 - re-fetches a record from the database by its primary key

[1.0.4] - 2026-02-26

Fixed

  • ExDoc build failure - doc/architecture.md was wiped by edoc before ex_doc could read it; moved to guides/architecture.md
  • Removed doc from hex include_files (generated output, not source)

Added

  • ExDoc build step in CI to catch documentation errors early

[1.0.3] - 2026-02-26

Added

  • Rolling deployment safety warnings - detect dangerous migration operations (drop_column, rename_column, modify_column, drop_table, add_column NOT NULL without default) and log warnings before execution
  • Optional safe/0 callback on kura_migration behaviour to suppress warnings when the expand-contract pattern has been followed
  • Rolling deployments guide (guides/rolling_deployments.md) documenting the expand-contract pattern with step-by-step examples

[1.0.2] - 2026-02-24

Added

  • kura_preloader module - extracted preload logic from kura_repo_worker into a dedicated module
  • kura_changeset_assoc module - extracted cast_assoc, put_assoc, and association coercion from kura_changeset
  • Comprehensive integration tests (356 total) covering all query operators, aggregates, joins, bulk ops, multi pipelines, on_conflict upserts, constraint errors, type round-trips, and all association/preload types

Fixed

  • Constraint error handling (unique_constraint, foreign_key_constraint, check_constraint) now correctly unwraps pgo {pgsql_error, Map} tuples
  • put_assoc for many_to_many now preserves primary key in changeset data

[1.0.1] - 2026-02-22

Added

  • Embedded schemas guide (guides/embedded-schemas.md)
  • Architecture doc linked in hexdocs extras
  • Logo displayed in hexdocs
  • Navigation groups: Getting Started, Guides, Reference

Changed

  • Hidden internal modules (kura_app, kura_sup) from docs

[1.0.0] - 2026-02-22

Changed

  • Consolidated CI and release into a single GitHub Actions workflow
  • Updated README features list to reflect all capabilities added since 0.2.0
  • Fixed stale log_queries config reference in README (now log)
  • Fixed stale migration discovery docs in README (module-based since 0.3.0)

[0.5.0] - 2026-02-14

Added

  • Embedded schemas (embeds_one, embeds_many) stored as JSONB with cast_embed/2,3
  • Many-to-many associations with join_through / join_keys, preloading, and persistence via cast_assoc / put_assoc
  • Schemaless changesets - cast/4 accepts a types map for validation-only workflows
  • Makefile for local test lifecycle management (make test with Docker Compose)
  • Schemaless changesets guide (guides/schemaless-changesets.md)

[0.4.1] - 2026-02-14

Added

  • Hex documentation guides for enums, telemetry, and cast_assoc

[0.4.0] - 2026-02-14

Added

  • Enum type ({enum, [atom()]}) - stored as VARCHAR(255), cast/dump/load between atoms and binaries
  • Query telemetry via sys.config - {kura, [{log, true | {M,F}}]} with timing on every query

  • kura_changeset:cast_assoc/2,3 - nested changeset casting for has_many and has_one associations
  • kura_changeset:put_assoc/3 - directly attach maps or pre-built changesets as association changes
  • assoc_changes field on #kura_changeset{} record
  • Nested insert/update - automatic transaction wrapping with FK propagation when assoc_changes present
  • kura_repo_worker:build_log_event/5 and default_logger/0 exported for testability
  • Architecture documentation (guides/architecture.md)
  • Feature guides: enums, telemetry, cast_assoc (guides/)

Changed

  • Telemetry reads from application:get_env(kura, log) instead of repo config/0 map
  • Removed log_queries application env default (replaced by log)
  • kura_repo_worker:insert/2 and update/2 refactored to support nested association persistence

Removed

  • maybe_log/2 internal function (replaced by centralized emit_log/5)

[0.3.0] - 2026-02-14

Added

  • Module-based migration discovery (no filesystem scanning)
  • Xref checks in CI
  • rebar3_kura plugin reference in README

Removed

  • migration_paths configuration (migrations now discovered automatically from compiled modules)

[0.2.0] - 2025-02-14

Added

  • Constraint declarations (unique_constraint, foreign_key_constraint, check_constraint) with automatic PostgreSQL error mapping
  • kura_multi for atomic transaction pipelines with insert/update/delete/run steps
  • Association support (belongs_to, has_one, has_many) with preloading
  • Nested preloads
  • Standalone preloading via kura_repo_worker:preload/4
  • Query preloads via kura_query:preload/2
  • Upsert support (insert/3 with on_conflict option)
  • Bulk operations: insert_all/3, update_all/3, delete_all/2
  • kura_changeset:validate_change/3 for custom validation functions
  • kura_changeset:apply_action/2
  • Module documentation (-moduledoc / -doc) for all public modules
  • Usage guides: Getting Started, Changesets, Query Builder, Associations, Multi, Migrations
  • CI/CD with GitHub Actions
  • CHANGELOG

[0.1.0] - 2025-01-15

Added

  • kura_schema behaviour for defining database-backed schemas
  • kura_changeset for casting, validation, and change tracking
  • kura_query composable query builder
  • kura_query_compiler for parameterized SQL generation
  • kura_repo behaviour and kura_repo_worker for CRUD operations
  • kura_types type system (id, integer, float, string, text, boolean, date, utc_datetime, uuid, jsonb, arrays)
  • kura_migration behaviour and kura_migrator for DDL migrations
  • Automatic timestamps (inserted_at, updated_at)
  • Query logging via application env