After years of reading it's time to ask first question :)
My problem is that after migrating the code from mySQLi to PDO we have got a problem as it seems PDO adds the apostrophes to the query.
PHP code goes like that:
$sort = $_GET['sort']; << table column name (mySQL VARCHAR only columns)
....
$query = 'SELECT * FROM table WHERE xxx > 0';
$query .= ' ORDER BY :sort ASC ;';
$qry_result= $db->prepare($query);
$qry_result->execute(array(':sort'=>$sort));
mysqli version went smoothly but now queries (mysql log file) looks like this:
SELECT * FROM table where xxx > 0 ORDER BY 'SORT_VAR_VALUE' ASC;
^ 2 problems ^
So the table is NOT sorted, as sort order (from mySQL point of view) is wrong.
phpinfo() does not get any results for search on "magic" nor "quotes" btw.
Any idea ??
The placeholders in PDO statements are for values only. If you want to add actual SQL to the query you need to do it another way.
First, you should sanitize $sort and surround it with backticks in the query.
$sort = preg_replace('/^[a-zA-Z0-9_]/', '', $sort);
Then you could double quote the query string and PHP will replace $sort with it's value for you:
$query = "SELECT * FROM table WHERE xxx > 0 ORDER BY `$sort` ASC";
Or you could replace it with preg_replace like so:
$query = 'SELECT * FROM table WHERE xxx > 0 ORDER BY `:sort` ASC';
$query = preg_replace('/:sort/', $sort, $query, 1);
I would use the preg_replace method because it allows you to reuse the query if you assign the results from preg_replace to another variable instead of overwriting the original variable.
by default pdo binds values as strings.
To fix this you will want to check that the column is actually a valid name and then add it to the query, you can do it the following way:
function validName($string){
return !preg_match("/[^a-zA-Z0-9\$_\.]/i", $string);
}
if(validName($sort)){
$db->prepare("SELECT * FROM table where xxx > 0 ORDER BY $sort ASC");
}
With PDO it's not possible to bind other things that variables in the WHERE statement. So you have to hard code the names of the columns you order by.
See How do I set ORDER BY params using prepared PDO statement?
or Can PHP PDO Statements accept the table or column name as parameter? for further explanations.
Related
Sorry my english. I'm trying to search mysql database that contains hashtags, but it returns all.
eg Search #NBA returns: #NBA, #NBA2021, #NBAscoreBoard, etc
I've tried every preg_replace on here. eg #(^|\s)#(\w*[a-zA-Z_]+\w*)# But how do I break after the specific search is met?
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$sql='SELECT * FROM status WHERE data LIKE "%#'.$fulltag.'%" LIMIT 12';
//
//
//
echo //the result containing the "# $fulltag";
As I said in my comment--just doing two queries with one to test if data = fullTag, and then if that returns nothing, then doing the wildcard search--is probably going to be simpler.
However, if you really want this to be a single query, you could do something like this, wherein you test to see if it is an exact match within a sub-query, then order by whether it's an exact match so that if there is an exact match, it will be the first result.
SELECT *
FROM (
SELECT
data,
CASE
WHEN data = "#NBA"
THEN 1
ELSE 0
END AS is_exact
FROM status
WHERE data LIKE "%#NBA%"
LIMIT 12
) AS matches
ORDER BY is_exact DESC
A separate note: You code right now is very vulnerable to SQL Injection. You should try using parameterized prepared statements instead of manually building your queries. See PDO or MySQLi prepared statements.
Here is an example of the above query using PDO, and this method of using CONCAT to safely add wildcards to your query:
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$pdo = new PDO(/* your connection details */);
$sql =
'SELECT *
FROM (
SELECT
data,
CASE
WHEN data = CONCAT("#", :tag1)
THEN 1
ELSE 0
END AS is_exact
FROM status
WHERE data LIKE CONCAT("%#", :tag2, "%")
LIMIT 12
) AS matches
ORDER BY is_exact DESC
';
$stmt = $pdo->prepare($sql);
$stmt->execute([
':tag1' => $fulltag,
':tag2' => $fulltag,
]);
Here's your simpler query using the same, safer approach:
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$pdo = new PDO(/* your connection details */);
$sql = 'SELECT * FROM status WHERE data LIKE CONCAT("%#", :fullTag, "%") LIMIT 12';
$stmt = $pdo->prepare($sql);
$stmt->execute([':fullTag' => $fulltag]);
I am reading out a database table, and excluding some rows with given values in a column.
But i cant get the query to get the right number for me. I suspect it still counts the kampanje_xxxxx rows. I use % to exclude.
$sql =
"SELECT *
FROM salg
WHERE
fnr='$fnr'
AND kategori NOT IN ('tilbehor', 'servicekontrakt', '%kampanje%')
AND dato BETWEEN '$fdato' AND '$tdato'"
;
$kjor = mysqli_query($connect, $sql);
$faste .= mysqli_num_rows($kjor). ", ";
The IN comparison operator takes list of values, it does not recognizes wildcards such as %. So your assumption that it still counts the kampanje_xxxxx rows is correct, since MySQL is actually searching for a litteral '%kampanje%' value.
You would need to create a separate LIKE condition to check the column againts the value that contains the wildcard. I believe that your query can be expressed as follows :
SELECT *
FROM salg
WHERE
fnr='$fnr'
AND kategori NOT IN ('tilbehor', 'servicekontrakt')
AND kategori NOT LIKE '%kampanje%'
AND dato BETWEEN '$fdato' AND '$tdato'
you should use OR not like and avoid wild char in IN set of values
"SELECT *
FROM salg
WHERE fnr='$fnr'
AND ( kategori NOT IN ('tilbehor', 'servicekontrakt')
OR NOT LIKE concat('%','kampanje','%') )
AND dato BETWEEN '$fdato' AND '$tdato'";
anyway you should avoid the use of php var in sql .. you are at risk for sqliject .. try take a look at ypour db driver for prepared statements and binding param
for see error eventually try show the exact erro message using
if (!$kjor) {
printf("Errormessage: %s\n", $connect->error);
}
I have tried using named placeholder to fill in the data as shown here:
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes WHERE id NOT IN ( :noredo_ids ) ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->bindParam(':noredo_ids', $_GET["noredo"]);
$STH->setFetchMode(PDO::FETCH_ASSOC);
$STH->execute();
As well as trying
$arr2["ids"] = $_GET["noredo"];
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes WHERE id NOT IN ( :ids ) ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->setFetchMode(PDO::FETCH_ASSOC);
$STH->execute($arr2);
But neither of those are working. But when I try to manually put the string in instead of using placeholders, it does work:
$arr2["ids"] = $_GET["noredo"];
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes WHERE id NOT IN (". $arr2['ids'] .") ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->setFetchMode(PDO::FETCH_ASSOC);
$STH->execute();
Should I manually escape the string? Am I missing something obvious? Thanks!
I am not certain since PHP's documentation on named placeholders for prepared statements is kinda vague about this. http://php.net/manual/en/pdostatement.bindparam.php
But if $_GET['noredo'] is an array of IDs you need to first implode(',',$_GET['noredo']) before passing into the prepared statement, I do not believe that the placeholder replacement is smart enough to flatten the array into a comma separated list acceptable for use in IN().
And in PHP docs, the replacement is a single value and not an array of values, so (and this is where I'm fuzzy) I don't believe that it looks for :named_placeholder in the array you pass it.
But then again, I've only used the ? placeholder for prepared statements...
A query parameter always takes the place of one single scalar value in an SQL expression.
So if you expect $_GET["noredo"] to be an array or a comma-separated list of values, what you're doing won't work. The query will be run as if you did this:
WHERE id NOT IN ( '1,2,3,4' )
A quoted string value that contains a comma-separated list is not the same as a series of comma-separated values. It's one string, and in a numeric context SQL will convert '1,2,3,4' into the scalar number 1.
So you need to use multiple placeholders if you want to bind multiple values in an IN() predicate.
$id_array = (array) $_GET["noredo"];
$placeholders = implode(",", array_fill(0,count($id_array),"?"));
$STH = $DBH->prepare("SELECT mixes.* FROM mixes JOIN(SELECT id FROM mixes
WHERE id NOT IN ( $placeholders ) ORDER BY RAND() LIMIT 1) ips on mixes.id = ips.id");
$STH->execute($id_array);
The second example doesn't work because you used $arr2["ids"] instead of $arr2[":ids"] as far as I can tell. That is, you still have to use the full parameter name.
Also, you should make sure that the values you try to insert are properly sanitized and cannot cause a syntax error.
I'm trying to execute this:
$colparam = 'abcd';
$stmt = $db->prepare("SELECT DISTINCT ? AS kol FROM katalog ORDER BY kol ASC");
$stmt->execute(array($colparam));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
and it's not working (no errors, just empty array as result).
Instead this works fine:
$stmt = $db->prepare("SELECT DISTINCT abcd AS kol FROM katalog ORDER BY kol ASC");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
So is there any catch with the use of parameter as a name of the column in PDO?
No, you can't use parameter replacements for any database objects (tables, columns, etc.) in MySQL.
When you think about what a prepared statement actually is, this makes complete sense. As how can MySQL prepare a query execution plan when it does not even know the database objects involved.
I certainly wish that more documentation would actually cover what a prepared statement actually does (beyond it's obvious use for parametrization).
Here is link to MySQL prepared statement documentation for more reading:
https://dev.mysql.com/doc/refman/5.6/en/sql-syntax-prepared-statements.html
I have a table with 4 record.
Records: 1) arup Sarma
2) Mitali Sarma
3) Nisha
4) haren Sarma
And I used the below SQL statement to get records from a search box.
$sql = "SELECT id,name FROM ".user_table." WHERE name LIKE '%$q' LIMIT 5";
But this retrieve all records from the table. Even if I type a non-existence word (eg.: hgasd or anything), it shows all the 4 record above. Where is the problem ? plz any advice..
This is my full code:
$q = ucwords(addslashes($_POST['q']));
$sql = "SELECT id,name FROM ".user_table." WHERE name LIKE '%".$q."' LIMIT 5";
$rsd = mysql_query($sql);
Your query is fine. Your problem is that $q does not have any value or you are appending the value incorrectly to your query, so you are effectively doing:
"SELECT id,name FROM ".user_table." WHERE name LIKE '%' LIMIT 5";
Use the following code to
A - Prevent SQL-injection
B - Prevent like with an empty $q
//$q = ucwords(addslashes($_POST['q']));
//Addslashes does not work to prevent SQL-injection!
$q = mysql_real_escape_string($_POST['q']);
if (isset($q)) {
$sql = "SELECT id,name FROM user_table WHERE name LIKE '%$q'
ORDER BY id DESC
LIMIT 5 OFFSET 0";
$result = mysql_query($sql);
while ($row = mysql_fetch_row($result)) {
echo "id: ".htmlentities($row['id']);
echo "name: ".htmlentities($row['name']);
}
} else { //$q is empty, handle the error }
A few comments on the code.
If you are not using PDO, but mysql instead, only mysql_real_escape_string will protect you from SQL-injection, nothing else will.
Always surround any $vars you inject into the code with single ' quotes. If you don't the escaping will not work and syntax error will hit you.
You can test an var with isset to see if it's filled.
Why are you concatenating the tablename? Just put the name of the table in the string as usual.
If you only select a few rows, you really need an order by clause so the outcome will not be random, here I've order the newest id, assuming id is an auto_increment field, newer id's will represent newer users.
If you echo data from the database, you need to escape that using htmlentities to prevent XSS security holes.
In mysql, like operator use '$' regex to represent end of any string.. and '%' is for beginning.. so any string will fall under this regex, that's why it returms all records.
Please refer to http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html once. Hope, this will help you.