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

I have a file with several sql language insert statements. I want perl to use the import file feature built into mysql. Problem is i don't see the nice error statement i get when i run the command from a shell. When i run this command on my linux server it tells me i have an sql syntax error and what line it is on.
mysql -u USERNAME -pPASSWORD MYDATABASE < /home/www/commands.sql

I am trying to have perl do the same thing, and print the error message out to me. But no error output. I've tried this a couple of ways to no avail, the code below does not return the error message i need.

use strict; my $usr = "USERNAME"; my $pass = "PASSWORD"; my $db = "MYDATABASE"; my $file = "/var/www/commands.sql"; open(HAND,"mysql -u $user -p$pass $db < $file |") or return "fail"; while (<HAND>){ print $_; }close HAND; exit;

Note, if there are no sql errors in my file, mysql will carry out my sql commands and insert the rows as expected.

thanks for reading,
jtrue

Replies are listed 'Best First'.
Re: No output using mysql import via perl
by Caron (Friar) on Apr 04, 2004 at 13:15 UTC

    If you want to use Perl for this task, why not using the DBI?

    #!/usr/bin/perl -w use strict; use DBI; my $db = 'dbname'; my $user = 'Caron'; my $password = 'secret!'; my $filename = shift or die "filename required!\n"; my $dbh = DBI->connect( "dbi:mysql:$db", $user, $password, {RaiseError=>1, PrintError=>0}) or die $DBI::errstr; open FILE, "<$filename" or die; { local $/ = ";\n"; while (<FILE>) { eval {$dbh->do($_)}; if ($@) { warn "Error at line $. ($DBI::errstr)\nstatement: $_\n"; # eventually, issue a die call here } } } close FILE;

    This way, you can have better control of the errors.

    HTH

Re: No STDERR output using mysql import via perl
by jarich (Curate) on Apr 04, 2004 at 13:30 UTC
    Okay, the simple answer to your problem is almost certainly that the mysql error on bad SQL is printed to STDERR. What you're doing in that open statement however, only pipes STDOUT to your program (and STDERR goes to where ever STDERR has been told to go (usually to the same place as your script's STDOUT)).

    You can ask for your new (open) process' STDERR to be redirected to the same place as its STDOUT by doing the following:

    open(HAND,"mysql -u $user -p$pass $db < $file | 2>&1") or return "failed: $!";
    the 2>&1 says "redirect fileno 2 (STDERR) (for this process) to the same place as fileno 1 (STDOUT) (for this process)". Note this won't work if you close any of STDIN, STDOUT, STDERR and then open another file. :)

    When using open in the way you do above, you have to be very sure of the values in $user, $pass, $db and $file. If you read in these values from a user (whether on the commandline, through a GUI or through a webpage) you need to use taint checking and make sure that these values look right.

    Consider the following example.

    my $user = <STDIN>; chomp $user; open (HAND, "mysql -u $user") or die "error $!"; # if $user = "fred; rm -rf *;" then your script will # try very very hard to remove everything in its # working directory.
    To avoid the problems above you need to ensure that the username looks like a real username:
    #!/usr/bin/perl -wT # the -T flag turns on taint checking use strict; my $user = <STDIN>; chomp $user; # make sure that $user only contains word characters # these are a-z, A-Z, 0-9 and _ # if this is so, assign the value found to $user. # otherwise, die with an error unless(($user) = ($user =~ /^(\w+/)$)) { die "Invalid username: $user\n"; } open (HAND, "mysql -u $user") or die "error $!"; # only good values of $user get to here

    If you're always hardcoding these values into a script then you should be fine. (Presumably anyone who has access to edit your script can already add lines in it to delete everything if they so desire...).

    One final security warning is that doing what you're doing in your script won't hide your password from people looking at ps (the processes listing). When you open a process this way, you are creating another process and its commandline arguments are there for everyone (with access) to see. If hiding your password is essential you may need to do this another way.


    The less simple answer depends on how much further this script is going to progress. If all you want is a simple wrapper around mysql so you don't have to type your username and password all the time then this is fine.

    If you want to be able to do stuff with the mysql output etc, then you really ought to consider using DBI or one of the many other database modules.

    I hope this helps.

    jarich

Re: No output using mysql import via perl
by ambrus (Abbot) on Apr 04, 2004 at 19:48 UTC

    Are you sure you want to use

    return "fail";
    in the code? Shouldn't it be
    die "fail";
    by some chance?
Re: No output using mysql import via perl
by true (Pilgrim) on May 28, 2004 at 08:39 UTC
    Finally got this thing working, but i had to switch the order of where i put the "2>&1" :) Here's an example of reading my current apache virtual host config. (See, i had the same trouble reading system output from apache as i did from sendmail).
    open(CHK,"httpd -S 2>&1 |") || die "error vhosts $!\n"; while (my $li = <CHK>){ print "--$li\n"; }close CHK;
    jtrue