Perl won't execute SQL statement in a while loop - perl

I wrote this piece of code: it is supposed to keep checking names until it finds one that does not exist, and then it should go on.
while ($repeating == 1) {
$new_name = $i . "_" . $file;
my $sql= "SELECT file_name FROM PDFdocument WHERE user_id = '$id' AND file_name = '$new_name' ";
my $sth = $dbh->prepare($sql);
$sth->execute();
while (my #row = $sth->fetchrow_array) {
//never enters here
if ($new_name ne $row[0]) {
$repeating = 0;
}
}
$i++;
}
It never enters the second while loop, so it gets stuck in this repeating loop. I don't know why it does not work; I do some other sql statements before and they all work. This is the only one that does not work.
Any help?

The problem is that you won't get any rows back if the name does not exist. The solution is to just check if you do get any rows - otherwise the file name mus be unused. BTW, let DBI escape stuff you sent to the database. This should work:
while ($repeating == 1) {
$new_name = $i . "_" . $file;
# the question marks are placeholders
my $sql= "SELECT file_name FROM PDFdocument WHERE user_id = ? AND file_name = ? ";
my $sth = $dbh->prepare($sql);
# filling the placeholders while executing
$sth->execute($id, $new_name);
if(!$sth->fetch) {
# no rows found? this name must be fresh
$repeating = 0;
}
$i++;
}
Edit: As #ikegami mentioned in a comment, the behavior of $sth->rows depends on the driver, so it might return different values for different database engines when dealing with SELECT statements (see also the DBI docs. Asking the driver to fetch a row should work the same on all drivers.
Keep in mind that this is susceptible to race conditions, i.e. if two scripts run at the same time, they might both chose the same "unused" filename. Make sure you're using some kind of locking mechanism to avoid that.

When you finally find the $i you should use, $sth->fetchrow_array returns an empty list, so = returns 0, so the loop isn't entered.
Solution 1:
my $new_name;
for (my $i=1; ; ++$i) {
$new_name = $i . "_" . $file;
$dbh->selectrow_arrayref(
"SELECT 1 FROM `PDFdocument` WHERE `user_id` = ? AND `file_name` = ?",
undef,
$id, $new_name,
)
and last;
}
Solution 2:
my $i = $dbh->selectrow_array(
"
SELECT CAST(LEFT(`file_name`, LOCATE("_", `file_name`)-1) AS INT) AS `i`
FROM `PDFdocument`
WHERE `user_id` = ?
AND `file_name` LIKE ?
ORDER BY DESC `i`
LIMIT 1
",
undef,
$id, "%\\_\Q$file\E"
);
++$i;
my $new_name = $i . "_" . $file;
Note the use of placeholders. Your buggy way of building the SQL statement leaves you vulnerable to malfunctions if not attacks.

Related

How to handle a Perl array the correct way?

I'm quite new in writing Perl code, so I'm hoping for some help here.
I'm querying a database, which works as long as I'm just selecting one row and write the queried variables into some variables in another database.
As soon as I want to create an array and do with multiple variables, what I am able to do with a single one, my code doesn't work.
Do you spot the mistake?
use strict;
use warnings;
use DBI;
my $host = "host";
my $database = "DBI:mysql:database";
my $tablename = "table";
my $user = "user";
my $pw = "password";
my $sql = "SELECT COUNT(`attribute`) FROM `table` WHERE `attribute` =".GETVALUE("attribute");
my $connect = DBI->connect($database, $user, $pw) or die "Couldn't connect to database: " . DBI->errstr;
my $sth = $connect->prepare($sql);
$sth->execute();
my $row;
my $sql2 = "SELECT `attribute2`,`name`, `adress` FROM `table` WHERE `attribute` =".GETVALUE("attribute");
my $sth2 = $connect->prepare($sql2);
$sth2->execute();
my(#attribute2, #name, #adress);
while (($row) = $sth->fetchrow()){
for (my $i = 0; $i < $row; $i++)
{
SETVALUE("Variable".$i , my $name->[$i].", ". my $adress->[$i]);
}
SETVALUE("CountAttribute", $row);
}
`
I already tried to exchange "fetchrow" with "fetchrow_array" and "fetchrow_arrayref", but this didnt work either.
Accepting the comment that fetchrow() is an undocumented alias for fetchrow_array():
The fetchrow_array() method returns the contents of the row, not an index. Assuming what you want to do is call SETVALUE() with each name and address, and then one last time with the number of rows fetched, your while() loop should be something along the lines of:
my $i = 0;
while ( my ( $attr2, $name, $addr ) = $sth->fetchrow_array() ) {
SETVALUE( "Value$i", "$name, $addr" );
$i++;
}
SETVALUE( "CountAttribute", $i );

Read an file in two hashes inorder to retain the order

I am trying to read a file with user information categorized under a location, I want to fill in the some of the fields using user input and output the file while keeping the fields under each location intact for eg - file
[California]
$;FIrst_Name =
$;Last_Name=
$;Age =
[NewYork]
$;FIrst_Name =
$;Last_Name=
$;Age =
[Washington]
$;FIrst_Name =
$;Last_Name=
$;Age =
Once user provides input from command line it should look it
[California]
$;FIrst_Name = Jack
$;Last_Name= Daner
$;Age = 27
[NewYork]
$;FIrst_Name = Jill
$;Last_Name= XYZ
$;Age = 30
[Washington]
$;FIrst_Name = Kim
$;Last_Name= ABC
$;Age = 25
The order of First_Name, Last_Name and Age within each location can change and even order of locations can change, but each location section should remain separate and intact. I wrote following code so far and some of my code works for taking whole file in one hash, but i am not able to preserve each location section within it! I tried using two hashes - can someone please help me as it is getting really complex for me! Thanks a lot. ( I had another issue with a similar file as well, but unfortunately could not resolve it either)
EDITED code
Open the file
use strict;
use warnings;
use Getopt::Long;
sub read_config {
my $phCmdLineOption = shift;
my $phConfig = shift;
my $sInputfile = shift;
open($input.file, "<$InputFile") or die "Error! Cannot open $InputFile
+ for reading: $!";
while (<$input.file>) {
$_ =~ s/\s+$//;
next if ($_ =~ /^#/);
next if ($_ =~ /^$/);
if ($_ =~ m/^\[(\S+)\]$/) {
$sComponent = $1;
next;
}
elsif ($_ =~ m/^;;\s*(.*)/) {
$sDesc .= "$1.";
next;
}
elsif ($_ =~ m/\$;(\S+)\$;\s*=\s*(.*)/) {
$sParam = $1;
$sValue = $2;
if ((defined $sValue) && ($sValue !~ m/^\s*$/)) {
$phfield->{$sCategory}{$sParam} = ["$sValue", "$sDesc"];
}
else {
$field->{$sCategory}{$sParam} = [undef, "$sDesc"];
}
}
$sParam = $sValue = $sDesc = "";
next;
}
}
Write the new file -
sub write_config {
my $phCmdLineOption = shift;
my $phConfig = shift;
my $sOut = shift;
open(outfile, ">$sOut") or die " $!";
foreach $sCategory (sort {$a cmp $b} keys %{$fields}) {
print $outfile "[$sCategory]\n";
foreach $sParam (sort {$a cmp $b} keys %{$fields-{$sCategory}}) {
$sDesc = ((defined $phConfig->{$sCategory}{$sParam}[1]) $fields->{$sCategory}{$sParam}[1] : "");
print $outfile ";;$sDesc\n" if ((defined $sDesc) && ($sDesc !~ m/^$/));
$sValue = ((defined $fields->{$sCategory}{$sParam}[0]) ? $fields->{$sCategory}{$sParam}[0] : undef);
print $outfile "$sValue" if (defined $sValue);
print $outfile "\n";
}
print $outfile "\n";
}
close($outfile);
return;
Note - I have posted this question on PerlMonks forum as well. Thanks a lot!
I think you're getting lost in the detail and skipping over some basics which is unnecessarily complicating the problem. Those basics are;
Indent your code properly (it's amazing the difference this makes)
Always use the /x modifier on regex and lots of whitespace to increase readability
When using lots of regexs, use "quote rule", qr, to seperate regex definition from regex use
Apart from that, you were headed in the right direction but there are a couple of insights on the algorithm you were missing which further increased the complexity.
Firstly, for small-time parsing of data, look out for the possibility that matching one type of line immediately disqualifies matching of other types of line. All the elsif's aren't necessary since a line that matches a category is never going to match a LastName or Age and vice versa.
Secondly, when you get a match, see if you can do what's needed immediately rather than storing the result of the match for processing later. In this case, instead of saving a "component" or "category" in a variable, put it immediately into the hash you're building.
Thirdly, if you're updating text files that are not huge, consider working on a new version of the file and then at the end of the program declare the current version old, and the new version current. This reduces the chances of unintentionally modifying something in place and allows comparison of the update with the original after execution - if necessary, "rollback" of the change in trivially easy which one of your users may be very grateful for one day.
Fourthly and most of all, you've only got a couple of attributes or components to worry about, so deal with them in the concrete rather than the abstract. You can see below that I've looped over qw( First_Name Last_Name Age) rather than all keys of the hash. Now obviously, if you have to deal with open-ended or unknown attributes you can't do it this way but in this case, AFAICT, your fields are fixed.
Here's a version that basically works given the above mentioned constraints.
#!/usr/bin/env perl
use v5.12 ;
use Getopt::Long ;
my %db ; # DB hash
my $dbf = "data.txt" ; # DB file name
my $dbu = "data.new" ; # updated DB file name
my $dbo = "data.old" ; # Old DB file name
my ($cat, $first, $last, $age) ; # Default is undef
GetOptions( 'cat=s' => \$cat ,
'first=s' => \$first ,
'last=s' => \$last ,
'age=i' => \$age
);
die "Category option (--cat=...) is compolsory\n" unless $cat ;
open my $dbh, '<', $dbf or die "$dbf: $!\n"; # DB Handle
open my $uph, '>', $dbu or die "$dbu: $!\n"; # UPdate Handle
# REs for blank line, category header and attribute specification
my $blank_re = qr/ ^ \s* $ /x ;
my $cat_re = qr/ ^ \[ (\w+) \] \s* $ /x ;
my $attr_re = qr/ ^ \$ ; (?<key>\w+) \s* = \s* (?<val>\N*) $ /x ;
while ( <$dbh> ) {
next unless /$cat_re/ ;
my %obj = ( cat => $1 ) ;
while ( <$dbh> ) {
$obj{ $+{key} } = $+{val} if /$attr_re/ ;
last if /$blank_re/
}
$db{ $obj{cat} } = \%obj
}
# Grab existing obj, otherwise presume we're adding a new one
my $obref = $db{ $cat } // { cat => $cat } ;
$obref->{ First_Name } = $first if defined $first ;
$obref->{ Last_Name } = $last if defined $last ;
$obref->{ Age } = $age if defined $age ;
# Update the DB with the modified/new one
$db{ $obref->{cat} } = $obref ;
for (sort keys %db) {
my $obref = $db{ $_ } ;
printf $uph "[%s]\n", $obref->{ cat } ;
for (qw( First_Name Last_Name Age )) {
printf $uph '$;' . "%s = %s\n", $_, $obref->{ $_ }
}
print $uph "\n"
}
close $dbh ;
close $dbu ;
rename $dbf , $dbo ;
rename $dbu , $dbf ;
exit 0
User input here need be organized, and for this we can use named options for each field, plus one for state. The Getopt option for reading into a hash is useful here. We also need to associate names of these options with field names. With that in hand it is simple to process the file since we have a ready mechanism to identify lines of interest.
By putting lines on a ref-array we can keep the order as well, and that refarray is a value for the section-key in the hash. The hash is not necessary but adds flexibility for future development. Once we are at it we can also keep the order of sections by using a simple auxiliary array.
use warnings;
use strict;
use Getopt::Long;
use feature qw(say);
# Translate between user input and field name ($;) in file
my ($o1, $o2, $o3) = qw(first last age);
my #tags = ('FIrst_Name', 'Last_Name', 'Age');
my %desc = ($tags[0] => $o1, $tags[1] => $o2, $tags[2] => $o3);
my (%input, $state);
GetOptions(\%input, "$o1=s", "$o2=s", "$o3=i", 'state=s' => \$state);
my $locinfo = 'loc_info.txt';
open my $in_fh, '<', $locinfo;
my (%conf, #sec_order, $section, $field);
while (my $line = <$in_fh>)
{
chomp($line);
next if $line =~ m/^\s*$/;
# New section ([]), for hash and order-array
if ($line =~ m/^\s*\[(.*)\]/) {
push #sec_order, $section = $1;
next;
}
# If we are in a wrong state just copy the line
if ($section ne $state) {
push #{$conf{$section}}, $line . "\n";
next;
}
if (($field) = $line =~ m/^\$;\s*(.*?)\s*=/ ) {
if (exists $input{$desc{$field}}) {
# Overwrite what is there or append
$line =~ s|^\s*(.*?=\s*)(.*)|$1 $input{$desc{$field}}|;
}
}
else { warn "Unexpected line: |$line| --" }
push #{$conf{$section}}, $line . "\n";
}
close $in_fh;
for (#sec_order) { say "[$_]"; say #{$conf{$_}}; }
Invocation
script.pl -state STATE -first FIRST_NAME -last LAST_NAME -age INT
Any option may be left out in which case that field is not touched. A field supplied on the command line will be overwritten if it has something. (This can be changed easily.) This works for a single-state entry as it stands but which is simple to modify if needed.
This is a basic solution. The first next thing would be to read the field names from the file itself, instead of having them hard-coded. (This would avoid the need to spot the typo FIrst and inconsistent spacings before =, for one thing.) But the more refinements are added, the more one is getting into template development. At some point soon it will be a good idea to use a module.
Note The regex delimiter above is different than elsewhere (|) to avoid the editor coloring all red.

Error Message: Can't call method "do" on an undefined value

# #################################################
# Subroutine to add data to the table BlastSearch
# Could be redone to be more general, but it seems more
# efficient to add data as it is pulled from the xml.
# #################################################
sub addData {
my (#data, $dbhandle) = #_;
print join(", ", #data) . "\n";
my $sqlcmd = "insert into BlastSearch values('" . join("','",#data) . "')";
$dbhandle->do($sqlcmd) or die $DBI::errstr;
}
This give the error message "Can't call method "do" on an undefined value." Is there anything in this particular method that is causing the problem? I can add more of the script if needed. My only guess is that it has to do with '#data,' which is filled in the code below:
# #################################################
# Subroutine to find the:
# Accession id
# e-value (Hsp_evalue)
# number of identites (Hsp_identity)
# of the top five matches.
# #################################################
sub parseBlastXML {
my ($file, $dbhandle) = #_;
my $xml = new XML::Simple();
my $data = $xml->XMLin($file, forcearray=>[qw(Hit)], keyattr=>[]);
my $entry_node = $data->{BlastOutput_iterations};
my $iterhit = $entry_node->{Iteration}->{Iteration_hits}->{Hit};
#quick find of uniprotID
my $uniProtID = substr($file, 0, 6);
my $count = 0;
foreach my $val (#$iterhit) {
my #dataarray;
if ($val->{Hit_hsps} && $count < 5) {
print "\n";
print "Hit accession: " . $val->{Hit_accession} . "\n";
print "e-value: " . $val->{Hit_hsps}->{Hsp}->{Hsp_evalue} . "\n";
print "number of ID's: " . $val->{Hit_hsps}->{Hsp}->{Hsp_identity} . "\n";
push(#dataarray, $val->{Hit_accession});
push(#dataarray, $val->{Hit_hsps}->{Hsp}->{Hsp_evalue});
push(#dataarray, $val->{Hit_hsps}->{Hsp}->{Hsp_identity});
push(#dataarray, $uniProtID);
addData(#dataarray, $dbhandle);
$count ++;
}
}
return $data;
}
The following is a bug as #data will always slurp all the values in #_, leaving $dbhandle undefined.
sub addData {
my (#data, $dbhandle) = #_; # $dbhandle will always be undefined
To fix, you need to reorder your arguments, and always have the array last in the assignment.
sub addData {
my ( $dbhandle, #data ) = #_;
...;
}
sub parseBlastXML {
...;
addData( $dbhandle, #dataarray );
Note: it would also be possible to pop the dbh off the end of the parameter list. However, such a coding style is not a good idea.
"Additionally, if anyone has any better way to add values to a row of an sqlite table than array and join, it'd be much appreciated."
Is your #data array always the same size? I presume so, as you've specified no column list in your INSERT statement, so the best way is to write
sub add_data {
my ($dbhandle, #data) = #_;
my $insert = $dbhandle->prepare('INSERT INTO BlastSearch VALUES (?, ?, ?, ?)');
$insert->execute(#data) or die $DBI::errstr;
}
with the correct number of ? placeholders, obviously. The prepare call is like compiling the statement, and ideally you should do it just once when your program first starts, after which you can call execute as many times as you like with different parameters.
If #data does vary in size, then all isn't lost. You can just do something like this
my $insert = sprintf 'INSERT INTO BlastSearch VALUES (%)',
join ', ', map '?', #data;
$insert = $dbhandle->prepare($insert);
$insert->execute(#data) or die $DBI::errstr;
but note that you will have to call prepare every time the parameter counts changes.
Note also that your identifier addData should ideally be add_data, as upper case letters are generally reserved for global identifiers.

display value in browser in perl cgi

I am able to fetch a data from db not able to display in browser.
below is the code-
my $q = CGI->new;
print $q->header,$q->start_html('testing');
my $title = $q->param('title');
my $perl = "";
#these is displayed properly
print "<font color=blue><b>TITLE:\"$title\"</b><br>";
print "<font color=blue><b>SCRIPT:\"$title\"</b>\n";
my $dbh = DBI->connect("DBI:ODBC:test","username","password") || die "Connection error: $DBI::errstr\n";
my $sql = "select * from tablename where title = '$title'";
my $sth = $dbh->prepare($sql);
$sth->execute;
my #row = $sth->fetchrow_array;
for(my $i=1;$i<=#row;$i++)
{
if($i == 5)
{
$perl = "$row[$i]";
}
}
#below is not displayed in browser
print $q->strong($title);
print $q->strong($perl);
$sth->finish();
$dbh->disconnect;
print $q->end_html;
I just want to print the value of $title and $perl in browser.
this program is running properly but cant able to display value of $title and $perl
The reason for the failure is not obvious to me, but you should use placeholders when performing queries:
my $sql = "select * from tablename where title = ?"; # placeholder
my $sth = $dbh->prepare($sql);
$sth->execute($sql); # $sql is used here
The placeholder is a question mark ?. This will ensure that your values are quoted properly, and prevent injection attacks. Using the data from the CGI object without sanitizing it is very dangerous.
Also, it seems that you are only taking one value from the array, so there is little need to use a loop in the first place. You could just do:
my $row = $row[5];
To see if the value was in the database, you can use if (defined $row), or if (#row >= 6). (Note that arrays start at 0, so the element with index 5 is actually the 6th element. Just pointing this out since you started your loop at 1.)
Try running it straight from the command line, without the browser.
See here and here.
You can also use the Perl debugger, if you start it with:
perl -d yourprogram

How can I check if a database query will return results?

Our website uses Perl to provide a simple mechanism for our HR people to post vacancies to our website. It was developed by a third party, but they have been long since kicked into touch, and sadly we do not have any Perl skills in-house. This is what happens when Marketing people circumvent their in-house IT team!
I need to make a simple change to this application. Currently, the vacancies page says 'We currently have the following vacancies:', regardless of whether there are any vacancies! So we want to change it so that this line is only displayed at the appropriate times.
I could, obviously, start to learn a bit of Perl, but we are already planning a replacement site, and it certainly won't be using Perl. So since the solution will be trivial for those with these skills, I thought I'd ask for some focused help.
Below is the start of the procedure that lists the vacancies.
sub list {
require HTTP::Date;
import HTTP::Date;
my $date = [split /\s+/, HTTP::Date::time2iso(time())]->[0];
my $dbh = DBI->connect($dsn, $user, $password)
|| die "cannot connect to $database: $!\n";
my $sql = <<EOSQL;
SELECT * FROM $table where expiry >= '$date' order by expiry
EOSQL
my $sth = $dbh->prepare($sql);
$sth->execute();
while (my $ref = $sth->fetchrow_hashref()) {
my $temp = $template;
$temp =~ s#__TITLE__#$ref->{'title'}#;
my $job_spec = $ref->{'job_spec'};
...etc...
The key line is while (my $ref = $sth->fetchrow_hashref()) {. I'm figuring that this is saying 'while I can pull off another vacancy from the returned recordset...'. If I place my print statement before this line, it will always be shown; after this line and it was be repeated for every vacancy.
How do I determine that there are some vacancies to be displayed, without prematurely moving through the returned recordset?
I could always copy the code within the while loop, and place it within an if() statement (preceding the while loop) which will also include my print statement. But I'd prefer to just have the simpler approach of If any records then print "We currently have.." line. Unfortunately, I haven't a clue to code even this simple line.
See, I told you it was a trivial problem, even considering my fumbled explanation!
TIA
Chris
A really simple way would be:
$sth->execute();
my $first = 1;
while (my $ref = $sth->fetchrow_hashref()) {
if( $first ) {
print "We currently have the following vacancies:\n";
$first = 0;
}
my $temp = $template;
...
}
if( $first ) {
print "No vacancies found\n";
}
If you are using Mysql, the "rows" method works just fine:
$sth->execute();
if($sth->rows) {
print "We have data!\n";
}
while(my $ref = $sth->fetchrow_hashref()) {
...
}
The method, and some caveats, are documented in extensive detail in "perldoc DBI". Always start with "perldoc".
This isn't so much a Perl question as it's a database question, and there is no good way to know how many results you have until you have them. You've got two choices here:
Do a query that does a "select count(*)" to see how many rows there are, and then another query to get the actual rows or
Do the query and store the results into a hash, then count how many entries you have in the hash, and then go through the hash and print out the results.
For example, off the top of my head:
my #results = ();
while (my $ref = $sth->fetchrow_hashref()) {
push #results, $ref;
}
if ($#results == 0) {
... no results
} else {
foreach $ref (#results) {
my $temp = $template;
....
}
Since everyone wants to optimize away the repeated tests for whether the header has been printed in Graeme's solution, I present this minor variation on it:
$sth->execute();
my $ref = $sth->fetchrow_hashref();
if ($ref) {
print "We currently have the following vacancies:\n";
while ($ref) {
my $temp = $template;
...
$ref = $sth->fetchrow_hashref();
}
} else {
print "No vacancies found\n";
}
Since your query is a SELECT, you cannot take advantage of rows or of the value returned by the execute itself.
However, you can pre-count how many rows (i.e. vacancies) your query will select by adding another query... something like this:
# Retrieve how many vacancies are currently offered:
my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
$sth = $dbh->prepare($query);
$sth->execute($date);
$numVacancies = $numinfo->fetchrow_arrayref()->[0];
# Debug:
print "Number of vacancies: " . $numVacancies . "\n";
if ( $numVacancies == 0 ) { # no vacancy found...
print "No vacancies found!\n";
}
else { # at least a vacancy has been found...
print "We currently have the following vacancies:\n";
# Retrieve the vacancies:
my $sql = "SELECT * FROM $table where expiry >= '$date' ORDER BY expiry";
my $sth = $dbh->prepare($sql);
$sth->execute();
...
}
Or, similarly, instead of "prepare" and "execute" the query and then use "fetchrow_array", you can do everything in a single call using selectrow_array:
# Retrieve how many vacancies are currently offered:
my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
my $numVacancies = $dbh->selectrow_array($query, undef, $date);
# Debug:
print "Number of vacancies: " . $numVacancies . "\n";
And the same is also true for selectall_arrayref:
# Retrieve how many vacancies are currently offered:
my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
my $numVacancies = $dbh->selectall_arrayref($query, {Slice => {}}, $date);
# Debug:
print "Number of vacancies: " . #$numVacancies[0]->{rows} . "\n";
However, if you use selectrow_array or selectall_arrayref, you can also retrieve the number of vacancies directly from the result of the original query:
# Retrieve the vacancies:
my $sql = "SELECT * FROM $table where expiry >= ? ORDER BY expiry";
my $vacancies = $dbh->selectall_arrayref($sql, {Slice => {}}, $date);
# Debug:
print "Number of vacancies: " . scalar #{$vacancies} . "\n";
A bit more efficient way (avoiding a conditional inside the loop), if you don't mind it changing the way the page is output a bit (all at once rather than a row at a time) you could make a variable to hold the output just before the loop:
my $output = '';
and then inside the loop, change any print statement to look like this:
$output .= "whatever we would have printed";
then after the loop:
if ($output eq '')
{
print 'We have no vacancies.';
}
else
{
print "We currently have the following vacancies:\n" . $output;
}
Just add another query.. something like this:
# count the vacancies
$numinfo = $dbh->prepare("SELECT COUNT(*) FROM $table WHERE EXPIRY >= ?");
$numinfo->execute($date);
$count = $numinfo->fetchrow_arrayref()->[0];
# print a message
my $msg = '';
if ($count == 0) $msg = 'We do not have any vacancies right now';
else $msg = 'We have the following vacancies';
print($msg);
use Lingua::EN::Inflect 'PL';
$sth->execute();
my $results = $sth->fetchall_arrayref( {}, $max_rows );
if (#$results) {
print "We currently have the following ", PL("vacancy",scalar #$results), ":\n";
for my $ref (#$results) {
...
}
}
Says perldoc DBI:
For a non-"SELECT" statement, "execute" returns the number of rows
affected, if known. If no rows were affected, then "execute"
returns "0E0", which Perl will treat as 0 but will regard as true.
So the answer is to check the return value of $sth->execute():
my $returnval = $sth->execute;
if (defined $returnval && $returnval == 0) {
carp "Query executed successfully but returned nothing";
return;
}