pet has asked for the wisdom of the Perl Monks concerning the following question:

I appreciate any help. In my Perl script I have a code part that fetches a data from database InterBase, everything works fine except in case when fetched data has size of over 1MB! Script(s) work just fine if the "content" in MEMO or BLOB fields of the database has smaller size than 1MBytes; if size of the file (data) is over 1MB then "ERROR: Blob exceeds maximum length" is generated in the "error.log" of the Apache Server (and of course data is not fetched):

# . . . . . - rest of the code use DBI; # . . . . . - rest of the code # name & location of the database $dbpath = 'C:\Database\database_01.fdb'; $dsn = "DBI:InterBase:database=$dbpath;host=123.456.78.9;ib_dialec +t=3"; # connesting to DB $dbh = DBI->connect($dsn, '', '', {AutoCommit => 0}) or die "$DBI: +:errstr"; # setting the size of the buffer (1KB) $dbh -> {LongReadLen} = 1024; # NO matter if Truncation is ON or OFF? Tried both cases! #$dbh -> {LongTruncOk} = 0; $dbh -> {LongTruncOk} = 1; # here, DATA is MEMO field in the database! $sql = "SELECT FIRST 1 DATA FROM CONFIGURATION ORDER BY CREATEDDAT +E DESC"; $sth = $dbh->prepare($sql) or die "Preparing: ", $dbh->errstr; $sth->execute or die "Executing: ", $sth->errstr; # opening FILE HANDLER for writing the content of the MEMO/BLO +B field open (F, ">./database1.txt"); # fetching the content while (@row = $sth->fetchrow_array()) { foreach (@row) { # saving into the file "database1.txt"! print F "$_"; } } close F; $sth->finish; $dbh->commit or warn $dbh->errstr; $dbh->disconnect or warn $dbh->errstr; # . . . . . - rest of the code

I have tried several workarounds (in the current code I've increased the size of the buffer, LongReadLen - EVEN to 10MB; also tried with chunks & blob_read method - to get small pieces of data & to compose/concatenate them again at the end BUT got nothing, even with data that had size of only 10KB!), everytime it was unsuccessfully! Maybe I should replace the DBD-InterBase with some other Perl module?
Installed Environment: Perl version 5.6.1, Firebird 1.5.0 (database: InterBase) and I have two similar scripts (Win32 & Unix), both work OK when fetched data had smaller size than 1MB, installed Perl modules: DBI 1.37, DBD-InterBase 0.40.

*NOTICE: I read this:
"The DBI currently defines no way to insert or update LONG/LOB values piece-wise - piece by piece. That means you're limited to handling values that will fit into your available memory."
as well as:
"Read/Write BLOB fields block by block not (yet) supported. The maximum size of a BLOB read/write is hardcoded to about 1MB."
- Does that mean that I can't get data using DBI and DBD:InterBase with size larger than 1MB?

Please help me with any ideas, hints or solutions!

Replies are listed 'Best First'.
Re: InterBase MEMO/BLOB fields, data with size > 1MB
by tilly (Archbishop) on May 28, 2004 at 15:34 UTC
    It looks like you have a bug to work around at least.

    Some DBD::* modules define their own ways to handle large BLOBs. (Well, DBD::Sybase does.) It looks like DBD::InterBase does not.

    If you're willing to compile from locally-modified source, with some poking around you're pretty likely to be able to find the 1 MB limit and change it to something more reasonable. That would move the problem, but you'd have to remember to patch it every time you install. With more work you might be able to remove the arbitrary restriction. In which case you could submit the patch back to the DBI (and possibly also the DBD::InterBase) folks.

    If what you need is only a little bigger than 1 MB then you could always use Compress::Zlib to deflate data before storing it, and inflate afterwards.

    Failing that, one way to work around the issue is to create a table where you store the data across several rows. You could then define a function call to take a large field and divide it into several pieces that you store under some ID. And a reverse function to fetch those rows back and reassemble them.

      Thanks for suggestion/comment; it was obvious that DBD::InterBase module has limit of exactly 1 MB while fetching data from the database; unfortunately I just can't find where that limit "lies" to change or even remove it! Other thing is that I prefer limit of 10MB instead!

      As I said, in my script(s) I use MEMO as well as BLOB field, and in this case data (in the BLOB field) is already zip-ed (so, there I fetch binary data):

      # . . . . . - rest of the code # here, NAME is CHAR(16) -not so important, while DATA is BLOB field! $sql = "SELECT NAME, DATA FROM DOCUMENTS WHERE DOCNAME='$something'"; # $something was previously defined! $sth = $dbh->prepare($sql) or die "Preparing: ", $dbh->errstr; $sth->execute or die "Executing: ", $sth->errstr; $ii = 1; $name = ""; # fetching the content while (@row = $sth->fetchrow_array()) { foreach (@row) { if ($ii%2 == 1) { # so it is DOKUMENT's NAME! $name = $_; } else { # so, this is DATA! # opening FILE HANDLER for writing the content of the BLOB field open (F, ">./$name"); binmode F; # saving into the file "$name"! print F "$_"; close F; $name = ""; }; $ii++; }; }; $sth->finish; $dbh->commit or warn $dbh->errstr; $dbh->disconnect or warn $dbh->errstr; # . . . . . - rest of the code

      so, I think that deflating and inflating is not a good idea. The same reason (binary data) is why I don't want to "break a large field into several rows" but prefer to change LIMIT from the default size of 1MB to 10MB.

      But, where and how can I fix that?

      Regards, Pet.

        I can't tell what you need to change without staring at sourcecode myself in detail and trying it.

        You're right that I'd missed the important detail that you already were compressing data. In that case then compressing again won't help significantly, compression is not magic. If it could have done better the first time around, then it should have...

        I don't understand your objection to splitting one large field across rows. The fact that it is binary data is OK, what you do is use substr to turn one string into several, and you insert them into a table with 3 fields, one an external ID so that you can join to other tables, one a sequence number so that you know what order to put the pieces back in, and the last one a data field. Then use join to join the pieces together.

        This strategy will work with arbitrary binary data to arbitrary size as long as your database handles binary data and your memory handles the string manipulations.