У меня есть приложение, которое обращается к базе данных PostgreSQL и должно читать некоторые большие двоичные данные из него в зависимости от некоторой необходимой обработки. Это может быть сотни МБ или даже несколько ГБ данных. Пожалуйста, не обсуждайте использование файловых систем или таких, как сейчас.Как подкласс IO :: Обрабатывать, чтобы правильно получить дескриптор файла низкого уровня без наличия файла или памяти?
Эти данные являются просто файлами различных типов, например. это может быть Zip-контейнер или какой-то другой архив. Некоторая необходимая обработка - это список содержимого Zip, возможно, даже извлечение некоторых элементов для дальнейшей обработки, возможно, хэш хранящихся данных ... В конце данные считываются несколько раз, но записываются только один раз, чтобы сохранить их.
Все LIBS Perl я использую в состоянии работать с ручками файлов, некоторые с IO::Handle
, другие с IO::String
или IO::Scalar
, некоторые другие только с ручками файлов низкого уровня. Итак, что я сделал, создайте подкласс IO::Handle
и IO::Seekable
, который действует как обертка для соответствующих методов около DBD::Pg
. В CTOR я создаю подключение к базе данных, откройте некоторый предоставленный LOID для чтения и хранения дескриптора, предоставленного Postgres в экземпляре. Мой собственный объект дескриптора затем перенаправляется тому, кто может работать с таким дескриптором файла и может напрямую читать и искать в блоке, предоставленном Postgres.
Проблема заключается в том, что библиотеки libs используют дескрипторы файлов низкого уровня или операции с файлами низкого уровня на IO::Handle
. Digest::MD5
похоже один, Archive::Zip
другой один. Digest::MD5
croak
s и говорит мне, что никакой ручки не было предоставлено, Archive::Zip
, с другой стороны, пытается создать новый собственный ручка у меня, вызывает IO::Handle::fdopen
и не работает в моем случае.
sub fdopen {
@_ == 3 or croak 'usage: $io->fdopen(FD, MODE)';
my ($io, $fd, $mode) = @_;
local(*GLOB);
if (ref($fd) && "".$fd =~ /GLOB\(/o) {
# It's a glob reference; Alias it as we cannot get name of anon GLOBs
my $n = qualify(*GLOB);
*GLOB = *{*$fd};
$fd = $n;
} elsif ($fd =~ m#^\d+$#) {
# It's an FD number; prefix with "=".
$fd = "=$fd";
}
open($io, _open_mode_string($mode) . '&' . $fd)
? $io : undef;
}
Я думаю, что проблема является копией низкого уровня ручки, который удаляет свой собственный экземпляр, так что нет ни одного случая, больше имея мое подключение к базе данных и все эти вещи.
Итак, возможно ли в моем случае предоставить IO::Handle
, который успешно может использоваться везде, где ожидается дескриптор файла низкого уровня?
Я имею в виду, что у меня нет реального дескриптора файла, у меня есть объект только там, где вызовы методов завернуты в соответствующие методы Postgres, для которых необходим дескриптор базы данных. Все эти данные нужно где-то хранить, необходимо сделать упаковку и т. Д.
Я попытался сделать то, что делают другие, например IO::String
, который дополнительно использует tie
. Но, в конце концов, вариант использования отличается, потому что Perl способен самостоятельно создать реальный дескриптор файла низкого уровня для некоторой внутренней памяти. Что-то, что не поддерживается вообще в моем случае. Мне нужно сохранить свой экземпляр вокруг, потому что только это знает о дескрипторе базы данных и т. Д.
Использование моего дескриптора как IO::Handle
методом вызова read
и такие работы, как ожидалось, но я хотел бы принять это немного дальше и быть более совместимым с тем, кто не планирует работать с объектами IO::Handle
. Так же, как IO::String
или File::Temp
, могут использоваться как ручки для файлов низкого уровня.
package ReadingHandle;
use strict;
use warnings;
use 5.10.1;
use base 'IO::Handle', 'IO::Seekable';
use Carp();
sub new
{
my $invocant = shift || Carp::croak('No invocant given.');
my $db = shift || Carp::croak('No database connection given.');
my $loid = shift // Carp::croak('No LOID given.');
my $dbHandle = $db->_getHandle();
my $self = $invocant->SUPER::new();
*$self->{'dbHandle'} = $dbHandle;
*$self->{'loid'} = $loid;
my $loidFd = $dbHandle->pg_lo_open($loid, $dbHandle->{pg_INV_READ});
*$self->{'loidFd'} = $loidFd;
if (!defined($loidFd))
{
Carp::croak("The provided LOID couldn't be opened.");
}
return $self;
}
sub DESTROY
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
$self->close();
}
sub _getDbHandle
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return *$self->{'dbHandle'};
}
sub _getLoid
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return *$self->{'loid'};
}
sub _getLoidFd
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return *$self->{'loidFd'};
}
sub binmode
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return 1;
}
sub close
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
return $dbHandle->pg_lo_close($loidFd);
}
sub opened
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $loidFd = $self->_getLoidFd();
return defined($loidFd) ? 1 : 0;
}
sub read
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $buffer =\shift // Carp::croak('No buffer given.');
my $length = shift // Carp::croak('No amount of bytes to read given.');
my $offset = shift || 0;
if ($offset > 0)
{
Carp::croak('Using an offset is not supported.');
}
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
return $dbHandle->pg_lo_read($loidFd, $buffer, $length);
}
sub seek
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $offset = shift // Carp::croak('No offset given.');
my $whence = shift // Carp::croak('No whence given.');
if ($offset < 0)
{
Carp::croak('Using a negative offset is not supported.');
}
if ($whence != 0)
{
Carp::croak('Using a whence other than 0 is not supported.');
}
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
my $retVal = $dbHandle->pg_lo_lseek($loidFd, $offset, $whence);
$retVal = defined($retVal) ? 1 : 0;
return $retVal;
}
sub tell
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
my $retVal = $dbHandle->pg_lo_lseek($loidFd);
$retVal = defined($retVal) ? $retVal : -1;
return $retVal;
}
1;
Очень мало использует ручки в качестве объектов. 'tie' - это определенно способ пойти, если вы хотите использовать объект в качестве дескриптора файла. Он связывает весь объект с дескриптором, поэтому утверждает, что он не позволил бы ему хранить дескриптор базы данных. – ikegami
Я уже пробовал один и тот же подход '' tie' 'IO :: String' и' Digest :: MD5' 'croak'ed, который теперь обрабатывается. Что это не без «галстука». И его источник - это только 'if (fh) ... else croak (...).' И глядя на 'fdopen', используемый' Archive :: Zip', не создает ли он собственный дескриптор, «связывать» что-нибудь? 'fdopen' не вызывается в моем экземпляре, а' IO :: File', созданный 'Archive :: Zip' сам по себе. –
Используйте поток или дочерний процесс для подачи трубы. – ikegami