DBIO-PostgreSQL-Async

 view release on metacpan or  search on metacpan

lib/DBIO/PostgreSQL/Async/Storage.pm  view on Meta::CPAN

  };
}

sub _conninfo_string {
  my ($self, $ci) = @_;
  $ci = $self->_current_async_connect_info($self->access_broker_mode)->[0]
    if ! defined $ci;
  return conninfo_string($ci);
}

# --- SQL Generation ---


sub sql_maker {
  my $self = shift;
  $self->{sql_maker} ||= do {
    my $class = $self->{sql_maker_class};
    $class->new(
      quote_char     => '"',
      name_sep       => '.',
      limit_dialect  => 'LimitOffset',
    );
  };
}

sub _generate_sql {
  my ($self, $op, @args) = @_;
  my $sm = $self->sql_maker;
  my $method = {
    select        => 'select',
    select_single => 'select',
    insert        => 'insert',
    update        => 'update',
    delete        => 'delete',
  }->{$op} or croak "Unknown operation: $op";

  return $sm->$method(@args);
}

# --- Async Query Execution ---


sub select_async {
  my $self = shift;
  return $self->_run_crud('select', $self->_pool_runner, @_);
}


sub select_single_async {
  my $self = shift;
  return $self->_run_crud('select_single', $self->_pool_runner, @_);
}


sub insert_async {
  my $self = shift;
  return $self->_run_crud('insert', $self->_pool_runner, @_);
}


sub update_async {
  my $self = shift;
  return $self->_run_crud('update', $self->_pool_runner, @_);
}


sub delete_async {
  my $self = shift;
  return $self->_run_crud('delete', $self->_pool_runner, @_);
}

# Build SQL for a CRUD op and execute it through $runner, a coderef
# that takes ($sql, \@bind) and returns a Future of result rows.
# All the PG-specific shaping (RETURNING on insert, first-row for
# select_single) lives here exactly once, so the pooled path
# (_pool_runner) and the pinned txn path (a pinned runner supplied by
# TransactionContext) share identical behavior.
sub _run_crud {
  my ($self, $op, $runner, @args) = @_;

  my $sm = $self->sql_maker;
  if ($op eq 'select' || $op eq 'select_single') {
    my ($sql, @bind) = $sm->select(@args);
    my $f = $runner->($sql, \@bind);
    return $f unless $op eq 'select_single';
    return $f->then(sub {
      my @rows = @_;
      return @rows ? $rows[0] : undef;
    });
  }
  elsif ($op eq 'insert') {
    my ($sql, @bind) = $sm->insert(@args);
    # PostgreSQL RETURNING for auto-generated columns
    $sql .= ' RETURNING *' unless $sql =~ /RETURNING/i;
    return $runner->($sql, \@bind);
  }
  elsif ($op eq 'update') {
    my ($sql, @bind) = $sm->update(@args);
    return $runner->($sql, \@bind);
  }
  elsif ($op eq 'delete') {
    my ($sql, @bind) = $sm->delete(@args);
    return $runner->($sql, \@bind);
  }
  croak "Unknown CRUD operation: $op";
}

# Runner that executes on a freshly-acquired pooled connection and
# releases it when done. This is the normal (non-transactional) path.
sub _pool_runner {
  my $self = shift;
  return sub {
    my ($sql, $bind) = @_;
    return $self->_query_async($sql, $bind);
  };
}

# Runner that executes on a pinned connection without releasing it.
# Used by TransactionContext so CRUD inside a txn hits the same
# connection that BEGIN/COMMIT ran on.
sub _pinned_runner {

lib/DBIO/PostgreSQL/Async/Storage.pm  view on Meta::CPAN

sub copy_in {
  my ($self, $table, $columns, $coderef) = @_;

  my $col_list = join(', ', map { $self->sql_maker->_quote($_) } @$columns);
  my $quoted_table = $self->sql_maker->_quote($table);
  my $sql = "COPY $quoted_table ($col_list) FROM STDIN";

  return $self->pool->acquire->then(sub {
    my $pg = shift;
    my $f = Future->new;

    $pg->query($sql, sub {
      my ($status, $err) = @_;
      if ($err) {
        $self->pool->release($pg);
        $f->fail($err);
        return;
      }

      my $put = sub {
        my $row = shift;
        my $line = join("\t", map { defined $_ ? $_ : '\N' } @$row) . "\n";
        $pg->put_copy_data($line);
      };

      eval { $coderef->($put) };
      if ($@) {
        $pg->put_copy_end($@);
        $self->pool->release($pg);
        $f->fail($@);
      } else {
        $pg->put_copy_end;
        $self->pool->release($pg);
        $f->done(1);
      }
    });

    return $f;
  });
}

# --- Sync Fallbacks ---
# These allow sync methods (->all, ->first etc.) to work
# by blocking the event loop. Useful for scripts/migrations.

sub select {
  my $self = shift;
  return $self->select_async(@_)->get;
}

sub select_single {
  my $self = shift;
  return $self->select_single_async(@_)->get;
}

sub insert {
  my $self = shift;
  return $self->insert_async(@_)->get;
}

sub update {
  my $self = shift;
  return $self->update_async(@_)->get;
}

sub delete {
  my $self = shift;
  return $self->delete_async(@_)->get;
}

sub txn_do {
  my $self = shift;
  return $self->txn_do_async(@_)->get;
}

# --- Schema Integration ---

sub schema { $_[0]->{schema} }
sub debug  { $_[0]->{debug} }

sub connected { defined $_[0]->{pool} && $_[0]->pool->available > 0 }

sub disconnect {
  my $self = shift;
  if ($self->{pool}) {
    $self->{pool}->shutdown;
    $self->{pool} = undef;
  }
  if ($self->{_listen_pg}) {
    $self->{_listen_pg}->finish;
    $self->{_listen_pg} = undef;
  }
}

sub DESTROY {
  my $self = shift;
  $self->disconnect if $self->{pool};
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

DBIO::PostgreSQL::Async::Storage - Async PostgreSQL storage driver using EV::Pg

=head1 VERSION

version 0.900000

=head1 DESCRIPTION

Implements L<DBIO::Storage::Async> using L<EV::Pg> — a non-blocking
PostgreSQL client that speaks libpq's async protocol directly.
No DBI, no DBD::Pg, just raw libpq performance.



( run in 4.277 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )