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