postgresql – 如何在没有文件或内存的情况下将IO :: Handle子类化为正确获取低级文件句柄?

postgresql – 如何在没有文件或内存的情况下将IO :: Handle子类化为正确获取低级文件句柄?,第1张

概述我有一个访问PostgreSQL数据库的应用程序,需要根据一些需要的处理从中读取一些大的二进制数据.这可能是数百MB甚至数GB的数据.请不要讨论使用文件系统等等,它就像现在这样. 该数据只是各种类型的文件,例如它可能是Zip容器或其他类型的存档.一些需要的处理是列出Zip的内容,甚至可以提取一些成员进行进一步处理,也可以散列存储的数据……最后,数据被多次读取,但只写入一次以存储它. 我使用的所有P 我有一个访问Postgresql数据库的应用程序,需要根据一些需要的处理从中读取一些大的二进制数据.这可能是数百MB甚至数GB的数据.请不要讨论使用文件系统等等,它就像现在这样.

该数据只是各种类型的文件,例如它可能是Zip容器或其他类型的存档.一些需要的处理是列出Zip的内容,甚至可以提取一些成员进行进一步处理,也可以散列存储的数据……最后,数据被多次读取,但只写入一次以存储它.

我使用的所有Perl库都可以使用文件句柄,一些使用IO :: Handle,另一些使用IO :: String或IO :: Scalar,其他一些只使用低级文件句柄.所以我所做的是创建一个IO :: Handle和IO :: Seekable的子类,它就像DBD :: Pg周围相应方法的包装器一样.在CTOR中,我创建了一个与数据库的连接,打开一些提供的LOID用于读取并存储Postgres在实例中提供的句柄.然后我自己的句柄对象被转发给能够使用这种文件句柄的人,并且可以直接在Postgres提供的blob中读取和搜索.

问题是在IO :: Handle上使用低级文件句柄或低级文件句柄 *** 作的库.摘要:: MD5似乎是一个,Archive :: Zip另一个. Digest :: MD5呱呱叫,告诉我没有提供句柄,另一方面,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,这样的工作就像预期的那样,但是我想更进一步,并且与那些不期望在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;
解决方法 有一种解决方法,但它有点奇怪.如果我正确阅读您的代码和评论,您的要求基本上是三倍的:

>尽可能像普通文件句柄/ IO :: Handle对象一样工作,这使得它不是用户不可见的真实文件.
>使用Archive :: Zip,它主要在常规Perl中实现,并调用您发布的IO :: Handle :: fdopen代码,因为它不是真正的句柄,因此无法复制句柄.
>使用Digest :: MD5,它是使用PerlIO在XS中实现的.由于基于绑定的技巧和perl内存中的“假”文件句柄在该级别不可用,因此它比2更加苛刻.

您可以使用PerlIO layers with PerlIO::via来实现所有这三个.代码类似于您使用tIE编写的代码(实现一些必需的行为方法).此外,您可以利用open的“open variable as as file”功能和IO :: file的预滚动IO :: Seekable IO :: Handle功能来简化上面的要求1(使其在Perl代码中可用)方式正常的IO ::处理对象是).

以下是一个可满足您需求的示例包.它有一些警告:

>它根本不扩展您的代码或与DB交互;它只使用提供的行arrayref作为文件数据.如果这似乎适合您的用例,您应该使其适应数据库.
>它实现了以下演示用法所需的最低限度.你需要实现更多的方法,使它在大多数非演示案例中“表现良好”(例如,它对SEEK,EOF,BINMODE,SEEK等等都不了解).请注意,您要实现的函数的参数/预期行为与您为tIE或Tie::Handle所执行的 *** 作不同; “界面”具有相同的名称,但不同的合同.
>接收调用者的所有方法都不应该直接用作hashref / globref;他们应该跟踪* $self-> {args} glob字段中的所有自定义状态.这是因为祝福对象被创建了两次(一次被PerliO祝福,一次被SUPER :: new祝福),因此需要通过共享引用来共享状态.如果替换args字段或添加/删除任何其他字段,它们将仅对创建它们的方法集可见:PerliO方法或“普通”对象方法.有关详细信息,请参阅构造函数中的注释.
> PerliO一般来说不容易内省.如果某些内容在sysread或< $fh>等低级 *** 作下失败,很多代码都会出错或做出意想不到的事情,因为它认为这些函数在 *** 作级别无法死亡/原子化.类似地,当弄乱PerliO时,失败模式很容易逃脱“死亡或返回错误值”的境界,并最终进入“段错误或核心转储”领域,特别是如果多个进程(fork())或线程是涉及(这些奇怪的情况,例如,为什么下面的模块没有实现在IO :: file-> new;后面是$file-> open(…“via:<($class)” );核心转储给我,不明白为什么). TL; DR调试为什么PerliO级别的东西出错可能很烦人,你被警告:)
>任何解决原始文件句柄或无法通过PerliO perlAPI函数工作的XS代码都不会遵守此规定.不幸的是,很多这些,但通常不是共同的,支持良好的CPAN模块.基本上,Digest :: MD5不适用于绑定句柄,因为它在“低于”平局的魔法水平下运行; PerliO比其低一级,但还有另一个级别.
>这段代码有点混乱,当然可以清理.特别是,直接打开()分层对象,跳过所有奇怪的伪间接对象,然后以其他方式将其包装在IO :: Handle中可能会更好一点,通过IO::Wrap.
> PerliO在许多旧版本的Perls上不起作用或工作方式不同.

包:

package TIEdThing;use strict;use warnings;use parent "IO::file";our @pushargs;sub new {    my ( $class,$args ) = @_;    # Build a glob to be used by the PerliO methods. This does two things:    # 1. Gets us a place to stick a shared hashref so PerliO methods and user-    # -defined object methods can manipulate the same data. They must use the    # {args} glob fIEld to do that; new fIElds written will .    # 2. UnifIEs the ways of addressing that across custom functions and PerliO    # functions. We Could just pass a hashref { args => $args } into PUSHED,but    # then we'd have to remember "PerliO functions receive a blessed hashref,# custom functions receive a blessed glob" which is lame.    my $glob = Symbol::gensym();    *$glob->{args} = $args;    local @pushargs = ($glob,$class);    my $self = $class->SUPER::new(\my $unused,"<:via($class)");    *$self->{args} = $args;    return $self;}sub custom {    my $self = shift;    return *$self->{args}->{customvalue};}sub PUSHED { return bless($pushargs[0],$pushargs[1]); }sub FILL { return shift(@{*$_[0]->{args}->{lines}}); }1;

用法示例:

my $object = TIEdThing->new({    lines => [join("\n",1..9,1..9)],customvalue => "custom!",});say "can call custom method: " . $object->custom;say "raw read with <>: " . <$object>;my $buf;read($object,$buf,10);say "raw read with read(): " . $buf;undef $buf;$object->read($buf,10);say "OO read via IO::file::read (end): " . $buf;my $checksummer = Digest::MD5->new;;$checksummer->addfile($object);say "Md5 read: " . $checksummer->hexdigest;my $dupto = IO::Handle->new;# Doesn't break/return undef; still not usable without implementing# more state sharing insIDe the object.say "Can dup handle: " . $dupto->fdopen($object,"r");my $archiver = Archive::Zip->new;# DIEs,but long after the fdopen() call. Can be fixed by implementing more# PerliO methods.$archiver->readFromfileHandle($object);
总结

以上是内存溢出为你收集整理的postgresql – 如何在没有文件或内存的情况下将IO :: Handle子类化为正确获取低级文件句柄?全部内容,希望文章能够帮你解决postgresql – 如何在没有文件或内存的情况下将IO :: Handle子类化为正确获取低级文件句柄?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/1162785.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-01
下一篇 2022-06-01

发表评论

登录后才能评论

评论列表(0条)

保存