Revision history for DBIO-PostgreSQL

0.900001  2026-07-12
    * Storage
        - Add DBIO::PostgreSQL::Storage::Async — the first REAL future_io async
          transport (core ADR 0030/0031, karr #25). connect(..., { async =>
          'future_io' }) now resolves it BY CONVENTION (ref($storage).'::Async',
          no registration) and runs genuinely non-blocking SELECT / INSERT /
          txn_do over DBD::Pg's pg_async binding driven through Future::IO. The
          Model-B orchestration is inherited from DBIO::Async::Storage; this
          class fills only the DB-specific transport seams (submit via
          {pg_async => PG_ASYNC}, collect via pg_ready/pg_result, socket fd via
          pg_socket, ?->$N placeholders, RETURNING * for the returned-columns
          hashref). dbio-async is an optional recommends -- only the future_io
          mode needs it, resolved by convention per ADR 0030, so a sync-only
          install never pulls the async stack; the live roundtrip test
          additionally wants a Future::IO::Impl (IO::Async recommended).
        - Register the 'ev' async mode (core ADR 0030): connect(..., { async => 'ev' })
          now resolves DBIO::PostgreSQL::EV::Storage via the core mode registry;
          this shadows the generic modes registered on DBIO::Storage::DBI and
          replaces the obsolete async_backend() / load_components('PostgreSQL::EV')
          opt-ins. Requires the optional dbio-postgresql-ev dist at first use;
          absent → the canonical "install DBIO::PostgreSQL::EV::Storage" croak
          (ADR 0030). The dist stays optional — no cpanfile dependency is added.
        - Reference-driver compliance for the storage-layer composition model
          (karr #28, core #70): storage_type is written once in the Schema
          component's connection(); extensions register plain storage LAYERS
          (composed via DBIO::Storage::Composed) instead of subclassing
          storage_type. _transform_sql stays the single ?->$N rewrite, run once
          inside the transport (no double-shaping); transport_capabilities =>
          (on_connect_replay), the ev transport carries LISTEN/NOTIFY/COPY/
          pipeline and future_io fails loud when a layer needs them. No
          behavioural change -- the driver's role is unchanged.

    * Diff
        - Fix DROP INDEX / DROP TABLE CASCADE collision in full-schema sync
          (karr #32). When diff reconciled a live schema containing a table
          absent from the target (removed from the app's DBIO classes, or an
          unmanaged leftover), it emitted both DROP TABLE <t> CASCADE and a
          later standalone DROP INDEX <t>_pkey; since CASCADE already dropped
          the table's own indexes, the standalone DROP INDEX died with
          "index ... does not exist" and aborted apply()/upgrade().
          DBIO::PostgreSQL::Diff::Index now receives the tables sections (via
          the aux-section wiring in DBIO::PostgreSQL::Diff) and suppresses a
          standalone DROP INDEX for any index whose owning table is itself
          being dropped in the same pass. Indexes of surviving tables are
          still dropped as before. Scoped to this driver; the same class of
          bug likely exists in sibling drivers' Diff/Index equivalents.

    * Documentation
        - Add docs/adr/0030-async-mode-registration.md — per-repo operationalisation
          of the core async mode registry mechanism for the PostgreSQL driver
        - Document the { async => 'ev' } opt-in in DBIO::PostgreSQL::Storage POD

    * Security
        - Add SECURITY.md following the CPAN Security Group author guidelines
          (v1.5.0): email-first private reporting to the maintainer with CPANSec
          escalation, and a note that Codeberg has no confidential issue channel.

    * Dependencies
        - requires DBIO 0.900001 (family-aligned).
        - DBIO::Async, the shared future_io backend base, is deliberately
          NOT declared in cpanfile (karr #27): the sync driver loads
          without it; only { async => 'future_io' } pulls it in, resolved
          lazily by convention (ADR 0030). Core's future_io resolver now
          names the missing module and the distribution that provides it
          when the adapter can't load (core karr #78), so a separate
          recommends pointer is no longer needed.

    * Tests
        - Drop references to removed modules so the suite passes on fresh
          installs: DBIO::PostgreSQL::Loader/::Loader::Model (removed with
          the generation contract) and DBIO::AccessBroker::Credentials
          (replaced in core by DBIO::AccessBroker::Static); the
          access-broker test now uses Static (Codeberg #1)
        - Make the generated-code assertions in both t/25 variants
          (-via-introspect and -live) quote-agnostic to match the
          B::perlstring double-quoted emitter output; the live variant's
          table/set_primary_key/pg_schema checks were missed in the earlier
          consolidation and failed against a real database (karr #30)
        - Expand t/35-async-mode.t: assert the 'ev' registration does not
          shadow the base 'immediate' mode, unknown modes resolve to undef,
          and an absent dbio-postgresql-ev croaks with the install hint
        - Add t/36-future-io-async.t (offline): the core resolver resolves
          future_io to DBIO::PostgreSQL::Storage::Async by convention, the
          adapter isa DBIO::Async::Storage, every transport seam is overridden
          (none left croaking), and the ?->$N / RETURNING * shaping is correct
        - Add t/37-future-io-live.t (gated on DBIO_TEST_PG_*): a live
          non-blocking future_io roundtrip — select_async / insert_async /
          txn_do_async (commit + rollback), insert_async yielding the
          returned-columns hashref with the autoinc PK via RETURNING, a raw
          ->then Future chain, and the high-level create_async / all_async API
        - Add t/38-storage-layer-composition.t (offline structural, 19
          assertions) and t/39-storage-layer-composition-live.t (live-gated,
          skips cleanly): a storage LAYER composes over DBIO::PostgreSQL::Storage
          and its ::Async mirror over DBIO::PostgreSQL::Storage::Async under
          { async => 'future_io' } — the walk resolves the driver transport off
          the composition base, not the layer mixin (karr #28)
        - xbin/dbio-pg-k8s: use the official postgres:18 image (src.ci retired)
        - Add t/40-diff-drop-table-index-cascade.t and extend t/05-diff-index.t:
          full-diff regression for the DROP INDEX / DROP TABLE CASCADE collision
          (karr #32) plus Diff::Index unit cases for the new tables-aware drop
          suppression, including an over-suppression guard (a surviving table's
          index is still dropped standalone)

0.900000  2026-06-23
    First release. The PostgreSQL driver for DBIO: PostgreSQL-native
    storage, SQL generation, schema introspection, diff, DDL and
    desired-state deployment.

    * Storage
        - DBIO::PostgreSQL::Storage, registered for dbi:Pg: DSNs and
          autodetected by core via the driver registry
        - Loadable as a schema component (load_components('PostgreSQL'))
          or via the use DBIO Schema => -pg shortcut, which pins
          +DBIO::PostgreSQL::Storage as the storage type
        - RETURNING on INSERT/UPDATE/DELETE, last_insert_id via sequences,
          AccessBroker connect support
        - DBIO::PostgreSQL::Result component and the -pg Result shortcut
          for PostgreSQL-native column/source features (schemas, indexes)

    * SQL Generation
        - DBIO::PostgreSQL::SQLMaker with native JSONB operators:
          @> / <@ containment (auto-serializes hashref/arrayref to JSON),
          @? / @@ JSONPath (PG 12+), and ? / ?| / ?& key existence
          (rewritten as jsonb_exists*() to avoid the DBI placeholder clash)
        - DBIO::PostgreSQL::JSONB jsonb() path-expression DSL:
          eq/ne/lt/le/gt/ge/like/ilike/is_null/is_not_null/as_order

    * Introspection
        - DBIO::PostgreSQL::Introspect builds a live model from pg_catalog:
          schemas, tables, columns, indexes, sequences, foreign keys,
          check constraints, functions, triggers, types/enums, extensions
          and row-level-security policies

    * Diff and DDL
        - DBIO::PostgreSQL::Diff compares two introspected models per
          object class (Schema/Table/Column/Index/Type/Function/Trigger/
          Policy/Extension) and emits the ALTER/CREATE/DROP statements
        - DBIO::PostgreSQL::DDL generates PostgreSQL-native DDL from the
          schema, including a CREATE TYPE enum pre-pass, schema-qualified
          CREATE VIEW for non-virtual views, and physical-table dedupe

    * Deploy
        - DBIO::PostgreSQL::Deploy orchestrates test-deploy-and-compare:
          introspect live, deploy desired to a temporary schema,
          re-introspect, diff, then drop the temp schema; supports
          install, diff, apply and upgrade
        - Storage declares dbio_deploy_class so DBIO::Schema->deploy()
          routes through DBIO::PostgreSQL::Deploy rather than
          SQL::Translator
