Revision history for DBIO-PostgreSQL-Age

0.900001  2026-07-12
    * Storage
        - AGE now composes onto any schema as a storage LAYER
          (register_storage_layer) instead of setting storage_type
          directly, so it stacks with sibling extensions (e.g. PostGIS)
          on one schema and runs on every async transport that can
          replay LOAD 'age' -- future_io and ev today. Age::Storage and
          Age::Storage::Async are now plain layers (no more `use base`
          on the PostgreSQL sync/async storage classes); the async
          layer is composed by core over whatever transport the mode
          resolves (future_io -> PostgreSQL::Storage::Async, ev ->
          EV::Storage) instead of hard-inheriting the future_io
          adapter. cypher_async / create_graph_async / drop_graph_async
          are dispatched from the sync side via core's _run_async (like
          core CRUD *_async: route to the backend on an async
          connection, degrade in-process under { async => 'immediate' },
          croak on a sync connection); _cypher_sql_bind and
          decode_agtype are shared by composition, never by inheriting
          the sync DBI storage. The '?' seam is transport-internal
          (core #70/ADR 0032): no caller-side _transform_sql call
          remains, and the $$...$$ cypher body / $name refs survive
          unshaped. required_transport_capabilities => (on_connect_replay)
          is declared and enforced by core's capability gate. LOAD 'age'
          + search_path setup rides the core pool on_connect-replay seam
          (karr #68) on every freshly spawned pool connection, on every
          transport -- no adapter-local LOAD hack. karr #5, #6, core #70.
        - New decode_agtype() decodes agtype scalars, maps, lists,
          vertices (id/label/properties), edges (id/label/start_id/
          end_id/properties) and paths, including older-AGE-version
          fallback when the ::vertex/::edge/::path type annotations are
          absent. cypher() (and cypher_async) accept an { auto_decode =>
          1 } option that runs it over every cell of every row; 4-arg
          calls are unaffected.

    * Fixes
        - DBIO::PostgreSQL::Age::Storage no longer registers itself as the Pg
          driver. The registration clobbered DBIO::PostgreSQL's entry, so any
          plain dbi:Pg: connection made after the AGE module was loaded would
          rebless into AGE storage and run graph SQL against a non-AGE database.
          AGE is activated explicitly via the component (which registers the
          Age storage layer), so the global registration was never needed.
          karr #2.

    * Tests
        - t/30-registry.t pins that loading AGE storage leaves the Pg
          driver resolving to the plain PostgreSQL storage
        - Expanded live/offline coverage to 141 tests: agtype decoding
          across every shape, an idempotent create_graph + MERGE deploy
          pattern, WHERE/ORDER BY/SET/REMOVE/MERGE/OPTIONAL MATCH/WITH +
          count()/variable-length-path Cypher coverage, layer stacking
          with PostGIS on one schema, dual-mode coexistence (ev +
          future_io instances on one schema class resolving distinct
          composed backends, ADR 0030), and immediate-mode smoke
          coverage. docker-compose and k8s test paths moved off the
          retired private src.ci/srv/postgres image onto the public
          apache/age image (postgres 18 + AGE 1.7); k8s/test.sh now
          bails early on a port already in use or a port-forward that
          never comes up, instead of reporting a false PASS against a
          dead socket.

    * Dependencies
        - Requires core DBIO and DBIO::PostgreSQL >= 0.900001
          (composition-carrying versions); DBIO::PostgreSQL::EV and
          DBIO::PostgreSQL::PostGIS are develop-recommends only (the
          offline suite gates on them and skips cleanly when absent)

    * Project
        - Added SECURITY.md (CPAN Security Group guidelines v1.5.0)

0.900000  2026-06-23
    First release. Apache AGE graph database support layered on DBIO::PostgreSQL.

    * Schema component
        - DBIO::PostgreSQL::Age, loaded via load_components('PostgreSQL::Age'),
          sets storage_type to DBIO::PostgreSQL::Age::Storage

    * Storage
        - DBIO::PostgreSQL::Age::Storage extends DBIO::PostgreSQL::Storage and
          registers for the Pg driver
        - connect_call_load_age connection callback: LOAD 'age' and set
          search_path to include ag_catalog on each connection

    * Graph lifecycle
        - create_graph($name) creates a named graph
        - drop_graph($name, $cascade) drops a graph, optionally cascading to
          all vertices and edges

    * Cypher queries
        - cypher($graph, $query, \@columns, \%params) executes openCypher and
          returns arrayrefs of hashrefs; result columns are declared as agtype
        - graph name validated as a plain identifier and inlined as a SQL
          literal, since Apache AGE requires the first cypher() argument to be
          a name constant rather than a placeholder
        - optional params hashref JSON-encoded and passed as AGE's third
          cypher() argument for parameterized queries
        - SQL/bind generation split into a pure _cypher_sql_bind helper for
          offline unit testing

    * Tests
        - t/00-load.t module load, t/20-cypher.t offline cypher SQL/bind
          coverage, t/10-age-live.t live graph create/drop and CREATE/MATCH
          queries (skips without a configured AGE-enabled PostgreSQL)

