insensate has asked for the wisdom of the Perl Monks concerning the following question:

I currently open a handle to send input to an sqlplus process. The input is printed to the handle within a for{} block. The statements that are passed have the potential to cause the sqlplus process to exit. (whenever SQLERROR exit...) If this occurs, I want to last out of the loop. My question is twofold:

1: What happens to the handle when the process is terminated by something other than calling close()?
2:Can I achive my desired results by saying print HANDLE "statement" or last;?

Thanks for your insight.
-Jason

Replies are listed 'Best First'.
Re: Managing Process Handles
by graff (Chancellor) on Oct 19, 2002 at 07:13 UTC
    1: What happens to the handle when the process is terminated by something other than calling close()?

    Having played with this a bit just now, it looks like a file handle that is writing to a pipe will stay open until the system generates a SIGPIPE, at which point the next attempt to print to that handle will return false. The problem is that you end up doing at least one print after the pipe process is actually dead, before there's a SIGPIPE. Consider the following script (taking the place of sqlplus in your app):

    #!/usr/bin/perl while (<>) { die "Pipe process is now dead\n" if ( /kill/ ); print "Pipe process got $_"; exit(0) if ( $. == 5 ); }

    That will exit with success after it prints the five lines from STDIN, unless one of those lines contains the word "kill", in which case it dies with an error.

    Now consider this routine for feeding that script from a loop; there are a couple variants on this theme, which you can control with the "config" variables "$useSig,$useKill,$useSleep":

    #!/usr/bin/perl use strict; my ($useSig,$useKill,$useSleep) = (1,0,1); my $deadPipe = 0; $SIG{PIPE} = \&catchSIGPIPE if $useSig; $|++; # Don't buffer stdout open( TOPIPE, "| /tmp/test.perl" ) || die "Pipe process would not star +t\n"; select TOPIPE; $|++; # Don't buffer TOPIPE my @test = qw/zero one two three four five six/; $test[2] = "kill" if $useKill; print STDOUT "Config: useSig=$useSig, useSleep=$useSleep, useKill=$use +Kill\n"; for (@test) { last if ( $useSig && $deadPipe ); print STDOUT "sending $_ to pipe process\n"; print TOPIPE "$_\n" or last; sleep 2 if $useSleep; } sub catchSIGPIPE { $deadPipe++; }

    The results are interesting; obviously, if you allow output buffering on the pipe handle, it will take longer to get a SIGPIPE interrupt, but even when you turn off buffering, the timing of the "for" loop can determine whether the signal will be caught -- when I turned off the "$useSleep" on my Pentium/linux, all seven iterations of "sending ..." were printed first, then the 3 or 5 reports from the pipe process, depending on whether "$useKill" was on or off.

    2:Can I achive my desired results by saying print HANDLE "statement" or last;?

    The short answer is "no". You'll always write at least one statement to the handle after the handle's process has ended; if the handle is buffered and/or the loop iterates quickly, you'll write many statements before realizing that the handle's process has ended.

    So my question is: are you really not able to look at one of these statements, before you write it to the pipe handle, and figure out whether it will cause sqlplus to exit? If exits might be caused by erroneous statements, then what other sorts of mayhem might be inflicted on table data without causing sqlplus to exit? ("Ooops, that last list of statements had "delete" instead of "update"...)

    Another question might be: why are you writing statements to sqlplus through a pipe handle, rather than using DBI and DBD::Oracle? Granted, if you don't have these installed and/or haven't used them yet, it's a steep learning curve at first, but a short one, usually; and it's also true that these are slower than "native" oracle apps like sqlplus and sqlloader. Still, the overall result would be more robust than losing track of which statements were executed and which were written to a dead pipe...

Re: Managing Process Handles
by Zaxo (Archbishop) on Oct 19, 2002 at 07:17 UTC
    1. The handle will be closed at block exit if the handle is lexical, at script end otherwise.
    2. Yes.

    After Compline,
    Zaxo

      Hmm. Okay, maybe I was being a bit rash when I said the short answer to "2." was "no" -- what I should have said was "yes, sort of, but you don't really get your desired result". At least, as I understand the OP, it would not be desirable to write a statement to the pipe handle after the downstream process has ended.

      And based on my experiments (simple-minded, but functional, I think), there's no way around losing at least one print statement after the pipe process exits (because the previous statement caused that process to terminate on its own initiative).

      Another thought -- not pretty, but... -- opening a pipe returns a pid, right? and maybe there's something that one could do within a loop, before printing to the pipe handle, to see if that pid is still running.

        there's no way around losing at least one print statement after the pipe process exits

        So interleave innocuous commands between the other messages.

        My suggestion is based on the assumption that a brittle kluge is all that is needed.

Re: Managing Process Handles
by jjdraco (Scribe) on Oct 19, 2002 at 02:43 UTC
    if I remember correctly, the handle will close when it goes out of scope or the script terminates, but some one else will have to verify that, I can't find where it says that in any of my books right now. on that not note, if you can you would be better off closing the handle yourself right be fore the last statement.
    as far as the print HANDLE "statement" or last; have you tried it, the worste that would happen is it wouldn't work. I don't know. I'm going to run a test script to see what happens and I'll let you know.

    Update: I was unable to get print HANDLE "statement" or last; to work in the for loop like you where wanting to do, the program kept crashing, and I have been unsuccessful in conferming what I said about handle scope, I've run acouple of searches on this site and couldn't find a messages that directly covered it.

    jjdraco
    learning Perl one statement at a time.
Re: Managing Process Handles
by sharkey (Scribe) on Oct 19, 2002 at 17:26 UTC
    Just to ask the obvious question: Is there a reason you are not using DBI / DBD::Oracle to do this?
      Oracle is installed on the box...DBI is not, and I lack the power to change that.
      -Jason
        That's no excuse. You can still download and install DBI/DBD modules in your own private lib directory.

        perl Makefile.PL lib=~/lib

        For more info see this node and the reply, and the definitive guide.