NAME
DBIO::PostgreSQL::Age::Storage - PostgreSQL storage with Apache AGE graph support
VERSION
version 0.900001
SYNOPSIS
# Loaded automatically via DBIO::PostgreSQL::Age component.
# Use connect_call_load_age to initialize AGE on each connection:
MyApp::Schema->connect(
$dsn, $user, $pass,
{ on_connect_call => 'load_age' },
);
my $storage = $schema->storage;
$storage->create_graph('social');
my $rows = $storage->cypher(
'social',
$$ MATCH (a:Person {name: $name})-[:KNOWS]->(b) RETURN b.name $$,
['friend'],
{ name => 'Alice' },
);
$storage->drop_graph('social', 1); # cascade
DESCRIPTION
A storage layer that adds Apache AGE graph database support -- connection initialization, graph lifecycle management, and Cypher query execution -- to a PostgreSQL storage. It is not a storage subclass: it is a plain method package composed over the resolved driver storage at connection time (see DBIO::Storage::Composed). DBIO::PostgreSQL::Age registers it via "register_storage_layer" in DBIO::Schema, so on $schema->connect the live storage isa both this layer and DBIO::PostgreSQL::Storage, and the methods below are callable on that composed storage. Because it is a layer, it stacks cleanly with other extension layers (its methods are disjoint from, e.g., DBIO::PostgreSQL::PostGIS::Storage).
Layer rules
Per the storage-layer composition model (DBIO core, karr #70): this package does not use base a driver storage, defines no constructor, and calls only the documented public storage surface -- dbh_do and throw_exception for the graph methods, plus the connect-action seam _do_query for connect_call_load_age -- each resolved through the composed MRO to the driver base at runtime. _do_query is deliberate: it is the same seam connect_call_do_sql uses, and it is what routes the LOAD 'age' replay onto each freshly-spawned async pool connection (core karr #68); a bare $self->dbh->do would run against the sync dbh instead and defeat the replay. The pure helpers _cypher_sql_bind and decode_agtype are DB-free class-level helpers, shared by composition with the async layer (DBIO::PostgreSQL::Age::Storage::Async) so sync and async build identical SQL and decode identically.
All result columns from cypher() are declared as agtype — Apache AGE's JSON-superset type that represents vertices, edges, paths, and scalar values. Values are returned as strings and can be decoded with a JSON parser.
METHODS
connect_call_load_age
{ on_connect_call => 'load_age' }
Connection callback that loads the Apache AGE shared library into the session and sets search_path to include ag_catalog. Must be called before any graph operations.
create_graph
$storage->create_graph('social');
Creates a new Apache AGE graph with the given name.
drop_graph
$storage->drop_graph('social');
$storage->drop_graph('social', 1); # cascade
Drops the named graph. Pass a true second argument to cascade the drop to all vertices and edges within the graph.
cypher_async
create_graph_async
drop_graph_async
The async counterparts of "cypher", "create_graph" and "drop_graph", reachable on the live (composed) storage. Each dispatches through core's DBIO::Storage/_run_async: on an async connection ({ async => 'future_io' }, 'ev', ...) they route to the embedded async backend (the composed DBIO::PostgreSQL::Age::Storage::Async layer, which does the real non-blocking work); under { async => 'immediate' } they run the sync method in-process and return an immediately-resolved Future with no event loop; on a plain sync connection they croak. This mirrors the core CRUD *_async methods ("select_async" in DBIO::Storage and friends) -- graph queries have a sync equivalent, so they degrade the same way rather than being backend-only.
decode_agtype
my $name = $storage->decode_agtype('"alice"'); # "alice"
my $age = $storage->decode_agtype('30'); # 30
my $v = $storage->decode_agtype(
'{"id": 1, "label": "Person", "properties": {"name": "alice"}}::vertex'
); # { id => 1, label => "Person", properties => { name => "alice" } }
my $rows = $storage->cypher(
'social',
$$ RETURN n $$,
['n'],
);
my $vertex = $storage->decode_agtype($rows->[0]{n}); # manual decode
Decodes a single agtype text value (as returned by cypher()) into native Perl data. Recognised shapes:
String scalar (quoted, e.g.
"alice") — string with quotes strippedInteger / float scalar (e.g.
42,3.14) — Perl numberBoolean (
true,false) —JSON::MaybeXStrue/false objectsNull (
null) —undefMap / list (e.g.
{"name": "alice"},[1, 2, 3]) — hashref / arrayrefVertex / edge — same as the underlying map; a trailing
::vertex/::edgecast annotation (only emitted by older AGE versions) is stripped before decoding.id,label,start_id,end_id,propertiesare preserved as JSON keys.Path — arrayref of decoded vertices and edges (structure preserved, not unwrapped)
Anything else — returned as-is so the caller can post-process it
cypher
my $rows = $storage->cypher(
'social',
$$ MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name $$,
[qw( person friend )],
);
# With Cypher parameters:
my $rows = $storage->cypher(
'social',
$$ MATCH (n:Person {name: $name}) RETURN n $$,
['node'],
{ name => 'Alice' },
);
# Auto-decode each cell into native Perl data:
my $rows = $storage->cypher(
'social',
$$ MATCH (n:Person {name: $name}) RETURN n $$,
['node'],
{ name => 'Alice' },
{ auto_decode => 1 },
);
# $rows->[0]{node} is now a hashref, not a string.
Executes a Cypher query against the named graph. $columns is an arrayref of result column names; all are declared as agtype. Returns an arrayref of hashrefs with one key per column.
An optional $params hashref is JSON-encoded and passed as AGE's third argument to cypher() for parameterized queries.
Pass an optional fifth argument, a hashref of options, to control result handling:
auto_decode => 1— apply "decode_agtype" to every cell of every row before returning. Without this option, every cell is a raw agtype string and decoding is the caller's responsibility.
DBIO::PostgreSQL::Age - Schema component that activates this storage
AUTHOR
DBIO Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 259:
alternative text 'DBIO::Storage/_run_async' contains non-escaped | or /
- Around line 357:
Unknown directive: =seealso