More on closing Oracle ref cursors



Hi. I'm working on a project using DBD::Oracle 1.16, DBI 1.43, Perl 5.6,
WinXP, and Oracle 8.1.7.4.

My script runs through a loop many times, and each time through it calls
two PL/SQL stored procs via a wrapper class I've built. At some point in
the loop I run out of ref cursors; Oracle returns a "maximum cursors
exceeded" error. Obviously the cursors that get opened and returned by
the stored procs aren't getting closed by Perl for some reason.

I was under the impression from previous postings on this list that
Oracle's ref cursors get closed automatically when the Perl variable
holding them goes out of scope. As far as I can see, the variable
holding the ref cursor goes out of scope at the end of each loop. But in
case Perl doesn't close the cursors automatically (there did seem to be
some debate on the issue) I'm trying and failing to close them
explicitly myself.

I've included the relevant bits of my code below -- if you have any
advice or experience on the issue, I'd love some pointers.

Thanks very much,

Mike

==== DETAILS ====

The way I've built my system, I have a wrapper class that stores a DBI
connection object in $self->dbh. Here's the constructor:

sub new {
my $class = shift;
my %args = (%DEFAULTS, @_);
DBI->trace_msg("Connecting to database\n");
my $dbh = DBI->connect( "dbi:Oracle:$DBNAME",
$DBUSER, $DBPASS, \%args )
or die "Unable to connect to $DBNAME: $DBI::errstr\n";
DBI->trace_msg("Storing database handle ($dbh)\n");
my $self = { dbh => $dbh };
bless $self, $class;
return $self;
}

Then I have a generic method that calls stored procs via the AUTOLOAD
method. Here's AUTOLOAD:

sub AUTOLOAD {
my $self = shift;
die "Not an object\n" unless ref $self;
my $name = our $AUTOLOAD;
$name =~ s/^.*::([^:]+)$/\1/;
return if $name eq 'DESTROY';
if ($name =~ /^(Get|Create|Delete|Add|Update)/ ) {
return $self->_do_proc(uc $1, $name, @_);
} elsif (exists $self->{$name}) {
if (@_) {
return $self->{$name} = shift;
} else {
return $self->{$name};
}
} else {
warn "Attempt to access non-existent method/property: $name\n";
}
}

And here's my generic _do_proc method:

sub _do_proc {
my $self = shift;
my ($type, $proc, @args) = @_;
unless ($type =~ /^(GET|CREATE|DELETE|ADD|UPDATE)$/) {
die "Unknown type ($type) passed to _do_proc!\n";
}
my $inout = 0;
$inout++ if $type =~ /^(GET|CREATE)$/;
my @params = ();
for (my $i = 0; $i < @args; $i++) {
push @params, ":a$i";
}
push @params, ":out" if $inout;
my $arg_str = join ', ', @params;
my $sql = __unindent(<<" SQLEND");
~BEGIN
~ $PKGNAME.$proc($arg_str);
~END;
SQLEND
my $sth = $self->dbh->prepare($sql);
#print "Prepared statement \n".$sth->{'Statement'}."\n";
my $out;
for (my $i = 0; $i < @args; $i++) {
$sth->bind_param($params[$i], $args[$i]);
}
if ($type eq 'GET') {
$sth->bind_param_inout(':out', \$out, 0, { ora_type => ORA_RSET
} );
} elsif ($type eq 'CREATE') {
$sth->bind_param_inout(':out', \$out, 10);
} else {
$out = 1;
}
my $ok = $sth->execute;
if ($ok) {
#print "Successfully executed statement\n";
} else {
return undef;
}
return $out;
}

Then in order to close the cursor returned from _do_proc I have a
CloseCursor method:

sub CloseCursor {
my ($self, $cursor) = @_;
unless (defined $cursor) {
warn "No cursor passed to CloseCursor!\n";
return 0;
}
my $sql = __unindent(<<" SQLEND");
~BEGIN
~ close :cur;
~END;
SQLEND
my $sth = $self->dbh->prepare($sql);
print $sth->{'Statement'};
## Method 1
$sth->bind_param_inout(':cur', \$cursor, 0, { ora_type => ORA_RSET }
);
## Method 2
#$sth->bind_param(':cur', $cursor, { ora_type => ORA_RSET } );
## Method 3
#my $ok = $self->dbh->do("BEGIN CLOSE ?; END;", undef, $cursor);
if ($sth->execute) { ## For Methods 1 and 2
#if ($ok) { ## Part of Method 3
return 1;
} else {
die "Error closing cursor: ".$self->dbh->errstr."\n";
return 0;
}
}

You can see the different methods there I've tried closing the cursor.

So my script basically looks like this:

my $sth = $db->GetSomething($input);
if ($db->err) { print $db->errstr; exit; }
my $data = $sth->fetchrow_hashref;
$sth->finish;
$db->CloseCursor($sth);

The error I get from Method 1 is:

ORA-01023: Cursor context not found (Invalid cursor number) (DBD: odescr
failed)

>From Method 2 I get:

ORA-01008: not all variables bound (DBD: oexec error)

And from Method 3 I get:

panic: dbd_rebind_ph alen 23 > maxlen 0 (incnul 0) at
C:/Perl/site/lib/DBI.pm line 1428.

I think I'm doing everything right; I've found all three methods from
different parts of the DBI and DBD::Oracle documentation. But obviously
something isn't working.

So if you have any suggestions as to how to make this work, I'd really
appreciate it!

Thanks.
.



Relevant Pages

  • Re: Cursor Replacement
    ... This is a question for the database theory newsgroup. ... of cursors, I find only a few situations: ... loop to build a temp table of keys, then loop inside a loop thru a ... second table to delete rows with those keys. ...
    (microsoft.public.sqlserver.programming)
  • Re: GCC 4.0 Ada.Containers Cursor danger.
    ... message headers, I think you are responding to my post?) ... able fast to do whatever i want whithout cursors under ADT. ... There is a performance cost if a library forces its user to ...
    (comp.lang.ada)
  • Re: GCC 4.0 Ada.Containers Cursor danger.
    ... what makes you sure that the programmers writing the procedure for Process in Generic_Iterate above will not use the wrong key in their procedure? ... more safe than Cursors? ... reinvent and reconsider every cursor operation as a smart pointer ... after the loop. ...
    (comp.lang.ada)
  • Re: Oracle cursor help
    ... Switching context from PL/SQL for the loop to SQL for the delete takes time ... ctr = 0; ... DELETE/WHERE clauses consume too many resources. ... WHERE clauses defeated the efficiency of cursors ...) ...
    (perl.dbi.users)
  • RE: More on closing Oracle ref cursors
    ... I can confirm the cursors stay in existence until the connect handle is disconnected/destroyed. ... More on closing Oracle ref cursors ... The way I've built my system, I have a wrapper class that stores a DBI ... Then I have a generic method that calls stored procs via the AUTOLOAD ...
    (perl.dbi.users)