DBIO-PostgreSQL-Async

 view release on metacpan or  search on metacpan

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

    });

    return $f;
  });
}

# --- Pipeline Mode ---


sub pipeline {
  my ($self, $coderef) = @_;

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

    my $result = eval { $coderef->($self) };
    my $err = $@;

    if ($err) {
      $pg->exit_pipeline;
      $self->pool->release($pg);
      return Future->fail($err);
    }

    # Sync the pipeline — callback fires when all results are in
    my $f = Future->new;
    $pg->pipeline_sync(sub {
      $pg->exit_pipeline;
      $self->pool->release($pg);
      if (ref $result && $result->can('then')) {
        $result->on_done(sub { $f->done(@_) })
               ->on_fail(sub { $f->fail(@_) });
      } else {
        $f->done($result);
      }
    });

    return $f;
  });
}

# --- LISTEN/NOTIFY ---


sub listen {
  my ($self, $channel, $cb) = @_;

  $self->{_listeners}{$channel} = $cb;

  # Use a dedicated connection for LISTEN (not from the pool).
  # EV::Pg->new returns before the socket is actually connected; query()
  # dispatched on a not-yet-connected handle throws "not connected".
  # We buffer LISTEN/UNLISTEN until on_connect fires and then flush.
  $self->{_listen_pg} ||= do {
    require EV::Pg;
    $self->{_listen_pending} = [];
    $self->{_listen_connected} = 0;
    my $pg = EV::Pg->new(
      conninfo   => $self->_conninfo_string,
      keep_alive => 1,
      on_connect => sub {
        $self->{_listen_connected} = 1;
        my $q = delete $self->{_listen_pending} || [];
        $self->{_listen_pending} = [];
        $self->{_listen_pg}->query($_, sub {}) for @$q;
      },
      on_error   => sub { warn "LISTEN connection error: $_[0]\n" },
      on_notify  => sub {
        my ($ch, $payload, $pid) = @_;
        if (my $handler = $self->{_listeners}{$ch}) {
          $handler->($ch, $payload, $pid);
        }
      },
    );
    $pg;
  };

  my $quoted = $self->sql_maker->_quote($channel);
  my $sql = "LISTEN $quoted";
  if ($self->{_listen_connected}) {
    $self->{_listen_pg}->query($sql, sub {});
  } else {
    push @{ $self->{_listen_pending} }, $sql;
  }
}


sub unlisten {
  my ($self, $channel) = @_;
  delete $self->{_listeners}{$channel};
  if ($self->{_listen_pg}) {
    my $quoted = $self->sql_maker->_quote($channel);
    my $sql = "UNLISTEN $quoted";
    if ($self->{_listen_connected}) {
      $self->{_listen_pg}->query($sql, sub {});
    } else {
      push @{ $self->{_listen_pending} }, $sql;
    }
  }
}


sub notify {
  my ($self, $channel, $payload) = @_;

  croak 'Channel name required' unless defined $channel && $channel ne '';

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

    # pg_notify() with bind params -- NOTIFY itself takes no placeholders,
    # and inlining the payload as a string literal invites quoting bugs.
    $pg->query_params('SELECT pg_notify($1, $2)', [ $channel, $payload // '' ], sub {
      my ($res, $err) = @_;
      $self->pool->release($pg);
      if ($err) {
        $f->fail($err);
      } else {
        $f->done;



( run in 2.713 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )