MySQL Query with variable in Perl - perl

My Perl is fairly rusty so please forgive. Trying to write a query using a variable. Have tried reformatting, just can't seem to get it correctly. Here is my code, not sure what I'm doing wrong.
my $d_var = "$3\n";
my $query="SELECT id FROM `accounts` WHERE (`accounts`.`named` = ?) LIMIT 1";
my $st_h = $db_h->prepare($query);
$st_h->bind_param(1, '$d_var');
$st_h->execute;
my $row = $st_h->fetchrow_array();

Please double check:
$3 contains something reasonable
concatenating $3's value and "\n" (by interpolation) is correct ("\n" in field?)
as ' doesn't interpolate => my $st_ht->bind_param(1, $d_var);
(I don't understand the DBI Docs as Chris Ledet does.)
On 2nd thought:
This code snippet:
my $v = "nix nix 1001";
print "$v\n";
print '$v\n', "\n";
if ($v =~ m/(nix) (nix) (\d+)/) {
print 'found: ', $3, "\n";
$sth = $dbh->prepare('SELECT * FROM sample01.csv WHERE GRUPPE=?');
$sth->bind_param(1, $3);
$sth->execute;
while(my #row = $sth->fetchrow_array()) {
print '|', join( '|', #row ), "|\n";
}
} else {
print "no match\n";
}
and the output:
DBI: 1.616 DBD::CSV: 0.33
|00000089-6d83-486d-9ddf-30bbbf722583|2011-09-17 16:25:09|1001|
|000004c9-92c6-4764-b320-b1403276321e|2011-11-09 13:52:30|2000|
nix nix 1001
$v\n
found: 1001
|00000089-6d83-486d-9ddf-30bbbf722583|2011-09-17 16:25:09|1001|
should illustrate:
' does not interpolate, your '$d_var' will pass this variable name literally to DBI
a valid match needs no "\n" to 'work'
the param sequence for bind_param is number, value

Not sure why you're even using bind_param. In my opinion, it's far simpler to just pass extra values into execute.
my $d_var = "$3\n";
my $query = 'SELECT id FROM accounts` WHERE (`accounts`.`named` = ?) LIMIT 1';
my $st_h = $db_h->prepare($query);
$st_h->execute($d_var);
my $row = $st_h->fetchrow_array();
Have you considered switching to DBIx::Class?

What does this mean?
my $st_ht->bind_param(1, '$d_var');
There is no variable being introduced, so why the my?

I have $3 printing out prior to execution and there is data contained within the string.
I have d_var="$3\n" as the variable $3 is being generated by a Regex string and doesn't seem to work without \n.
Tried what Chris suggested above, however did not work.

Aside from what Dan said (removing the single quote), you binded your param to a possibly none Statement handle object $st_ht->bind_param(...) it should be $st_h->bind_param(...).

Related

Forward Slash issue with DBI

I'm new to using DBI for SQL queries in a perl script. The issue I'm having pertains to data in fields that have a forward slash. I'm wanting to use variables as input for my where clause, but it is doing what DBI intends a forward slash to do: stop the query. I tried numerous different work arounds from binds, quotes, etc. but none worked, is it even possible? Data in this is consistent. The line with the my $sql variable is where the trouble is.
#!/usr/bin/perl
# Modules
use DBI;
use DBD::Oracle;
use strict;
use warnings;
# Connection Info
$platform = "Oracle";
$database = "mydb";
$user = "user";
$pw = "pass";
# Data Source
$ds = "dbi:Oracle:$database";
my $dbh = DBI->connect($ds, $user, $pw);
# my $dbh = DBI->connect();
my $XCOD = $dbh->quote('cba');
my $a = $dbh->quote('abc');
my $b = $dbh->quote('123');
# tried this as well my $pid = $dbh->quote('$a/$b');
my $sql = "SELECT P_ID FROM MyTable WHERE P_ID=$a/$b AND XCOD=$XCOD";
my $sth = $dbh->prepare($sql);
$sth->execute();
my $outfile = 'superunique.txt';
open OUTFILE, '>', $outfile or die "Unable to open $outfile: $!";
while(my #re = $sth->fetchrow_array) {
print OUTFILE #re,"\n";
}
close OUTFILE;
$sth->finish();
$dbh->disconnect();
I don't like to see folks use variable interpolation in SQL queries. Try using placeholders:
[ snip ]
my $P_ID = "$a/$b"
my $sql = "SELECT P_ID FROM MyTable WHERE P_ID = ? AND XCOD = ?";
my $sth = $dbh->prepare($sql);
$sth->execute($P_ID, $XCOD);
[ snip ]
You have been given the correct solution to your problem (use placeholders) but you might be interested to see why what you are doing doesn't work.
The problem is that you seem to misunderstand the quote method. The documentation says this:
Quote a string literal for use as a literal value in an SQL statement,
by escaping any special characters (such as quotation marks) contained
within the string and adding the required type of outer quotation
marks.
You use quote in these three lines.
my $XCOD = $dbh->quote('cba');
my $a = $dbh->quote('abc');
my $b = $dbh->quote('123');
It would be instructive to print out the values of $XCOD, $a and $b (as an aside $a and $b are really bad names for variables - apart from their non-descriptive nature, they are also special variables used in sorting).
I suspect that you'll see "cba", "abd" and "123". The method has found no special characters to escape, so all it has done is to add quote marks around the strings.
You then interpolate these values into your SQL.
my $sql = "SELECT P_ID FROM MyTable WHERE P_ID=$a/$b AND XCOD=$XCOD";
Again, you should take a close look at what $sql contains after this statement has been executed. It will look something like this:
SELECT P_ID FROM MyTable WHERE P_ID="abc"/"123" AND XCOD="cba"
It's probably the first part of the WHERE clause that is a problem. Oracle is treating that as a division. And who knows what Oracle does when you divide one string by another. So you end up looking for a row where P_ID is some strange (perhaps undefined) value.
So this looks to be an example where the simplest of debugging techniques (a few print statements in the code) would have guided you in the right direction.

Understanding Perl variables

I just joined a project and I'm supposed to understand the previous person's scripts.
I am new to Perl. Can someone please tell me what this statement would mean?
my $name = $1;
How can a variable be assigned a value $something? I tried to print it but it gives me an error message.
The $1 is a special variable used to return the first capture group in a regex.
For example you might see something like:
my $string = 'this is an example of 34 a match! 99 foo bar';
$string =~ /\d+\s(.*?)(\d+)/;
In this case the first captured group (a match!) will be stored in the variable $1, and the second (99) in $2.
print "first capture group = $1\nsecond capture group = $2\n";
You might want to reassign these:
my $match = $1;
my $number = $2;
Beside #fugu said, I want to express more. When you used $3, there will be nothing in $3, because in that example, only match 2 times. In perl, many variables are used, like $1, $2, $3, there are $_ for current variables in the loop; $_[0] , $_[1],.. for variables in the function; ARGV[0].. for argv of the script, and so on. Use these to save you manual work.

Not able to split a string in perl - getting unmatched ( in regex; marked by (-- Here in m/ error

The below is my code
$var = ' "jjjjjjjj&Q_30006_47=540IT%20(540%2FOR%2FHPSC%2FD%2F02%2F11&Q_30006_4=&Q_30006_6=12&Q_30006_7=&Q_30006_" &';
($temp1,$temp2) = split($var,"&");
print $temp1;
I need to get
$temp1 = "jjjjjjjj
and
$temp2 as the remaining part of the string after the first &.
I am getting error because of the '(' in the string.
Can anyone please advise on how to split this.
Thanks!!
I think that you have the parameter orders wrong. The pattern should be first:
($temp1,$temp2) = split("&", $var);
However, that will split on all & characters. You probably are looking for this (the 2 is the limit):
($temp1,$temp2) = split("&", $var, 2);
($temp1,$temp2) = split '&', $var, 2;

How to write a simple sub in perl that takes a string and returns a string?

sub getHeading
{
my $var = $_[0];
my $match;
if ($match = ($var =~ m/$fivetonine/))
{
return "=";
}
if ($match = ($var =~ m/$tentofourteen/))
{
return "==";
}
if ($match = ($var =~ m/$fifteentonineteen/)){
return "===";
}
return "===";
}
my $ref_to_getHeading = \getHeading;
and I am calling it via:
$html =~ s/(.*)<font size="([^"]+)">(.+)<\/font>(.*)/$ref_to_getHeading($2)$1$3$4$ref_to_getHeading($2)/m;
I am wanting to pass a string in to this function, I want to check if it is one of 3 different matches and return the appropriate number of = signs, I am doing this wrong but I can't figure out how to make it take parameters? I get a run time error saying $var is initialised? I tried using #_ but I don't really understand what the difference is.
Any help much appreciated, I have never written perl before and this is my first real program.
Double mistake there.
First, you aren't taking a reference to a function - You need to add the ampersand.
But even if you do that, it won't work. You are missing the /e flag in your substitution: You can't dereference a coderef within a string like you'd normally do with (scalar|hash|array)ref:
my $example = sub { return "hello" };
say "$example->()"; #Will stringify the coderef.
You either need the /e flag,
$html =~ s/etc/$ref_to_getHeading->($2) . "$1$3$4" . $ref_to_getHeading->($2)/em;
Or a little trick:
$html =~ s/etc/#{[$ref_to_getHeading->($2)]}$1$3$4#{[$ref_to_getHeading->($2)]}/m;
EDIT: Gosh, am I a slow typist..
Anyhow, with either way, you should be able to call the sub directly, so no need for the coderef.
The line my $ref_to_getHeading = \getHeading; doesn't do what you think it does. To take a reference to a subroutine:
my $ref_to_getHeading = \&getHeading; # note the &
So you were actually calling getHeading and storing the result. Since you passed no arguments, you got the undefined value warning.
The substitution however will never call the coderef, for that to happen, you need to add the e modifier to run the replacement text through eval:
$html =~ s/.../join '' => getHeading($2), $1, $3, $4, getHeading($2)/me;
you may run into issues here with getHeading resetting the match vars too early. In which case, try writing it this way:
$html =~ s{...}{
my $body = $1 . $3 . $4;
my $heading = getHeading($2);
$heading . $body . $heading
}me;
The bracket change for s/// was not necessary, I just find it easier to read a multi-line curly block.

How can I expand a string like "1..15,16" into a list of numbers?

I have a Perl application that takes from command line an input as:
application --fields 1-6,8
I am required to display the fields as requested by the user on command line.
I thought of substituting '-' with '..' so that I can store them in array e.g.
$str = "1..15,16" ;
#arr2 = ( $str ) ;
#arr = ( 1..15,16 ) ;
print "#arr\n" ;
print "#arr2\n" ;
The problem here is that #arr works fine ( as it should ) but in #arr2 the entire string is not expanded as array elements.
I have tried using escape sequences but no luck.
Can it be done this way?
If this is user input, don't use string eval on it if you have any security concerns at all.
Try using Number::Range instead:
use Number::Range;
$str = "1..15,16" ;
#arr2 = Number::Range->new( $str )->range;
print for #arr2;
To avoid dying on an invalid range, do:
eval { #arr2 = Number::Range->new( $str )->range; 1 } or your_error_handling
There's also Set::IntSpan, which uses - instead of ..:
use Set::IntSpan;
$str = "1-15,16";
#arr2 = Set::IntSpan->new( $str )->elements;
but it requires the ranges to be in order and non-overlapping (it was written for use on .newsrc files, if anyone remembers what those are). It also allows infinite ranges (where the string starts -number or ends number-), which the elements method will croak on.
You're thinking of #arr2 = eval($str);
Since you're taking input and evaluating that, you need to be careful.
You should probably #arr2 = eval($str) if ($str =~ m/^[0-9.,]+$/)
P.S. I didn't know about the Number::Range package, but it's awesome. Number::Range ftw.
I had the same problem in dealing with the output of Bit::Vector::to_Enum. I solved it by doing:
$range_string =~ s/\b(\d+)-(\d+)\b/expand_range($1,$2)/eg;
then also in my file:
sub expand_range
{
return join(",",($_[0] .. $_[1]));
}
So "1,3,5-7,9,12-15" turns into "1,3,5,6,7,9,12,13,14,15".
I tried really hard to put that expansion in the 2nd part of the s/// so I wouldn't need that extra function, but I couldn't get it to work. I like this because while Number::Range would work, this way I don't have to pull in another module for something that should be trivial.
#arr2 = ( eval $str ) ;
Works, though of course you have to be very careful with eval().
You could use eval:
$str = "1..15,16" ;
#arr2 = ( eval $str ) ;
#arr = ( 1..15,16 ) ;
print "#arr\n" ;
print "#arr2\n" ;
Although if this is user input, you'll probably want to do some validation on the input string first, to make sure they haven't input anything dodgy.
Use split:
#parts = split(/\,/, $fields);
print $parts[0];
1-6
print $parts[1];
8
You can't just put a string containing ',' in an array, and expect it to turn to elements (except if you use some Perl black magic, but we won't go into that here)
But Regex and split are your friends.