Re: odbc_lob_read chunk is short

"Martin J. Evans" <[email protected]>
Newsgroups gmane.comp.lang.perl.modules.dbi.general
Organization at home
Message-ID <[email protected]>
On 06/09/2013 17:52, Martin J. Evans wrote:
> On 06/09/13 09:18, Martin J. Evans wrote:
>> On 05/09/13 20:28, Mike Grau wrote:
>>> Hello,
>>>
>>> I am completely new at this, but I am trying to read a BLOB in chunks
>>> via ODBC in Oracle 11g using DBD::ODBC-1.43, unixODBC-2.3.1 and
>>> oracle-instantclient11.2-odbc-11.2. I can read an entire BLOB in one
>>> read and write it to the filesystem, but I really need to be able to do
>>> it in chunks, I think.
>>>
>>> Reading the BLOB in chunks, but I am always getting one byte less than
>>> requested, losing the last byte, which results in a corrupt document (a
>>> PDF) when written to the filesystem. I'm at a loss as to why this is
>>> happening and how to correct it.
>>>
>>> Using this snippet based on the example provided by DBD::ODBC...
>>>
>>> while($len = $s->odbc_lob_read(1, \my $x, 8, {TYPE => 999})) {
>>>      print "len=$len, x=$x\n";
>>> }
>>>
>>> ... produces this output:
>>>
>>> len=7, x=435886
>>> len=7, x=0000 n
>>> len=7, x=0001282
>>> len=7, x=60 0000
>>>
>>> And a tracefile shows the same.
>>>
>>> SQLGetData(col=1,type=-2)=1 (retlen=2046252)
>>>   <- odbc_lob_read= ( 7 ) [1 items] at /root/lob_read.pl line 45
>>>   >> odbc_lob_read DISPATCH (DBI::st=HASH(0x203c940) rc1/1 @5 g2 ima0
>>> pid#11494) at /root/lob_read.pl line 45
>>>   -> odbc_lob_read for DBD::ODBC::st
>>> (DBI::st=HASH(0x203c940)~0x203c9d0 1
>>> SCALAR(0x1e0a440) 8 HASH(0x203c970)) thr#1cea010
>>> SQLGetData(col=1,type=-2)=1 (retlen=2046244)
>>>   <- odbc_lob_read= ( 7 ) [1 items] at /root/lob_read.pl line 45
>>>   >> odbc_lob_read DISPATCH (DBI::st=HASH(0x203c940) rc1/1 @5 g2 ima0
>>> pid#11494) at /root/lob_read.pl line 45
>>>   -> odbc_lob_read for DBD::ODBC::st
>>> (DBI::st=HASH(0x203c940)~0x203c9d0 1
>>> SCALAR(0x1e0a440) 8 HASH(0x203c9a0)) thr#1cea010
>>>
>>> Can someone please give me some guidance how to get the full chunk
>>> requested?
>>>
>>> I'm using
>>>    unixODBC-2.3.1 - built from the tarball
>>>    DBD-ODBC-1.43  - installed from the tarball (perl Makefile.PL; make;
>>>    make install)
>>>    oracle-instantclient11.2-odbc-11.2.0.3 installed from RPM
>>>    DBI is the distro's perl-DBI-1.617-4.1.1.x86_64 (openSUSE 12.3)
>>>    The database and client are both x86_64
>>>
>>> The instant client is link against libodbcinst.so.1 and unixODBC now
>>> provides libodbcinst.so.2, so I symlinked them:
>>> libodbcinst.so.1 -> /usr/local/lib64/libodbcinst.so.2
>>> Ugly, but seems to work.
>>>
>>> Ultimately I want to index documents stored in Oracle with Sphinx and so
>>> must use ODBC. I'd like to know that this much is working before moving
>>> on to Sphinx.
>>>
>>> TIA -- Mike G.
>>>
>>
>> Bear with me and I'll take another look at that code. If I cannot see
>> anything I might need to send you a new version with slightly
>> different logging. By all means nag me if I don't get back to you by
>> tomorrow.
>>
>> Martin
>
> When I tested odbc_lob_read it does not miss bytes out but it does
> return at most buf len - 1 bytes and this is because SQLGetData always
> puts a trailing NUL byte in the buffer if the type is a char. So if you
> want 8 bytes at a time you currently need to pass a buf len of 9 if you
> want them back as chars. My test was retrieving bytes back as SQL_C_CHARs.
>
> It is arguable, DBD::ODBC should add 1 knowing that SQLGetData will
> terminate with a NUL byte if the type is a SQL_C_CHAR. I'd accept an RT
> for that as if everyone is using odbc_lob_read as documented they should
> never assume the buffer returned contains buf len bytes - they should
> use the returned length.
>
> As for that TYPE=>999 which I'm assuming you got from the example
> included with DBD::ODBC, it was a mistake that got checked in when I was
> testing the TYPE parameter - set it to SQL_BINARY or omit it if your
> sure Oracle has this down as a binary column.
>
> The bug in DBD::ODBC is that if you retrieve a lob as SQL_BINARY it is
> still assuming the buffer is NUL terminated and it is not. The secondary
> bug is TYPE is coded as Type. The following patch should fix both of these:
>
> $ git diff
> diff --git a/ODBC.xs b/ODBC.xs
> index 574dc47..8cc8e1a 100644
> --- a/ODBC.xs
> +++ b/ODBC.xs
> @@ -155,7 +155,7 @@ odbc_lob_read(sth, colno, bufsv, length, attr = NULL)
>        if (attr) {
>            SV **svp;
>            DBD_ATTRIBS_CHECK("odbc_lob_read", sth, attr);
> -         DBD_ATTRIB_GET_IV(attr, "Type", 4, svp, sql_type);
> +         DBD_ATTRIB_GET_IV(attr, "TYPE", 4, svp, sql_type);
>        }
>        if (SvROK(bufsv)) {
>           bufsv = SvRV(bufsv);
> diff --git a/dbdimp.c b/dbdimp.c
> index 5abfb27..ff3226e 100644
> --- a/dbdimp.c
> +++ b/dbdimp.c
> @@ -5938,7 +5938,10 @@ IV odbc_st_lob_read(
>                         "Driver did not return the lob length -
> SQL_NO_TOTAL)");
>               return -1;
>           }
> -        retlen = length - 1;
> +        retlen = length;
> +        if (col_type == SQL_C_CHAR) {
> +            retlen -= 1;
> +        }
>       } else if (rc == SQL_SUCCESS) {
>           if (len == SQL_NULL_DATA) {
>               return 0;
>
>
> If you can apply those changes to your a newly download copy of
> DBD::ODBC and rebuild it does that fix your issue? If you cannot do that
> I'll try and release a new test version tonight but I'm mid moving git
> repositories right now so I'm not promising that.
>
> Martin
>
>

Sorry but the github app is now totally broken so I cannot even clone 
repositories on my Windows machine and so I'm unable to generate a test 
release of DBD::ODBC. I'll let you know if I manage to fix it. In the 
mean time if you can apply the above patch that is your best solution.

Martin
-- 
Martin J. Evans
Wetherby, UK
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.