Bobak, Mark wrote:
>
> From: Martin J. Evans [mailto:[email protected]]
> Sent: Wednesday, April 07, 2010 2:49 PM
> To: Bobak, Mark
> Cc: [email protected]
> Subject: Re: Help with handling CLOBs in Oracle and ORA-1460 error
>
> Bobak, Mark wrote:
>
> Hi Martin,
>
> Well, actually, a lot has happened since I wrote my original mail to this
> list.
>
> I realise this as I've been in contact with John.
>
> Oh, ok, good. I wasn't aware of that.
>
> First, I was able to come up with a simple, reproducible test case, and I
> provided that to John Scoles. From that, he has provided me with a simple,
> (2 lines of C code) patch to oci8.c.
> I've got that and applied it.
>
> Ok....
>
> We have built a custom DBD::Oracle based on it, and it does seem to solve
> the LOB leaking problem. The count in V$TEMPORARY_LOBS stays zero for
> CACHE_LOBS after this fix.
>
> ok, so just to be certain - before the patch you found v$temporary_lobs view
> was showing increasing CACHE_LOBS and since the patch is applied this has
> gone away?
>
> Yes, that's correct. Before the patch, if I used 'prepare_cached' instead of
> 'prepare', I would see CACHE_LOBS increasing in V$TEMPORARY_LOBS and after
> the patch, it goes away.
>
> If so that is not my case as CACHE_LOBS increases forever.
>
> Hmm...so there's still more to the puzzle.
>
> How are you retrieving your lobs? I am using reference cursors returned from
> a procedure then reading them via the lob locator.
>
> Well, I have a really simple, brain dead test case, that I provided to John,
> which demonstrates the problem. I'll send it to you. (Not sure if this
> mailing list will accept attachments, so I'll send it in a separate email
> directly to you.) But, then I also have a more complex test program, based
> on our actual codebase. Both of these demonstrate a leak before the patch,
> and lack of a leak after the patch.
>
> The simple test case I'm happy to provide, but, it doesn't really do
> *anything* with the LOB. It's truly a braindead test case, but fit the bill
> in terms of the smallest piece of code I could reproduce the problem with.
> The more complex test case may take some time to distill down to something I
> can package up.
>
> As to the definition of V$TEMPORARY_LOBS, I know what you mean. The
> documentation is pretty useless on this view.
>
> so it is - I've found very little that says enough for me to be confident in
> it.
>
> As to the actual definition, that can be seen from:
>
> SQL> select view_definition from v$fixed_view_definition where view_name
> ='V$TEMPORARY_LOBS';
>
> VIEW_DEFINITION
>
> ------------------------------------------------------------------------------------------------------------------------------------------------------
>
> select SID, CACHE_LOBS, NOCACHE_LOBS, ABSTRACT_LOBS from
> GV$TEMPORARY_LOBS where inst_id = USERENV('Instance')
>
>
>
> SQL> select view_definition from v$fixed_view_definition where view_name
> ='GV$TEMPORARY_LOBS';
>
>
>
> VIEW_DEFINITION
>
> ------------------------------------------------------------------------------------------------------------------------------------------------------
>
> select kdlt.inst_id, kdlt.kdltsno, sum(kdlt.kdltctmp),
> sum(kdlt.kdltnctmp), abs.count from X$KDLT
> kdlt, X$AB
>
> STRACT_LOB abs group by kdlt.inst_id,
> kdlt.kdltsno, abs.count order by kdltsno
>
> But, it's not particularly useful, since X$ABSTRACT_LOB and X$KDLT don't
> really get you anywhere....
>
> Anyhow, I'm hopeful that John Scoles patch will solve the problem. The
> initial testing I've done with his patch, seems to indicate that it's
> working. It probably needs a lot more testing, though.
>
>
>
> -Mark
>
> Doesn't seem to make a difference to me. I'm not at work now but I will try
> and isolate my example tomorrow.
>
> Thanks for coming back on this.
>
> Martin
>
> Thanks Martin!
>
> -Mark
>
>
>
> -----Original Message-----
>
> From: Martin Evans [mailto:[email protected]]
>
> Sent: Wednesday, April 07, 2010 12:38 PM
>
> To: Bobak, Mark
>
> Cc: [email protected]<mailto:[email protected]>
>
> Subject: Re: Help with handling CLOBs in Oracle and ORA-1460 error
>
>
>
> Martin J. Evans wrote:
>
>
>
> Bobak, Mark wrote:
>
>
>
> Hi all,
>
>
>
> I'm new to the list, and I'm a DBA, not really a Perl programmer, but I'm
> running into this issue that I hope someone can help with.
>
>
>
> I think I'm getting dangerously close to getting this to work. But, I'm just
> not quite getting there.
>
>
>
> We have an application coded in Perl w/ DBI * DBD::Oracle. Versions:
> Database: Oracle 11.2.0.1.0, Perl 5.10.1, DBI 1.609, DBD::Oracle 1.23.
>
>
>
> We've had a long standing problem of leaking temporary LOBs when handling
> lobs. (i.e., count of lobs reported in V$TEMPORARY_LOBS continues to
> increase till the session disconnects from the database.) I recently
> discovered that if you use ORA_CLOB and bind by reference (bind_param_inout)
> it solves the leak problem. But my problem now is, prepare works, bind
> works, but execute hits ORA-1460 "unimplemented or unreasonable conversion
> requested".
>
>
>
> Were you looking at CACHE_LOBS?
>
>
>
> As I said in my previous reply we have a leak with DBD::Oracle which
>
> I've never been able to track down and we use a lot of lobs.
>
>
>
> My daemon process currently has over 400 CACHE_LOBS (after 10 minutes of
>
> running) and it is steadily increasing.
>
>
>
> Do you know what CACHE_LOBS are and are you sure this is a sign of
>
> leaking temporary lobs?
>
>
>
>
>
> BTW, forgot to mention that I used the following SQL to locate the SQL
>
> that created the temporary lobs. It returns far less rows that you'd
>
> expect from CACHE_LOBS (above):
>
>
>
> select b.SQL_TEXT, a.USERNAME, a.SQL_ID, a.CONTENTS, a.SEGTYPE,
>
> a.SEGFILE#, a.SEGBLK#, a.EXTENTS, a.BLOCKS from V$TEMPSEG_USAGE a
>
> join v$sql b on a.sql_id = b.sql_id
>
>
>
> I've not found a good definition of v$temporay_lobs yet.
>
>
>
>
>
> So, my questions are:
>
> If I'm using temporary lobs that are created/managed in Perl, I should use:
>
> $sth->bind_param_inout(':pi_cit_comprep', \$paramHash->{cit_comprep},
> {ora_type => ORA_CLOB});
>
>
>
> to do the bind? I think this is correct, as the LOB leaking goes away, but,
> doing it this way, I hit ORA-1460 on CLOBs larger than 32k.
>
>
>
> Previously, I was binding with:
>
> $sth->bind_param(':pi_cit_comprep', $paramHash->{cit_comprep}, {ora_type =>
> ORA_CLOB});
>
>
>
> And this never hit the ORA-1460 error, but would leak temporary LOBs.
>
>
>
> So, how do I *both* avoid leaking temporary LOBs *and* not encounter ORA-1460
> on LOBs larger than 32k? Is this a bug? In Oracle? Perl? DBI?
> DBD::Oracle?
>
>
>
> Any thoughts or suggestions would be greatly appreciated!
>
>
>
> Thanks,
>
>
>
> -Mark
Below is a test I've written to check DBD::Oracle is leaking no more
than 1 temp lob (as it does not clean up until the next execute). It
(the test code) works for me with DBD::Oracle 1.24a and the following
patch provided by John:
int
dbd_rebind_ph_lob(SV *sth, imp_sth_t *imp_sth, phs_t *phs)
{
dTHX;
D_imp_dbh_from_sth ;
sword status;
ub4 lobEmpty = 0;
++ if (phs->desc_h && phs->desc_t == OCI_DTYPE_LOB)
++ ora_free_templob(sth, imp_sth, (OCILobLocator*)phs->desc_h);
if (!phs->desc_h) {
Also, in my tests it does not seem to break anything.
However, it is not as yet stopping the leaking of temporary lobs in my
application (which uses prepare_cached). I will investigate further.
John, it may be worth including this test in DBD::Oracle.
mar...@bragi:~/svn/dbd-oracle/trunk$ perl -I blib/lib -I blib/arch
t/91_lob_leak.pl
1..7
ok 1 - found sid 84
ok 2 - found 0 cached lobs
ok 3 - created test function
ok 4 - found 0 cached lobs
ok 5 - Not leaking temporary lobs on prepare
ok 6 - found 1 cached lobs
ok 7 - Not leaking temporary lobs on prepare_cached
#!perl -w
##
----------------------------------------------------------------------------
## 91_lob_leak.pl
## By Martin Evans, Easysoft Limited
##
----------------------------------------------------------------------------
## Test we are not leaking temporary lobs
##
----------------------------------------------------------------------------
use Test::More;
use DBI;
use Config;
use DBD::Oracle qw(:ora_types);
use strict;
use warnings;
use Data::Dumper;
unshift @INC ,'t';
require 'nchar_test_lib.pl';
$| = 1;
my $dsn = oracle_test_dsn();
my $dbuser = $ENV{ORACLE_USERID} || 'scott/tiger';
my $dbh = DBI->connect($dsn, $dbuser, '');
if ($dbh) {
plan tests => 7;
} else {
plan skip_all => "Unable to connect to Oracle ($DBI::errstr)\nTests
skipped.\n";
}
# get SID and cached lobs
# if sid not passed in we run 2 tests, get the sid and the cached lobs
# if sid passed in we run 1 test which is to get the cached lobs
sub get_cached_lobs
{
my ($dbh, $sid) = @_;
my $cached_lobs;
if (!defined($sid)) {
SKIP: {
eval {
($sid) = $dbh->selectrow_array(
q/select sid from v$session where audsid =
SYS_CONTEXT('userenv', 'sessionid')/);
};
skip 'unable to find sid', 2 if ($@ || !defined($sid));
pass("found sid $sid");
};
}
if (defined($sid)) {
SKIP: {
eval {
$cached_lobs = $dbh->selectrow_array(
q/select CACHE_LOBS from V$TEMPORARY_LOBS where sid
= ?/, undef, $sid);
};
skip 'unable to find cached lobs', 1
if ($@ || !defined($cached_lobs));
pass("found $cached_lobs cached lobs");
};
}
return ($sid, $cached_lobs);
}
sub setup_test
{
my ($h) = @_;
my ($sth, $ev);
my $fn = 'p_DBD_Oracle_drop_me';
my $createproc = << "EOT";
CREATE OR REPLACE FUNCTION $fn(pc IN CLOB) RETURN NUMBER AS
BEGIN
NULL;
RETURN 0;
END;
EOT
eval {$h->do($createproc);};
BAIL_OUT("Failed to create test function - $@") if $@;
pass("created test function");
return $fn;
}
sub call_func
{
my ($dbh, $function, $how) = @_;
eval {
my $sth;
my $sql = qq/BEGIN ? := $function(?); END;/;
if ($how eq 'prepare') {
$sth = $dbh->prepare($sql) or die($dbh->errstr);
} elsif ($how eq 'prepare_cached') {
$sth = $dbh->prepare_cached($sql) or die($dbh->errstr);
} else {
BAIL_OUT("Unknown prepare type $how");
}
$sth->{RaiseError} = 1;
BAIL_OUT("Cannot prepare a call to $function") if !$sth;
my ($return, $clob);
$clob = 'x' x 1000;
$sth->bind_param_inout(1, \$return, 10);
$sth->bind_param(2, $clob, {ora_type => ORA_CLOB});
$sth->execute;
};
BAIL_OUT("Cannot call $function successfully") if $@;
}
my ($sid, $cached_lobs);
my ($function);
SKIP: {
($sid, $cached_lobs) = get_cached_lobs($dbh); # 1 2
skip 'Cannot find sid/cached lobs', 5 if !defined($cached_lobs);
$function = setup_test($dbh); # 3
my $new_cached_lobs;
foreach my $type (qw(prepare prepare_cached)) {
for my $count(1..100) {
call_func($dbh, $function, $type);
};
($sid, $new_cached_lobs) = get_cached_lobs($dbh, $sid);
# we expect to leak 1 temporary lob as the last statement is
# cached and the temp lob is not thrown away until you next
# execute
if ($new_cached_lobs > ($cached_lobs + 1)) {
diag("Looks like we might be leaking temporary lobs from
$type");
fail("old cached lobs: $cached_lobs " .
"new cached lobs: $new_cached_lobs");
} else {
pass("Not leaking temporary lobs on $type");
}
$cached_lobs = $new_cached_lobs;
}
};
END {
if ($dbh) {
local $dbh->{PrintError} = 0;
local $dbh->{RaiseError} = 1;
eval {$dbh->do(qq/drop function $function/);};
if ($@) {
warn("function p_DBD_Oracle_drop_me possibly not dropped" .
"- check - $...@\n") if $dbh->err ne '4043';
} else {
diag("function p_DBD_Oracle_drop_me dropped");
}
}
};
Martin
--
Martin J. Evans
Easysoft Limited
http://www.easysoft.com