Script gives database error on just one system - mysql

I am a VOIP administrator and I have script for updating directory database in Perl which was purchased from vendor before I was employed here.
The script is working fine on all of the servers except for one.
#!/usr/bin/perl
use lib "/opt/asterisk/lib/";
use DBI;
use Asterisk::config;
sub trim($);
# database information
$db="kesc";
$host="sip-ho.kesc.com.pk";
$userid="foo";
$passwd="bar";
$connectionInfo="dbi:mysql:$db;$host";
$hubname = "";
# make connection to database
$dbh = DBI->connect($connectionInfo,$userid,$passwd);
# Perl trim function to remove whitespace from the start and end of the string
sub trim($)
{
my $string = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
}
my $rc = new Asterisk::config (file=>'/etc/asterisk/sip.conf',keep_resource_array=>0);
#list = $rc->fetch_sections_list();
$n = 1;
foreach (#list)
{
if ($_ ne "general") {
$entry = $rc->fetch_keys_hashref(section=>$_);
while ( my ($key, #value) = each(%$entry) )
{
if ($key eq "callerid") {
#vars = split('<',$value[0][0]);
$query = "insert into directory (extension,name,hub) values (" . trim($_) . ", '" . trim($vars[0]) . "', '$hubname') ON DUPLICATE KEY UPDATE hub='$hubname'";
$sth = $dbh->prepare($query);
$sth->execute();
}
}
}
$n++;
}
Now I get below mentioned error when executing it.
DBD::mysql::st execute failed: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near 's Office', '') ON DUPLICATE KEY UPDATE hub=''' at line 1 at ./directory line 39.
I have also replaced it from other server with same MySQL version where it works perfectly.
Please guide me.

Thank you for the code. It is as I suspected; you really shouldn't insert values directly into an SQL statement
Change line 37, 38, and 39 to this and it should work for you
$query = 'INSERT INTO directory (extension, name, hub) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE hub = ?';
$sth = $dbh->prepare($query);
$sth->execute( trim($_), trim($vars[0]), $hubname, $hubname );
Beware that the same problem is likely to exist elsewhere in the code base, so it really should be completely reviewed

Related

Insert/Update MySQL into longtext Column

How do I insert the following string into MySQL:
$myValue ouputs: [Hey, this is a multi text file that has special characters like this ' and this '' and this ,,"", and this ''' and this '''' and this !#$ and whatever]
But the following will not work because of special characters:
$sql = "UPDATE `mytable` SET NEWS=('$myValue') WHERE _id='1'";
I do not want to manually escape every character (like adding an ' before every ')
Update/Insert should should start at [ and end at ] (as seen in $myValue)
EDIT (mysqli)
$_myValue = mysqli_real_escape_string($myValue);
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "UPDATE `mytable` SET NEWS='$_myValue' WHERE _id='1'";
if ($conn->query($sql) === TRUE) {
echo "Record updated successfully";
} else {
echo "Error updating record: " . $conn->error;
}
From the syntax of your code I assume that php is used to submit the queries to mysql.
If you just want to escape special characters in a string variable passed to a field, then use
PDO::quote() (if you use PDO)
mysqli_real_escape_string() (if you use mysqli)
mysql_real_escape_string() (if you use mysql, although you should not)
If you are looking for a more generic solution gainst sql injection, then consider using prepared statements. See this landmark SO topic on how to prevent SQL injection in php-mysql environment.
If your using php you could look at using PDO;
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );
$sql = "UPDATE `mytable` SET NEWS=:myValue WHERE _id=1";
$st = $conn->prepare( $sql );
$st->bindValue(":myValue", $myValue, PDO::PARAM_STR);
$st->execute();
This will input all the data stored in $myValue. I would look at sanatising the input too.

MySQL query 2 tables, 1 insert

I'm trying to pull data from 2 tables and then insert result into a third table. My code follows but only does 1 correct entry, and the rest are blank. There are 348 entries total. What am I missing here?
$dbh = DBI->connect(
"DBI:mysql:$mysqldatabase:$mysqlhostname",
"$mysqlusername",
"$mysqlpassword"
);
if(!$dbh) { die("Error: could not get DBI handle\n"); }
$sqlConnect = 1;
$SQL =<<SQL;
SELECT * FROM oscmax2.info2
SQL
$sth = $dbh->prepare($SQL);
if(!$sth) { die("Error: " . $dbh->errstr . "\n"); }
if(!$sth->execute) { die("Error4: " . $sth->errstr . "\n"); }
while (my #row = $sth->fetchrow_array) {
$products_id = $FORM{'product_id'};
$affiliate_id = $FORM{'affiliate_id'};
$demo = $FORM{'demo'};
}
if($sth->rows != 0) {
$total_rows = $sth->rows;
for ($counter = 0; $counter < $total_rows; $counter++) {
$SQL =<<SQL;
SELECT products_attributes_id FROM oscmax2.products_attributes
WHERE products_id = '$products_id'
SQL
$sth = $dbh->prepare($SQL);
if(!$sth) { die("Error: " . $dbh->errstr . "\n"); }
if(!$sth->execute) { die("Error: " . $sth->errstr . "\n");}
while (my #row = $sth->fetchrow_array) {
$products_attributes_id = $FORM2{'products_attributes_id'};
}
$SQL =<<SQL;
INSERT INTO oscmax2.products_attributes_download(
products_attributes_id, products_attributes_filename,
products_attributes_maxdays, products_attributes_maxcount
)
VALUES
('$products_attributes_id', '$affiliate_id/$demo', '7', '1')
SQL
$dbh->do($SQL) || die("Error5: " . $dbh->errstr . "\n");
}
}
$sth->finish();
if($sqlConnect) { $dbh->disconnect();
The blocks
while (my #row = $sth->fetchrow_array) {
$products_id = $FORM{'product_id'};
$affiliate_id = $FORM{'affiliate_id'};
$demo = $FORM{'demo'};
}
and
while (my #row = $sth->fetchrow_array) {
$products_attributes_id = $FORM2{'products_attributes_id'};
}
are wrong. You use #row to accept the data from each row of the result, but never use it. The database fetch won't affect %FORM and %FORM2 so you are just collecting the same data from them several times over
Update
At a guess you want something like this. Please study it and use the techniques rather than copying and testing it as it is, as I have no way of knowing what the structure of your database is and I have made several guesses
You should note the following points
There is no need to test the status of each DBI operation and die if it failed. By default the PrintError option is enabled and DBI will raise a warning if there are any errors. If you want your program to die instead, which is wise, then you can enable RaiseError and disable PrintError and DBI will do it all for you
There is no need to fetch all of the data from a table into memory (which I think is what you are trying to do with your while loops. You should fetch each row into an array and process the data row by row unless you have a reason to do otherwise
You should always prepare your statement and use placeholders. Then you can pass the actual parameters to the execute call and DBI will correctly quote them for you. Furthermore you can move all the prepare calls top the top of the program, making your logic much clearer to read
There is almost never a reason to call finish or disconnect. Perl will do the right thing for you when your database or statement handles go out of scope or your program ends
I have named the statement handles $select1 and $select2. These are very poor names, but I don't know the structure of your database so I couldn't write anything better. That shouyldn't stop you from improving them
I have had to guess at the columns returned by the first SELECT statement. If the three variables don't correspond to the first three elements of #row then you need to correct that
You should avoid using capital letters in Perl lexical identifiers. They are reserved for globals like package names, and nasty clashes of purpose can be caused if you don't abide by this rule
use strict;
use warnings;
my ($mysqldatabase, $mysqlhostname, $mysqlusername, $mysqlpassword) = qw/ dbase host user pass /;
my $dbh = DBI->connect(
"DBI:mysql:$mysqldatabase:$mysqlhostname",
"$mysqlusername",
"$mysqlpassword",
{RaiseError => 1, PrintError => 0}
) or die "Unable to connect to database: $DBI::errstr";
my $select1 = $dbh->prepare('SELECT * FROM oscmax2.info2');
my $select2 = $dbh->prepare(<<__END_SQL__);
SELECT products_attributes_id FROM oscmax2.products_attributes\
WHERE products_id = ?
__END_SQL__
my $insert = $dbh->prepare(<<__END_SQL__);
INSERT INTO oscmax2.products_attributes_download (
products_attributes_id,
products_attributes_filename,
products_attributes_maxdays,
products_attributes_maxcount
)
VALUES ( ?, ?, ?, ? )
__END_SQL__
$select1->execute;
while ( my #row = $select1->fetchrow_array ) {
my ($products_id, $affiliate_id, $demo) = #row;
$select2->execute($products_id);
while ( my #row = $select2->fetchrow_array ) {
my ($products_attributes_id) = #row;
$insert->execute($products_attributes_id, "$affiliate_id/$demo", 7, 1 );
}
}

inserting special chracters into mysql using perl

hi i am very new to perl..
I have a temp_data.txt file like this
Id Comments
--------------------------------
1 this is a 'comment'
2 special comment
3 user comment 'user'
-----------------------------------
open (MYFILE, 'temp_data.txt');
while (<MYFILE>) {
if($_=~/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/)
{
$id=$1;
$comment = $2;
}
while(<MYFILE>)
{
$line=$_;
if($line=~/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/)
{
seek(MYFILE, -length($_), 1);
last;
}
else
{ if($_=~/\s*(.*)/)
{
$comment .=$1;
}
}
}
my $queryString = "INSERT INTO Headline (id,comment) VALUES ('$id', ' $comment')";
$sth = $dbh->prepare($queryString);
$sth->execute() or die $DBI::errstr;
$sth->finish();
}
but while inserting into data base if it encounters a special character throwing a error like this.
DBD::mysql::st execute failed: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near 'comment')' at line 1 at head.pl line 1.
can anyone help me?
thanks in advance
Maybe, the data, you are inserting has special symbols. Use parametrized queries for this (it will protect you from SQL injection):
my $queryString = "INSERT INTO Headline (id,comment) VALUES (?, ?)";
$sth = $dbh->prepare($queryString);
$sth->execute($id, $comment) or die $DBI::errstr;
$sth->finish();

Unable to insert a record into MySQL database using DBI

I am trying to insert a record into a MySQL database using Perl DBI. I am not getting any errors but the insert is not working. However, I am able to successfully fetch records from the database using DBI.
Here is the code that does the insert:
#!"C:\xampp\perl\bin\perl.exe"
use diagnostics;
use DBI;
use strict;
use warnings;
my $driver = "mysql";
my $database = "mysql";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "password";
my $buffer;
my #pairs;
my $pair;
my $name;
my $value;
my %FORM;
# Read in text
my $ENV;
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "GET")
{
$buffer = $ENV{'QUERY_STRING'};
}
# Split information into name/value pairs
#pairs = split(/&/, $buffer);
foreach $pair (#pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
my $first_name= $FORM{name};
my $address = $FORM{address};
my $city = $FORM{city};
my $occupation = $FORM{occupation};
my $age = $FORM{age};
my $dbh = DBI->connect("dbi:mysql:dbname=mysql", "root", "password",{ AutoCommit => 0,RaiseError => 1}, ) or die ("Couldn't connect to database: ") , $DBI::errstr;
# my $sth = $dbh->prepare("INSERT INTO persons
# (FirstName, LastName,Address,City)
# values
# ($first_name, $last_name,$address,$city)");
my $query = "insert into userrecords(Address,Age,City,Name,Occupation)
values (?, ?, ?, ?, ?) ";
my $statement = $dbh->prepare($query) or die ("Couldn't connect to database: "), $DBI::errstr;
$statement->execute($address,$age,$city,$name,$occupation) or die ("Couldn't connect to database: "), $DBI::errstr;
$dbh->disconnect();
my $URL = "http://.....:81/cgi-bin/showdata.cgi";
print "Location: $URL\n\n";
exit(0);
When I run my code in the Padre IDE, I get the following errors:
****Error*********
Useless use of a variable in void context at InsertRecord.cgi line 50 (#1)
(W void) You did something without a side effect in a context that does
nothing with the return value, such as a statement that doesn't return a
value from a block, or the left side of a scalar comma operator. Very
often this points not to stupidity on your part, but a failure of Perl
to parse your program the way you thought it would. For example, you'd
get this if you mixed up your C precedence with Python precedence and
said
$one, $two = 1, 2;
when you meant to say
($one, $two) = (1, 2);
Another common error is to use ordinary parentheses to construct a list
reference when you should be using square or curly brackets, for
example, if you say
$array = (1,2);
when you should have said
$array = [1,2];
The square brackets explicitly turn a list value into a scalar value,
while parentheses do not. So when a parenthesized list is evaluated in
a scalar context, the comma is treated like C's comma operator, which
throws away the left argument, which is not what you want. See
perlref for more on this.
This warning will not be issued for numerical constants equal to 0 or 1
since they are often used in statements like
1 while sub_with_side_effects();
String constants that would normally evaluate to 0 or 1 are warned
about.
Useless use of a variable in void context at InsertRecord.cgi line 59 (#1)
Useless use of a variable in void context at InsertRecord.cgi line 60 (#1)
Use of uninitialized value in transliteration (tr///) at InsertRecord.cgi line
23 (#2)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you the
name of the variable (if any) that was undefined. In some cases it cannot
do this, so it also tells you what operation you used the undefined value
in. Note, however, that perl optimizes your program and the operation
displayed in the warning may not necessarily appear literally in your
program. For example, "that $foo" is usually optimized into "that "
. $foo, and the warning will refer to the concatenation (.) operator,
even though there is no . in your program.
Use of uninitialized value $ENV{"REQUEST_METHOD"} in string eq at
InsertRecord.cgi line 24 (#2)
Use of uninitialized value $buffer in split at InsertRecord.cgi line 29 (#2)
Location: http://.......:81/cgi-bin/showdata.cgi
Press any key to continue . . .
***********END***********************
What is the issue?
When I was editing your code so that it was more readable, I stumbled upon what I assume is the solution:
You are using $name when inserting into the database, but you use $first_name when getting the value $FORM{name}. So since you used $name above, it has the value of the last name used, whatever that might be. The relevant code snippets:
($name, $value) = split(/=/, $pair);
...
$FORM{$name} = $value;
...
my $first_name = $FORM{name};
...
$statement->execute($address,$age,$city,$name,$occupation)
# ^^^^^--- should be $first_name
Your problem would have been solved if you had used proper scope on your variables, namely something like this:
foreach my $pair (#pairs) {
my ($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
Then when you later would try to use $name, you would get the error
Global variable "$name" requires explicit package name ...
Which would alert you to your mistake and save you hours in debugging time. When you declare variables at the top of the script, instead of in the smallest possible scope, you effectively disable the protection that use strict 'vars' offers. So don't do that.
Also, you should probably use the CGI module instead of trying to handle it manually. It will make things easier, and safer. Don't forget to perform sanity checks on your data to prevent database injection attacks.
Your script when cleaned up and properly formatted looks like this.
What happens when you replace your code with this:
#!"C:\xampp\perl\bin\perl.exe"
use strict;
use warnings;
use diagnostics;
use DBI;
use CGI qw[param redirect];
my $driver = "mysql";
my $database = "mysql";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "password";
my $dbh = DBI->connect("dbi:mysql:dbname=mysql", "root", "password",
{ AutoCommit => 0,RaiseError => 1}, )
or die "Couldn't connect to database: ", $DBI::errstr;
my $query = "insert into userrecords(Address,Age,City,Name,Occupation)
values (?, ?, ?, ?, ?) ";
my $statement = $dbh->prepare($query)
or die "Couldn't connect to database: " , $DBI::errstr;
$statement->execute(param('address'), param('age'), param('city'),
param('name'), param('occupation'))
or die "Couldn't connect to database: " , $DBI::errstr;
$dbh->disconnect();
my $URL = "http://.....:81/cgi-bin/showdata.cgi";
print redirect($URL);
I've basically made two changes:
Use the CGI.pm module to handle the CGI interaction (getting the parameters and printing the redirection header).
Fixed your "void context" errors by removing the misplaced parentheses in all of your calls to die.
I'm made no substantive changes to the code, but at least we now have a clean version to go with.
Update: D'oh. It's obvious now the code is cleaned up a bit. If you have "Autocommit" turned off, then you need to commit your changes. Add $dbh->commit between the calls to execute() and disconnect().
The warning comes from this:
or die ("Couldn't connect to database: ") , $DBI::errstr;
The , $DBI::errstr is outside of the die and nothing is done with it, thus being in void context. You want something like this:
or die ("Couldn't connect to database: $DBI::errstr");
Also, your form handling code has some issues. If you're writing CGI scripts, you may as well use the CGI module. Here's a quick cleanup of your code:
#!"C:\xampp\perl\bin\perl.exe"
use diagnostics;
use CGI ':standard';
use DBI;
use strict;
use warnings;
my $driver = "mysql";
my $database = "mysql";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "password";
my $name = param('name');
my $address = param('address');
my $city = param('city');
my $occupation = param('occupation');
my $age = param('age');
my $dbh = DBI->connect( $dsn, $userid, $password,
{ AutoCommit => 1, RaiseError => 1 },
) or die("Couldn't connect to database: $DBI::errstr");
my $query = <<'END';
INSERT INTO userrecords(Address,Age,City,Name,Occupation)
VALUES ( ?, ?, ?, ?, ?)
END
my $statement = $dbh->prepare($query);
$statement->execute( $address, $age, $city, $name, $occupation );
$dbh->disconnect();
my $URL = "http://.....:81/cgi-bin/showdata.cgi";
print "Location: $URL\n\n";
Note that I've removed many or die statements because you already have RaiseError set to a true value.
For simplicity's sake, I've also (reluctantly) turned on AutoCommit.

Perl Import large .csv to MySQL, don't repeat data

I am trying to import several .csv files into a mysql database, the script below works except that it only imports the first row of my csv data into the database. Both my tables are populated with exactly one data entry.
Any help would be appreciated.
Thank you
#!/usr/bin/perl
use DBI;
use DBD::mysql;
use strict;
use warnings;
# MySQL CONFIG VARIABLES
my $host = "localhost";
my $user = "someuser";
my $pw = "somepassword";
my $database = "test";
my $dsn = "DBI:mysql:database=" . $database . ";host=" . $host;
my $dbh = DBI->connect($dsn, $user, $pw)
or die "Can't connect to the DB: $DBI::errstr\n";
print "Connected to DB!\n";
# enter the file name that you want import
my $filename = "/home/jonathan/dep/csv/linux_datetime_test_4.26.13_.csv";
open FILE, "<", $filename or die $!;
$_ = <FILE>;
$_ = <FILE>;
while (<FILE>) {
my #f = split(/,/,$_);
if (length($f[4]) < 10) {
print "No Weight\n";
}
else {
#insert the data into the db
print "insert into datetime_stamp\n";
}
my $sql = "INSERT INTO datetime_stamp (subject, date, time, weight)
VALUES('$f[1]', '$f[2]', '$f[3]', '$f[4]')";
print "$sql\n";
my $query = $dbh->do($sql);
my $sql = "INSERT INTO subj_weight (subject, weight) VALUES('$f[1]', '$f[2]')";
my $query = $dbh->do($sql);
close(FILE);
}
As has been commented, you close the input file after reading the first data entry, and so only populate your database with a single record.
However there are a few problems with your code you may want to consider:
You should set autoflush on the STDOUT file handle if you are printing diagnostics as the program runs. Otherwise perl won't print the output until either it has a buffer full of text to print or the file handle is closed when the program exits. That means you may not see the messages you have coded until long after the event
You should use Text::CSV to parse CSV data instead of relying on split
You can interpolate variables into a double-quoted string. That avoids the use of several concatenation operators and makes the intention clearer
Your open is near-perfect - an unusual thing - because you correctly use the three-parameter form of open as well as testing whether it succeeded and putting $! in the die string. However you should also always use a lexical file handle as well instead of the old-fashioned global ones
You don't chomp the lines you read from the input, so the last field will have a trailing newline. Using Text::CSV avoids the need for this
You use indices 1 through 4 of the data split from the input record. Perl indices start at zero, so that means you are droppping the first field. Is that correct?
Similarly you are inserting fields 1 and 2, which appear to be subject and date, into fields called subject and weight. It seems unlikely that this can be right
You should prepare your SQL statements, use placeholders, and provide the actual data in an execute call
You seem to diagnose the data read from the file ("No Weight") but insert the data into the database anyway. This may be correct but it seems unlikely
Here is a version of your program that includes these amendments. I hope it is of use to you.
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Text::CSV;
use IO::Handle;
STDOUT->autoflush;
# MySQL config variables
my $host = "localhost";
my $user = "someuser";
my $pw = "somepassword";
my $database = "test";
my $dsn = "DBI:mysql:database=$database;host=$host";
my $dbh = DBI->connect($dsn, $user, $pw)
or die "Can't connect to the DB: $DBI::errstr\n";
print "Connected to DB!\n";
my $filename = "/home/jonathan/dep/csv/linux_datetime_test_4.26.13_.csv";
open my $fh, '<', $filename
or die qq{Unable to open "$filename" for input: $!};
my $csv = Text::CSV->new;
$csv->getline($fh) for 1, 2; # Drop header lines
my $insert_datetime_stamp = $dbh->prepare( 'INSERT INTO datetime_stamp (subject, date, time, weight) VALUES(?, ?, ?, ?)' );
my $insert_subj_weight = $dbh->prepare( 'INSERT INTO subj_weight (subject, weight) VALUES(?, ?)' );
while (my $row = $csv->getline($fh)) {
if (length($row->[4]) < 10) {
print qq{Invalid weight: "$row->[4]"\n};
}
else {
#insert the data into the db
print "insert into datetime_stamp\n";
$insert_datetime_stamp->execute(#$row[1..4]);
$insert_subj_weight->execute(#$row[1,4]);
}
}