PDO prepared statement with conditions not working - php

I am trying to to get my data from MYSQL using PDO but I am not having any luck.
Here is what I have tried:
$postQuery = $DBH->prepare("SELECT title, views, rating, thumb FROM posts WHERE category=:category and status=1 ORDER BY :sort DESC");
$postQuery ->bindParam(':category', $category);
$postQuery ->bindParam(':sort', $sort);
$postQuery ->execute();
This works without errors but it returns all of the posts in alphabetical order, ignoring the category and the sort.
I tried this:
$postQuery = $DBH->query("SELECT title, views, rating, thumb FROM posts WHERE category={$category} and status=1 ORDER BY {$sort} DESC");
This did work but I don't get the protection of the prepared statement. Any ideas on why one statement works but the other one does not?

Your bound parameter :sort gets expanded to a string literal, not a SQL identifier. That is, you are effectively evaluating something along the lines of:
ORDER BY 'rating' DESC
Since literals like this are constant for every record, it has no effect on the order of the resultset.
You can't parameterise identifiers, so must concatenate that part of the SQL into your prepared statement (the safest way is to set $sort from a predetermined set of safe values, based on whatever logic is appropriate to your needs).

You can't use place holders in ORDER BY clauses. See this question: How do I set ORDER BY params using prepared PDO statement?

Related

Order the data as the IDs are ordered in the given list

I'm running wordpress.
So have a comma separated string of post IDs and need to order found posts by the order which they are listed in the string.
Without sql query I can reach this simply by adding 'orderby' => 'post__in' in arguments.
But I need to do it via sql query. Here is my SQL query:
$idList = '2,50,10,25,150,1200,356';
$sql = "SELECT * FROM wp_posts WHERE ID IN ($idList)";
Above query return results in growing order, like this: $idList = '2,10,25,50,150,356,1200'; but I need how they are ordered in my string. Didn't find how I can do the same in sql query. Any ideas please?
There is a way to define a custom ORDER BY order in mySQL by using FIELD()
SELECT * FROM wp_posts WHERE ID IN ($idList) ORDER BY field(ID,$idList);
EDIT :
Check out this https://stackoverflow.com/a/9378709/5671807. I am quoting from
Note however, that
1 - It makes your SQL less portable, as other DBMSs might not have such
function.
2- When your list of languages (or other values to sort by) gets
much longer, it's better to have a separate table with sortorder
column for them, and join it to your queries for ordering.
You can use ...ORDER BY FIND_IN_SET(ID, $idList).
Query:
SELECT *
FROM wp_posts
WHERE ID IN ($idList)
ORDER BY FIND_IN_SET(ID, $idList)
Note:
FIND_IN_SET() function
This function returns 0 when search string does not exist in the string list and returns NULL if either of the arguments is NULL.
Syntax:
FIND_IN_SET (search string, string list)

PDO Connection : row count using where clause

I want to count the no of rows in my database..along with where clause and AND clause..
please show me how can we do that in pdo..
these are my queries:
$stmt=$dbh->prepare("SELECT * from questions where (category_id = ?) AND (complexity_id = ?) ORDER BY RAND()");
$stmt->execute(array($_POST['category'],$_POST['complexity']));
$data = $stmt->fetchAll(PDO::FETCH_OBJ)
count:
$nRows = $dbh->query("SELECT COUNT(*) FROM questions");
i need to write both queries in one query only..
you can use fetchColumn.
$number_of_rows=$stmt->fetchColumn(); // will give no. of rows
$data = $stmt->fetchAll(PDO::FETCH_ASSOC)// give the data array
If your queries are different
If your queries are different, like it shown in the question, then keep them as two separate queries. There is no way to get them in one. Just leave it two separate queries, it's all right.
If your queries actually the same
If your queries actually the same, and you want to get your data filtered and also get the number of rows, then you don't need the second query at all. In PHP, there is a function called count(). You can use it to count results returned by your first query.
$nRows = count($data);
as simple as that
Can you just use
$stmt=$dbh->prepare("SELECT COUNT(*) from questions where (category_id = ?) AND (complexity_id = ?)");
query?

Error in query syntax

i am using ORDER BY in mysql SELECT query but i dont know ots not ordering the data.. if i use this query its showing the table but not ordering the data in ascending order
$result = mysql_query("SELECT *FROM learningmaterial ORDER BY 'order' ASC")or die(mysql_error());
but if i use
$result = mysql_query("SELECT *FROM learningmaterial ORDER BY order ASC")or die(mysql_error());
then it give error that the syntax of the query is not right...i've seen on various sites but i couldnot found anything unique in my code...i think its right,...please check the query and mend a solution. Thankx in advance :)
You need backticks, not single quotes (a):
... SELECT * FROM learningmaterial ORDER BY `order` ASC ...
By using single quotes, you're ordering the rows by a constant (each row gets the same constant) so effectively not ordering them at all.
By using a "naked" column name of order, you're confusing the SQL parser, since order is a reserved word.
(a): Of course, this problem goes away if you stop using reserved words as column names but I assume you did that for a reason (such as a bucket-load of programs already depending on the fact that the column is called order).
Myself, I tend not to use generic names for columns (such as order or date), preferring instead things that don't conflict with the language (such as order_num or start_date). That way I don't have to worry about escaping.
You are using SQL reserved keyword order as a column name so use back-ticks to escape...like this
SELECT * FROM learningmaterial ORDER BY `order` ASC
I would suggest you to change the columnn name
Reference For List Of Reserved Keywords
ORDER is a reserved sql syntax keyword. you cannot use it directly
SELECT *FROM learningmaterial ORDER BY `order` ASC
-------------------------------^---------
in second case
SELECT *FROM learningmaterial ORDER BY order ASC
---------------------------------^-------^--
//this is a sql error
it doesn't make any sense.
Since Order is a reserved word, you need to wrap them using backticks not single quotes.
SELECT * FROM `learningmaterial` ORDER BY `order` ASC

SQL injection even when the variable is escaped [duplicate]

This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 2 years ago.
The sql injection will work only when my query looks like below sample
SELECT * FROM login WHERE id = $my_id_va;
Assume if my query is
SELECT * FROM login WHERE id = $my_id_va ORDER BY id DESC
Than I will get following error
#1064 - 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 'order by id desc' at line 1
So, this 1 or 1=1; SHOW TABLES will not work, correct?
My site was hacked successively many times.
I want one quick answer: When my query looks like the following one, what ways or which types of query can they use to hack my site?
SELECT * FROM login WHERE id = $my_id_va ORDER BY id DESC
What are the ways to execute the show table in the following query
SELECT * FROM login WHERE id = $my_id_va ORDER BY id DESC
I am also using escaping function to handle the query string values, like mysql_real_escape_string($my_id_va). Yes, obviously this for single related hack, but not sure.
Added some more
SELECT EventActuallyCharged, EventDate FROM tblevent WHERE EventDate between '2011-07-21 or 1=1; SHOW TABLES --' and '2011-07-31' ORDER BY EventDate DESC
but show table not worked
If you are using PHP5, use parametarized query, use PDO.
Int cast
If id is a number, you can int-cast your variable as well. Integers are safe to use:
$x = (int)$yourInputVar;
$s = "select * from Table where id = $x";
mysql_real_escape_string
If you want to pass a string, you can, and should, use mysql_real_escape_string, but this function escapes only those characters that are inside the string. You will still need to add quotes around the string, so:
$x = mysql_real_escape_string('hello');
$s = "select * from Table where id = $x";
.. will result in the query: select * from Table where id = hello. This is obiously not a valid query, since hello should be in quotes.
Change the query to:
$x = mysql_real_escape_string('hello');
$s = "select * from Table where id = '$x'";
.. and everything works fine. You add the quotes around, and mysql_real_escape_string takes care of special characters inside the string, if any.
Parameters
Another solution is to use parameterized queries. This can by done using MySQLi or PDO. The advantage is that you only tell your database where a variable should be inserted, and the database takes care of the escaping yourself.
It also may add a performance benefit, because these queries could be cached without their parameters, make a more efficient use of the query cache. This doesn't really work yet in current versions of MySQL, though.
You are right that 1 or 1=1; SHOW TABLES will give a syntax error but this will work:
1 or 1=1 --
The -- comments out the rest of the query.
In your case the value is an integer so instead of using mysql_real_escape_string you can use intval.
If you set $my_id_va to:
1 or 1=1; SHOW TABLES --
The -- will comment out the rest of the command, effectively terminating it.
I'm not sure what effect mysql_real_escape_string will have on the query. What you should be doing is parameterized queries.
1. First query somehow secured
$sql = sprintf('SELECT * FROM login WHERE id = %d ORDER BY id DESC', mysql_real_escape_string($my_id_va));
2. Second query somehow secured
$sql = sprintf("SELECT EventActuallyCharged, EventDate FROM tblevent WHERE EventDate BETWEEN '%s' AND '%s' ORDER BY EventDate DESC",
mysql_real_escape_string($start_date),
mysql_real_escape_string($end_date));
Read the docs about sprintf if you don't understand it.
However, as others have said, it would be very very secure if you would use parameterized queries with a class such as PDO or MySQLi.

PDO Parameterized Query - Reuse named placeholders?

In essence, I have a value that I have to call a couple times in my SQL query. Thus, is it possible to reuse the same named placeholder in the statement e.g.
SELECT :Param FROM Table WHERE Column = :Param, then simply bindValue(":Param"), and have the value be there for both :Params?
PDO::prepare states that "you cannot use a named parameter marker of the same name twice in a prepared statement", so I guess that's a no then.
You can if you set PDO::ATTR_EMULATE_PREPARES = true.
E.g. $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);.
If you're using Laravel you can set this in an options array in config/database.php. e.g. PDO::ATTR_EMULATE_PREPARES => true
Apart from reuse, the main issue here is that you are trying to dynamically change col names.
This answer posted by an anonymous user on http://php.net/manual/en/pdo.prepare.php :
To those wondering why adding quotes to around a placeholder is wrong,
and why you can't use placeholders for table or column names:
There is a common misconception about how the placeholders in prepared
statements work: they are not simply substituted in as (escaped)
strings, and the resulting SQL executed. Instead, a DBMS asked to
"prepare" a statement comes up with a complete query plan for how it
would execute that query, including which tables and indexes it would
use, which will be the same regardless of how you fill in the
placeholders.
The plan for "SELECT name FROM my_table WHERE id = :value" will be the
same whatever you substitute for ":value", but the seemingly similar
"SELECT name FROM :table WHERE id = :value" cannot be planned, because
the DBMS has no idea what table you're actually going to select from.
Even when using "emulated prepares", PDO cannot let you use
placeholders anywhere, because it would have to work out what you
meant: does "Select :foo From some_table" mean ":foo" is going to be a
column reference, or a literal string?
When your query is using a dynamic column reference, you should be explicitly white-listing the columns you know to exist on the table, e.g. using a switch statement with an exception thrown in the default: clause.
Many queries like yours can be rewritten to use only one placeholder.
SELECT :Param FROM Table WHERE Column = :Param
would be the same as
SELECT Column FROM Table WHERE Column = :Param
But sometimes it's not that simple. For example:
SELECT *
FROM my_table
WHERE first_name LIKE :Param
OR last_name LIKE :Param
OR biography LIKE :Param
In such case you could reuse the parameter value storing it in a cross joined derived table (subquery in FROM clause):
SELECT t.*
FROM my_table t
CROSS JOIN (SELECT :Param as Param) AS x
WHERE first_name LIKE x.Param
OR last_name LIKE x.Param
OR biography LIKE x.Param
There's a workaround:
function search($criteria) {
$sql = "SELECT * FROM my_table
WHERE column_1 like CONCAT('%', :criteria1, '%')
OR column_2 like CONCAT('%', :criteria2, '%')
OR column_3 like CONCAT('%', :criteria3, '%')
";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':criteria1', $criteria);
$stmt->bindParam(':criteria2', $criteria);
$stmt->bindParam(':criteria3', $criteria);
$stmt->execute();
return($stmt->fetchAll(PDO::FETCH_ASSOC));
}
In summary, use different placeholders with the same criteria.

Categories