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

Hello fellow celibates . . . I have a problem I can't find the answer to anywhere else, I hope someone can help. I have the following piece of code:
# We put these next statements into a loop because we might encoun +ter an error # we can recover from. If so, the 'eval' will catch it and throw + it to 'if ($@)' while ($sqlSuccess == 0) { eval { $dbh->{RaiseError} = 1; $sth = $dbh->prepare($sqlStatement); $sth->execute(); print $fileHandle "rows affected - " . $sth->rows . "\n"; while (my @row=$sth->fetchrow_array()) { if (defined $queryFiles{$query}) { print queryLog "@row\n"; } $rowCount++; } $sqlSuccess = 1; }; if ($@) { print $fileHandle "caught error $@\n"; my $errString = $@; print $fileHandle "here err string is |$errString|\n"; if ($errString =~ m|Table '(.*)' already exists|) { print $fileHandle "got to here\n"; my $sql = "drop table " . $1; my $drop = $dbh->prepare($sql); $drop->execute(); } else { return "Error found - $errString"; } } }
If I invoke my program from the command line, this works fine, that is, if the table I'm trying to overwrite (an Access table) already exists, the 'eval' loop catches it and the table is dropped and the process continues. BUT . . . the user wanted it to be more GUI, so I wrote a Tkx thing, which includes this:
my $Email3; $Email3 = $mw->new_button( -text => "Email3", -command => [\&doTheWork,"Email3"], ); $Email3->g_pack(-padx=>10, -pady=>10,);
When I use the 'Email3' button, the program goes ahead and does a whole bunch of work UNTIL it gets to the case where the table already exists, then it just bombs out and nothing I have tried so far catches the error "table already exists". I'm fresh out of ideas. Anyone know what to do about this? I'm running ActiveState (downloaded and installed this week) on Windows XP Pro, service pack 3.

Replies are listed 'Best First'.
Re: eval doesn't work under tkx
by Corion (Patriarch) on Jul 15, 2010 at 20:28 UTC

    Are you sure the error is within the eval { ... } block? What does your program do when you click that button? Does it die? With what error message? Maybe the preparation or execution of $drop fails in that case.

      Yes, I'm sure - if I put 'print' statements around the lines of code, it's certain that it fails on 'execute()' and comes up with 'Table ... already exists. (SQL-42501)' If I execute the program from the command line, the 'eval' loop works properly, catches the 'already exists' error and executes the 'drop table' statement, before re-entering the loop and succeeding on the sql statement in question, which is
      select ... into <table>
      But if I run it from the Tkx window, it just dies on the sql-42501 error.
Re: eval doesn't work under tkx
by shmem (Chancellor) on Jul 16, 2010 at 04:55 UTC

    Try resetting $@ after handling the error condition, or localizing $@ to the scope of the while-loop where your eval is called.

    You could also add a local $SIG{__DIE__} to that scope and have the handler output a stacktrace to see what happens. If that handler isn't called, your program is dying somewhere else from leftovers of the failed call - again, I guess that's related to $@.

      Hi and thanks for replying. I don't know what you mean by 'resetting $@' . . . also, I'm certain that the code never gets to the error handler. If I put 'print' statements around the 'execute' part of it, like
      print "hi" $sth->execute(); print "bye"
      I'll only get "hi" as output. If I put a print statement inside the error handler, it doesn't show. But if I invoke the function without the Tkx, it works fine. I swear, the only difference between it working and not working is the presence or absence of the Tkx loop, which is in another module - the code I've shown you earlier is identical in both cases.

        I mean the following:

        while ($sqlSuccess == 0) { eval { ... }; if ($@) { my $errString = $@; $@ = ''; # <-- here print $fileHandle "caught error $errString\n"; ... } }
Re: eval doesn't work under tkx
by Gangabass (Vicar) on Jul 16, 2010 at 03:26 UTC

    I'm absolutely sure that your eval error is not related to the Tkx (as Corion said). Can you post a bit more code (ideally it must be program which produce same error: Access database with only one table, one button on Tkx and SQL query which produce table already exists error)?

      I can't put all the code up here - its confidential. But the full code of the sub in question is
      sub runQueries { my $LogFilePath = $ENV{"DATALOADFILEROOT"}."/files/"; my ($thingToDo, $fileHandle, $queriesToRunRef, $queryCodeRef, $dbh +) = @_; # 'queries' is a list of queries we need to run in a certain order # 'queryCode' is a hash list of queries, keyed on query name. my @queries = @$queriesToRunRef; my %queryCode = %$queryCodeRef; my $sqlStatement = ""; my $sth; foreach my $query(@queries) { print $fileHandle "next query is $query\n"; if (defined $queryCode{$query}) { print "1\n"; my $queryLogFile = $LogFilePath . $query; my $sqlSuccess = 0; print "2\n"; if (defined $queryFiles{$query}) { print $fileHandle "writing to file - $queryLogFile\n"; open(queryLog, ">$queryLogFile") or return "Couldn't o +pen $logFilePath . $queryLogFile - $!"; } print "3\n"; my $sqlStatement = $queryCode{$query}; my $rowCount = 0; print $fileHandle "code - $sqlStatement\n"; # We put these next statements into a loop because we migh +t encounter an error # we can recover from. If so, the 'eval' will catch it a +nd throw it to 'if ($@)' while ($sqlSuccess == 0) { print "4\n"; eval { print "5\n"; #$dbh->{RaiseError} = 1; print "6\n"; $sth = $dbh->prepare($sqlStatement); print "7\n"; $sth->execute(); print "8\n"; print $fileHandle "rows affected - " . $sth->rows +. "\n"; #my $result = $sth->fetchall_arrayref({}); while (my @row=$sth->fetchrow_array()) { if (defined $queryFiles{$query}) { print queryLog "@row\n"; } $rowCount++; } $sqlSuccess = 1; }; if ($@) { print $fileHandle "caught error $@\n"; my $errString = $@; print $fileHandle "here err string is |$errString| +\n"; if ($errString =~ m|Table '(.*)' already exists|) { print $fileHandle "got to here\n"; my $sql = "drop table " . $1; my $drop = $dbh->prepare($sql); $drop->execute(); } else { return "Error found - $errString"; } } } if ($rowCount > 0 && (defined $queryFiles{$query}) && $que +ryFiles{$query} eq "stop") { return "query '$query' wrote $rowCount rows - this nee +ds investigating\n"; } if (defined $queryFiles{$query}) { print $fileHandle "Wrote $rowCount rows\n"; close querylog; } } else { print $fileHandle "******* NO CODE FOR THIS KEY ********\n +"; return "Can't proceed - no code for query $query\n"; } $sqlStatement = "update dataLoadProgress set progress = '" . $ +query . "'"; $sth = $dbh->prepare($sqlStatement) or return $DBI::errstr; $sth->execute() or return $DBI::errstr; } $sqlStatement = "update dataLoadProgress set progress = '" . $thin +gToDo . ":Success'"; $sth = $dbh->prepare($sqlStatement) or return $DBI::errstr; $sth->execute() or return $DBI::errstr; return "Success"; }
      This sub is kept in a '.pm' file which is called in a 'use' statement in the top-level script. If the top-level script is a command-line module, with no Tkx in it, the 'eval' works properly. If the top-level script has Tkx in it, it dies without going to the exception handler. I say this because the output from all those print statements is: 1 2 3 4 5 6 7 and the print statement in the error handler doesn't show The tkx code is extremely simple. Here it is:
      Tkx::package_require("style"); Tkx::style__use("as", -priority => 70); my $mw = Tkx::widget->new("."); $mw->g_wm_title("PSC Dataload"); $mw->g_wm_minsize(300,200); my $Email1; $Email1 = $mw->new_button( -text => "Email1", -command => [\&doTheWork,"Email1"], ); $Email1->g_pack(-padx=>10, -pady=>10,); my $Email2; $Email2 = $mw->new_button( -text => "Email2", -command => [\&doTheWork,"Email2"], ); $Email2->g_pack(-padx=>10, -pady=>10,); my $Email3; $Email3 = $mw->new_button( -text => "Email3", -command => [\&doTheWork,"Email3"], ); $Email3->g_pack(-padx=>10, -pady=>10,); my $Forms; $Forms = $mw->new_button( -text => "Forms", -command => [\&doTheWork,"Forms"], ); $Forms->g_pack(-padx=>10, -pady=>10,); my $LDG; $LDG = $mw->new_button( -text => "LDG", -command => [\&doTheWork,"LDG"], ); $LDG->g_pack(-padx=>10, -pady=>10,); my $Load; $Load = $mw->new_button( -text => "Do Data Load", -command => [\&doTheWork,"Load"], ); $Load->g_pack(-padx=>10, -pady=>10,); my $Log; $Log = $mw->new_button( -text => "Write Logs", -command => [\&doTheWork, "Logs"], ); $Log->g_pack(-padx=>10, -pady=>10,); $formalityList = $mw->new_button( -text => "Write Formality List", -command => \&writeFormalityList, ); $formalityList->g_pack(-padx=>10, -pady=>10,); my $b; $b = $mw->new_button( -text => "Exit", -command => sub { $b->m_configure( -text => "Bye Nora :\)", ); cleanUp(); Tkx::after(1500, sub {$mw->g_destroy; }); }, ); $b->g_pack(-padx=>10, -pady=>10,); Tkx::tk___messageBox( -parent => $mw, -icon => "info", -title => "Tip of the Day", -message => "Please be nice!", );
      Then there's some preliminaries and initialization, then
      #Start the GUI loop Tkx::MainLoop();
      If I remove that from the top-level program, the eval statement in the 'use' module works.

        My guess is, like shmem suspects, that you (or Tkx) are somewhere freeing up an object which uses eval in its destructor, which cleans out $@, so you never notice the exeption.

        Try::Tiny tries to prevent that, or you can code your eval like this:

        my $ok = eval { ... $sth->execute(); ... 1; }; if (! $ok) { print "Caught error: $@"; };
Re: eval doesn't work under tkx
by aquarium (Curate) on Jul 16, 2010 at 04:08 UTC
    can't remember exactly but I'm fairly certain that DBI provides such basic additional functionality as checking for table existence. so you shouldn't have to eval in the first place. if you cannot find this in DBI docs or others help, there's also the implementation specific (M$) system table in Access, which contains information about all the tables etc and can be queried.
    the hardest line to type correctly is: stty erase ^H
      I used the 'eval' because the sql statement in question is one of many that are got from a hash list populated by another program at another time, only some of which contain 'select ... into' statements. I could parse the sql statement and try to figure out what the table name is, then check for its existence, but it seemed to me the 'evel' was a more elegant way of doing it. Again, the perplexing thing, as I said to shmem, is that the code I showed you, which lives in its own module, is utterly unchanged whether I call it from a command-line module, or a tkx module. The command-line invocation works, the tkx invocation doesn't. I'm stumped.