DBIO
view release on metacpan or search on metacpan
lib/DBIO/Storage/Async.pm view on Meta::CPAN
package DBIO::Storage::Async;
# ABSTRACT: Base class for async storage implementations
use strict;
use warnings;
use base 'DBIO::Storage';
use Carp 'croak';
use Scalar::Util qw(blessed weaken);
use DBIO::Storage::Async::TransactionContext;
use DBIO::SQL::Util ();
use namespace::clean;
sub new {
my ($class, $schema, $args) = @_;
my $self = bless {
schema => $schema,
pool => undef,
connect_info => undef,
sql_maker => undef,
_conninfo_provider => undef,
debug => $ENV{DBIO_TRACE} || 0,
debugobj => undef,
}, $class;
weaken($self->{schema}) if ref $self->{schema};
return $self;
}
sub future_class {
croak 'Subclass must override future_class';
}
sub pool {
croak 'Subclass must override pool';
}
sub _is_access_broker_connect_info {
my ($self, $info) = @_;
return 0 unless ref $info eq 'ARRAY' && @$info == 1;
return 0 unless blessed($info->[0]);
return $info->[0]->isa('DBIO::AccessBroker');
}
sub _setup_access_broker {
my ($self, $broker) = @_;
$self->set_access_broker($broker, 'write');
$self->{_conninfo_provider} = sub {
return $self->_async_broker_conninfo($self->access_broker_mode);
};
return $self;
}
sub _clear_access_broker {
my $self = shift;
$self->clear_access_broker;
$self->{_conninfo_provider} = undef;
return $self;
}
sub _conninfo_provider { $_[0]->{_conninfo_provider} }
sub _async_broker_conninfo {
croak 'Subclass must override _async_broker_conninfo to consume an AccessBroker';
}
sub connect_info {
my ($self, $info) = @_;
if ($info) {
# karr #66: strip DBIO-private attrs before the DB-specific reshape seam,
# so _normalize_conninfo (and DBI->connect) only see real DBD attributes.
$info = $self->_strip_private_connect_attrs($info);
lib/DBIO/Storage/Async.pm view on Meta::CPAN
sub _strip_private_connect_attrs {
my ($self, $info) = @_;
return $info
if ref $info ne 'ARRAY'
or $self->_is_access_broker_connect_info($info);
my @args = @$info; # shallow copy; the attr hashes below are copied before mutation
my @attr_slots;
if (ref $args[0] eq 'CODE') { # coderef + optional \%extra_attributes
push @attr_slots, \$args[1] if ref $args[1] eq 'HASH';
}
elsif (ref $args[0] eq 'HASH') { # single \%hash (Catalyst-style config)
push @attr_slots, \$args[0];
}
else { # dsn/user/pass + \%attrs [+ \%extra_attrs]
push @attr_slots, \$args[$_] for grep ref $args[$_] eq 'HASH', 3, 4;
}
my @storage_opt_names = ($self->_dbio_storage_option_names, 'cursor_class');
my %storage_options;
for my $slot (@attr_slots) {
my %attrs = %{ $$slot }; # copy before mutating
# storage_options + cursor_class -> keep for the storage, out of
# DBI->connect. Same sink the sync path uses (DBIO::Storage::DBI stashes
# these as storage_options). Held for karr #68 (pool on_connect seam:
# on_connect_do / on_connect_call) to consume; no further processing here.
$storage_options{$_} = delete $attrs{$_}
for grep exists $attrs{$_}, @storage_opt_names;
# quote_char/name_sep/quote_names -> sql_maker config, not DBD attrs. The
# async sql_maker path (_sql_maker_args) does not source quoting from
# connect_info yet (driver adapters set quote_char statically), so dropping
# them is correct for now. Their proper future sink is the backend's
# sql_maker -- see the TODO block in t/storage/async_connect_info_leak.t so
# a later async-quoting feature does not pull them back into raw DBD attrs.
delete @attrs{ $self->_dbio_sql_maker_option_names };
# async -> mode selector; the backend already knows its own mode, so it must
# never reach DBI->connect. ignore_version -> consumed by the
# DeploymentHandler version-storage component, never by the DBD.
delete @attrs{qw( async ignore_version )};
$$slot = \%attrs;
}
# Slot the storage options aside for karr #68; nothing consumes them yet.
$self->{_storage_options} = \%storage_options;
return \@args;
}
sub _owner_storage {
my $self = shift;
if (@_) {
$self->{_owner_storage} = $_[0];
weaken($self->{_owner_storage}) if ref $self->{_owner_storage};
}
return $self->{_owner_storage};
}
sub _setup_pool_connection {
my ($self, $conn) = @_;
my $owner = $self->_owner_storage or return;
return unless $owner->can('_run_pool_connect_actions');
$owner->_run_pool_connect_actions(sub {
$self->_run_pool_connect_statement($conn, @_);
});
return;
}
sub _teardown_pool_connection {
my ($self, $conn) = @_;
my $owner = $self->_owner_storage or return;
return unless $owner->can('_run_pool_disconnect_actions');
$owner->_run_pool_disconnect_actions(sub {
$self->_run_pool_connect_statement($conn, @_);
});
return;
}
sub _run_pool_connect_statement {
my ($self, $conn, $sql, @args) = @_;
my $dbh = $self->_pool_connect_dbh($conn);
$dbh->do($sql, @args);
return;
}
# Extract a blocking-do-capable DBI handle from a pool connection value. The two
# shapes the DBD-based Model-B backends use are a bare $dbh and the future_io
# { dbh => $dbh } wrapper (DBIO::PostgreSQL::Storage::Async::_create_pool_connection
# and friends). Anything else is a native backend that must override
# _run_pool_connect_statement.
sub _pool_connect_dbh {
my ($self, $conn) = @_;
return $conn
if blessed($conn) && $conn->can('do');
return $conn->{dbh}
if ref $conn eq 'HASH' && blessed($conn->{dbh}) && $conn->{dbh}->can('do');
croak 'Cannot run pool connect action: connection is neither a do-capable '
. 'DBI handle nor a { dbh => $dbh } wrapper -- override '
. '_run_pool_connect_statement for this backend';
}
( run in 0.862 second using v1.01-cache-2.11-cpan-995e09ba956 )