Re: Help with handling CLOBs in Oracle and ORA-1460 error
[email protected] ("Martin J. Evans")
| Newsgroups | perl.dbi.users |
|---|---|
| Message-ID | <[email protected]> |
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. martin@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