non-blocking upgrade for part_pkg, #29155
[freeside.git] / FS / FS / Cursor.pm
index f3bc1e2..d94151f 100644 (file)
@@ -2,12 +2,12 @@ package FS::Cursor;
 
 use strict;
 use vars qw($DEBUG $buffer);
-use base qw( Exporter );
-use FS::Record qw(qsearch dbdef dbh);
-use Data::Dumper;
+use FS::Record;
+use FS::UID qw(myconnect);
 use Scalar::Util qw(refaddr);
 
-$DEBUG = 0;
+$DEBUG = 2;
+
 # this might become a parameter at some point, but right now, you can
 # "local $FS::Cursor::buffer = X;"
 $buffer = 200;
@@ -39,24 +39,28 @@ and returns an FS::Cursor object to fetch the rows one at a time.
 sub new {
   my $class = shift;
   my $q = FS::Record::_query(@_); # builds the statement and parameter list
+  my $dbh = myconnect();
 
   my $self = {
     query => $q,
     class => 'FS::' . ($q->{table} || 'Record'),
     buffer => [],
+    dbh   => $dbh,
   };
   bless $self, $class;
 
   # the class of record object to return
   $self->{class} = "FS::".($q->{table} || 'Record');
 
+  # save for later, so forked children will not destroy me when they exit
+  $self->{pid} = $$;
+
   $self->{id} = sprintf('cursor%08x', refaddr($self));
   my $statement = "DECLARE ".$self->{id}." CURSOR FOR ".$q->{statement};
 
-  my $dbh = dbh;
   my $sth = $dbh->prepare($statement)
     or die $dbh->errstr;
-  my $bind = 0;
+  my $bind = 1;
   foreach my $value ( @{ $q->{value} } ) {
     my $bind_type = shift @{ $q->{bind_type} };
     $sth->bind_param($bind++, $value, $bind_type );
@@ -101,6 +105,15 @@ sub refill {
   scalar @$result;
 }
 
+sub DESTROY {
+  my $self = shift;
+  return unless $self->{pid} eq $$;
+  $self->{dbh}->do('CLOSE '. $self->{id})
+    or die $self->{dbh}->errstr; # clean-up the cursor in Pg
+  $self->{dbh}->rollback;
+  $self->{dbh}->disconnect;
+}
+
 =back
 
 =head1 TO DO
@@ -111,6 +124,12 @@ Replace all uses of qsearch with this.
 
 Doesn't support MySQL.
 
+The cursor will close prematurely if any code issues a rollback/commit. If
+you need protection against this use qsearch or fork and get a new dbh
+handle.
+Normally this issue will represent itself this message.
+ERROR: cursor "cursorXXXXXXX" does not exist.
+
 =head1 SEE ALSO
 
 L<FS::Record>