#!perl -w ##---------------------------------------------------------------------------- ## 36lob_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, '',,{ PrintError => 0, }); if ($dbh) { plan tests => 7; } else { $dbh->{PrintError}=1; plan skip_all => "Unable to connect to Oracle"; } # 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; if ($function){ eval {$dbh->do(qq/drop function $function/);}; if ($@) { diag("function p_DBD_Oracle_drop_me possibly not dropped" . "- check - $@\n") if $dbh->err ne '4043'; } else { note("function p_DBD_Oracle_drop_me dropped"); } } } }