my Mysql DBD insert call suddently silently fails - perl

use DBI();
What is causing this insert to fail to err 1, the SELECT call works OK so its not credential issue.
$dbh = DBI->connect("DBI:mysql:$dbname:$dbhost","$dbuser","$dbpass");
my $sth = $dbh->prepare( "INSERT INTO call_fields VALUES ( ?, ?, ?, ? )" ) or die print "$DBI:errstr";
$sth->execute( "NULL", 0, 0, "testing" ) or die print "er $DBI::errstr";
mysql ver 5.5
This is perl 5, version 14, subversion 1 (v5.14.1) built for MSWin32-x64
NOTE: This syntax works OK:
$dbh->do(q/insert into call_fields values(null,0,0,"testing") /) or die "$dbh::errstr";

Set the RaiseError connection attribute and let DBI do the error handling.
The SQL NULL value is represented as undef in Perl, not the quoted string NULL.
use strictures;
use DBI qw();
my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass, { RaiseError => 1 });
my $sth = $dbh->prepare('INSERT INTO call_fields VALUES ( ?, ?, ?, ? )');
$sth->execute(undef, 0, 0, 'testing');

The statement
die print "$DBI:errstr";
is suspect. As Alan Haggai Alavi pointed out in the comments, you are missing a colon. It should be $DBI::errstr. Also, it is displaying 1 in stdERR (not stdout) because you chain die with print. print returns 1 for a successful print, which is returned to die and then displayed.
It should also display the print -- in stdout -- or at least a warning of undefined value in print, but unless you are using use warnings, it will be rather silent. (It might print :errstr.)
So:
die $DBI::errstr;
Should improve your situation. Adding
use strict;
use warnings;
if it is not already there will further improve it.

In the hope that you're just a little confused trying to collate all the information that you've been given, here is your code rewritten to use all of the suggestions you've been given (along with a couple of other changes that I've made).
I've commented the changes I've made on each line.
# Declared $dbh
# Removed unnecessary quote marks.
my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass);
# Removed unnecessary call to print
# Replaced $DBI::errstr with $sth->errstr
my $sth = $dbh->prepare( 'INSERT INTO call_fields VALUES ( ?, ?, ?, ? )' )
or die $sth->errstr;
# Replaced string "NULL" with undef (which DBI treats as NULL)
# Removed unnecessary call to print
# Replaced $DBI::errstr with $sth->errstr
$sth->execute( undef, 0, 0, 'testing' ) or die $sth->errstr;
I suspect it's the removal of the unnecessary print calls and switching "NULL" to undef that actually fix your problem.
In addition, I'd strongly recommend a) setting the RaiseError flag when connecting to the DB and b) adding the column names to the INSERT statement and then removing the first, nullable, column from the statement completely.

Your error printing:
... or die print "er $DBI::errstr";
doesn't look right.
As Alan mentioned, you fixed the double colon, but the call to print that die is executing is returning 1. (1 == successful print...somewhere)
Change your die syntax to this:
... or die "er $DBI::errstr";
You should get something like this:
er {DBI_ERROR_MESSAGE} at {Script_Name} line XXX.
You wrote:
$dbh = DBI->connect("DBI:mysql:$dbname:$dbhost","$dbuser","$dbpass");
my $sth = $dbh->prepare( "INSERT INTO call_fields VALUES ( ?, ?, ?, ? )" ) or die print "$DBI:errstr";
$sth->execute( "NULL", 0, 0, "testing" ) or die print "er $DBI::errstr";
and another example:
$dbh->do(q/insert into call_fields values(null,0,0,"testing") /) or die "$dbh::errstr";
This is an "apples and oranges" comparison. The first one is doing an insert of the string "NULL", but the second one is inserting the 'null' value.
I assume your call_fields table that is getting the first prepared input value is not accepting strings, and thus the $sth->execute is failing.

Related

Having trouble in saving scalar variable into DB (sqlite3)?

# I am saving output to an array and the array looks like this:-
60=20130624-09:45:02.046|21=1|38=565|52=20130624-09:45:02.046|35=D|10=085|40=1|9=205|100=MBTX|49=11342|553=2453|34=388|1=30532|43=Y|55=4323|54=1|56=MBT|11=584|59=0|114=Y|8=FIX.4.4|
# Then i converted this array to scalar variable like this:-
$scal=join('' , #arr);
# And now I am trying to save this into db:-
my $st = qq(INSERT INTO demo (fix)
VALUES ($scal));
my $r = $dbh->do($st) or die $DBI::errstr;
#And my table schema is:-
CREATE TABLE demo (fix varchar);
And I keep getting errors :- DBD::SQLite::db do failed: near ":45": syntax error at pdb.pl line 92, <STDIN> line 1.
DBD::SQLite::db do failed: near ":45": syntax error at pdb.pl line 92, <STDIN> line 1.
Any help will be appreicated
The way you denote your array is a bit weird. Usually you would write it as
my #arr = ( '60=20130624-09:45:02.046',
'21=1',
'38=565',
... );
or whatever your actual content is. But this is not the problem here because you flatten it to the string $scal anyway.
One way to insert this string into your DB is to put ticks (') around it:
my $st = qq(INSERT INTO demo (fix) VALUES ('$scal'));
my $r = $dbh->do($st) or die $DBI::errstr;
But this is bad because it's vulnerable to SQL injection (http://imgs.xkcd.com/comics/exploits_of_a_mom.png).
Consider the case your string is foo'); delete from demo; --. The final result would then be
INSERT INTO demo (fix) VALUES ('foo'); delete from demo; --')
The second reason why this is bad: Your string could contain ticks ($scal="foo's bar") and that also would mess up the resulting INSERT statement:
INSERT INTO demo (fix) VALUES ('foo's bar');
Conclusion: it's always better to use parameterized queries:
my $st = 'INSERT INTO demo (fix) VALUES (?)';
my $r = $dbh->do($st, undef, $scal) or die $DBI::errstr;
The undef is for additional SQL options (I've rarely seen anything different from undef here). The following parameters are replaced for the ?s in the statement. The DB driver does all the quoting for you. The more ? you use, the more parameters you must supply to do():
my $st = 'INSERT INTO sample_tbl (col1, col2, col3) VALUES (?, ?, ?)';
my $r = $dbh->do($st, undef, 'foo', 42, $scal) or die $DBI::errstr;

PERL SCRIPT ERROR (DBD ERROR: error possibly near <*> indicator atrror possibly near <*> indicator at char 53 in 'insert

i am getting below error while running below perl script. Can anybody help to figure it out?
$ENV{PATH}= '/appl/OMS/scripts:/etc:/usr/bin:/usr/sbin:/b...';
$ENV{PATH}= '/appl/OMS/scripts:/etc:/usr/bin:/usr/sbin:/bin:/usr/local/bin:/usr/local/opt/oracle/client/11.2.0.4/bin';
$ENV{ORACLE_HOME} ='/usr/local/opt/oracle/client/11.2.0.4';
$ENV{NLSPATH} = '/usr/lib/nls/msg/%L/%N:/usr/lib/nls/msg/%L/%N.cat';
$ENV{CLASSPATH} = 'CLASSPATH=/usr/local/opt/oracle/client/11.2.0.4/jdbc/lib';
$ENV{JAVA_HOME} = '/appl/OMS/Software/java';
$ENV{PERL5LIB} = '/appl/OMS/perl/lib';
use lib "/appl/OMS/perl/lib";
}
use DBI;
use DBD::Oracle;
use Data::Dumper;
use POSIX qw/strftime/;
use Switch;
use Term::ANSIColor;
print " Input the environment name:\n";
chomp($env=<STDIN>);
print " Input attuid:\n";
chomp($attuid=<STDIN>);
print " Input first name:\n";
chomp($fname = <STDIN>);
print " Input last name:\n";
chomp($lname= <STDIN>);
#chomp($lname);
my $dbInst="t1oms4d8.db.att.com";
my $dbUser="OMS1AT01utils";
my $dbPass="pswd4conn";
my $host = "t1oms5c1.sldc.sbc.com";
#$dsn= "dbi:oracle:T2OMS1D4.db.att.com:t2oms1c1.hydc.sbc.com:1521";
#$DBIconnect= DBI->connect($dsn,OMS0BT08utils,Pwd0wner1);
my $dbh=DBI->connect("dbi:Oracle:$dbInst", $dbUser, $dbPass);
my $sth = $dbh->prepare('create table temp_table1 as (select * from users where user_id like '%SR508W%')');
$sth-> execute();
print "test1";
my $sth = $dbh->prepare("update temp_table1 set user_id= ".$attuid.", first_nm= ".$fname.", last_nm= ".$lname." where user_id like '%SR508W%' " );
$sth-> execute();
print "test2";
my $sth = $dbh->prepare("insert into users (select * from temp_table1)");
$sth-> execute();
my $sth = $dbh->prepare("insert into user_password(user_id,password1) values (".$attuid." ,(select PASSWORD1 FROM user_password where user_id like '%SR508W%'))");
$sth-> execute();
my $sth = $dbh->prepare("insert into user_role(user_id,role_id) values (".$attuid." ,'1')");
$sth-> execute();
my $sth = $dbh->prepare("select * from temp_table1");
$sth-> execute();
my $sth = $dbh->prepare("drop table temp_table1");
$sth->execute();
$sth->finish();
$dbh->commit or die $DBI::errstr;
I am running the script as below:
$ ./dbtrial.pl
Input the environment name:
oms1at01
Input attuid:
sm501u
Input first name:
swapnil
Input last name:
mahindrakar
The error message is the following:
DBD::Oracle::st execute failed: ORA-00904: "MAHINDRAKAR": invalid identifier (DBD ERROR: error possibly near <*> indicator at char 69 in 'update temp_table1 set user_id= sm501u, first_nm= swapnil, last_nm= <*>mahindrakar where user_id like '%SR508W%' ') [for Statement "update temp_table1 set user_id= sm501u, first_nm= swapnil, last_nm= mahindrakar where user_id like '%SR508W%' "] at ./dbtrial.pl line 45, <STDIN> line 4.
DBD::Oracle::st execute failed: ORA-00984: column not allowed here (DBD ERROR: error possibly near <*> indicator at char 53 in 'insert into user_password(user_id,password1) values (<*>sm501u ,(select PASSWORD1 FROM user_password where user_id like '%SR508W%'))') [for Statement "insert into user_password(user_id,password1) values (sm501u ,(select PASSWORD1 FROM user_password where user_id like '%SR508W%'))"] at ./dbtrial.pl line 50, <STDIN> line 4.
DBD::Oracle::st execute failed: ORA-00984: column not allowed here (DBD ERROR: error possibly near <*> indicator at char 47 in 'insert into user_role(user_id,role_id) values (<*>sm501u ,'1')') [for Statement "insert into user_role(user_id,role_id) values (sm501u ,'1')"] at ./dbtrial.pl line 52, <STDIN> line 4.
commit ineffective with AutoCommit enabled at ./dbtrial.pl line 58, <STDIN> line 4.
test1test2----- websphe cldv0011 /appl/OMS/scripts/trials ---
Apart from missing a #! /usr/bin/perl (or similar) line, the most obvious thing wrong with your script is that you're not properly quoting your variables when you use them in SQL statemnents.
The easiest way to fix that is to use placeholders (?) in your $dbh->prepare() statements. And then supply the actual values in the $sth-execute()
For example:
my $sth = $dbh->prepare("update temp_table1 set user_id=?,
first_nm=?, last_nm=?
where user_id like '%SR508W%'");
$sth->execute($attuid,$fname,$lname);
This will automagically quote the variables that need quoting, which seems to be all of them for this example as they're all string values.
Also, you only have to define the scope of your variables once. You don't need to (and shouldn't) say my $sth= every time you set it to a new value. Either declare it separately before using it at all (with just my $sth;, or only declare it the very first time you use it.
And add a blank line or two to separate each different section of the script. That will make it MUCH easier to read than the ugly wall of text it is now.
Finally, you might want to consider taking the input from the command line. This will make testing much easier as you can use your shell's command-line history and recall features (i.e. hit up arrow and enter to execute the same command again) rather than having to enter the same four values into the script every time you run it. e.g.
# environment name is first arg
my $env = shift;
# attuid is second arg
my $attuid = shift;
# First name is third arg
my $fname = shift;
# Last name is fourth arg
my $lname = shift;
Run it like this:
$ ./dbtrial.pl oms1at01 sm501u swapnil mahindrakar
(Note: this is really primitive argument handling. Use Getopt::Std or Getopt::Long if you need something better than just taking the first four args from the command line).
Personally, I'd be inclined to rewrite your script to something more like this (with better formatting, removal of variables and modules that aren't used, better usage of DBI features, and improved sql string construction):
#! /usr/bin/perl
use strict;
use warnings;
use lib "/appl/OMS/perl/lib";
use DBI;
use DBD::Oracle;
my ($env,$attuid,$fname,$lname);
# environment name is first arg
$env = shift;
# attuid is second arg
$attuid = shift;
# First name is third arg
$fname = shift;
# Last name is fourth arg
$lname = shift;
my ($dbInst, $dbUser, $dbPass, $host, $dbh, $sth, $sql, $where);
$dbInst="t1oms4d8.db.att.com";
$dbUser="OMS1AT01utils";
$dbPass="pswd4conn";
$dbh=DBI->connect("dbi:Oracle:$dbInst",$dbUser,$dbPass);
$where=' where user_id like \'%SR508W%\'';
$dbh->do('create table temp_table1 as (select * from users ' . $where . ')');
print "test1";
$sql = 'update temp_table1 set user_id=?, first_nm=?, last_nm=?' . $where;
$sth = $dbh->prepare($sql);
$sth->execute($attuid,$fname,$lname);
print "test2";
$dbh->do('insert into users (select * from temp_table1)');
$sql = 'insert into user_password(user_id,password1) values (?,(select PASSWORD1 FROM user_password' . $where . ' ))';
$sth = $dbh->prepare($sql);
$sth->execute($attuid);
$sql = 'insert into user_role(user_id,role_id) values (?,?)';
$sth = $dbh->prepare($sql);
# does '1' need to be quoted here? if field is a varchar or similar then yes, otherwise no. assuming yes as in the original script.
$sth->execute($attuid,'1');
$dbh->do('select * from temp_table1');
$dbh->do('drop table temp_table1');
$sth->finish();
$dbh->commit or die $DBI::errstr;
$dbh->finish();

Error while running sql statement on a sqlite3 db using Perl DBI

I am trying to run a select statement to a sqlite3 database using Perl DBI. The following is the code:
my $dbh = DBI->connect( "DBI:SQLite:dbname=./GenBankData.db" , "" , "" , { PrintError => 0 , RaiseError => 1 } );
my $Sql = 'select AccessionQueryResultID, AccessionNumber, Definition from AccessionQueryResult';
my $sth = $dbh->prepare( $Sql ) or die "Couldn't prepare statement: " . $dbh->errstr;;
$sth->execute( $Sql) or die "Couldn't execute statement: " . $dbh->errstr;
But I am getting the following error:
DBD::SQLite::st execute failed: called with 1 bind variables when 0 are needed at /home/mysite.cgi line 33
I have checked that the database and table exist and the same query works fine if I used the sqlite3 command line to run the query.
Thanks
$sth is a statement handle. It represents the SQL statement/query, so having to provide the SQL statement to the object again makes no sense.
The args $sth->execute expects are values needed to fill in replaceable parameters (?) in the query.
my $sth = $dbh->prepare("INSERT INTO MyTable VALUES (?, ?)");
$sth->execute(1, "a");
$sth->execute(2, "b");
$sth->execute(3, "c");
In your case, since your query doesn't use any replaceable parameters, you should not pass any args to execute.
$sth->execute();
You're calling the execute command with incorrect parameters. You've already setup the SQL statement in the previous line, you don't need to do it again. Try this instead:
$sth->execute() or die "Couldn't execute statement: " . $dbh->errstr;

How do I insert values from a hash into a database using Perl's DBI module?

I need to insert values from a hash into a database. Following is the code template I have to insert values in table1 column key and value:
use DBI;
use strict;
%hash; #assuming it already contains desired values
my $dbh = DBI->connect(
"dbi:Sybase:server=$Srv;database=$Db",
"$user", "$passwd"
) or die sprintf 'could not connect to database %s', DBI->errstr;
my $query= "Insert INTO table1(key, values) VALUES (?,?) ";
my $sth = $dbh->prepare($query)
or die "could not prepare statement\n", $dbh->errstr;
$sth-> execute or die "could not execute", $sth->errstr;
I know how to insert values using array i.e use execute_array(), but do not know how to insert values present in %hash in table1.
Any suggestions?
The following uses the execute_array function as mentioned in your question. I tested it.
my $dbh = DBI->connect("DBI:mysql:database=$DB;host=$host;port=$port", $user, $password);
my %hash = (
1 => 'A',
2 => 'B',
0 => 'C',
);
my #keys = keys %hash;
my #values = values %hash;
my $sth = $dbh->prepare("INSERT INTO table1(id, value) VALUES (?,?);");
$sth->execute_array({},\#keys, \#values);
(Sorry, I don't have a Sybase database to work with, or I'd use it as an example.)
Try SQL::Abstract
use DBI;
use SQL::Abstract;
use strict;
%hash; #assuming it already contains desired values
my $dbh = DBI->connect(
"dbi:Sybase:server=$Srv;database=$Db",
"$user", "$passwd"
) or die sprintf 'could not connect to database %s', DBI->errstr;
my ($query, #bind) = $sql->insert("tableName", \%hash);
my $sth = $dbh->prepare($query)
or die "could not prepare statement\n", $dbh->errstr;
$sth-> execute (#bind) or die "could not execute", $sth->errstr;
Here's a mostly easy way to build the query. I will typically do something like this because I haven't found another workaround yet.
use strict;
use DBI;
my $dbh = Custom::Module::Make::DBH->connect('$db');
my %hash = (
apple => 'red',
grape => 'purple',
banana => 'yellow',
);
my $keystr = (join ",\n ", (keys %hash));
my $valstr = join ', ', (split(/ /, "? " x (scalar(values %hash))));
my #values = values %hash;
my $query = qq`
INSERT INTO table1 (
$keystr
)
VALUES (
$valstr
)
`;
my $sth = $dbh->prepare($query)
or die "Can't prepare insert: ".$dbh->errstr()."\n";
$sth->execute(#values)
or die "Can't execute insert: ".$dbh->errstr()."\n";
But it's possible I also didn't understand the question correctly :P
Maybe you could try using
for my $key (keys %hash) {
$sth->execute($key, $hash{$key}) or die $sth->errstr;
}
Is this what you're trying to achieve?
If I understand the manual correctly ("Execute the prepared statement once for each parameter tuple (group of values) [...] via a reference passed ...") it should also be possible to simply to
($tuples, $rows) = $sth->execute_array(\%hash) or die $sth->errstr;

How do I insert values from parallel arrays into a database using Perl's DBI module?

I need to insert values in database using Perl's DBI module. I have parsed a file to obtain these values and hence these values are present in an arrays, say #array1, #array2, #array3. I know how to insert one value at a time but not from an arrays.
I know insert one value at a time:
$dbh = DBI->connect("dbi:Sybase:server=$Srv;database=$Db", "$user", "$passwd") or die "could not connect to database";
$query= "INSERT INTO table1 (id, name, address) VALUES (DEFAULT, tom, Park_Road)";
$sth = $dbh->prepare($query) or die "could not prepare statement\n";
$sth-> execute or die "could not execute statement\n $command\n";
I am not sure if I have array1 containing ids, array2 containing names, and array3 containing address, how would I insert values.
Since you have parallel arrays, you could take advantange of execute_array:
my $sth = $dbh->prepare('INSERT INTO table1 (id, name, address) VALUES (?, ?, ?)');
my $num_tuples_executed = $sth->execute_array(
{ ArrayTupleStatus => \my #tuple_status },
\#ids,
\#names,
\#addresses,
);
Please note that this is a truncated (and slightly modified) example from the documentation. You'll definitely want to check out the rest of it if you decide to use this function.
Use placeholders.
Update: I just realized you have parallel arrays. That is really not a good way of working with data items that go together. With that caveat, you can use List::MoreUtils::each_array:
#!/usr/bin/perl
use strict; use warnings;
use DBI;
use List::MoreUtils qw( each_array );
my $dbh = DBI->connect(
"dbi:Sybase:server=$Srv;database=$Db",
$user, $passwd,
) or die sprintf 'Could not connect to database: %s', DBI->errstr;
my $sth = $dbh->prepare(
'INSERT INTO table1 (id, name, address) VALUES (?, ?, ?)'
) or die sprintf 'Could not prepare statement: %s', $dbh->errstr;
my #ids = qw( a b c);
my #names = qw( d e f );
my #addresses = qw( g h i);
my $it = each_array(#ids, #names, #address);
while ( my #data = $it->() ) {
$sth->execute( #data )
or die sprintf 'Could not execute statement: %s', $sth->errstr;
}
$dbh->commit
or die sprintf 'Could not commit updates: %s', $dbh->errstr;
$dbh->disconnect;
Note that the code is not tested.
You might also want to read the FAQ: What's wrong with always quoting "$vars"?.
Further, given that the only way you are handling error is by dying, you might want to consider specifying { RaiseError => 1 } in the connect call.
How could you not be sure what your arrays contain? Anyway the approach would be the iterate through one array and assuming the other arrays have corresponding values put those into the insert statement
Another way would be to use a hash as an intermediate storage area. IE:
my $hash = {};
foreach(#array1) {
$hash->{id} = $array1[$_];
$hash->{name} = $array2[$_];
$hash->{address} = $array3[$_];
}
foreach( keys %$hash ) {
$sql = "insert into table values(?,?,?)";
$sth = $dbh->prepare($sql) or die;
$sth->execute($hash->{id}, $hash->{name}, $hash->{address}) or die;
}
Though again this depends on the three arrays being synced up. However you could modify this to do value modifications or checks or greps in the other arrays within the first loop through array1 (ie: if your values in array2 and array3 are maybe stored as "NN-name" and "NN-address" where NN is the id from the first array and you need to find the corresponding values and remove the NN- with a s// regex). Depends on how your data is structured though.
Another note is to check out Class::DBI and see if it might provide a nicer and more object oriented way of getting your data in.