NAME
DBIO::MySQL::EV::Storage - Async MySQL/MariaDB storage driver using EV::MariaDB
VERSION
version 0.900001
DESCRIPTION
The native ev async transport for MySQL/MariaDB (core ADR 0030 / 0031): a thin transport over EV::MariaDB, a non-blocking client that speaks the MariaDB C client library directly. No DBI, no DBD::MySQL, just raw MariaDB performance.
It resolves as the ev async mode of DBIO::MySQL::Storage (registered there via register_async_mode( ev => ... )), so a schema connected with { async => 'ev' } answers the six *_async storage methods over EV::MariaDB while the same schema keeps serving synchronous queries over DBI/DBD::MySQL.
Thin transport: shared machinery is inherited
The Model-B orchestration (ADR 0030 §4) is inherited unchanged from DBIO::Storage::Async: the CRUD runner ("_run_crud" in DBIO::Storage::Async with its pooled / pinned runners), "txn_do_async" in DBIO::Storage::Async bracketing, the sql_maker plumbing and the sync ->get fallbacks. This class fills only the loop-/wire-specific seams and the genuine MySQL value-add:
"sql_maker_class" / "_sql_maker_args" -- the MySQL SQLMaker (backtick quoting,
LIMIT offset, rowspagination)"_transform_sql" -- identity. MySQL/DBD speaks
?natively, so there is no placeholder rewrite; the seam is kept only to honour the uniform core #70 / ADR 0032?-in contract"_query_async" / "_query_async_pinned" -- native EV::MariaDB query dispatch (pooled and pinned), routed through DBIO::MySQL::EV::QueryExecutor
"insert_async" + "_run_crud_pinned" + "_insert_returned_columns" -- MySQL has no RETURNING clause, so INSERT reads
SELECT LAST_INSERT_ID()on the same pinned connection and folds the auto-increment id into the returned-columns hashref (ADR 0031 §3). This is the one CRUD path core's generic RETURNING-shaped runner cannot serve, so it is overridden here"_run_pool_connect_statement" -- synchronous connect-action replay on a freshly-spawned EV::MariaDB pool connection (karr #68 / karr #18 seam)
"connect_info" / "_normalize_async_connect_info" / "_conninfo_hash" -- the EV::MariaDB named-parameter conninfo transport shape (incl.
dbname->database)"_txn_context_class" / "_txn_conn_accessor" -- the pinned-connection transaction context
Transport value-add
MySQL/MariaDB has no LISTEN/NOTIFY and no COPY, so -- unlike the PostgreSQL EV transport -- this transport carries no such wire value-add and declares none. EV::MariaDB pipelines queries automatically at the wire level (consecutive query calls are batched, up to 64 in flight) with no explicit pipeline-mode API to bracket; the core _pipeline_enter/_pipeline_sync/ _pipeline_exit seam therefore does not apply and the pipeline capability is not declared (see "transport_capabilities"). The only capability this transport provides is on_connect_replay.
METHODS
sql_maker_class
The DBIO::MySQL::SQLMaker subclass used to generate SQL. Shared with the sync driver, so the maker keeps emitting ? placeholders -- which MySQL/DBD understands natively ("_transform_sql" is identity).
_sql_maker_args
MySQL SQLMaker construction args: backtick identifier quoting, . name separator. (The MySQL LIMIT offset, rows pagination dialect is baked into DBIO::MySQL::SQLMaker itself.)
_post_insert_sql
Empty string -- MySQL has no RETURNING clause. The INSERT path ("_run_crud_pinned") reads SELECT LAST_INSERT_ID() on the pinned connection instead of appending a RETURNING suffix, so core's generic _run_crud insert branch (which consumes this seam) is never reached for this transport; the seam is defined only to satisfy the base contract.
future_class
Returns 'Future' -- uses Future.pm from CPAN. Plain Future ->then callbacks auto-wrap a non-Future return value into a resolved Future (ADR 0031 §4), so ResultSet/Row *_async callbacks that return plain values resolve without an explicit Future->done(...) wrap.
transport_capabilities
my @caps = DBIO::MySQL::EV::Storage->transport_capabilities;
Class method (see "transport_capabilities" in DBIO::Storage::Async). Declares the wire capabilities this transport really provides, so "_async_storage" in DBIO::Storage::DBI lets an async extension layer that requires any of them compose onto it (and croaks naming the gap otherwise, rather than silently dropping a feature). This transport provides only:
on_connect_replay-- its pool (DBIO::MySQL::EV::Pool, a DBIO::Storage::PoolBase) drives core's "_setup_pool_connection" in DBIO::Storage::Async on every freshly-spawned connection, and "_run_pool_connect_statement" replays the owning sync storage'son_connect_do/on_connect_callagainst it (karr #68 / #18).
It does not declare listen, notify or copy (MySQL has neither LISTEN/NOTIFY nor COPY), nor pipeline (EV::MariaDB pipelines automatically at the wire level; there is no explicit pipeline-mode API for the core seam to bracket).
connect_info
$storage->connect_info([ \%conninfo, \%opts ]);
Set connection parameters. %conninfo is passed to EV::MariaDB as named connection parameters (host, user, password, database, ...). When the embedding sync storage feeds its own DBI-form connect info (['dbi:MariaDB:...', $user, $pass, \%attrs]) it is translated into the async [ \%conninfo, \%opts ] shape first (EV::MariaDB cannot parse a DSN string). AccessBroker connect info ([ $broker ]) is detected and wired to the per-spawn credential provider (the inherited broker seam), exactly as the sync path does.
This override (rather than the inherited base connect_info) is retained because the EV transport folds the DBI-form DSN into EV::MariaDB named params inline via "dbi_to_conninfo" in DBIO::MySQL::EV::ConnectInfo.
_async_broker_conninfo
my $conninfo = $storage->_async_broker_conninfo($mode);
AccessBroker seam (see "ACCESSBROKER CONSUMPTION" in DBIO::Storage::Async): return one fresh, storage-native EV::MariaDB conninfo hash for a single new pool connection, built from the current broker credentials via the inherited normalisation.
_normalize_async_connect_info
my ($conninfo, $pool_size, $opts) = $storage->_normalize_async_connect_info($info);
MySQL-specific override of the base normaliser: besides stripping pool_size out of the conninfo, it maps dbname to database (EV::MariaDB's spelling) so a hash-form connect that used the DBI-style dbname key still reaches the native client correctly. Works on copies so the caller's structures are never mutated.
_conninfo_hash
my $conninfo = $storage->_conninfo_hash;
Returns the already-normalised stored EV::MariaDB conninfo hashref (or a passed override). Normalisation (dbname->database, pool_size extraction) is done once in "_normalize_async_connect_info", so this returns it as-is.
pool
Returns the DBIO::MySQL::EV::Pool connection pool, created lazily on first access. The pool is wired with storage => $self so its core-shared spawn path ("_spawn_connection" in DBIO::Storage::PoolBase) replays the owning sync storage's connect actions on each new connection via "_run_pool_connect_statement" (karr #68 / #18). Fed the per-spawn conninfo_provider when an AccessBroker is attached, otherwise the static conninfo hash.
_txn_context_class
The pinned-connection transaction context the inherited "txn_do_async" in DBIO::Storage::Async hands to its coderef: DBIO::MySQL::EV::TransactionContext.
_txn_conn_accessor
The constructor key the pinned connection is passed under -- mdb, matching DBIO::MySQL::EV::TransactionContext.
_transform_sql
Transport-internal SQL shaping (core #70 / ADR 0032). Identity for MySQL: MySQL/DBD speaks the ? placeholder dialect natively, so the sql_maker output needs no rewrite. The seam is retained (and applied first by "_query_async" / "_query_async_pinned") purely to keep the transport seam contract uniform across drivers -- do not reintroduce a dialect rewrite here.
_query_async
Transport seam. Execute a query on a freshly-acquired pooled connection, releasing it once the Future is ready. Returns a Future of the raw result rows (list of column arrayrefs for a result set, or the affected-row count for plain DML -- ADR 0031 §3), exactly what the inherited _run_crud expects.
Receives SQL in the sql_maker ?-placeholder dialect and shapes it via "_transform_sql" (identity for MySQL) before it reaches the executor -- the core #70 / ADR 0032 seam contract.
_query_async_pinned
Transport seam. Like "_query_async" but runs on the supplied pinned connection and does not release it -- used for queries inside a pinned transaction and for the INSERT + LAST_INSERT_ID() pair (which must ride the same session). Shapes the incoming ?-dialect SQL via "_transform_sql" exactly as "_query_async" does.
insert_async
my $future = $storage->insert_async($source, \%vals);
Resolve the returned-columns hashref -- the supplied insert data overlaid with the auto-increment PK the engine populated (ADR 0031 §3). MySQL has no RETURNING clause, so the INSERT and a following SELECT LAST_INSERT_ID() must run on the same session: this override acquires one pooled connection, runs the whole insert pinned on it via "_run_crud_pinned", and releases it when the Future settles. (The base insert_async uses the pooled runner, which would acquire a fresh connection per query and read a foreign session's LII.)
_run_crud_pinned
my $future = $storage->_run_crud_pinned($op, $conn, @args);
Build SQL and run a CRUD op on a pinned connection (the entry point the DBIO::MySQL::EV::TransactionContext and "insert_async" use). For every op except insert this delegates to the inherited runner. The insert op is the MySQL value-add: it runs the INSERT and then SELECT LAST_INSERT_ID() on the same $conn (so the session-scoped LII is the one the INSERT produced), captures it for legacy "last_insert_id" callers, and folds it into the returned-columns hashref via "_insert_returned_columns".
_insert_returned_columns
$hashref = $storage->_insert_returned_columns($source, \%to_insert, $lii_row);
Assemble the returned-columns hashref from a SELECT LAST_INSERT_ID() row (ADR 0031 §3). MySQL has no RETURNING clause, so the INSERT path captures the LII on the pinned connection and hands the single-column row to this method. The hashref is { %$to_insert, $col => $lii_id }, where $col is the source's first is_auto_increment column (falling back to the first PK column whose value was not supplied, matching the sync "insert" in DBIO::Storage::DBI heuristic). Overrides the inherited default that maps a positional RETURNING row onto a column list.
_run_pool_connect_statement
$storage->_run_pool_connect_statement($conn, $sql, $attrs, @bind);
Native-backend override of the core connect-action runner seam ("_run_pool_connect_statement" in DBIO::Storage::Async). The base default drives a blocking DBI do and croaks on a non-DBI connection; an EV::MariaDB handle is neither a { dbh => $dbh } wrapper nor a do-capable DBI handle, so this override drives the statement synchronously to completion on that connection over the EV loop.
Called once per freshly-spawned pool connection from "_setup_pool_connection" in DBIO::Storage::Async, so the owning sync storage's on_connect_do / on_connect_call replay on every new EV pool connection, identical to the DBD-based pools (karr #68 / #18). The freshly-spawned EV::MariaDB handle connects asynchronously, so we first drive the loop until it is up, then run the statement and drive the loop until it completes. Bindless statements (the usual SET ... connect actions) go on the queueable query path; a bound statement is routed through prepare + execute. $attrs (the DBI attribute hashref) has no EV::MariaDB analogue and is ignored.
last_insert_id
my $id = $storage->last_insert_id;
Returns the auto-increment value from the last INSERT performed on this storage instance (captured from SELECT LAST_INSERT_ID() by "_run_crud_pinned"). Valid after insert_async or the sync insert succeeds.
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.