Tôi có một ứng dụng truy cập cơ sở dữ liệu PostgreSQL và cần đọc một số dữ liệu nhị phân lớn của nó tùy thuộc vào một số xử lý cần thiết. Điều này có thể là hàng trăm MB hoặc thậm chí một số GB dữ liệu. Xin vui lòng không thảo luận về việc sử dụng hệ thống tập tin thay vào đó hoặc như vậy, đó là cách nó là ngay bây giờ.Làm thế nào để phân lớp IO :: Xử lý để có được một xử lý tập tin cấp độ thấp mà không cần phải có một tập tin hoặc bộ nhớ?
Dữ liệu đó chỉ đơn giản là các tệp thuộc nhiều loại khác nhau, ví dụ: nó có thể là một thùng chứa Zip hoặc một số loại lưu trữ khác. Một số xử lý cần thiết là liệt kê nội dung của Zip, thậm chí có thể trích xuất một số thành viên để xử lý tiếp, có thể băm dữ liệu được lưu trữ ... Cuối cùng dữ liệu được đọc nhiều lần, nhưng chỉ viết một lần để lưu trữ nó.
Tất cả các thư viện Perl tôi sử dụng đều có thể làm việc với các tay cầm tệp, một số có số IO::Handle
, một số khác có IO::String
hoặc IO::Scalar
, một số khác chỉ với trình xử lý tệp cấp thấp. Vì vậy, những gì tôi đã làm là tạo một lớp con của IO::Handle
và IO::Seekable
hoạt động như trình bao bọc cho các phương pháp tương ứng xung quanh DBD::Pg
. Trong CTOR tôi tạo một kết nối tới cơ sở dữ liệu, mở một số LOID được cung cấp để đọc và lưu trữ xử lý được cung cấp bởi Postgres trong cá thể. Đối tượng xử lý của riêng tôi sau đó được chuyển tiếp tới bất cứ ai có thể làm việc với một trình xử lý tệp như vậy và có thể trực tiếp đọc và tìm kiếm trong blob do Postgres cung cấp.
Sự cố là libs sử dụng các trình xử lý tệp cấp thấp hoặc các thao tác xử lý tệp cấp thấp trên IO::Handle
. Digest::MD5
có vẻ là một, Archive::Zip
một số khác. Digest::MD5
croak
s và nói với tôi rằng không có tay cầm nào được cung cấp, Archive::Zip
mặt khác cố gắng tạo một tay cầm mới, riêng của tôi, gọi số IO::Handle::fdopen
và không thành công trong trường hợp của tôi.
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;
}
Tôi đoán vấn đề là bản sao cấp thấp của tay cầm, loại bỏ trường hợp của riêng tôi, vì vậy không có trường hợp nào nữa có kết nối cơ sở dữ liệu của tôi và tất cả nội dung đó.
Vì vậy, thậm chí có thể trong trường hợp của tôi để cung cấp một số IO::Handle
thành công nào có thể được sử dụng ở bất kỳ nơi nào có xử lý tệp cấp thấp không?
Ý tôi là, tôi không có một tập tin thực sự xử lý, tôi có một đối tượng chỉ nơi các cuộc gọi phương thức được bao bọc theo các phương thức Postgres tương ứng của chúng. Tất cả dữ liệu đó cần phải được lưu trữ ở đâu đó, gói cần phải được thực hiện vv
Tôi đã cố gắng làm những gì người khác đang làm, như IO::String
, sử dụng thêm tie
chẳng hạn. Nhưng cuối cùng trường hợp sử dụng là khác nhau, bởi vì Perl có thể tạo ra một tập tin cấp thấp thực sự xử lý cho một số bộ nhớ nội bộ của riêng mình. Một cái gì đó mà không được hỗ trợ ở tất cả trong trường hợp của tôi. Tôi cần phải giữ cho trường hợp của tôi xung quanh, bởi vì chỉ có biết về xử lý để cơ sở dữ liệu, vv
Sử dụng tay cầm của tôi như một phương pháp gọi read
và như vậy, nhưng tôi muốn thực hiện thêm một chút và tương thích hơn với bất kỳ ai không mong đợi làm việc trên các đối tượng IO::Handle
. Giống như IO::String
hoặc File::Temp
có thể được sử dụng như các trình xử lý tệp cấp thấp.
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;
sử dụng rất ít xử lý như đối tượng. 'tie' chắc chắn là con đường để đi nếu bạn muốn sử dụng một đối tượng như một tập tin xử lý. Nó liên kết toàn bộ một đối tượng với một tay cầm, vì vậy tuyên bố rằng nó sẽ không cho phép nó giữ một cơ sở dữ liệu xử lý là khá sai. – ikegami
Tôi đã thử phương pháp 'tie' giống' IO :: String' đang sử dụng và 'Digest :: MD5'' croak'ed mà bây giờ xử lý được đưa ra. Mà nó không có 'tie'. Và nguồn của nó chỉ là một 'if (fh) ... khác croak (...).' Và nhìn vào 'fdopen' được sử dụng bởi' Archive :: Zip', nó không tạo ra một tay cầm riêng mà không có tôi để 'tie' bất cứ điều gì? 'fdopen' không được gọi trên cá thể của tôi, nhưng' IO :: File' được tạo bởi 'Archive :: Zip'. –
Sử dụng một quy trình hoặc một tiến trình con để nạp một đường ống, sau đó. – ikegami