NAME
DBIO::Extension::Protocol - Runs the phases of the extension interface over the active extensions and merges their answers
VERSION
version 0.900004
SYNOPSIS
my $protocol = DBIO::Extension::Protocol->new(
entries => [ $list->entries ],
driver => { quote_ident => sub { ... } },
connection_schema => 'app',
restricted => 0,
);
my $states = $protocol->inspect($dbh); # phase 0
my $claims = $protocol->claims($model); # phase 1
my $triples = $protocol->annotate($model); # phase 2
my $answer = $protocol->verdict($change); # phase 3
my $active = $protocol->activation; # per connection
DESCRIPTION
Core's side of ADR 0066 sections 2.2 to 2.4. It builds the context $ctx (see "THE CONTEXT" in DBIO::Extension), asks every active extension the same question as a copy of the same input, and merges the answers. Addresses stay opaque to it: it sorts, de-duplicates and compares them, and the driver applies them to its model ("_apply_extension_claims" in DBIO::Deploy::Base).
The merges are unions with a refusal on conflict and are done in name order, whatever order the extensions were called in, so no result depends on the order. Failures are DBIO::Exceptions that name the extensions.
METHODS
new
Arguments (hash):
entries-
The active entries, as "entries" in DBIO::Extension::List returns them (with the installed state attached). Sorted by name here.
driver-
Hash reference of the driver's helpers; becomes
$ctx->{driver}. connection_schema,restricted-
Become the same keys of the context.
run_order-
Array reference of names. Overrides the order the extensions are called in (default: name order). The merge order stays by name. For DBIO::Test::ExtensionSet.
verify_input-
True: an extension that hands back a changed copy of its input makes the call throw. For DBIO::Test::ExtensionSet; off by default because every call already gets its own copy.
trace-
True: keeps the raw answer of every extension call, see "trace".
names
The names of the active extensions, sorted.
trace
my $trace = $protocol->trace; # { $name => { $method => [ $answer, ... ] } }
With trace on: a copy of every raw answer, in call order. Empty otherwise.
states
The "inspect" states by extension name, as a copy.
models
$protocol->models(source => $model, target => $model);
Sets the models after phase 2 that the verdicts get in $ctx->{models}. "extend_models" does it.
inspect
my $states = $protocol->inspect($dbh);
Phase 0. Calls inspect of every extension with $ctx->{dbh} set and no state in the context. Keeps the answers that are defined, per name, and returns them as a hash reference (a copy).
claims
my $addresses = $protocol->claims($model);
Phase 1. The union of the addresses every extension claims on $model, de-duplicated and sorted. Claiming is idempotent: there is no conflict.
annotate
my $triples = $protocol->annotate($model);
Phase 2. The union of the [ $address, $attribute, $value ] triples, de-duplicated and sorted by address and attribute. The same address and attribute with two different values throws, naming every extension involved and its value; the message does not depend on the order of the calls.
verdict
my $answer = $protocol->verdict($change);
Phase 3, asked by the driver for one change it is about to write (set the models with "models" first). Returns undef when no extension answers, { using => $expr } for one answer (the same answer from several is one), or { refuse => \@messages }:
every
refuseof an extension is a message"name: text", in name order; any refusal refuses, andusinganswers beside it are ignored;two different
usinganswers are a refusal naming both extensions.
Every refusal is also kept for "refusals" and "assert_no_refusals". An answer that is neither undef nor a hash with exactly one of using and refuse throws.
refusals
my @messages = $protocol->refusals;
All refusals of the verdicts asked so far, in the order they were given.
conflicts
my @messages = $protocol->conflicts;
The verdicts that were refused because two extensions gave different using answers (also in "refusals"), apart from the plain refuse answers of single extensions. DBIO::Test::ExtensionSet reports them: a set whose members contradict each other on its fixture does not combine.
assert_no_refusals
Throws a DBIO::Exception listing every refusal when there is one, so a diff with a refused change is not applied. Returns true otherwise.
activation
my $active = $protocol->activation;
# { load => [ 'age', 'postgis' ], search_path => [ 'ag_catalog', 'gis' ] }
The merge of the activation data of every extension, composed in name order and de-duplicated by first appearance. load is a list of library names; two extensions that give different libraries for one name throw. The driver turns this into statements and puts the namespace of the connection in front of search_path (ADR 0066 section 3). It refuses what it cannot compose.
Every library name and every search_path name is an identifier (ADR 0077). Core refuses one with ;, a line break, CR or NUL (a DBIO::Exception that names load or search_path and the extension, never the value). The driver writes each name only through its identifier quoting (or as a bind) or refuses it with a DBIO::Exception; it never joins a name into a statement by string concatenation. The Names list of DBIO::Test::Conformance asks this of every driver.
extend_models
my ($source, $target) = $protocol->extend_models($deploy, $source, $target);
Phases 1 and 2 on both sides of a diff with the same set of extensions: copies of the two models, with the claims applied by $deploy->_apply_extension_claims, then the annotations by $deploy->_apply_extension_annotations, then the "inspect" states by $deploy->_apply_extension_states, in this order (phase 2 reads the model after phase 1; the states are added last so they are not part of any phase's input). The models are also set for "verdict". Returns the two new models.
AUTHOR
DBIO & DBIx::Class Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.