prepare command not working with DBI using perl - perl

$sth = $dbh->prepare("select CODE_ID,NAME_CODE,SUM(INR_COL + OUT_COL) AS "TOTAL SUM" from nwsa where CODE_ID='L01A' OR CODE_ID='L01B'OR CODE_ID='L01C' OR CODE_ID='L01D' OR CODE_ID='L01DA' OR CODE_ID='L01E' OR CODE_ID='L02A' OR CODE_ID='L02B' OR CODE_ID='L02C' OR CODE_ID='L02E' OR CODE_ID='L02N' group by CODE_ID,NAME_CODE "); # your query here
$sth->execute( );
$out = DBIx::Dump->new('format' => csv, # excel or csv
'output' => $FILENAME, # file to save as
'sth' => $sth);
error coming as bareword found near "TOTAL SUM" ..
what is the error..?

Escape the quotes:
$sth = $dbh->prepare("select CODE_ID,NAME_CODE,SUM(INR_COL + OUT_COL) AS \"TOTAL SUM\" ........
or use single quotes:
$sth = $dbh->prepare('select CODE_ID,NAME_CODE,SUM(INR_COL + OUT_COL) AS "TOTAL SUM" ........');

Related

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();

Perl/DBI - Insert rows from Postgres to an Oracle table

I am trying to write a script that will read the data from postgresql table and insert it to an oracle table, here is my script :
#!/usr/local/bin/perl
use strict;
use DBI;
use warnings FATAL => qw(all);
my $pgh = pgh(); # connect to postgres
my $ora = ora(); # connect to oracle
my #rows;
my $rows =[] ;
my $placeholders = join ", ", ("?") x #rows;
my $sth = $pgh->prepare('SELECT * FROM "Employees"');
$sth->execute();
while (#rows = $sth->fetchrow_array()) {
$ora->do("INSERT INTO employees VALUES($placeholders)");
}
#connect to postgres
sub pgh {
my $dsn = 'DBI:Pg:dbname=northwind;host=localhost';
my $user = 'postgres';
my $pwd = 'postgres';
my $pgh = DBI -> connect($dsn,$user,$pwd,{'RaiseError' => 1});
return $pgh;
}
#connect to oracle
sub ora {
my $dsn = 'dbi:Oracle:host=localhost;sid=orcl';
my $user = 'nwind';
my $pwd = 'nwind';
my $ora = DBI -> connect($dsn,$user,$pwd,{'RaiseError' => 1});
return $ora;
}
I am getting the following error :
DBD::Oracle::db do failed: ORA-00936: missing expression (DBD ERROR: error possibly near <*> indicator at char 29 in 'INSERT INTO employees VALUES(<*>)') [for Statement "INSERT INTO employees VALUES()"] at /usr/share/perlproj/cgi-bin/scripts/nwind_pg2ora.pl line 19.
Please help me to get my code correct.
Many thanks !!
Tonya.
See the documentation for DBD::Oracle you have to bind the parameter value for the BLOBs like :
use DBD::Oracle qw(:ora_types);
$sth->bind_param($idx, $value, { ora_type=>ORA_BLOB, ora_field=>'PHOTO' });
my #rows;
my $rows =[] ;
my $sth = $pgh->prepare('SELECT * FROM "Employees"');
$sth->execute();
while (#rows = $sth->fetchrow_array()) {
my $placeholders = join ", ", ("?") x #rows;
$ora->do("INSERT INTO employees VALUES($placeholders)");
}
You're joining an empty #rows to create an empty $placeholders.
perform the join inside the while loop, before the do().
The following lazily creates a statement handle for inserting into the Oracle database based off the number of columns in the returned records.
It then inserts those column values into the database, so obviously we're assuming the table structures are identical:
use strict;
use DBI;
use warnings FATAL => qw(all);
my $pgh = pgh(); # connect to postgres
my $ora = ora(); # connect to oracle
my $sth = $pgh->prepare('SELECT * FROM "Employees"');
$sth->execute();
my $sth_insert;
while (my #cols = $sth->fetchrow_array()) {
$sth_insert ||= do {
my $placeholders = join ", ", ("?") x #cols;
$ora->prepare("INSERT INTO employees VALUES ($placeholders)");
};
$sth_insert->execute(#cols);
}

Perl DBI and sql now()

I have been trying to use sql NOW() function while I update a table. But nothing happens to that date field, I think DBI just ignores that value. Code is :
dbUpdate($id, (notes => 'This was an update', filesize => '100.505', dateEnd => 'NOW()'));
and the function is :
sub dbUpdate {
my $id = shift;
my %val_hash = #_;
my $table = 'backupjobs';
my #fields = keys %val_hash;
my #values = values %val_hash;
my $update_stmt = "UPDATE $table SET ";
my $count = 1;
foreach ( #fields ) {
$update_stmt .= "$_ = ? ";
$update_stmt .= ', ' if ($count != scalar #fields);
$count++;
}
$update_stmt .= " WHERE ID = ?";
print "update_stmt is : $update_stmt\n";
my $dbo = dbConnect();
my $sth = $dbo->prepare( $update_stmt );
$sth->execute( #values, $id ) or die "DB Update failed : $!\n";
$dbo->disconnect || die "Failed to disconnect\n";
return 1;
}#dbUpdate
As you can see this is a "dynamic" generation of the sql query and hence I dont know where an sql date function(like now()) come.
In the given example the sql query will be
UPDATE backupjobs SET filesize = ? , notes = ? , dateEnd = ? WHERE ID = ?
with param values
100.55, This was an update, NOW(), 7
But the date column still shows 0000-00-........
Any ideas how to fix this ?
I have been trying to use sql NOW() function while I update a table. But
nothing happens to that date field, I think DBI just ignores that
value.
No, it doesn't. But the DB thinks you are supplying a datetime or timestamp data type when you are in fact trying to add the string NOW(). That of course doesn't work. Unfortunately DBI cannot find that out for you. All it does is change ? into an escaped (via $dbh->quote()) version of the string it got.
There are several things you could do:
Supply a current timestamp string in the appropriate format instead of the string NOW().
If you have it available, you can use DateTime::Format::MySQL like this:
use DateTime::Format::MySQL;
dbUpdate(
$id,
(
notes => 'This was an update',
filesize => '100.505',
dateEnd => DateTime::Format::MySQL->format_datetime(DateTime->now),
)
);
If not, just use DateTime or Time::Piece.
use DateTime;
my $now = DateTime->now->datetime;
$now =~ y/T/ /;
dbUpdate(
$id,
(notes => 'This was an update', filesize => '100.505', dateEnd => $now));
Tell your dbUpdate function to look for things like NOW() and react accordingly.
You can do something like this. But there are better ways to code this. You should also consider that there is e.g. CURRENT_TIMESTAMP() as well.
sub dbUpdate {
my $id = shift;
my %val_hash = #_;
my $table = 'backupjobs';
my $update_stmt = "UPDATE $table SET ";
# Check for the NOW() value
# This could be done with others too
foreach ( keys %val_hash ) {
if ($val_hash{$_} =~ m/^NOW\(\)$/i) {
$update_stmt .= "$_ = NOW()";
$update_stmt .= ', ' if scalar keys %val_hash > 1;
delete $val_hash{$_};
}
}
# Put everything together, join keeps track of the trailing comma
$update_stmt .= join(', ', map { "$_=?" } keys %val_hash );
$update_stmt .= " WHERE ID = ?";
say "update_stmt is : $update_stmt";
say "values are: ", join(', ', values %val_hash);
my $dbo = dbConnect();
my $sth = $dbo->prepare( $update_stmt );
$sth->execute( values %val_hash, $id ) or die "DB Update failed : $!\n";
$dbo->disconnect || die "Failed to disconnect\n";
return 1;
}
Write your queries yourself.
You're probably not going to do it and I'll not add an example since you know how to do it anyway.
Here's something else: Is this the only thing you do with your database while your program runs? It is not wise to connect and disconnect the database every time you make a query. It would be better for performance to connect the database once you need it (or at the beginning of the program, if you always use it) and just use this dbh/dbo everywhere. It saves a lot of time (and code).
Sorry but you can't use NOW() as an interpolated value like that.
when a ? is used in an SQL statement, the corresponding value is (via some mechanism or other) passed to the database escaped, so whatever value is interpreted as a string, not as SQL. So you are in effect attempting to use the string 'NOW()' as a date value, not the function NOW().
To use NOW() as a function, you will have to insert it into the SQL itself rather than pass it as a bound value. This means you will either have to use a hack of your dbupdate function or write a new one, or obtain the time as a string in perl and pass the resulting string the dbupdate.

How can I use a query with placeholder inside quotes? (perl / postgresql)

I'm trying to execute the following script:
#!/usr/bin/perl -w
use strict;
use DBI;
my $db = "Pg";
my $db_database = "whatever";
my $user = "whatever";
my $password = "whatever";
my $dbh = DBI->connect("dbi:$db:dbname=$db_database", $user, $password);
my $query = $dbh->prepare (q{SELECT
arrival_date - INTERVAL '? MINUTE'
FROM emails LIMIT 1})
or die ("unable to prepare");
$query->execute(60) or die("unable to execute");
print $query->fetchrow_array, "\n";
(arrival_date has this format: timestamp with time zone NOT NULL default CURRENT_TIMESTAMP)
The problem is that the question mark placeholder is not detected because its inside single quotes:
DBD::Pg::st execute failed: called with 1 bind variables when 0 are needed
It does not help if I use qq{}, $1 placeholder, and tried a few variations with $dbh->quote. How can I make this work?
You can't use placeholders inside quotes. You can use SQL string concatenation, but in this case, it's easier to do it by using multiplication:
my $query = $dbh->prepare (q{SELECT
arrival_date - ? * INTERVAL '1 MINUTE'
FROM emails LIMIT 1});
$query->execute(60);
That way, you don't have to append ' minutes' to the number when you execute the query.
I just found a solution here: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=321917
It works using ?::interval instead of 'interval ?':
my $query = $dbh->prepare (q{SELECT
arrival_date - ?::interval
FROM emails LIMIT 1});
$query->execute('60 MINUTE');
my $query = $dbh->prepare (<<SQL) or die ("unable to prepare");
SELECT arrival_date - INTERVAL ? FROM emails LIMIT 1
SQL
$query->execute("60 MINUTE");
or
my $query = $dbh->prepare (<<SQL) or die ("unable to prepare");
SELECT arrival_date - INTERVAL CONCAT(?, " MINUTE") FROM emails LIMIT 1
SQL
$query->execute(60);

DBD::CSV: Problem with file-name-extensions

In this script I have problems with file-name-extensions:
if I use /home/mm/test_x it works, with file named /home/mm/test_x.csv it doesn't:
#!/usr/bin/env perl
use warnings; use strict;
use 5.012;
use DBI;
my $table_1 = '/home/mm/test_1.csv';
my $table_2 = '/home/mm/test_2.csv';
#$table_1 = '/home/mm/test_1';
#$table_2 = '/home/mm/test_2';
my $dbh = DBI->connect( "DBI:CSV:" );
$dbh->{RaiseError} = 1;
$table_1 = $dbh->quote_identifier( $table_1 );
$table_2 = $dbh->quote_identifier( $table_2 );
my $sth = $dbh->prepare( "SELECT a.id, a.name, b.city FROM $table_1 AS a NATURAL JOIN $table_2 AS b" );
$sth->execute;
$sth->dump_results;
$dbh->disconnect;
Output with file-name-extention:
DBD::CSV::st execute failed:
Execution ERROR: No such column '"/home/mm/test_1.csv".id' called from /usr/local/lib/perl5/site_perl/5.12.0/x86_64-linux/DBD/File.pm at 570.
Output without file-name-extension:
'1', 'Brown', 'Laramie'
'2', 'Smith', 'Watertown'
2 rows
Is this a bug?
cat test_1.csv
id,name
1,Brown
2,Smith
5,Green
cat test_2.csv
id,city
1,Laramie
2,Watertown
8,Springville
DBD::CSV provides a way to map the table names you use in your queries to filenames. The same mechanism is used to set up per-file attributes like line ending, field separator etc. look for 'csv_tables' in the DBD::CSV documentation.
#!/usr/bin/env perl
use warnings;
use strict;
use DBI;
my $dbh = DBI->connect("DBI:CSV:f_dir=/home/mm", { RaiseError => 1 });
$dbh->{csv_tables}->{table_1} = {
'file' => 'test_1.csv',
'eol' => "\n",
};
$dbh->{csv_tables}->{table_2} = {
'file' => 'test_2.csv',
'eol' => "\n",
};
my $sth = $dbh->prepare( "SELECT a.id, a.name, b.city FROM table_1 AS a NATURAL JOIN table_2 AS b" );
$sth->execute();
$sth->dump_results();
$dbh->disconnect();
In my case I had to specify a line ending character, because I created the CSV files in vi so they ended up with Unix line endings whereas DBD::CSV assumes DOS/Windows line-endings regardless of the platform the script is run on.
I looks like even this works:
#!/usr/bin/env perl
use warnings; use strict;
use 5.012;
use DBI;
my $dbh = DBI->connect("DBI:CSV:f_dir=/home/mm/Dokumente", undef, undef, { RaiseError => 1, });
my $table = 'new.csv';
$dbh->do( "DROP TABLE IF EXISTS $table" );
$dbh->do( "CREATE TABLE $table ( id INT, name CHAR(64), city CHAR(64) )" );
my $sth_new = $dbh->prepare( "INSERT INTO $table( id, name, city ) VALUES ( ?, ?, ? )" );
$dbh->{csv_tables}->{table_1} = { 'file' => '/tmp/test_1.csv', 'eol' => "\n", };
$dbh->{csv_tables}->{table_2} = { 'file' => '/tmp/test_2.csv', 'eol' => "\n", };
my $sth_old = $dbh->prepare( "SELECT a.id, a.name, b.city FROM table_1 AS a NATURAL JOIN table_2 AS b" );
$sth_old->execute();
while ( my $hash_ref = $sth_old->fetchrow_hashref() ) {
state $count = 1;
$sth_new->execute( $count++, $hash_ref->{'a.name'}, $hash_ref->{'b.city'} );
}
$dbh->disconnect();
I think you might want to take a look at the f_ext and f_dir attributes. You can then class your table names as "test_1" and "test_2" without the csv but the files used will be test_1.csv and test_2.csv. The problem with a dot in the table name is a dot is usually used for separating the schema from the table name (see f_schema).