{{$NEXT}}

    Features:
    - Volatile columns: a column whose stored value the database may set or change on write can be marked `volatile` (generated, identity, server-default, on-update, and trigger-set columns are auto-detected during introspection). QuickORM does not keep a stale value for one: a value you send is kept, but a value the database fills or changes is lazily fetched from the database on next access instead of trusting what was sent (a column that is both volatile and omitted is cleared after the write); use auto_refresh to read the whole row back at once. Tables can be marked volatile-free with `no_volatile` (DSL or `quick(no_volatile => [...])`) to assert no volatility and silence the per-table trigger warning; the connection can list the tables that have no volatile columns.
    - Column affinity is now derived from the database's own type catalog when a type name is not in the built-in name map: the numeric SQL type code is mapped to an affinity, so standard numeric/char/binary/date types no longer need a code patch. A type unknown even to the catalog warns once (asking for a ticket) and defaults to string affinity.

    Bug Fixes:
    - A trigger-driven column value is now read back consistently across databases: because a RETURNING clause is computed before AFTER triggers run, a table with triggers is detected during introspection (has_triggers) and its writes read the row back with a follow-up fetch instead of trusting RETURNING, matching how databases without RETURNING already behave. Trigger detection is implemented for SQLite, PostgreSQL, and MySQL/MariaDB.
    - Defining two tables (or two schemas, dbs, or servers) with the same name in one registry now croaks instead of silently letting the last definition win (re-opening an alt/variant frame is unaffected).
    - Importing DBIx::QuickORM with 'only' or 'skip' no longer strips the generated import()/builder() helpers, so a downstream `use My::ORM` keeps working.
    - An unknown import 'type' now croaks at use-time instead of being silently accepted.
    - `no DBIx::QuickORM` now removes DSL functions that were installed under a renamed name (via the 'rename' import option).
    - A database whose registered name contains a dot is now fetchable by that name; the server.db form is only taken when the leading segment names a defined server.
    - DSL argument parsing (index, column, link, plugins, autoskip, and the table class-path) no longer truncates its remaining arguments when an argument stringifies false, such as the literal '0'.
    - The autoname 'table' and 'link' hooks no longer corrupt other hooks registered for the same stage; the mutated table hashref / links arrayref is threaded through instead of a name string or undef.
    - Merging an introspected schema with a declaration that renames a physical table via db_name now collapses them into a single source under the declared ORM name, instead of leaving both the declared name and the database name behind.
    - A generated relationship accessor that collides with a column accessor is now caught even when the relationship is only introduced by the merged (declared) schema, rather than being silently dropped.
    - Table definitions now croak when a unique constraint or index references a column that does not exist, or when a link entry is not a Link object; undefined/expression index key-parts surfaced by introspection are ignored.
    - The cached row manager now amortizes a sweep of dead cache entries, so a workload that inserts an unending stream of distinct primary keys no longer grows memory unbounded.
    - A statement handle that outlives a connection reconnect no longer croaks when it finalizes: clearing an async/aside/fork entry from a registry that reconnect wiped is now a silent no-op.
    - Using a driver-level async statement handle after its connection has reconnected now croaks with a clear "invalidated by a database reconnect" message instead of a raw driver error, and such a handle finalizes cleanly at destruction without a stray warning; aside and forked handles run on their own connections and are unaffected.
    - A forked write whose child fails now warns from its destructor instead of throwing an exception out of it.
    - Reading a forked statement handle (next/result/ready) from a different process than the one that created it now croaks instead of stealing frames off the shared pipe.
    - SQLite introspection: a permanent table shadowed by a temporary table of the same name now keeps its own column, index, foreign-key, and DDL metadata instead of being poisoned with the temporary table's.
    - DuckDB generated-column detection now handles comma-bearing types like DECIMAL(10,2) and ignores GENERATED text inside string literals and comments, so generated columns are classified correctly.
    - DuckDB now clears its in-transaction flag even when a COMMIT or ROLLBACK is rejected, so a later transaction is no longer wrongly refused as already open, and the flag only tracks the dialect's own handle.
    - DuckDB supports_type now reports native names for text (TEXT) and varchar (VARCHAR).
    - JSON type deflation no longer requires an affinity argument and encodes the value directly.
    - Writes (insert/update/delete/cas) through a handle-as-subquery (derived-table) source now croak with a clear message instead of dying deep in statement construction or on a missing method.
    - omit() on a handle-as-subquery source now croaks (its columns are not enumerable) instead of silently returning the column it was told to omit.
    - handle() now croaks on an unknown -flag (e.g. a misspelled -allow_overide) instead of silently storing and ignoring it, which previously left the intended protection disabled with no diagnostic.
    - upsert() on an already-stored bound row now reports an "upsert" error instead of the shared insert body's "insert" wording.
    - Two columns that resolve to the same generated accessor name now croak during autorow generation instead of silently dropping one (matching the existing link-vs-link collision behavior).
    - Generated field lists (and thus SELECT column order) are now deterministic, ordered by each column's position instead of hash order.
    - and(), or(), and the join methods (left_join/right_join/inner_join/full_join/cross_join) now croak when called in void context, like the other query-building methods, instead of silently doing nothing.
    - Declaring a table's primary_key twice now croaks (pass { override => 1 } to intentionally replace it) instead of silently keeping only the last declaration.
    - Referencing a table class through view() (or a view class through table()) now croaks on the kind mismatch instead of silently building the wrong source type.
    - SQLite introspection no longer records a partial (WHERE ...) or expression unique index as a table-level unique constraint (which previously produced spurious desyncs or a garbage unique key).
    - Invalidating a row no longer emits a spurious "pending data" warning from a rolled-back frame, and resurrecting an invalidated row clears its stale invalidation reason.
    - An async single-row proxy now resolves boolean and ready() opportunistically, so a completed-empty result reads as false/ready without needing another method call first.
    - Autorow generation no longer silently swallows a compile error in a user's row/base class (it rethrows instead of substituting the stock Row), and builds the correct %INC key for multi-part class names.
    - Saving or deleting a join row now fans out to its sub-rows in a deterministic foreign-key-safe order (parents before children on save, children first on delete) instead of hash order.
    - An async/aside/forked statement handle inherited by a forked child no longer touches the shared driver socket or pipe when the child is destroyed, avoiding corruption of the owner's connection.
    - Reads on a handle bound to a row whose table has no primary key now croak instead of scanning the whole table (and possibly returning the wrong row).
    - Building a join from a non-table source (e.g. a literal/derived-table source) now croaks early instead of failing deep in join construction with obscure errors.
    - A hashref link spec without local_table resolved on a join now croaks with a clear message instead of dying on a missing name() method.
    - A bare (unqualified) field that exists in more than one joined component now reports a clean ambiguity instead of silently binding to the first match.
    - Inferring a join's from-side now detects an ambiguous table even when it is the primary source (self-join), and alias.field protos split on the first dot consistently; a caller-supplied join alias must be a valid identifier.
    - Resolving links on a join whose components link to the same third table via same-named columns no longer croaks; distinct links are kept and reported as a clean ambiguity.
    - A self-join no longer duplicates a link's aliases when merging its repeated links, fixing a false "Ambiguous link" error.
    - Join::Row field accessors now return undef for a LEFT-JOIN-missed sub-row and croak cleanly on an unknown alias instead of dying on an undefined value.
    - Join::Row is_valid now requires every sub-row to be valid, so a join row with an invalidated sub-row is no longer both valid and invalid.
    - A hashref link spec with an unknown key now croaks clearly instead of misreading the key as the other-table name.
    - Extending a cloned Join (re-joining a table already present) no longer corrupts the original join's alias lookup.
    - Committing or rolling back an outer transaction object from inside a nested transaction's action now croaks instead of silently resolving the inner transaction.
    - A blessed row_manager instance already in use by another live connection can no longer be silently rebound; it croaks instead of corrupting the first connection's state, but once that connection is disconnected the manager is free to rebind.
    - Connections now have an explicit disconnect() (and a connected() predicate), and $orm->disconnect actually disconnects the cached handle instead of only dropping the reference.
    - A database handle returned by a connect callback now gets the same default attributes (RaiseError, AutoCommit, etc.) as the DSN path instead of only AutoInactiveDestroy.
    - Committing a transaction whose rollback was refused by a guard now croaks instead of silently issuing a ROLLBACK while reporting success.
    - auto_retry_txn(action => sub {...}) (the two-element flat form) now retries as documented instead of running the action exactly once.
    - Cancelling an async query no longer tries to collect its result (which croaks) or run its on_ready callback after the cancel.
    - reconnect() after a fork no longer explicitly disconnects the handle inherited from the parent, which could tear down the parent's database session.
    - A forked query whose child dies before producing a result is now detected by ready() instead of hanging wait()/DESTROY forever.
    - Destroying a parent transaction while a child savepoint is still open no longer wedges the connection with a phantom open transaction.
    - A delete or update committed by a savepoint now correctly clears/updates the row state of the enclosing transaction instead of leaving stale stored data or a masked update.
    - merge_hash_of_objs no longer crashes when the second hash maps a key to an explicit undef while the first holds a reference.
    - Affinity detection now recognizes datetime, character, character varying, and smallserial so user-declared columns of those types no longer croak.
    - by_id() now rejects an undefined primary key value instead of silently degrading to a "primary key IS NULL" lookup that can never match.
    - Upserts on dialects without RETURNING support no longer corrupt a literal write value that contains the word "returning".
    - DSL variants, coderef defaults, and table-class metadata now preserve the intended user values.
    - The DSL exports connect/index/socket shadow the Perl built-ins in the importing package; a call shaped like the built-in now croaks with a hint to use CORE:: instead of silently misrouting into the builder.
    - JSON deflate no longer dies on top-level booleans and now honors TO_JSON for blessed values.
    - Inserts that only provide database-generated columns now croak cleanly instead of emitting invalid empty INSERT SQL.
    - Bulk deletes on primary-key-less tables now skip cache maintenance instead of failing before the delete runs.
    - cas() now croaks on unsupported limit, offset, order_by, and distinct clauses instead of silently ignoring them.
    - cas() guard warnings now inspect structured where guards without treating operator keys as column names.
    - Bulk update/delete diagnostics now describe deferred-handle restrictions correctly and fix a garbled internal-transaction message.
    - Table merges now croak when column aliasing would map two database columns to one ORM field instead of silently dropping one.
    - Declared links now treat a target table's primary key as unique even when the key is not also listed in the table's unique constraints.
    - Merging duplicate links now preserves uniqueness when either link knows the relationship points to one row.
    - Link parsing now keeps unique and aliases options from confusing single-key table shorthand.
    - Link cloning now honors an explicit created override instead of always dropping created state.
    - MySQL-family CAS reliability checks now inspect the real MariaDB found-rows attribute name.
    - SQL type affinity detection now recognizes PostgreSQL int/float aliases and DuckDB unsigned and huge integer types as numeric.
    - Column affinity validation now rejects invalid explicit affinities and avoids caching dialect-dependent type-object affinities across dialects.
    - Columns declared with type class names now keep using those classes for field inflate/deflate.
    - Schema merge utilities now preserve scalar and falsey hash values while cloning nested metadata.
    - UUID comparisons now treat matching non-UUID raw values as equal instead of croaking during desync checks.
    - Plugins without a custom munge hook now work as no-op plugins instead of failing every build.
    - Lazy field fetches inside transactions now roll back with the transaction instead of leaking into the row's base state.
    - CAS field-list guards now croak when the guarded field was not fetched instead of treating it as NULL.
    - by_id() now validates primary-key arguments, honors extra hash constraints on loaded rows, and returns hashrefs in data_only mode even on cache hits.
    - Upserts now preserve RETURNING clauses when literal write SQL contains the word "returning".
    - Handle omit() can now append to an existing omit set, and all_fields() now clears any previous omit.
    - Columns declared with blessed type instances now use those instances for field inflate/deflate.
    - Table names are now quoted in FROM clauses, fixing queries against reserved-word or mixed-case table names.
    - Non-RETURNING inserts now preserve caller-supplied primary keys instead of replacing them with last_insert_id().
    - JSON typed columns can now read back scalar JSON values written by the ORM, such as strings and numbers.
    - Masked inflated values now delegate numeric comparisons to the wrapped object instead of forcing direct numification.
    - Row-bound handle operations now reject rows from a different source or connection instead of applying their primary keys to the wrong query.
    - update() now validates desync on non-updated fields before staging, preventing a failed update from leaving its changes armed in PENDING.
    - Passing a non-object as `row` to a handle now croaks with the ORM's own message instead of dying on an unblessed-reference method call.
    - The async-mode croak in all() now correctly directs users to iterator() instead of the sync-only iterate().
    - Row _field() no longer rejects a column named "0" due to a truthiness check on the field name.

0.000027  2026-07-01 11:18:00-07:00 America/Los_Angeles

    Features:
    - A handle passed to handle() is now used as a query source: the inner query is spliced in as a derived table (aliased "subquery" by default, or via subquery_alias($alias)), its binds are threaded into the outer statement, and its real columns keep their types.

    Bug Fixes:
    - Autofill now croaks at schema-build time when two generated relationship accessors (or a relationship and a column accessor) would resolve to the same name, instead of silently dropping one; resolve with distinct link aliases or an autoname hook.
    - Scalar-ref literal write values (e.g. \'NOW()') and literal-with-bind values ([\'expr ?', @binds]) in insert/update/upsert/cas are now emitted as SQL instead of being silently stored as a stringified reference; the row's cached value is recovered via RETURNING where supported, otherwise a refresh.

    INCOMPATIBLE CHANGES:
    - Passing an existing handle to handle() now wraps it as a subquery source instead of reusing/refining it; to refine a handle without wrapping it, call methods like where() on the handle directly (they return a refined clone).

0.000026  2026-06-29 17:41:35-07:00 America/Los_Angeles

    Security:
    - Quote all SQL identifiers so caller-supplied names (order_by, where keys, field/returning lists, upsert columns, and join aliases) can no longer break out into SQL injection (CVE-2026-13766).

    INCOMPATIBLE CHANGES (from the CVE-2026-13766 fix):
    - SQL identifiers are now quoted, changing two caller-facing behaviors:
      - order_by/field bare strings must be column names, not raw SQL.
      - A bare "name DESC" or "COUNT(*)" no longer works; use \'...' or {-desc => 'name'}.
      - LiteralSource croaks on a non-identifier 'subquery' alias.

0.000025  2026-06-28 10:59:16-07:00 America/Los_Angeles

    Internal / cleanup:
    - Backfill the missing changelog entries for the 0.000023 and 0.000024 releases.

0.000024  2026-06-28 09:48:16-07:00 America/Los_Angeles

    Features:
    - Add compare-and-set via cas() on handles and rows: a guarded single-row update that reports a lost race instead of throwing (GitHub #29).

    Documentation:
    - Document compare-and-set and batch schema introspection in the Manual.

    Internal / cleanup:
    - Refactor schema introspection to run in a fixed number of queries per database across all dialects.

0.000023  2026-06-13 22:20:26-07:00 America/Los_Angeles

    Features:
    - Add OFFSET and DISTINCT support to handles.
    - Support link-less CROSS joins with validated join arguments.
    - Add a primary_key override option to the DSL builder.
    - Add the async contract to the base Dialect class.

    Documentation:
    - Record the connection/transaction and row/schema/handle audit changes as ARCHITECTURE.md addenda.

    Internal / cleanup:
    - Audit and harden the connection, transaction, handle, dialect, and row-state subsystems.
    - Harden the forked-query pipe protocol, process ownership, and parent row cache.
    - Fast-destroy ephemeral test databases to quiet teardown under prove -j.

    Fixes:
    - Fix savepoint frame merges leapfrogging rolled-back transactions.
    - Make a refused transaction finalize recoverable and croak on double-finalize.
    - Fix count() on handles with omit, joins, or distinct.
    - Treat LIMIT 0 as a real limit instead of dropping it.
    - Allow order_by/limit on a handle without a WHERE clause.
    - Quote primary-key identifiers in upserts and quote join field aliases.
    - Scope PostgreSQL introspection to the search_path and fix identifier quoting.
    - Fix SQLite recording plain indexes as unique constraints.
    - Mark SQLite rowid-alias columns as identity and fix AUTOINCREMENT false positives.
    - Croak on conflicting primary keys in Table::merge unless overridden.
    - Croak when saving desynced rows or vivifying an already-loaded row.
    - Restore find-or-create with a data-loss warning.
    - Emit database= in MySQL-family DSNs.
    - Fix the row cache key escaping, undef primary-key handling, and dead-entry cleanup.
    - Harden Connection reconnect and rebuild the dialect on reconnect.

0.000022  2026-05-28 13:26:49-07:00 America/Los_Angeles

    Bug fixes:
    - SQLite dialect now queries sqlite_master/sqlite_temp_master instead of the modern sqlite_schema/sqlite_temp_schema aliases (which require SQLite 3.33+, 2020). Bump DBD::SQLite minimum to 1.70, which embeds SQLite 3.36 and is the first bundled version with the RETURNING clause (SQLite 3.35) the dialect emits; this is also well past pragma_table_xinfo's `hidden` flag for GENERATED columns (SQLite 3.31, relied on by generated-column detection added in 0.000021).

    Internal / cleanup:
    - Audit dist.ini prereq minimums against actual feature usage. Bump pinned versions where the code uses features that require a specific minimum (List::Util zip/mesh -> 1.56, Sub::Util set_subname -> 1.40, Data::Dumper Trailingcomma -> 2.171, Role::Tiny -> 2.000003, Cpanel::JSON::XS -> 3.0224, etc.). Core / basic-usage modules left at 0.

0.000021  2026-05-27 20:30:06-07:00 America/Los_Angeles

    Features:
    - Detect database-generated columns (stored or virtual GENERATED ALWAYS AS) at autofill time across SQLite, PostgreSQL, MySQL/MariaDB, and DuckDB. Generated columns are readable on fetch but excluded from INSERT/UPDATE: handle-level insert/upsert/update silently drop them, row->field and row->update croak.

    Documentation:
    - Add Manual::SQLBuilder, documenting the SQL builder contract and how to write a custom builder.

0.000020  2026-05-25 22:18:44-07:00 America/Los_Angeles

    Features:
    - Add DBIx::QuickORM->quick(), a DSL-free interface returning a ready ORM/connection.
    - quick() gains autorow, row_manager, and auto_types options.
    - Add a DuckDB dialect (DBIx::QuickORM::Dialect::DuckDB), auto-selected for DBD::DuckDB.
    - Add Type::DateTime, a lazy DateTime type with database-string stringification.
    - Add Util::Mask, a lazy object-hiding wrapper that keeps values out of stack traces.
    - Add a subquery option to LiteralSource to wrap a full statement as a derived table.
    - Use Atomic::Pipe with zstd compression for forked-query IPC.

    Documentation:
    - Rebuild the Manual: hub plus QuickStart, Concepts, guides, recipes, features, and a DBIx::Class concept-map page.
    - Document the DateTime type and value masking in Manual::Types.
    - Complete and annotate the DSL export reference; move examples into the Manual.
    - Add inline POD across nearly all modules.

    Internal / cleanup:
    - Drop the inlined HashBase and use Object::HashBase directly.
    - Make transaction state authoritative from the result; add an exception attribute.
    - Resolve links by keyword (table/alias/columns) and drop the broken scalar-ref spec.
    - Remove the vestigial, never-implemented schema->DDL generation scaffolding from Dialect.
    - Align declared prereqs with actual usage.
    - Default do_for_all_dbs test concurrency to 4.
    - Exclude dev-only files (old*/, agent_scripts/, AI_DOCS/, *.md) from the build tarball.
    - Add [PruneCruft] to dist.ini so build artifacts no longer leak into release tarballs.

    Fixes:
    - Fix compare_affinity_values: boolean comparison was inverted; handle both-undef without warnings.
    - Fix Link::merge clobbering the created note and aliases of the merged link.
    - Fix Row field-hash accessors passing the wrong view method.
    - Fix Connection::state_cache_lookup to honor its documented arguments.
    - Fix LiteralSource::new: copy the SQL instead of blessing the caller's scalar ref.
    - Fix UUID and JSON types to use defined checks instead of truthiness for values.
    - Fix Autofill define_autorow to use the saved error instead of $@ directly.
    - Quote savepoint names in MySQL and SQLite transaction statements.
    - Fix the forked-query child to exit non-zero on error and reap it to avoid zombies.
    - Fix STH::Fork::cancel to handle an already-exited child gracefully.
    - Fix STH::next to finalize before throwing on too-many-rows.
    - Fix STH::Async/STH::Fork result() to use exists instead of truthiness.
    - Fix Transaction DESTROY to roll back (not commit) when falling out of scope.
    - Fix creds() to validate the builder context before invoking the credential callback.
    - Fix Connection update_or_insert and find_or_insert delegating to nonexistent methods.
    - Fix Cached::uncache to extract values from the primary-key hashref.
    - Fix Cached::uncache and _merge_state to read existing state from the target row.
    - Fix PostgreSQL build_tables_from_db to use the current table name after hooks.
    - Fix MySQL build_table_keys_from_db and build_indexes_from_db argument handling.
    - Fix Table::merge to fall back to the other side's primary key when missing.
    - Fix Column affinity() to return after the scalar-ref-type branch.
    - Fix not_null to default nullable to 0 when called with no arguments.
    - Fix Row::Async::can() and check_sync after the row swaps itself out.
    - Fix Iterator to use the READY() constant for its hash key.
    - Fix Handle -unknown flag comparison and fork child error exit.
    - Fix transaction stack mismatch precedence and undef-safe state comparisons.
    - Fix merge_hash_of_objs default params and value dereference.
    - Fix _normalize_omit to build a hashref instead of a scalar-context map count.
    - Fix Join::Row missing List::Util import; JSON qorm_sql_type reachable die.
    - Fix unimport_from to remove exported subs; typo in a Dialect error message.

0.000019  2025-08-22 16:17:34-07:00 America/Los_Angeles

    - Add report test

0.000018  2025-08-22 15:08:58-07:00 America/Los_Angeles

    - Fix improper use of roles

0.000017  2025-08-22 14:40:01-07:00 America/Los_Angeles

    - Add more context for conflation methods

0.000016  2025-08-22 12:01:33-07:00 America/Los_Angeles

    - Fix #25, add Carp::Always to test and develop deps
    - Fix #26, Add Parallel::Runner to deps, fix interface.t when db is missing
    - Fix #27, Fix upsert with multiple fields

0.000015  2025-06-13 17:37:50-07:00 America/Los_Angeles

    - Make it possible to add DB info after prepaing the orm/schema

0.000014  2025-06-10 14:46:48-07:00 America/Los_Angeles

    - Doc updates

0.000013  2025-06-08 21:54:10-07:00 America/Los_Angeles

    - Doc updates

0.000012  2025-06-08 14:47:28-07:00 America/Los_Angeles

    - Add versions to all modules
    - Add upsert support

0.000011  2025-06-07 15:36:38-07:00 America/Los_Angeles

    - Rename select to query
    - Rename sqla-source to query-source
    - Work on eliminating the assumption that SQLAbstract will always do the work

0.000010  2025-05-07 06:36:38-07:00 America/Los_Angeles (TRIAL RELEASE)

    - Still felshing out functionality after the rewrite
    - Add 'forked' async emulation
    - Add 'aside' async functionality
    - Add more txn callbacks

0.000009  2025-05-06 08:17:08-07:00 America/Los_Angeles (TRIAL RELEASE)

0.000008  2025-05-02 01:20:14-07:00 America/Los_Angeles (TRIAL RELEASE)

0.000007  2025-05-02 01:04:21-07:00 America/Los_Angeles (TRIAL RELEASE)

0.000006  2025-05-02 01:02:32-07:00 America/Los_Angeles

0.000005  2025-05-02 00:19:35-07:00 America/Los_Angeles (TRIAL RELEASE)

0.000004  2024-10-27 23:40:03-07:00 America/Los_Angeles

    - Fix some DB specific tests
    - Add report on module and database versions

0.000003  2024-10-27 09:33:28-07:00 America/Los_Angeles

    - Add missing deps
    - Include minimum versions for some deps

0.000002  2024-10-26 21:01:35-07:00 America/Los_Angeles

    - Fix #3
    - Some doc fixes
    - Fix some tests

0.000001  2024-10-26 00:33:57-07:00 America/Los_Angeles

    - Initial upload of incomplete project to cpan
