SQL Sanitization Output/Datatype Error - php

I'm attempting to sanitize the input of some PHP/SQL code, but I keep receiving the following error when checking the number of rows:
mysqli_num_rows() expects parameter 1 to be mysqli_result
It feels like I'm missing a method to convert/handle the query after execution, and there is little in the documentation to bridge this gap. Assuming $conn is a properly connected mysqli database call, here is my code:
$qry = mysqli_prepare($conn,'SELECT * FROM table WHERE attribute=?');
mysqli_stmt_bind_param($qry,'s',$_SESSION['string']);
mysqli_stmt_execute($qry);
/* Should something go here? */
if(mysqli_num_rows($qry) > 0)
{
//foo
}
I avoided object notation because it wasn't working either - this simply appeared a little more explicit, but I'm not opposed to either method.
Looking forward to hearing any thoughts - thank you in advance!

I don't use mysqli very often, but I believe the issue stems from the fact you are trying to call the mysqli_num_rows() method against a sql string. After running the execute command, pull the results of the execution into a variable and pass that into your mysqli_num_rows() call.
// Added this to capture the results of the execution
$result = mysqli_stmt_get_result($qry);
if(mysqli_num_rows($result) > 0)
{
//foo
}

Okay, so I was able to discover a way to count the number of rows and retrieve the db output:
$stmt = mysqli_prepare($conn, 'SELECT blah FROM table WHERE attribute=?');
mysqli_stmt_bind_param($stmt,'s',$string);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
mysqli_stmt_bind_result($stmt,$bindingvar);
mysqli_stmt_fetch($stmt);
if(mysqli_stmt_num_rows($stmt) > 0) {
//foo
}
And output is now assigned to $bindingvar.
It's cumbersome, but it does everything I want it to, and it's nice and procedural.
It should be noted this method doesn't work well for more than one result from a database, but given the level of problems with "get_result()" this is far better than nothing.
Hope this helps!
- M

Related

PHP / mysqli: Prepared Statements with num_rows constantly returning nothing

In my test-surroundings there is a database containing some Person Information (Name, E-Mail, Adress etc.). These Informations can be inserted by anyone into the database via a form. In the background they are inserted with a parameterized INSERT into the database after submission.
What I now would like to do is to detect if some person tries to insert the same values into the database again, and if he does, not inserting the new values and instead showing an error message. (So every person name in the database is unique, there are no multiple rows linked to one name).
I had a numerous number of ideas on how to accomplish this. My first one was to use a query like REPLACE or INSERT IGNORE, but this method would not give me feedback so I can display the error message.
My second attempt was to first do a SELECT-query, checking if the row already exists, and if num_rows is greater than 0, exit with the error message (and else do the INSERT-part). For this to work I will have to use parameterized queries for the SELECT too, as I´m putting some user input into it. Figuring that parameterized queries need special functions for everything you could normally do with way less lines of code, I researched in the internet on how to get num_rows from my $statement parameterized-statement-object. This is what I had in the end:
$connection = new mysqli('x', 'x', 'x', 'x');
if (mysqli_connect_error()) {
die("Connect Error");
}
$connection->set_charset("UTF-8");
$statement = $connection->stmt_init();
$statement = $connection->prepare('SELECT Name FROM test WHERE Name LIKE ?');
flags = "s";
$statement->bind_param($flags, $_POST["person_name"]);
$statement->execute();
$statement->store_result();
$result = $statement->get_result(); //Produces error
if ($result->num_rows >= 1) {
$output = "Your already registered";
} else {
$output = "Registering you...";
}
exit($output);
After all, I can´t get why mysqli still won´t give me num_rows from my statement. Any help is appreciated, thanks in advance!
Oh, and if you guys could explain to me what I have to do to get affected_rows,that would be awesome!
EDIT: I know I could to this by using unique constraints. I also found out that I can find out if INSERT IGNORE skipped the INSERT or not. But that won´t answer my complete question: Why does the SELECT num_rows alternative not work?
ANOTHER EDIT: I changed the code snippet to what I now have. Although my mysql(i)-version seems to be 5.6.33 (I echo´d it via $connection->server_info) get_result() produces the following error message:
Fatal error: Call to undefined method mysqli_stmt::get_result() in X on line X (line of get_result)
The behaviour of mysqli_num_rows() depends on whether buffered or unbuffered result sets are being used. For unbuffered result sets, mysqli_num_rows() will not return the correct number of rows until all the rows in the result have been retrieved. Note that if the number of rows is greater than PHP_INT_MAX, the number will be returned as a string.
Also make sure that you declare ->store_result() first. Moreover the function doesn't work with LIMIT used jointly with SQL_CALC_FOUND_ROWS. If you want to obtain the total rows found you must do it manually.
EDIT:
If nothing from the suggestions does not work for you, then I would propose to rewrite your SQL query:
SELECT `Name`, (SELECT COUNT(*) FROM `Persons`) AS `num_rows` FROM `Persons` WHERE `Name` LIKE ?
This query will return the total number from your Persons table, as well as Name, if exist.

multi query select using wrong array?

I have a multi query select which half works. The first query is straight forward.
$sql = "SELECT riskAudDate, riskClientId, RiskNewId FROM tblriskregister ORDER BY riskId DESC LIMIT 1;";
The second one doesn't seem to work even when I do it on its own:
$sql ="SELECT LAST(riskFacility) FROM tbleClients";
If I get rid of the LAST it returns the first entry in that field of the table. I want to use the LAST to get the LAST entry in that field.
When I do the first query on its own I get the data returned and I can echo it to the screen. When I add the second (with out the LAST) I get nothing. Here is what I am using
$result = $conn->query($sql);
if ($result == TRUE){
$r = $result->fetch_array(MYSQLI_ASSOC);
echo $r['riskAudDate'];
echo $r['riskClientId'];
echo $r['RiskNewId'];
echo $r['riskFacility'];
echo "<pre>";
print_r($r);
echo "</pre>";
}
The last bit is just for me to see whats in the array and just for testing.
So I have worked out that its the results array that is not right.
If I change the actual query to multi query I get this:
Call to a member function fetch_array() on boolean
So the array bit seems to be wrong for a multi query. The data returned is one row from each table. It works for the top query but add in the second (which I'm not sure is correct anyway) and the whole things crashes. So I guess it's a two part question. Whats wrong with my inserts and whats wrong with my returned array?
There is no last() function in mysql, it is only supported in ms access, if I'm not much mistaken. In mysql you can do what you do in the 1st query: do an order by and limit the results to 1.
According to the error message, the $conn->query($sql) returns a boolean value (probably true), therefore you cannot call $result->fetch_array(MYSQLI_ASSOC) on it. Since we have no idea what exactly you have in $sql variable, al I can say is that you need to debug your code to detrmine why $conn->query($sql) returns a boolean value.
Although it is not that clear from mysqli_query()'s documentation, but it only supports the execution of 1 query at a time. To execute multiple queries in one go, use mysqli_multi_query() (you can call this one in OO mode as well, see documentation). However, for security reasons I would rather call mysqli_query() twice separately. It is more difficult to execute a successful sql injection attack, if you cannot execute multiple queries.
It seems to me you are trying to do two SQL-queries at once.
That is not possible. Do a separate
$result = $conn->query($sql);
if ($result == TRUE){
while( $r = $result->fetch_array(MYSQLI_ASSOC)) {
...
}
}
for each SQL-query.
concerning :
$sql ="SELECT LAST(riskFacility) FROM tbleClients";
since the last function does not exists in MySQL i would recommend doing a sort like this(because i don't know what you mean with last )
$sql ="SELECT riskFacility FROM tbleClients order by riskFacility desc limit 0,1";

What is the difference between get_result() and store_result() in php?

This is my code. I am checking if a user exists or not in a login/registration system:
public function userExist($email){
$stmt = $this->conn->prepare("select email from users where email= ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows>0){
}
else{
}
}
Can I use get_result instead of store_result() ?
It depends on how you plan to read the result set. But in the actual example you have given, you are not interested in reading any returned data. The only thing that interests you is whether there is a record or not.
In that case your code is fine, but it would work equally well with get_result.
The difference becomes more apparent, when you want to get for example the userid of the user with the given email:
SELECT id FROM users WHERE email = ?
If you plan to read out that id with $stmt->fetch, then you would stick to
store_result, and would use bind_result to define in which variable you want to get this id, like this:
$stmt->store_result();
$stmt->bind_result($userid); // number of arguments must match columns in SELECT
if($stmt->num_rows > 0) {
while ($stmt->fetch()) {
echo $userid;
}
}
If you prefer to get a result object on which you can call fetch_assoc() or any of the fetch_* variant methods, then you need to use get_result, like this:
$result = $stmt->get_result(); // You get a result object now
if($result->num_rows > 0) { // Note: change to $result->...!
while ($data = $result->fetch_assoc()) {
echo $data['id'];
}
}
Note that you get a result object from get_result, which is not the case with store_result. You should get num_rows from that result object now.
Both ways work, and it is really a matter of personal preference.
As usual, the accepted answer is too localized, being much more focused on the insignificant details than on the answer itself.
There is also, sadly, a deleted answer, which, although being laconic, makes a perfect rule of thumb:
Use get_result whenever possible and store_result elsewhere.
Both functions load the pending resultset from the database to PHP process' memory, with get_result() being much more versatile and therefore preferred.
The only difference between get_result() and store_result() is that the former gets you a familiar mysqli_result resource/object which can be used to fetch the data using familiar fetch_row() / fetch_assoc() routines, as well as a slightly modern fetch_all(), all of those being incomparably more convenient than bind_result() routine which is the only option with store_result().
It could be noted that get_result() is only available when mysqlnd driver is used, which is not an issue in 2019 and beyond. If it's not available, you are probably to tick some checkbox in your shared host's configuration.

Getting a basic PDO statement to execute

I am attempting to get the following PDO statement to work and running into issues. When I am trying to get the number of rows, I keep getting 0, yet I know there should be 1 row. When I ran it as a mysqli statement( before trying to change it to PDO) it worked perfectly.
Here is the code:
require_once ('pdo.php');
$isbn = $_POST['isbn'];
// check to see if the isbn is a "problem" isbn or not
$problem = $conn->prepare("select isbn, note from problem where isbn = :isbn");
$problem->bindParam(":isbn", $isbn);
$problem->execute();
print_r($problem);
$num_rows = $problem->rowCount();
print_r($num_rows); die;
EDIT: Here is pdo.php:
<?php
function db_connect()
{
$db = new PDO("mysql:host=localhost; db=bookcell_BCOS_final", "xxxxx", "xxxxx");
return($db);
}
?>
I know that my connection works, but I get 0 for $num_rows. What mistakes am I making here?
Besides a little quirk and a optimalisation your code looks fine to me. The posted value isbn could be the reasong that you are getting no data:
$problem = $conn->prepare("select isbn, note from problem where isbn = :isbn");
$problem->bindParam(":isbn", $_POST['isbn'], PDO::PARAM_STR); // <-- thats what parameter binding is for
$problem->execute();
print_r($problem);
$num_rows = $problem->rowCount(); // <-- gives the number of rows, not columnCOunt
print_r($num_rows); die;
The Syntax for $num_rows = $problem->columnCount(); is totally correct. You may try,
$problem->execute(array("isbn" => $isbn));
instead of bindParam.
for getting the no. of rows, you need to use pdo::rowCount() -- manual here
In PDO to verfiy if your execute statement did work, check the return value (bool):
$success = $problem->execute();
if (!$success) {
$arr = $problem->errorInfo();
print_r($arr);
}
Also you might be looking for rowCount() instead of columnCount() but I think the error handling is your furthermost issue.
Additionally you can make PDO throw an exception each time an error appears, compare:
Switching from PHP's mysql extension to PDO. Extend class to reduce lines of code
How do I raise PDOException?
Depending on the database driver and the mode it's running, PDO may not be able to give you a row count. Look carefully at the documentation for PDOStatement::rowCount():
If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement. However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.
This is because in many cases the database uses a cursor rather than fetching the full results and buffering them (which is how the old mysql_* functions behave). In this case the database doesn't know how many rows there are until you have looked at all the rows. Think of a cursor as something like a filesystem pointer--you can't know the filesize until you seek to the end of the file.

PDO “Uncaught exception 'PDOException' .. Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll().”

I know this question has been asked many times, but I've read the answers to many of the questions and still cannot understand why I am receiving this error:
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[HY000]: General error: 2014 Cannot execute queries while
other unbuffered queries are active. Consider using
PDOStatement::fetchAll(). Alternatively, if your code is only ever
going to run against mysql, you may enable query buffering by setting
the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.'
The first thing that is odd, is that I do not get an error on my localhost (wampserver), but I do get it on my web server. The php version on my localhost is 5.3.10, and on my web server it is 5.3.13.
I have read that the source of this error is making a query when data left in the buffer from a previous query. This is not the case for me -- I have echo'd out all of the data and I know for a fact that every row returned in a query is being fetched.
With that said, I have found that changing one of my queries to fetchAll instead of fetch fixes the problem, but it simply makes no since because I know that all of the rows returned are being read. When I used fetchAll for the query (it is being made in a loop), I printed out the array each loop, and only one item was in the array for each query in the loop.
One more piece of information. It's not the query that I changed to fetchAll (which makes the error go away) that throws the PDO error, there is another query later in my php file that throws the error. My file is basically like this:
... code ...
query 1
... code ...
loop
query 2
end loop
... code ...
query 3
If I comment out query 3, there is no error. If I comment out, or change to fetchAll, query 2, there is no error. query 1 has no affect whatsoever.
I would also like to add that I have tried adding LIMIT 1 to all of the queries on the page (at the same time), and the error is still there. I think this proves there is not unread data in the buffer, right?
I'm really confused, so I would appreciate your advice. Before someone asks, I can't post the full code for this, but here is a simplified version of my code:
$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
for loop
makeQuery($stmt, array(':par' => $var));
$row2 = $stmt->fetch(PDO::FETCH_ASSOC);
... [use row2] ...
end for loop
$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row3 = $stmt->fetch(PDO::FETCH_ASSOC);
Here is makeQuery().
/**************************************************************************************************************
* Function: makeQuery *
* Desc: Makes a PDO query. *
* Pre conditions: The statement/query and an array of named parameters (may be empty) must be passed. *
* Post conditions: The PDO query is executed. Exceptions are caught, displayed, and page execution stopped. *
**************************************************************************************************************/
function makeQuery($stmt, $array, $errMsg = '')
{
try
{
$stmt->execute($array);
}
catch (PDOException $e)
{
print $errMsg != ''?$errMsg:"Error!: " . $e->getMessage() . "<br/>";
die();
}
}
Thanks for your help!
EDIT: I also tried doing the following after query 2 (since that seems to be the source of the problem:
$row2 = $stmt->fetch(PDO::FETCH_ASSOC); var_dump($row2);
The output was:
bool(false)
Have I stumbled across a PDO bug?
You need to fetch until a row fetch attempt fails. I know you may only have one row in the result set and think one fetch is enough, but its not (when you're using unbuffered queries). PDO doesn't know how many rows there are until it reaches the end, where it tries to fetch the next row, but it fails.
You probably have other statements where you didn't fully "fetch until a fetch failed". Yes, I see that you fetch until the fetch failed for one of the statements, but that doesn't mean you did it for all of them.
To clarify -
When you execute a query via execute(), you create a result set that must be fetched from the db into php. PDO can only handle 1 of these "result set in progress of being fetched" at a time (per connection). You need to completely fetch the result set, all the way to the end of it, before you can start fetching a different result set from a different call to execute().
When you "call fetch() until a fetch() fails", the fact that you reached the end of the results is internally noted by PDO when that final call to fetch() fails due to there being no more results. PDO is then satisfied that the results are fully fetched, and it can clean up whatever internal resources between php and the db that were established for that result set, allowing you to make/fetch other queries.
There's other ways to make PDO "call fetch() until a fetch() fails".
Just use fetchAll(), which simply fetches all rows, and so it will hit the end of the result set.
or just call closeCursor()
*if you look at the source for closeCursor(), the default implementation literally just fetches the rows and discards them until it reaches the end. It's written in c obviously, but it more or less does this:
function closeCursor() {
while ($row = $stmt->fetch()) {}
$this->stmtFullyFetched = true;
}
Some db drivers may have a more efficient implementation that doesn't require them to fetch lots of rows that nobody cares about, but that's the default way PDO does it. Anyway...
Normally you don't have these problems when you use buffered queries. The reason is because with buffered queries, right after you execute them, PDO will automatically fully fetch the db results into php memory, so it does the "call fetch() until a fetch() fails" part for you, automatically. When you later call fetch() or fetchAll() yourself, it's fetching results from php memory, not from the db. So basically, the result set is immediately fully fetched when using buffered queries, so there's no opportunity to have more than 1 "result set in progress of being fetched" at the same time (because php is single threaded, so no chance of 2 queries running at the same time).
Given this:
$sql = "select * from test.a limit 1";
$stmt = $dbh->prepare($sql);
$stmt->execute(array());
Ways to fully fetch the result set (assuming you only want the first row):
$row = $stmt->fetch();
$stmt->closeCursor();
or
list($row) = $stmt->fetchAll(); //tricky
or
$row = $stmt->fetch();
while ($stmt->fetch()) {}
After struggling with this issue for days, I finally found that this worked for me:
$db = new PDO ($cnstring, $user, $pwd);
$db->setAttribute (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
This also happen if you are trying to fetch a non SELECT query (Eg - UPDATE/INSERT/ALTER/CREATE). Make sure to use fetch or fetchAll only for SELECT queries.
Possible Duplicate Answer/Question

Categories