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