MySQL, Perl
The following select works fine with no placeholders, but doesn't with placeholders. It doesn't generate any SQL errors, but it returns all blanks/zeros - not the same counts as the same statement without placeholders.
my $sql="SELECT ?, SUM(IF(H1='1',1,0)) AS banner1 FROM table_name WHERE (?!='' and ? IS NOT NULL) GROUP BY ?";
my $sth = $dbh->prepare($sql);
my $variable = "Q1";
$sth->execute($variable, $variable, $variable, $variable);
What am I doing wrong?
Am I trying to use placeholders in ways not intended? It works when I only use placeholders in the WHERE clause. It does not work when I use a placeholder in the SELECT or GROUP BY clause. Is that the issue - placeholders can only be used in the WHERE clause?
You can't use placeholders in the SELECT portion of an SQL statement. This is described in the documentation:
With most drivers, placeholders can't be used for any element of a statement that would prevent the database server from validating the statement and creating a query execution plan for it. For example:
"SELECT name, age FROM ?" # wrong (will probably fail)
"SELECT name, ? FROM people" # wrong (but may not 'fail')
You can't use placeholders to substitute a column or table name. Even in your WHERE clause, it's not doing what you think it's doing. When you substitute Q1 for the placeholder, you get:
WHERE ('Q1'!='' and 'Q1' IS NOT NULL)
i.e. an expression that is always true.
Related
I am dissecting some code and came across this,
$sql = 'SELECT page.*, author.name AS author, updator.name AS updator '
. 'FROM '.TABLE_PREFIX.'page AS page '
. 'LEFT JOIN '.TABLE_PREFIX.'user AS author ON author.id = page.created_by_id '
. 'LEFT JOIN '.TABLE_PREFIX.'user AS updator ON updator.id = page.updated_by_id '
. 'WHERE slug = ? AND parent_id = ? AND (status_id='.Page::STATUS_REVIEWED.' OR status_id='.Page::STATUS_PUBLISHED.' OR status_id='.Page::STATUS_HIDDEN.')';
I am wondering what the "?" does in the WHERE statement. Is it some sort of parameter holder?
Prepared statments use the '?' in MySQL to allow for binding params to the statement. Highly regarded as more secure against SQL injections if used properly. This also allows for quicker SQL queries as the request only has to be compiled once and can be reused.
The question mark represents a parameter that will later be replaced. Using parameterized queries is more secure than embedding the parameters right into the query.
SQL Server calls this parameterize queries, and Oracle calls it bind variables.
The usage varies with the language that you are executing the query from.
Here is an example of how it is used from PHP.
assuming that $mysqli is a database connection and people is a table with 4 columns.
$stmt = $mysqli->prepare("INSERT INTO People VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $firstName, $lastName, $email, $age);
The 'sssd' is a flag identifying the rest of the parameters, where s represents string and d represents digits.
? has no special meaning in MySQL WHERE = statements, only in prepared statements
The most common case where we see it is due to special meaning given to ? by several web frameworks like PHP and Rails.
? is just a syntax error at:
CREATE TABLE t (s CHAR(1));
SELECT * FROM t WHERE s = ?;
because it is unquoted, and in:
INSERT INTO t VALUES ('a');
INSERT INTO t VALUES ("?");
SELECT * FROM t WHERE s = '?';
it returns:
s
?
thus apparently without special meaning.
MySQL 5.0 prepared statements
MySQL 5.0 added the prepared statement feature which has similar semantics to the question mark in web frameworks.
Example from the docs:
PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
SET #a = 3;
SET #b = 4;
EXECUTE stmt1 USING #a, #b;
Output:
hypotenuse
5
These also escape special characters as expected:
PREPARE stmt1 FROM 'SELECT ? AS s';
SET #a = "'";
EXECUTE stmt1 USING #a;
Output:
s
'
Rails example
In Rails for example, the question mark is replaced by an argument given by a variable of the library's programming language (Ruby), e.g.:
Table.where("column = ?", "value")
and it automatically quotes arguments to avoid bugs and SQL injection, generating a statement like:
SELECT * FROM Table WHERE column = 'value';
The quoting would save us in case of something like:
Table.where("column = ?", "; INJECTION")
These are prepared statements ,prepared statements offer two major benefits:
The query only needs to be parsed (or prepared) once, but can be
executed multiple times with the same or different parameters. When
the query is prepared, the database will analyze, compile and optimize
its plan for executing the query. For complex queries this process can
take up enough time that it will noticeably slow down an application
if there is a need to repeat the same query many times with different
parameters. By using a prepared statement the application avoids
repeating the analyze/compile/optimize cycle. This means that prepared
statements use fewer resources and thus run faster.
The parameters to prepared statements don't need to be quoted; the
driver automatically handles this. If an application exclusively uses
prepared statements, the developer can be sure that no SQL injection
will occur (however, if other portions of the query are being built up
with unescaped input, SQL injection is still possible).
http://php.net/manual/en/pdo.prepared-statements.php
Its very weird situation I know, nut I have got myself into it somehow. I have to connect to some other system service by passing some parameters in url.
In their service they are creating some query using parameter I pass.
For my case I have to pass 'Select' as a parameter name which is actually some class name on their side. So they end up in creating query as Select * from select
and some condition.
On execution I am getting error response as:
'There was a syntax error in a SQL query or filter expression at line
1, position 186. Saw \"Select\" but expected
'..SQL: \"SELECT col1, col2 FROM Select AS D where
some condition.
Can somebody help me on this.
Since Select is reserved word, you have to escape it by enclosing in backticks characters in order for MySQL to process your query:
select * from `select`
Its recommended not to use MySQL reserved keywords.. but if its necessary there is a solution..
Use this, it will work for you :
select * from yourdatabasename.select
I have some issue with a sql query using quotes with variables. (In general I use "bind" so I don't have this kind of problem). Here's the query :
$myquery = mysql_query("SELECT * FROM mytable ORDER BY id ASC WHERE var='".$var."'");
The syntax seems not to be correct, can anybody help ?
well you can try something like this:
$query = sprintf("SELECT * FROM mytable WHERE var='%s' ORDER BY id ASC",mysql_real_escape_string($var));
$result = mysql_query($query) or die("Error:" . mysql_error());
Also note that ORDER BY is at wrong place.
It is more readable and you don't need to bother with single qoute concating.
Also it is safe for mysql injection.
Hope this helps!
In general you should use the parameter binding features provided by your DBD (Database Driver for Perl) or other language and driver combination. I gather that you're using PHP (though you should tag your questions accordingly to remove the ambiguity.
Here's a StackOverflow thread on How to bind SQL parameters in PHP (using PDO). Note there are limitations to the PHP PDO::bindParam method as compared to similar features in other languages. So read the linked thread for caveats.
Here's another discussion about Binding Parameters to Statements ... for Perl (but conceptually applicable to other programming languages and their SQL libraries/drivers).
You can use it like
$myquery = mysql_query("SELECT * FROM mytable ORDER BY id ASC WHERE var='$var'");
I have perl script as following my $tb = 'rajeev';
$query = 'select * from table where name = ?'
$sth = $dbh->prepare($query);
$sth->execute($tb);
Does $tb replaced by rajeev or 'rajeev' when query executes ? means does query executs as select * from table where name = rajeevorselect * from table where name = 'rajeev'
DBI handles all the escaping for you. In the case of a string, it will be 'rajeev'. Calling select * from table where name = rajeev will give you an error.
If you provide a number, it will not add quotation marks because they are not needed.
See the DBI Doc. It also says:
The quote() method should not be used with "Placeholders and Bind Values".
Using placeholders sometimes takes care of the quoting for you, depending on which DBD you are using. In your case the DBD::mysql calls $dbh->quote() as mentioned in the doc:
An alternative approach is
$dbh->do("INSERT INTO foo VALUES (?, ?)", undef, $number, $name);
in which case the quote method is executed automatically.
If you have access to the query log you can check what the queries look like. If you have queries that take a long time you can also open a mysql console and say SHOW FULL PROCESSLIST; to see a list of the running queries. That will also hold the complete SQL statements for you to look at. On Windows you could use HeidiSQL to do it.
I have couple of mysql queries in perl but some of the values of the where clause contain space between words e.g. the gambia. When my scripts runs with the where clause arguments containing a space it ignore the second word.
I want to know how can I solve this problem i.e. if I type the gambia it should be treated the gambia not the.
If you are using DBI, you can use placeholders to send arbitrary data to database without need to care about escaping. The placeholder is question mark in prepare statement, actual value is given to execute:
use DBI;
$dbh = DBI->connect("DBI:mysql:....",$user,$pass)
or die("Connect error: $DBI::errstr");
my $sth = $dbh->prepare(qq{ SELECT something FROM table WHERE name = ? });
$sth->execute('the gambia');
# fetch data from $sth
$dbh->disconnect();
Edit: If you are composing the query (as you suggested in comments), you can utilize quote method:
my $country = "AND country = " . $dbh->quote('the gambia');
my $sth = $dbh->prepare(qq{ SELECT something FROM table WHERE name = ? $country});
Well, firstly, you should look at using something like DBIx::Class instead of raw SQL in your application.
But if you're stuck with raw SQL, then (assuming that you're, at least, using DBI) you should use bind points in your SQL statements. This will handle all of your quoting problems for you.
$sth = $dbh->prepare('select something from somewhere where country = ?');
$sth->execute('The Gambia');
See the DBI docs for more information about binding.