How can I insert strings with quotes into Perl DBI queries? - mysql

What is the preferred way to insert strings that can contain both single and double quotes (",') into MySql using DBI? For example, $val1 and $val2 can contain quotes:
my $dbh = DBI->connect( ... );
my $sql = "insert into tbl_name(col_one,col_two) values($val1, $val2)";
my $sth = $dbh->prepare($sql);
$sth->execute();

Use a bound query using
$sth = $dbh->prepare("insert into tbl_name(col_one,col_two) values(?,?)");
$sth->execute($val1, $val2);
If you use bound variables, everything is escaped for you.
Update: Changed my example to correspond with the example edited into the question.
Update: I don't know why Adam deleted his answer, but if for some reason you can't use bound variables (aka "placeholders"), you can also use $dbh->quote($var) on the variable. For example:
$sql = sprintf "SELECT foo FROM bar WHERE baz = %s",
$dbh->quote(q("Don't"));

Use the quote() method. It will intelligently handle the quoting for you. Example from the docs:
$sql = sprintf "SELECT foo FROM bar WHERE baz = %s",
$dbh->quote("Don't");
Slightly modified to have both types of quotes:
$sql = sprintf "SELECT foo FROM bar WHERE baz = %s",
$dbh->quote(q("Don't"));

One small caveat on the bound placeholders, I build a rather large database-loading script that initially used bound placeholders in an older version of Perl/DBI and found what appears to be a memory leak in the placeholder implementation, so if you're looking at using them in a persistent process/daemon or in a high-volume context you may want to make sure process size doesn't become an issue. Switching over to building the query strings using the quote() method eliminated the issue for me.

DBI placeholders are awesome. They shine when you need to execute the same query in a loop. Consider this:
my $dbh = DBI->connect(...);
my $name_pairs = get_csv_data("data.csv");
my $sth = $dbh->prepare("INSERT INTO t1 (first_name, last_name) VALUES (?,?)");
for my $pair (#$name_pairs) {
unless ($sth->execute(#$pair)) {
warn($sth->errstr);
}
}
In this case, having the prepared statement handle is, er, handy.
However, barring this sort of tight-loop cases, I like to see the actual statement that was sent to the server. This is where I lean heavily on quote and frankly sprintf.
# Here, I am confident about the hash keys, less so about the values
$sql = sprintf("INSERT INTO t1 (%s) VALUES (%s)",
join(",", keys(%hash)),
join("," map { $dbh->quote($_) } values(%hash))
);
$sth = $dbh->prepare($sql);
unless ($sth->execute) {
warn($sth->{Statement});
}
Note that you do have to set RaiseError => 0 on $dbh so that you can see the SQL that failed, but this has helped me a great deal in the past.
Cheers.

Related

Is it possible to insert sql query in php array value?

for($count = 0; $count < count($_POST["item_sub_category"]); $count++)
{
$data = array(
':item_sub_category_id'
=> SELECT r_name FROM Repair where r_id = $_POST["item_sub_category"][$count]
);
$query = "INSERT INTO Repairlog (description,visitID) VALUES (:item_sub_category_id,'1')";
$statement = $connect->prepare($query);
$statement->execute($data);
}
As far as concerns, your code won't work. The SQL query that you are passing as a parameter will simply be interpreted as a string.
You could avoid the need for a loop by taking advantage of the INSERT INTO ... SELECT ... syntax. The idea is to generate an IN clause that contains all values that are in the array, and then run a single query to insert all records at once.
Consider:
$in = str_repeat('?,', count($_POST["item_sub_category"]) - 1) . '?';
$query = "INSERT INTO Repairlog (description,visitID) SELECT r_name, 1 FROM Repair WHERE r_id IN ($in)";
$statement = $connect->prepare($query);
$statement->execute($_POST["item_sub_category"]);
Note: it is likely that visitID is an integer and not a string; if so, then it is better not to surround the value with single quotes (I removed them in the above code).
TLDR; No.
Your question can be re-framed as: Can I write SQL code in php. The answer is NO. You can write the SQL code within a String type variable (or parameter) in php.
This is a general rule for any programming language, you cannot have multiple languages within the same file, as the language parser will not be able understand which syntax is that.
In order to embed a different language in another language, you need some kind of separator that will define when the new language or special type will start and when it will end.

perl DBI, fastest way to get a single scalar value

I have this code to get a value count.
Short way:
my $count = $dbh->selectrow_array("SELECT COUNT(name) AS RESCOUNT FROM users");
Long way
my $sth = $dbh->prepare("SELECT COUNT(name) AS RESCOUNT FROM users");
$sth->execute() or die "$DBI::errstr";
my $count = $sth->fetchrow_array();
$sth->finish;
selectrow_array, fetchrow_array --> but I don't need an array. I checked the docs, but found nothing for scalars. Just methods for arrays and hashes.
The method I use is fast enough, but I was just curious if there is a better, fastest way to get a single value from the call. Or this is the fastest possible way?
The fastest way is to use fetchrow_arrayref or selectrow_arrayref, depending on how many executes you have. This only really makes a difference if executed in a loop and you have thousands (or rather hundreds of thousands) of rows.
When using fetchrow_array, it will make a copy every time, which slows you down. Also keep in mind that the behaviour for scalar context is only partly defined.
If called in a scalar context for a statement handle that has more than one column, it is undefined whether the driver will return the value of the first column or the last. So don't do that.
You can also do bind_col, which works with references.
There used to be a good presentation on DBI speeds from about 10 or more years ago that I can't find right now. Also take a look at this very old Perlmonks post, that explains quite a bit about performance.
Keep in mind that you should only do optimisation when you really know you need it. Most of the time you won't.
If "modern" means "I only heard of it recently", I'm feeling all modern with DBI's bind_col and bind_columns. Cribbing from a post by DBI hero Tim Bunce...
For your case:
my $sth = $dbh->prepare("SELECT COUNT(name) AS RESCOUNT FROM users");
my $count = 0;
$sth->bind_col(1,\$count); # bind to a reference to the variable
$sth->execute() or die "$DBI::errstr";
$sth->fetch;
print $count;
In a loop for a SELECT statement returning multiple records:
my $sth = $dbh->prepare(qq{SELECT name FROM users WHERE zip_code == '20500'});
my $name = '';
$sth->bind_col(1,\$name); # bind to a reference to the variable
$sth->execute() or die "$DBI::errstr";
while ($sth->fetch) {
print $name, "\n";
}
And with bind_columns this works:
my $sth = $dbh->prepare(qq{SELECT name,phone,address FROM users WHERE zip_code == '20500'});
my #fields = qw/name phone address/;
# With a 'SELECT All * ...', get all columns with #{$sth->{NAME_lc}}
my %data;
$sth->bind_columns( \( #data{#fields} ) ); # \(...) gives references to its elements
$sth->execute() or die "$DBI::errstr";
while ($sth->fetch) {
print "$data{name} lives at $data{address}, with phone $data{phone}.", "\n";
}
Once the setup is handled, the looping is simple to write and fast to run. (But, benchmark).
HTH, apologize if this diverges too much from the OP's problem statement. But it's the simplest and most direct way to get your returned data into the form of variable(s) you want, so you can move on to doing something with it...

How to create subroutine in mysql perl with variables

I have two mysql commands. I want to create a subroutine with these two mysql commands for the rest of my data that I have to search through. I have a lot of entries. Is there a way to create a subroutine such that in place of the actual numbers and characters I put in my two mysql commands, I put in variables that I can replace so I can then copy and paste the actual values of those variables and the commands are executed for the remaining entries?
For example, I have a command that says
$sth = $dbh->prepare ("select name from table1 where number > 5");
$sth->execute();
#row;
while (#row = $sth->fetchrow_array) {
print "$row[0]\tquestion1\n";
In place of the "5" listed in the select command and "question1" listed in the print command, I want to put something in place of it like "variables" so that I can create a subroutine with these commands in it, but you execute and can run the subroutine over and over by plugging in different values for those variables.
something like this: $dbh = DBI->connect($dsn, $user, $password);
not sure how to go about doing this for mysql perl.
You can use placeholders in your query to use different values in a search.
$sth = $dbh->prepare ("select name from table1 where number > ?");
$sth->execute(5);
After that, it should be trivial to make a subroutine for your print.
For example:
my #values = qw(5 10 15 20);
my #fields = qw(question1 question2);
# ... other code..
my $sth = $dbh->prepare ("select name from table1 where number > ?");
for my $field (#fields) {
for my $value (#values) {
printfields($sth, $value, $field);
}
}
sub printfields {
my ($sth, $value, $field) = #_;
$sth->execute($value);
while (my #row = $sth->fetchrow_array) {
print "$row[0]\t$field\n";
}
}
I prefer to build the select query before running prepare(). Then passing it into prepare() as a scalar. You could also add placeholders in the scalar (if you don't want to simply rebuild it for each query) that could easily be replaced via calls of s///;, which could be easily named/identified as well. This would make the code a bit easier to read than passing bare values to execute().

mysql_real_escape_string with variables extracted from an array

original post:
My script is not working (it's not recording the data). It was working before I added the mysql_real_escape_string, so I'm wondering if maybe I have not implemented it correctly:
$array = json_decode($downstream,TRUE);
$name = $array["status"]["name"];
$title = $array["status"]["title"];
$table = "mrTable";
$insert = "INSERT INTO $table (name, title) VALUES ('".mysql_real_escape_string($name)."', '".mysql_real_escape_string($title)."')";
Does that implementation at INSERT look correct to you?
UPDATE:
Here is the entire code, hopefully this will help. It is still not working though. When the real_escape_string function is used, NONE of the data elements get recorded in the database. As soon as I remove the escape function, data is written fine (unless of course an apostrophe shows up).
Here we go:
//read contents of this file:
$json_data = file_get_contents('../list.txt');
//json to a php array
$array = json_decode($json_data,TRUE));
//store in mysql table
$table = "table1";
$name = mysql_real_escape_string($array["current"]["name"]);
$code = mysql_real_escape_string($array["current"]["code"]);
$insert="INSERT INTO $table (name, code) VALUES ('$name', '$code')";
$con = mysql_connect($db, $user, $pass);
if (!$con)
{
die ('Could not connect: ' . mysql_error());
};
mysql_select_db($yup, $con);
mysql_query($insert) OR die(mysql_error());
mysql_close($con);
UPDATE 2
Fixed! You need to connect to the database before first mentioning mysql_real_escape_string. Everything is working now...no blank data.
You need to be connected to a database to use mysql_real_escape_string. You don't seem to be. Make sure mysql_connect is over your line where you define $insert
Never insert values directly into a query string! Even if they are escaped, it's not a smart idea. Instead, use parametrised statements as such, which will render attacks like ' OR 1 = 1-- useless. You don't need to escape values for parametrised statements either...
PREPARE statement FROM
'INSERT INTO table (col1, col2)
VALUES
(?, ?)'
EXECUTE statement USING ('val1', 'val2')
DEALLOCATE statement
Deallocate only when you're done. You can re-execute as many times as you'd like with different values. If you are going to re-execute anyways, there is a gain in performance as well from doing it this way! (Because the statement is only prepared once for an infinite number of executions.) I advise you to implement this method and then come back if you are still having problems.
Please don't try to escape your parameters. Use bind variables. See http://bobby-tables.com/php.html for examples.

PHP/MySQL - Best use and practice of escaping strings [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Best way to prevent SQL Injection in PHP
What is the best way to escape strings when making a query?
mysql_real_escape_string() seems good but I do not exactly know how to use it in properly.
Does this code do the job properly?
<?php
/* Let's say that the user types "'#""#''"\{(})#&/\€ in a textfield */
$newStr = mysql_real_escape_string($str);
$query = "INSERT INTO table username VALUES ($str)";
mysql_query($query);
?>
EDIT:
Now I have this code:
$email = $_POST['email'];
$displayName = $_POST['displayName'];
$pass = $_POST['pass1'];
$email = mysqli_real_escape_string($link, $email);
$displayName = mysqli_real_escape_string($link, $displayName);
$pass = mysqli_real_escape_string($link, $pass);
$insert = "INSERT INTO profiles (email, displayName, password)
VALUES ('$email', '$displayName', md5('$pass'))";
mysqli_query($link, $insert)
or die(mysqli_error($link));
But I get this error:
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 '!"#!#^!"#!"#!"#^'''''' at line 1
If the user enters:
'**!"#!#^!"#!"*#!"#^''''
The best way is not to escape the string at all, but instead use a parameterized query, which does it for you behind the scenes.
Using mysql_real_escape_string like that will work, but you need to:
Add quotes around the value.
Use the result $newStr, not the original value $str.
Change the tablename to a name that isn't a reserved keyword.
Add parentheses around the column list.
Try this:
$query = "INSERT INTO yourtable (username) VALUES ('$newStr')";
I also suggest that you check the result of mysql_query($query) and if there is an error, you can examine the error message:
if (!mysql_query($query))
{
trigger_error(mysql_error());
}
You should also consider using one of the newer interfaces to MySQL. The old mysql_* functions are deprecated and should not be used in new code.