How to use postgres dbd placeholders for DB Object names - perl

(or How to iterate thru information schema Using perl DBI (DBD::PG) and placeholders?)
Windows 7, ActiveState Perl 5.20.2, PostgreSQL 9.4.1 .
Cases A, B and C below were successful when using a placeholder for a COLUMN VALUE. In order
no placeholder used
passed a literal
passed a variable (populated with same literal)
It would be great to raise it up a level to DB Objects.. (tables, views etc)
Here's the output with the error for Case D:
Z:\CTAM\data_threat_mapping\DB Stats\perl scripts>test_placeholder.pl
A Row Count: 1
B Row Count: 1
C Row Count: 1
DBD::Pg::st execute failed: ERROR: syntax error at or near "$1"
LINE 1: SELECT COUNT(*) FROM $1 WHERE status = 'Draft';
^ at Z:\CTAM\data_threat_mapping\DB Stats\perl
scripts\test_placeholder.pl line 34.
Much obliged for any direction!
#!/usr/bin/perl -w
use strict;
use diagnostics;
use DBI;
my $num_rows = 0;
# connect
my $dbh = DBI->connect("DBI:Pg:dbname=CTAM;host=localhost",
"postgres", "xxxxx",
{ 'RaiseError' => 1, pg_server_prepare => 1 });
#---------------------
# A - success
my $sthA = $dbh->prepare(
"SELECT COUNT(*) FROM cwe_compound_element WHERE status = 'Draft';"
);
$sthA->execute(); # no placeholders
#---------------------
# B - success
my $sthB = $dbh->prepare (
"SELECT COUNT(*) FROM cwe_compound_element WHERE status = ?;"
);
$sthB->execute('Draft'); # pass 'Draft' to placeholder
#---------------------
# C - success
my $status_value = 'Draft';
my $sthC = $dbh->prepare(
"SELECT COUNT(*) FROM cwe_compound_element WHERE status = ?;"
);
$sthC->execute($status_value); # pass variable (column value) to placeholder
#---------------------
# D - failure
my $sthD = $dbh->prepare(
"SELECT COUNT(*) FROM ? WHERE status = 'Draft';"
);
$sthD->execute('cwe_compound_element'); # pass tablename to placeholder
I've tried single/double/sans quotes (q, qq)...

If
SELECT * FROM Foo WHERE field = ?
means
SELECT * FROM Foo WHERE field = 'val'
then
SELECT * FROM ?
means
SELECT * FROM 'Table'
and that's obviously wrong. Placeholders can only be used in an expression. Fix:
my $sthD = $dbh->prepare("
SELECT COUNT(*)
FROM ".$dbh->quote_identifier($table)."
WHERE status = 'Draft'
");
$sthD->execute();

Related

Why do I get the error "called with 1 bind variables when 0 are needed" when I try to execute a query with DBI?

I'm trying to select some rows from a table (PostgreSQL) using the following code:
my $kadadbh = DBI->connect(
"dbi:Pg:dbname=$dbname;host=$host",
$dbuser,
$dbpasswd
);
my $subject_nar_sel= $kadadbh->prepare(
'SELECT * FROM subject WHERE SUBSTRING(CAST(id AS text),1,6) = "?";'
);
$nar=605812;
$subject_nar_sel->execute($nar);
But I get an error:
called with 1 bind variables when 0 are needed at ...
I get the same error when I swap the single and double quotes:
"SELECT * FROM subject WHERE SUBSTRING(CAST(id AS text),1,6) = '?';"
How can I fix this?
You current query is testing whether SUBSTRING(CAST(id AS text),1,6) matches the literal string suspiciously-named quoted identifier "?". So don't quote the ?, even when the bind parameter has a string type:
SELECT * FROM subject WHERE SUBSTRING(CAST(id AS text),1,6) = ?

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

How do I use Placeholders in Perl to Inject a Variable into Mysql

I keep getting errors when attempting to use placeholders in my perl script for a Mysql routine.
Code :
use DBI;
my $driver = "mysql";
my $database = "database";
my $user = "exxxxxx";
my $password = "xxxxx";
my $dsn = "DBI:mysql:$database;mysql_local_infile=ON";
my $dbh = DBI->connect($dsn,$user,$password);
$dbh->do("SET \#tempc5 = (SELECT temp FROM day5 WHERE time = '00:00') ");
my $inter1 = i24;
$sth = $dbh->prepare( "SET \#sumadd5 = (SELECT ? FROM humid WHERE temp=\#tempc5) " );
$sth->bind_param( 1, $inter1 );
$sth->finish();
$dbh->disconnect();
This produces the following error:
Global symbol "$sth" requires explicit...
If I add a my $sth I get the following error:
Scalar found where operator expected...
Note that I am have no objection in trying this with $dbh->do("SET"
if possible.
The placeholders are not allowed for column names according to MySQL Manual for mysql_stmt_prepare() which is the function behind prepare.
The markers are legal only in certain places in SQL statements. For
example, they are permitted in the VALUES() list of an INSERT
statement (to specify column values for a row), or in a comparison
with a column in a WHERE clause to specify a comparison value.
However, they are not permitted for identifiers (such as table or
column names), or to specify both operands of a binary operator such
as the = equal sign. The latter restriction is necessary because it
would be impossible to determine the parameter type. In general,
parameters are legal only in Data Manipulation Language (DML)
statements, and not in Data Definition Language (DDL) statements.
If you think about it, it would not make sense to prepare a statement where you can change a column. Preparation of statement includes execution plan, but you can't plan execution of a statement where you don't know if given column has or doesn't have an index on it.
You can't use a placeholder there.
When you call prepare, all structural information about your tables is baked into the query, waiting for you to pass in data values to replace placeholders when you execute the query.
But you're trying to use a placeholder for a column name, which is part of the table's structure.
If you fix the Perl syntax to be:
my $inter1 = 'i24';
my $sth = $dbh->prepare( "SET \#sumadd5 = (SELECT ? FROM humid WHERE temp=\#tempc5) " );
$sth->execute($inter1);
it should run, but the ? will be treated as a data value rather than a column name (structural information). So you'll get the results of the SQL query
SET #sumadd5 = (SELECT 'i24' FROM humid WHERE temp=#tempc5)
instead of
SET #sumadd5 = (SELECT i24 FROM humid WHERE temp=#tempc5)
The subquery will return the literal value "i24" for each matching row rather than the value found in column i24.
You didn't quoted the vaule of $inter1. Change $inter1 = i24; to $inter1 = 'i24';. Just edited in your code, this will not give you syntax error.
use warnings;
use strict;
use DBI;
my $driver = "mysql";
my $database = "database";
my $user = "exxxxxx";
my $password = "xxxxx";
my $dsn = "DBI:mysql:$database;mysql_local_infile=ON";
my $dbh = DBI->connect($dsn,$user,$password);
$dbh->do("SET \#tempc5 = (SELECT temp FROM day5 WHERE time = '00:00') ");
my $inter1 = 'i24';
my $sth = $dbh->prepare( "SET \#sumadd5 = (SELECT ? FROM humid WHERE temp=\#tempc5) " );
$sth->bind_param( 1, $inter1 );
$sth->finish();
$dbh->disconnect();

How do I use bind values in a PostgreSQL UPDATE statement?

For my UPDATE statement on PostgreSQL (from Perl via DBI and DBD::Pg) I want to bind a value like this:
my $sql = 'UPDATE mytable SET foo = % WHERE id = 42';
my $foo_value = 23;
$dbh->do($sql, {}, $foo_value);
This gives the following error in the do() line:
DBD::Pg::db do failed: called with 1 bind variable when 0 are needed at...
I tried other syntaxes for placeholders, $1 and ?. Both fail with
DBD::Pg::db do failed: ERROR: invalid input syntax for type numeric: "" at...
What is the correct syntax?
The ? character is the correct placeholder.
my $sql = 'UPDATE mytable SET foo = ? WHERE id = 42';
my $foo_value = 23;
$dbh->do($sql, undef, $foo_value);

Perl DBI insert and select

I want to copy a single record from a table, modify some of the fields in the record and insert into the same table. The table has 90 columns.
Thought of using insert..select in one statement but there are 90 columns and i need to tell the column name in the select query. How can i do it in a better way in perldbi. Pls provide me an example.
Fetch and cache the column names for your table using the NAME statement attribute
my $sth = $dbh->prepare('SELECT * FROM my_table where 1=0');
$sth->execute;
my $cols = $sth->{NAME};
then use $cols to construct your insert...select using some replacement function to inject your modifications in the select.
my %mods_for_column = ( 'foo' => 'foo+10', 'bar' => 'trim(bar)' );
my $inscols = join(',', #$cols);
my $selcols = join(',',
map { exists($mods_for_column($_)) ? $mods_for_column($_) : $_ } #$cols
);
my $sql = "insert into my_table ($inscols) select $selcols from my_table where mumble...";