MySQLi prepared statement silently failing second execution - php

I am attempting to make a simple system where, once you view forum thread it updates an existing array entry with the current UNIX time in the DB table that holds the user's account information. The column is set to accept MEDIUMTEXT and is set as NOT NULL.
However, while the first prepared statement works as intended and I receive the correct value, and the json_table is correctly re-encoded, the second query does not update the column data. No errors (no matter what error level I set mysqli to). Neither of the parameters I am binding are NULL and are correct.
I am stumped as to why this is happening. MySQLi magic that I haven't learned? Restrictions somewhere?
$DB = GetDatabaseConnection();
$Statement = $DB->prepare("SELECT `forum_newpost_icondata` FROM `user_accounts` WHERE `id` = ?");
print(mysqli_error($DB));
$Statement->bind_param("i", $UserLoggedIn['id']);
$Statement->bind_result($JSONTable);
$Statement->execute();
$PostData = json_decode($JSONTable);
$PostData[$ThreadID] = time();
$PostData = (string)json_encode($PostData);
$Statement->free_result();
$Statement->close();
$Reinsertion = $DB->prepare("UPDATE `user_accounts` SET `forum_newpost_icondata` = ? WHERE `id` = ?");
print(mysqli_error($DB));
$Reinsertion->bind_param("si", $PostData, $UserLoggedIn['id']);
$Reinsertion->execute();
print(mysqli_error($DB)); // Checking after execute is still blank
$DB->close();

Related

Unable to run mysql stored procedure from php code

I am not able to run MySQL stored procedure from PHP, no matter what I use. I am from .Net background and not well versant with PHP.
If I try the same query from PHP it doesn't work, whereas if I try in the SQL editor in PHPMyAdmin it works fine.
<?php
$sql="SET #p0='".$path.$imageName."'; SET #p1='".$_SESSION['uid']."'; CALL `upload_image`(#p0, #p1);";
$result = mysqli_query($con,$sql);
?>
and in Mysql Phpmyadmin tried this, which worked:-
SET #p0='images/user/Ganesh_1566681875.png'; SET #p1='1'; CALL `upload_image`(#p0, #p1);
and here is my stored procedure:-
CREATE DEFINER=`root`#`localhost` PROCEDURE `upload_image`(IN `imagepath` VARCHAR(250), IN `userid` INT)
BEGIN
UPDATE user SET profile_image_path = imagepath WHERE id = userid;
END$$
DELIMITER ;
I must be able to run the stored procedure by passing value from PHP variables and it must update the database for the user.
Update
I'm not sure why you're taking the approach you have. There is no need to assign values to variables first, you can simply call the procedure directly e.g.
$sql = "CALL `upload_image`('$path.$imageName', $_SESSION['uid'])";
$result = mysqli_query($con,$sql);
Regardless, it would be far preferable to use prepared statements to protect yourself from SQL injection. Something like this:
$stmt = $con->prepare("CALL `upload_image(?, ?)");
$stmt->bind_param("si", $path.$imageName, $_SESSION['uid']);
$stmt->execute();
$result = $stmt->get_result();
Additionally, your stored procedure being only one statement means that it would be more optimal to just execute that statement directly:
$stmt = $con->prepare("UPDATE user SET profile_image_path = ? WHERE id = ?;");
$stmt->bind_param("si", $path.$imageName, $_SESSION['uid']);
$stmt->execute();
$result = $stmt->get_result();
Original Answer
Your problem is that you are trying to run three separate queries (each SET counts as a query), which mysqli_query doesn't support. You have two options, you can use mysqli_multi_query:
$sql="SET #p0='".$path.$imageName."'; SET #p1='".$_SESSION['uid']."'; CALL `upload_image`(#p0, #p1);";
$result = mysqli_multi_query($con,$sql);
Or you can run them as three separate queries:
$sql="SET #p0='".$path.$imageName."'";
$result = mysqli_query($con,$sql);
$sql="SET #p1='".$_SESSION['uid']."'";
$result = mysqli_query($con,$sql);
$sql="CALL `upload_image`(#p0, #p1)";
$result = mysqli_query($con,$sql);
The latter is probably preferred as mysqli_multi_query makes you more vulnerable to SQL injection.

PHP PDO Service No Data

I have the below REST web service that I am using to get user information from User table:
$name = htmlentities($_GET["name"]);
$name = strtoupper($name);
$dbh = new PDO("oci:dbname= $dbhost", $dbuser, $dbpass);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sth = $dbh->prepare("select * from Users where username =:name");
$sth->bindParam(':name', $name);
$sth->execute();
$result = array();
$result["User"] = $sth->fetchAll((PDO::FETCH_ASSOC));
print_r ($result); //returns no data
When I print out the results, no data is returned. If I hard code a username value instead of using :name, then data comes back:
$sth = $dbh->prepare("select * from Users where username ='TESTUSER'");
I am not sure what I am doing wrong with the binding of the variable that is causing the SQL to run incorrectly. I tried using bindValue and bindParam and still returns no data. I am not recieving any errors, just no data.
UPDATE: It looks like the syntax is correct. Is there anything on the Oracle side that would prevent a prepared statement from being run?
I figured out why data wasn't returning on the query. The database has the username field set as a CHAR(8) and usernames that were being passed only had 7 characters so it was failing. I need to append a blank space at the end of the string for it to match.

PDOStatement's bindValue only working with value of "0"

I'm trying to get data from a MySQL database using PDO. For example, I would run blixUserGetInfo("2767207") if the user's id was 2767207.
But, whenever I run it with a User ID other than 0, blixUserGetInfo("0"), null is returned, and no errors are thrown, even though the supplied ID does exist in the database.
function blixUserGetInfo($userid){
$connection = //database connection
// replacing this with
// "SELECT * FROM `users` WHERE `id` = $userid"
// works for some reason
$statement = "SELECT * FROM `users` WHERE `id` = ?";
$prepared = $connection->prepare($statement);
$prepared->bindValue(1, $userid); //returns TRUE
$prepared->execute(); //also returns TRUE
$code = $prepared->errorCode(); //returns '00000' (no error)
return $prepared->fetch(); //returns null
}
But, If I change $statement from
"SELECT * FROM `users` WHERE `id` = ?"
to
"SELECT * FROM `users` WHERE `id` = $userid"
and remove the bindValue statement, it works as expected.
I've also tried changing the bindValue statement to bindValue(1, $userid, PDO::PARAM_INT), but still have no luck. Both of the times the statement returns true.
Why doesn't the first example work like it should? Is it a bug, am I doing something wrong, or is it expected behavior?
I really dislike this bindValue and bindParam complexity unless it's absolutely necessary. You can simplify your code using parameters for execute
$statement = "SELECT * FROM `users` WHERE `id` = ?";
$prepared = $connection->prepare($statement);
$prepared->execute(array($userid));
While i know this does not answer why bindValue is not working, it takes away having to worry about pass by reference and pass by value stuff which could be a factor in this question.
If this execute does not return an error and your table does have data for that condition than its almost surely going to return it.
Although i didn't confirm it but your act of setting the connection to null before you fetch might be the actual reason you fail to see any data. Why would you set the connection to NULL and try to fetch afterwards ?
I suggest you just not use the bindValue() function anymore.
The bindParam() function works better in this case. Use the value placeholder instead of ?'s (:[placeholder name])
$statement = "SELECT * FROM `users` WHERE `id` = :id";
$prepared = $connection->prepare($statement);
$prepared -> bindParam(":id", $value)
$prepared -> execute();

mysqli prepared statement without bind_param

I have this code for selecting fname from the latest record on the user table.
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
$sdt=$mysqli->('SELECT fname FROM user ORDER BY id DESC LIMIT 1');
$sdt->bind_result($code);
$sdt->fetch();
echo $code ;
I used prepared statement with bind_param earlier, but for now in the above code for first time I want to use prepared statement without binding parameters and I do not know how to select from table without using bind_param(). How to do that?
If, like in your case, there is nothing to bind, then just use query()
$res = $mysqli->query('SELECT fname FROM user ORDER BY id DESC LIMIT 1');
$fname = $res->fetch_row()[0] ?? false;
But if even a single variable is going to be used in the query, then you must substitute it with a placeholder and therefore prepare your query.
However, in 2022 and beyond, (starting PHP 8.1) you can indeed skip bind_param even for a prepared query, sending variables directly to execute(), in the form of array:
$query = "SELECT * FROM `customers` WHERE `Customer_ID`=?";
$stmt = $db->prepare($query);
$stmt->execute([$_POST['ID']]);
$result = $stmt->get_result();
$row = $result->fetch_assoc();
The answer ticked is open to SQL injection. What is the point of using a prepared statement and not correctly preparing the data. You should never just put a string in the query line. The point of a prepared statement is that it is prepared. Here is one example
$query = "SELECT `Customer_ID`,`CompanyName` FROM `customers` WHERE `Customer_ID`=?";
$stmt = $db->prepare($query);
$stmt->bind_param('i',$_POST['ID']);
$stmt->execute();
$stmt->bind_result($id,$CompanyName);
In Raffi's code you should do this
$bla = $_POST['something'];
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
$stmt = $mysqli->prepare("SELECT `fname` FROM `user` WHERE `bla` = ? ORDER BY `id` DESC LIMIT 1");
$stmt->bind_param('s',$_POST['something']);
$stmt->execute();
$stmt->bind_result($code);
$stmt->fetch();
echo $code;
Please be aware I don't know if your post data is a string or an integer. If it was an integer you would put
$stmt->bind_param('i',$_POST['something']);
instead. I know you were saying without bind param, but trust me that is really really bad if you are taking in input from a page, and not preparing it correctly first.

Best way to avoid preparing the same PDO statement more than once?

Currently I save prepared statements into a private variable, because I ignore how they really work in the deepness, and do it just in case.
So the question is really simple, if I iterate over the same $PDO->prepare(), will it prepare again the same query?
foreach( $arr as $docid ) {
if( $this->dbLink === null ) { // PDO resource, saved in the object.
throw new Exception( 'Must first connect to DB' );
}
if( $this->queryCheckAccess === null ) {
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
}
else {
$result = $this->queryCheckAccess->execute(array(':id'=>$docid));
}
}
Will it matter ? Or the DB Engine / PHP is smart enough to know that it is the same prepared statement?
Thanks a lot.
----------------- EDIT --------------
I think I was misunderstood.
What I ask is what happens if I do:
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
And what happens if I do:
if( $this->queryCheckAccess === null ) {
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
}
Will the engine prepare the query 4 times in the first example? Or will notice it is the same query and just "jump" that?
Your code only prepares the query once, because after the first loop iteration, it's not NULL so it the conditional block won't run. But it's a waste of time to check the condition every time through the loop.
But to answer your question, if you prepare() the same query, it does do redundant work, even if the query is identical to the one you prepared before. So you should avoid that.
But you don't need to prepare inside the loop at all. Prepare once before you start the loop, and bind a variable to the parameter. You don't need to bind every time in the loop, just change the value of that variable.
if( $this->dbLink === null ) { // PDO resource, saved in the object.
throw new Exception( 'Must first connect to DB' );
}
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->bindParam(':id' => $docidparam);
foreach( $arr as $docid ) {
$docidparam = $docid;
$result = $this->queryCheckAccess->execute();
}
I'm not sure if you can bind the variable and also use it as the loop variable, there might be a scope conflict.
Another suggestion for this query: why not just run one query to search for a list of values?
$list = implode(",", array_fill(1, count($arr), "?"));
$query = "SELECT * FROM something WHERE id IN ( $list )";
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->execute($arr);
PS: Also you should check for errors. If you enable PDO error mode EXCEPTION, then errors will automatically throw exceptions. If you don't enable that mode, you need to check the return value of prepare() and execute(), which return false if there's an error.
I just RUN a code similar to your example, and enabled MySQL Query LOG I found that all prepare requests are sent to MySQL Server
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Test code:
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth->bindParam(1, $user);
$sth->execute();
Then, the best way is to prepare once, and Bind different values and then execute.
$sth = $dbh->prepare($sql);
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();
$user = "test2";
$sth->bindParam(1, $user);
$sth->execute();
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();
No, that's one of the main features of prepared statements. If you're going to run the same query multiple times but with different variables then preparing the query will give you a speed increase. Especially if you make use of transactions (requires InnoDB storage engine).
To answer the question from the title (which is quite different from questions in the body), the best way to avoid preparing the same statement more than once, apparently would be to avoid running multiple similar queries at all.
To answer the question from the question body - no, DB Engine / PHP is not "smart" enough to know that it is the same query were prepared again. With every new prepare() call another statement is created. And I would be first to hate such a "smart" behavior. The "smarter" your tool, the more unpredictable results you get.
To answer the real problem in the code, a smart developer would use a right tool to save himself a trouble.
With safeMysql whole mess will be reduced to one query and one line of code
$data = $this->dbLink->getAll('SELECT * from somth where id IN (?a)', $arr);
S0 - no multiple queries, no multiple preparations, no multiple questions.
By the way, you are losing first id with your code.
Yet you're losing all of them but last one if you don't use the result in place.

Categories