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 refuse of an extension is a message "name: text", in name order; any refusal refuses, and using answers beside it are ignored;

  • two different using answers 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.