perl DBI execute doesn't recognize '?' - database

I have this code:
if($update[$entity_id])
{
my $sql = "UPDATE cache SET date = '?', value = '?' WHERE item_id = ? AND level = ? AND type = ?;";
}
else
{
my $sql = "INSERT INTO cache (date, value, item_id, level, type) VALUES ('?','?',?,?,?);";
}
my $db = $self->{dbh}->prepare(q{$sql}) or die ("unable to prepare");
$db->execute(time2str("%Y-%m-%d %X", time), $stored, $entity_id, 'entity', 'variance');
But when it want to run the update I get this error:
DBD::Pg::st execute failed : called with 5 bind variables when 0 are needed.
Why?

If you had turned on strict and/or warnings, you would see what your problem is.
You're writing
if (...) {
my $sql = ...;
} else {
my $sql = ...;
}
execute($sql);
Which means that the $sql variables that you declare in the if branches aren't in scope and you're trying to execute completely empty SQL.

You're preparing literal '$sql', but that is not your only problem, lexical $sql variables go out of scope outside {}.
Try,
use strict;
use warnings;
#...
my $sql;
if($update[$entity_id])
{
$sql = "UPDATE cache SET date = ?, value = ? WHERE item_id = ? AND level = ? AND type = ?";
}
else
{
$sql = "INSERT INTO cache (date, value, item_id, level, type) VALUES (?,?,?,?,?)";
}
my $st = $self->{dbh}->prepare($sql) or die ("unable to prepare");
$st->execute(time2str("%Y-%m-%d %X", time), $stored, $entity_id, 'entity', 'variance');

Related

update query sql table in yii

How can I update row in table in yii? I am using the following code but it is not working
$sql = "UPDATE auth_assignment SET itemname = 'Authenticated' WHERE userid = $user->accountID";
$command = $connection->createCommand($sql);
$command->execute();
My guess is that $user is being converted to a string and thus the ->accountID is not working. You have two methods, one unsafe and one safe.
Unsafe - Add {} around the $user->accountID.
$sql = "UPDATE auth_assignment SET itemname = 'Authenticated' WHERE userid = {$user->accountID}";
Safer - Use a parametrized query:
$sql = "UPDATE auth_assignment SET itemname = 'Authenticated' WHERE userid = :userid";
$command = $connection->createCommand($sql);
$command->execute(array(':userid' => $user->accountID))
ok I forgot to put commas around
'user->accountID'

How to store the current value of the array in a variable inside a Perl for loop

My requirement is, I want to map some checklist values to some groups. The following is my code:
#selectbox1 => contains the selected select groups
#selectbox2 => contains selected checklist
Code:
foreach $select1(#selectbox1) {
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box1 = $dbslave -> prepare($sql_select1);
$box1 -> execute();
while($select_box1= $box1->fetchrow_array())
{
push (#box1,$select_box1);
}
my $box_1 = #box1; # currently I tried like this to store the current value .NEED CORRECTION HERE
foreach $select2(#selectbox2) {
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box2 = $dbslave -> prepare($sql_select2);
$box2 -> execute();
while($select_box2 = $box2->fetchrow_array())
{
push (#box2,$select_box2);
}
my $box_2 = #box2; # currently I tried like this to store the current value .NEED CORRECTION HERE
my $sql_insert = "insert into checklist_group_mapping values ('',$box_2,$box_1)";
my $ins = $dbslave -> prepare($sql_insert);
$ins -> execute();
}
}
How can I assign the current value of the array to a variable so that I can insert it into the mapping table?
You need to read up on 'context', and in particular 'scalar context' and 'array context'.
When you write:
my $box_1 = #box1;
you are providing scalar context, and in scalar context, #box1 returns the number of elements in the array. If you wrote:
my($box_1) = #box1;
you would be providing array context, and in array context, the first element of #box1 would be assigned to the first element of the array context, $box_1 — and the remaining elements of #box1 would be dropped. (This may well be what you're after; it is likely that you are trying to select the single ID value for each of the various names in #selectbox1.)
Judging from how you're trying to use the $box_1 and $box_2 variables in your code, you are looking to obtain a single string containing all the values from #box1 and another single string containing all the values from #box2, and they probably need to be presented to the DBI driver enclosed in single quotes.
You can get space-separated values into a string using:
my $box_1 = "#box1";
If you need comma-separated values, you can use:
my $box_1;
{ local $" = ","; $box_1 = "#box_1"; }
The $" (aka $LIST_SEPARATOR under use English '-no_match_vars';) must be localized to prevent damage, but that means you have to separate the definition of $box_1 from the assignment (because if you don't, $box_1 is destroyed when you leave the {...} block).
Now, to protect that so that the SQL can work, you need to use the quote method:
$box1 = $dbslave->quote($box1);
or:
my $box1 = $dbslave->quote("#box1");
Assembling these changes, we get:
#!/usr/bin/env perl
use strict;
use warnings;
### Improved, but not operational
# use DBI;
my #selectbox1 = ( "group1", "group2", "group3" );
my #selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
foreach my $select1 (#selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box1 = $dbslave->prepare($sql_select1);
$box1->execute();
my #box1;
while (my $select_box1 = $box1->fetchrow_array())
{
push #box1, $select_box1;
}
my $box_1 = $dbslave->quote("#box1");
foreach my $select2(#selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box2 = $dbslave->prepare($sql_select2);
$box2->execute();
my #box2;
while (my $select_box2 = $box2->fetchrow_array())
{
push #box2, $select_box2;
}
my $box_2 = $dbslave->quote("#box2");
my $sql_insert = "insert into checklist_group_mapping values ('', $box_2, $box_1)";
my $ins = $dbslave->prepare($sql_insert);
$ins->execute();
}
}
Note that the two SELECT statements assume that the select box strings contain no funny characters (specifically, no single quotes). If you're in charge of the content of #selectbox1 and #selectbox2, that's OK. If they contain user input, you have to sanitize that input, or use $dbslave->quote() again, or use place-holders. I'm going to ignore the issue.
You are also using scalar context with $box1->fetchrow_array(), which is not going to yield the answer you want (although fetchrow_array() is context sensitive, the manual warns you to be careful). I would use something like:
my #box1;
while (my #row = $box1->fetchrow_array())
{
push #box1, $row[0];
}
my $box_1 = $dbslave->quote("#box1");
You also need to use functions. There's a glaring repeat in your code that can be encapsulated into a single function used twice:
#!/usr/bin/perl
use strict;
use warnings;
# use DBI;
my #selectbox1 = ( "group1", "group2", "group3" );
my #selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
sub fetch_all
{
my($dbh, $sql) = #_;
my $sth = $dbh->prepare($sql);
$sth->execute();
my #results;
while (my #row = $sth->fetchrow_array())
{
push #results, $row[0];
}
my $result = $dbslave->quote("#results");
return $result;
}
foreach my $select1 (#selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box_1 = fetch_all($dbslave, $sql_select1);
foreach my $select2(#selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box_2 = fetch_all($dbslave, $sql_select2);
my $sql_insert = "insert into checklist_group_mapping values ('', $box_2, $box_1)";
my $ins = $dbslave->prepare($sql_insert);
$ins->execute();
}
}
The INSERT statement should be converted to use placeholders so it can be prepared once and used many times:
my $sql_insert = "insert into checklist_group_mapping values ('', ?, ?)";
my $ins = $dbslave->prepare($sql_insert);
foreach my $select1 (#selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box_1 = fetch_all($dbslave, $sql_select1);
foreach my $select2(#selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box_2 = fetch_all($dbslave, $sql_select2);
$ins->execute($box_1, $box_2);
}
}
Indeed, the two SELECT statements should also be parameterized and prepared once and reused. I've not shown that change because (a) I'm lazy and (b) there's a bigger change that is still more effective.
When we look at what you're really doing, it should all be a single SQL statement:
#!/usr/bin/perl
use strict;
use warnings;
# use DBI;
my #selectbox1 = ( "group1", "group2", "group3" );
my #selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
sub placeholder_list
{
my($n) = #_;
die "$n should be larger than 0" if $n <= 0;
my $list = "(?" . ",?" x ($n - 1) . ")";
return $list;
}
my $sql_insert = qq%
INSERT INTO checklist_group_mapping(col1, col2, col3)
SELECT '', gm.id, cl.id
FROM group_management AS gm
CROSS JOIN checklisst AS cl
WHERE gm.group_name IN X1
AND cl.checklist_name IN X2
%;
my $X1 = placeholder_list(scalar(#selectbox1));
my $X2 = placeholder_list(scalar(#selectbox2));
$sql_insert =~ s/X1/$X1/;
$sql_insert =~ s/X2/$X2/;
my $ins = $dbslave->prepare($sql_insert);
$ins->execute(#selectbox1, #selectbox2);
The big advantage of this is that there are far fewer round trips for information flowing between the application and the database, which (almost) invariably improves performance, often dramatically.
The only residual issue is whether your DBMS supports explicit CROSS JOIN like that. If not, you'll need to replace the words CROSS JOIN with a single comma.
There are still things that should be fixed, such as checking that the prepared statements were successfully prepared, and so on. But this may have given you some insight into how to think about using the DBI with Perl.
The trick is to use $_ variable inside your foreach. Like this:
my $current_value;
foreach $select2(#selectbox2) {
$current_value = $_;
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
......
my $box_2 = $current_value;

How to execute insert command inside perl script?

#!/usr/bin/perl
use strict;
use DBI;
use Data::Dumper;
use Asterisk::AGI;
my $agi = new Asterisk::AGI;
my $extension = '8315';
my $cti = '7702009896';
my $service_id =1;
my $DSN = q/dbi:ODBC:SQLSERVER/;
my $uid = q/ivr/;
my $pwd = q/ivr/;
my $DRIVER = "Freetds";
my $dbh = DBI->connect($DSN,$uid,$pwd) or die "Coudn't Connect SQL";
my $sql3=(qq{
declare '#' + callnumber as int
set '#' + callnumber = $callnumber
set '#' + callnumber = (Select '#'+'#' + identity)
exec "insert into rpt_call_detail (call_start_time,call_number,call_service_id,call_step_name,call_step_type,call_step_discription) values(getdate(),'#' + callnumber,$service_id,'START',0,'CLI:' + $cli)"
});
my $call_insert1 = $dbh->prepare($sql3);
$call_insert1->execute();
How to set a sql server variable inside Perl script? I want to set callnumber as ##identity I'm not able execute above code successfully.Please help me.
declare '#' + callnumber as int is not valid (MS) SQL. You want it to say declare #callnumber as int instead, so after escaping the #, you end up with declare \#callnumber as int. I'm not familiar with using ##IDENTITY, but assuming that part is correct, your $sql3 variable would look like:
my $sql3=(qq{
declare \#callnumber as int
set \#callnumber = $callnumber
set \#callnumber = (Select \#\#identity)
exec "insert into rpt_call_detail (call_start_time,call_number,call_service_id,call_step_name,call_step_type,call_step_discription) values(getdate(),'#' + callnumber,$service_id,'START',0,'CLI:' + $cli)"
});

Perl - Hash of Arrays across Module

I am a bit new to Perl and I need some help regarding moving my Hash of Arrays across Modules.
Currently I have a db module that stores an array like so:
sub getSourceCriteria {
my($self) = shift();
my($sourceId) = shift();
chomp $sourceId;
my(%criteria) =();
$logger->debug("Getting records for Source ID: " . $sourceId);
$dbh=DBI->connect('dbi:ODBC:StkSkrnDB', 'RTETET', 'XXuser01',{ RaiseError => 1, AutoCommit => 0 }) || \
$logger->err_die("Database connection not made: $DBI::errstr\n");
my($sth) = "select a.criteria_id, a.criteria_type, a.criteria_props,a.generalcriteria_id,b.field_id ";
$sth = $sth . "from t_criteria a, t_sourceMapping b where a.generalcriteria_id = (select generalcriteria_id from t_sourcecriteria where source_id =?) ";
$sth = $sth . "and a.criteria_id=b.criteria_id";
my($qry) = $dbh->prepare($sth);
$qry->execute($sourceId) || $logger->error("Could not query for Source Criteria: $DBI::errstr\n");
my(#row)=();
my($tempCount) = 0;
while ( #row = $qry->fetchrow_array ) {
$tempCount = scalar #row;
$logger->debug("Size of retrieved SQL Array : $tempCount");
$criteria{$row[0]} = \#row;
###{$criteria{$row[0]} } = \#row;
}
return %criteria;
}
And I have a seperate perl script that reads the SQL output from the code above:
foreach my $criteria (keys %criterias) {
#temp = exists( $criterias{$criteria} ) ? #{ $criterias{$criteria} } : ();
##my $tempStr = exists( $criterias{$criteria} ) ? "Yes" : "No";
$arraySize = scalar #temp;
$logger->debug("GENERALCRITERIA_ID is $GENERALCRITERIA_ID and size of array is $arraySize and $temp[0]");
$genCrit_ID = $temp[$GENERALCRITERIA_ID];
$logger->debug("Criteria ID $criteria has Gen Criteria ID $genCrit_ID");
if (0!=$generalCriteria_ID || $generalCriteria_ID != $genCrit_ID ) { ## test for uniqueness
$generalCriteria_ID = -1;
}
else {
$generalCriteria_ID = $genCrit_ID;
}
}# do something with $key and $value
$generalCriteria = $generalCriteria_ID;
}
The problem is I keep getting 0 as the retrieved array size( 2nd snippet) even though when I store the array ( in the 1st snippet ) I check and get the actual array size.
Please any help/clarification would be greatly appreciated.
EDIT
Added more code in the DB interface code.
In your while loop, you are assigning to #row and then storing a reference to that array. However, each time the loop iterates, you are replacing the contents of #row without declaring a new array. So at the end, each of your references point towards the same thing.
In your code here:
my(#row)=();
my($tempCount) = 0;
while ( #row = $qry->fetchrow_array ) {
$tempCount = scalar #row;
$logger->debug("Size of retrieved SQL Array : $tempCount");
$criteria{$row[0]} = \#row;
###{$criteria{$row[0]} } = \#row;
}
Each time the while loop iterates, you assign new values to the #row array. But since the my(#row)=(); line occurs outside of the loop, the #row array is always the same. So each time you assign to the array, you are changing what is stored in all of the references you have already taken.
To fix the problem, you need to declare a new array for each iteration. The simplest way to do this is to move the declaration into the while condition:
my($tempCount) = 0;
while ( my #row = $qry->fetchrow_array ) {
$tempCount = scalar #row;
$logger->debug("Size of retrieved SQL Array : $tempCount");
$criteria{$row[0]} = \#row;
###{$criteria{$row[0]} } = \#row;
}
Now each time you take the reference \#row you will be getting a reference to a new array.
If your $qry->fetchrow_array method returned an array reference, you would not have had the issue:
my $row;
while ($row = $qry->fetchrow_array) {
$logger->debug("Size of retrieved SQL Array : ".#$row);
$criteria{$$row[0]} = $row; # already a reference
}
But I would still write that as while (my $row = ... in my own code, since keeping scopes small is a good thing.

JDBC - prepareStatement - How should I use it?

I saw this example somewhere:
rs = connection.prepareStatement("select * from table").executeQuery();
Could I use this format, if I want to execute a query like this "Select * from table where column = "hello" "?
The way in which I usual I use prepareStatement object is something like this:
String sql = "select * from adresa where column = ?";
PreparedStatement pre = con.prepareStatement(sql);
pre.setString(1, i);
rs = pre.executeQuery();
Later Edit:
I don't understand. Pascal Thivent wrote that I can use the short version with In parameters, but Liu tells me this is not possible. :) Anw, using Pascal's version, i receive this error: void cannot be dereferenced
Here's a partial example how to use this interface:
static final String USER = "root";
static final String PASS = "newpass";
Connection conn = DriverManager.getConnection(myUrl, USER, PASS);
// create a sql date object so we can use it in our INSERT statement
Calendar calendar = Calendar.getInstance();
java.sql.Date startDate = new java.sql.Date(calendar.getTime().getTime());
// the mysql insert statement
String query = " insert into students (ID, last_name, first_name, birthday, hometown)"
+ " values (?, ?, ?, ?, ?)";
// create the mysql insert preparedstatement
PreparedStatement preparedStmt = conn.prepareStatement(query);
preparedStmt.setInt(1, 808027);
preparedStmt.setString(2, "Davis");
preparedStmt.setString(3, "Felicita");
preparedStmt.setDate(4, startDate);
preparedStmt.setString(5, "Venice");
// execute the preparedstatement
preparedStmt.execute();
conn.close();
You can only use the first form if there are no bind variables (question marks) in the query. It's just a shortened version of what you posted.
Also, if you use the shortened form you won't have the opportunity to reuse the PreparedStatement object.
of course u can use a string variable for the query in which u put in ur dynamic data and run it.
rs = connection.prepareStatement(variable).executeQuery();
The long form is often, but prepared statements can be precompiled by the db, and if used properly will help prevent sql injection.
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
conn = getConn();
ps = conn.prepareStatement("select * from x where y = ? "); //note no sb.append()'s or +'s, to helps prevent sql injection
ps.setLong(1, 12l);
rs = ps.executeQuery();
while (rs.next()) {
... act ...
}
} catch ( Exception e) {
} finally {
if (rs != null) rs.close();
if (ps != null) ps.close();
if (conn != null) conn.close();
}
Who said java was verbose. :)

Resources