INOUT parameter failure for stored procedure called from prepared statement - php

Here's my stored procedure:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `testProc`(INOUT num INT(11), INOUT num2 INT(11))
BEGIN
set num2 = num+7;
END
Here's the code that calls it:
$newId = 1;
$type - 2;
if ($stmt = mysqli_prepare($con, 'call testProc(?,?)')) {
mysqli_stmt_bind_param($stmt, 'ii', $type,$newId);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $type,$newId);
echo $newId;
exit;
}
I expected $newId to hold 9. It still holds 1.
mysqli_stmt_bind_result() seems redundant, as I don't actually have a result set (and I think its existence causes bugs later because I don't have a result set), but this code falls over without it (My actual code doesn't but I don't know why). That's may be moot though.
Please can anyone show me how to change this code to make it work?

The PHP manual states that you've got to use session variables (MySQL sessions, not PHP)
INOUT/OUT parameter
The values of INOUT/OUT parameters are accessed using session variables.
mysqli_stmt_bind_result() is to be used to bind values from a resultset like a SELECT statement. This could be a SELECT statement in a stored procedure, see the chapter Stored Routine Syntax of the MySQL manual:
MySQL supports a very useful extension that enables the use of regular
SELECT statements (that is, without using cursors or local variables)
inside a stored procedure. The result set of such a query is simply
sent directly to the client.
So you could do it with
// set your IN parameter using a prepared statement
if ($stmt = mysqli_prepare($con, 'SET #type := ?')) {
mysqli_stmt_bind_param($stmt, 'i', $type);
mysqli_stmt_execute($stmt);
}
// do the same for your second parameter
if ($stmt = mysqli_prepare($con, 'SET #newId := ?')) {
mysqli_stmt_bind_param($stmt, 'i', $newId);
mysqli_stmt_execute($stmt);
}
// No need to use a prepared statement for the procedure call anymore
$result = mysqli_query($con, 'call testProc(#type, #newId)');
// If the procedure would return something else than the INOUT or OUT parameter
// then you would get this result now
// getting the value of the INOUT parameter
$result = mysqli_query($con, 'SELECT #newId as newId');
$row = mysqli_fetch_assoc($result);
var_dump($row);
Output:
Connected to databasearray(1) {
["newId"]=>
string(1) "9"
}
Note:
Error handling left as an exercise to show only the main issue.

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.

Using PHP variable in SQL query

I'm having some trouble using a variable declared in PHP with an SQL query. I have used the resources at How to include a PHP variable inside a MySQL insert statement but have had no luck with them. I realize this is prone to SQL injection and if someone wants to show me how to protect against that, I will gladly implement that. (I think by using mysql_real_escape_string but that may be deprecated?)
<?php
$q = 'Hospital_Name';
$query = "SELECT * FROM database.table WHERE field_name = 'hospital_name' AND value = '$q'";
$query_result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($query_result)) {
echo $row['value'];
}
?>
I have tried switching '$q' with $q and that doesn't work. If I substitute the hospital name directly into the query, the SQL query and PHP output code works so I know that's not the problem unless for some reason it uses different logic with a variable when connecting to the database and executing the query.
Thank you in advance.
Edit: I'll go ahead and post more of my actual code instead of just the problem areas since unfortunately none of the answers provided have worked. I am trying to print out a "Case ID" that is the primary key tied to a patient. I am using a REDCap clinical database and their table structure is a little different than normal relational databases. My code is as follows:
<?php
$q = 'Hospital_Name';
$query = "SELECT * FROM database.table WHERE field_name = 'case_id' AND record in (SELECT distinct record FROM database.table WHERE field_name = 'hospital_name' AND value = '$q')";
$query_result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($query_result)) {
echo $row['value'];
}
?>
I have tried substituting $q with '$q' and '".$q."' and none of those print out the case_id that I need. I also tried using the mysqli_stmt_* functions but they printed nothing but blank as well. Our server uses PHP version 5.3.3 if that is helpful.
Thanks again.
Do it like so
<?php
$q = 'mercy_west';
$query = "SELECT col1,col2,col3,col4 FROM database.table WHERE field_name = 'hospital_name' AND value = ?";
if($stmt = $db->query($query)){
$stmt->bind_param("s",$q); // s is for string, i for integer, number of these must match your ? marks in query. Then variable you're binding is the $q, Must match number of ? as well
$stmt->execute();
$stmt->bind_result($col1,$col2,$col3,$col4); // Can initialize these above with $col1 = "", but these bind what you're selecting. If you select 5 times, must have 5 variables, and they go in in order. select id,name, bind_result($id,name)
$stmt->store_result();
while($stmt->fetch()){ // fetch the results
echo $col1;
}
$stmt->close();
}
?>
Yes mysql_real_escape_string() is deprecated.
One solution, as hinted by answers like this one in that post you included a link to, is to use prepared statements. MySQLi and PDO both support binding parameters with prepared statements.
To continue using the mysqli_* functions, use:
mysqli_prepare() to get a prepared statement
mysqli_stmt_bind_param() to bind the parameter (e.g. for the WHERE condition value='$q')
mysqli_stmt_execute() to execute the statement
mysqli_stmt_bind_result() to send the output to a variable.
<?php
$q = 'Hospital_Name';
$query = "SELECT value FROM database.table WHERE field_name = 'hospital_name' AND value = ?";
$statement = mysqli_prepare($conn, $query);
//Bind parameter for $q; substituted for first ? in $query
//first parameter: 's' -> string
mysqli_stmt_bind_param($statement, 's', $q);
//execute the statement
mysqli_stmt_execute($statement);
//bind an output variable
mysqli_stmt_bind_result($stmt, $value);
while ( mysqli_stmt_fetch($stmt)) {
echo $value; //print the value from each returned row
}
If you consider using PDO, look at bindparam(). You will need to determine the parameters for the PDO constructor but then can use it to get prepared statements with the prepare() method.

MYSQLI prepared statement bind_param types does not work

I have been using prepared insert statements for some years and assumed it was binding parameters properly or would give an error but it seems not as the following php binds and inserts a record without any errors but changes a string which should be an int to a zero. So it may be ok for preventing SQL injection attacks but you would end up with a spurious record in the table. e.g.:
$q = "INSERT INTO `table` (id1, id2) VALUES (?, ?)";
$stmt = mysqli_stmt_init($dbc);
$stmt->prepare($q);
$id1 = 'aaaaaaa';
$id2= 'aaaaaaa';
$result = $stmt->bind_param('ii', $id1, $id2);
echo '<p>' . $result . '</p>'; // Trying to bind a string to an int returns true!
echo $dbc->error; // nothing
$stmt->execute(); // inserts record changing $id2 to zero but auto-increments primary key $id1
echo $dbc->error; // nothing
This is running in an Xampp environment with Apache/2.2.14, PHP/5.3.1 and MySQL 5.1.41. Can anyone tell me what is going on?
$stmt->bind_param() doesn't check the given variables for a certain type, it only converts them into the specified type. And your string 'aaaaaaa' is converted into an int-value: 0. That's the way php does it.
The database insert statement is the wrong place to check, if your variables contain useful/correct values. Do that before and only try to insert them, if your validations work.
To do the validation for an int, you could use the php-function is_numeric() or is_int().
I'm not an expert for sure, but at first look.
You have:
$id1 = 'aaaaaaa';
$id2= 'aaaaaaa';
$result = $stmt->bind_param('ii', $id1, $id2);
Thing is your 'ii' parameter says that you will be binding integers! And in fact your $id1 and $id2 are strings. For strings you should go with:
$result = $stmt->bind_param('ss', $id1, $id2);

PHP prepared statement within a prepared statement

I'm going through a video tutorial about doing a menu using a db. Instead of doing it with procedural PHP like in the video, I tried doing it with prepared statements OOP style. It doesn't work and I can't figure out why.
It runs fine until line 17, where it dies with this error:
Fatal error: Call to a member function bind_param() on a non-object in C:\wamp\www\widget_corp\content.php on line 17
And here's the code:
<?php
$query = $connection->prepare('SELECT menu_name, id FROM subjects ORDER BY position ASC;');
$query->execute();
$query->bind_result($menu_name, $sid);
while ($query->fetch()){
echo "<li>{$menu_name} {$sid}</li>";
$query2 = $connection->prepare('SELECT menu_name FROM pages WHERE subject_id = ? ORDER BY position ASC;');
$query2->bind_param("i", $sid); //This is line 17
$query2->execute();
$query2->bind_result($menu_name);
echo "<ul class='pages'>";
while ($query2->fetch()){
echo "<li>{$menu_name}</li>";
}
echo "</ul>";
}
$query->close();
?>
Is it impossible to do a prepared statement within stmt->fetch();?
Figured it out:
After executing and binding the result, it has to be stored (if another prepared statement is to be put in the fetch). So the fetching in this case has to be read from a buffered result.
In other words, can't execute another query until a fetch on the same connection is in progress.
The working code:
$query = $connection->prepare("SELECT menu_name, id FROM subjects ORDER BY position ASC;");
$query->execute();
$query->bind_result($menu_name, $sid);
$query->store_result();
$stmt = mysqli_prepare($con,"SELECT menu_name, id FROM subjects ORDER BY position ASC");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $menu_name, $id);
while (mysqli_stmt_fetch($stmt))
{
$stmt2 = mysqli_prepare($con2,"SELECT menu_name FROM pages WHERE subject_id = ? ORDER BY position ASC;");
mysqli_stmt_bind_param($stmt2,$id);
mysqli_stmt_execute($stmt2);
mysqli_stmt_bind_result($stmt2, $name);
while (mysqli_stmt_fetch($stmt2))
echo $name;
}
look at the $con and $con2, you can not execute a prepare statement within another ps using the same connection !!!
Yes, you can have several prepared statements : one of the ideas of prepared statements is "prepare once, execute several times".
So, you should prepare the statement outside of the loop -- so it's prepared only once
And execute it, several times, insidde the loop.
The Fatal error you get means that $query2 on line 17, is not an object -- which means the prepare failed.
A prepare typically fails when there is an error in it ; are you sure your query is valid ? The tables and columns names are OK ?
You should be able to get an error message, when the prepare fails, using mysqli->error() -- or PDO::errorInfo()
You don't say what DB extension you are using but you don't seem to test the return value of any function you are using. You can't assume that DB calls will always run flawlessly.

SELECT * FROM in MySQLi

My site is rather extensive, and I just recently made the switch to PHP5 (call me a late bloomer).
All of my MySQL query's before were built as such:
"SELECT * FROM tablename WHERE field1 = 'value' && field2 = 'value2'";
This made it very easy, simple and friendly.
I am now trying to make the switch to mysqli for obvious security reasons, and I am having a hard time figuring out how to implement the same SELECT * FROM queries when the bind_param requires specific arguments.
Is this statement a thing of the past?
If it is, how do I handle a query with tons of columns involved? Do I really need to type them all out every time?
I could be wrong, but for your question I get the feeling that bind_param() isn't really the problem here. You always need to define some conditions, be it directly in the query string itself, of using bind_param() to set the ? placeholders. That's not really an issue.
The problem I had using MySQLi SELECT * queries is the bind_result() part. That's where it gets interesting. I came across this post from Jeffrey Way: http://jeff-way.com/2009/05/27/tricky-prepared-statements/(This link is no longer active). The script basically loops through the results and returns them as an array — no need to know how many columns there are, and you can still use prepared statements.
In this case it would look something like this:
$stmt = $mysqli->prepare(
'SELECT * FROM tablename WHERE field1 = ? AND field2 = ?');
$stmt->bind_param('ss', $value, $value2);
$stmt->execute();
Then use the snippet from the site:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
And $results now contains all the info from SELECT *. So far I found this to be an ideal solution.
"SELECT * FROM tablename WHERE field1 = 'value' && field2 = 'value2'";
becomes
"SELECT * FROM tablename WHERE field1 = ? && field2 = ?";
which is passed to the $mysqli::prepare:
$stmt = $mysqli->prepare(
"SELECT * FROM tablename WHERE field1 = ? && field2 = ?");
$stmt->bind_param( "ss", $value, $value2);
// "ss' is a format string, each "s" means string
$stmt->execute();
$stmt->bind_result($col1, $col2);
// then fetch and close the statement
OP comments:
so if i have 5 parameters, i could potentially have "sssis" or something (depending on the types of inputs?)
Right, one type specifier per ? parameter in the prepared statement, all of them positional (first specifier applies to first ? which is replaced by first actual parameter (which is the second parameter to bind_param)).
While you are switching, switch to PDO instead of mysqli, It helps you write database agnositc code and have better features for prepared statements.
http://www.php.net/pdo
Bindparam for PDO:
http://se.php.net/manual/en/pdostatement.bindparam.php
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = :value1 && field2 = :value2");
$sth->bindParam(':value1', 'foo');
$sth->bindParam(':value2', 'bar');
$sth->execute();
or:
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = ? && field2 = ?");
$sth->bindParam(1, 'foo');
$sth->bindParam(2, 'bar');
$sth->execute();
or execute with the parameters as an array:
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = :value1 && field2 = :value2");
$sth->execute(array(':value1' => 'foo' , ':value2' => 'bar'));
It will be easier for you if you would like your application to be able to run on different databases in the future.
I also think you should invest some time in using some of the classes from Zend Framwework whilst working with PDO. Check out their Zend_Db and more specifically [Zend_Db_Factory][2]. You do not have to use all of the framework or convert your application to the MVC pattern, but using the framework and reading up on it is time well spent.
Is this statement a thing of the past?
Yes. Don't use SELECT *; it's a maintenance nightmare. There are tons of other threads on SO about why this construct is bad, and how avoiding it will help you write better queries.
See also:
What is the reason not to use select *?
Performance issue in using SELECT *?
Why is using '*' to build a view bad?
You can still use it (mysqli is just another way of communicating with the server, the SQL language itself is expanded, not changed). Prepared statements are safer, though - since you don't need to go through the trouble of properly escaping your values each time. You can leave them as they were, if you want to but the risk of sql piggybacking is reduced if you switch.
you can use get_result() on the statement.
http://php.net/manual/en/mysqli-stmt.get-result.php
I was looking for a nice and complete example of how to bind multiple query parameters dynamically to any SELECT, INSERT, UPDATE and DELETE query. Alec mentions in his answer a way of how to bind result, for me the get_result() after execute() function for SELECT queries works just fine, and am able to retrieve all the selected results into an array of associative arrays.
Anyway, I ended up creating a function where I am able to dynamically bind any amount of parameters to a parametrized query ( using call_user_func_array function) and obtain a result of the query execution. Below is the function with its documentation (please read before it before using - especially the $paremetersTypes - Type specification chars parameter is important to understand)
/**
* Prepares and executes a parametrized QUERY (SELECT, INSERT, UPDATE, DELETE)
*
* #param[in] $dbConnection mysqli database connection to be used for query execution
* #param[in] $dbQuery parametrized query to be bind parameters for and then execute
* #param[in] $isDMQ boolean value, should be set to TRUE for (DELETE, INSERT, UPDATE - Data manipulaiton queries), FALSE for SELECT queries
* #param[in] $paremetersTypes String representation for input parametrs' types as per http://php.net/manual/en/mysqli-stmt.bind-param.php
* #param[in] $errorOut A variable to be passed by reference where a string representation of an error will be present if a FAUILURE occurs
* #param[in] $arrayOfParemetersToBind Parameters to be bind to the parametrized query, parameters need to be specified in an array in the correct order
* #return array of feched records associative arrays for SELECT query on SUCCESS, TRUE for INSERT, UPDATE, DELETE queries on SUCCESS, on FAIL sets the error and returns NULL
*/
function ExecuteMySQLParametrizedQuery($dbConnection, $dbQuery, $isDMQ, $paremetersTypes, &$errorOut, $arrayOfParemetersToBind)
{
$stmt = $dbConnection->prepare($dbQuery);
$outValue = NULL;
if ($stmt === FALSE)
$errorOut = 'Failed to prepare statement for query: ' . $dbQuery;
else if ( call_user_func_array(array($stmt, "bind_param"), array_merge(array($paremetersTypes), $arrayOfParemetersToBind)) === FALSE)
$errorOut = 'Failed to bind required parameters to query: ' . $dbQuery . ' , parameters :' . json_encode($arrayOfParemetersToBind);
else if (!$stmt->execute())
$errorOut = "Failed to execute query [$dbQuery] , erorr:" . $stmt->error;
else
{
if ($isDMQ)
$outValue = TRUE;
else
{
$result = $stmt->get_result();
if ($result === FALSE)
$errorOut = 'Failed to obtain result from statement for query ' . $dbQuery;
else
$outValue = $result->fetch_all(MYSQLI_ASSOC);
}
}
$stmt->close();
return $outValue;
}
usage:
$param1 = "128989";
$param2 = "some passcode";
$insertQuery = "INSERT INTO Cards (Serial, UserPin) VALUES (?, ?)";
$rowsInserted = ExecuteMySQLParametrizedQuery($dbConnection, $insertQuery, TRUE, 'ss', $errorOut, array(&$param1, &$param2) ); // Make sure the parameters in an array are passed by reference
if ($rowsInserted === NULL)
echo 'error ' . $errorOut;
else
echo "successfully inserted row";
$selectQuery = "SELECT CardID FROM Cards WHERE Serial like ? AND UserPin like ?";
$arrayOfCardIDs = ExecuteMySQLParametrizedQuery($dbConnection, $selectQuery, FALSE, 'ss', $errorOut, array(&$param1, &$param2) ); // Make sure the parameters in an array are passed by reference
if ($arrayOfCardIDs === NULL)
echo 'error ' . $errorOut;
else
{
echo 'obtained result array of ' . count($arrayOfCardIDs) . 'selected rows';
if (count($arrayOfCardIDs) > 0)
echo 'obtained card id = ' . $arrayOfCardIDs[0]['CardID'];
}

Categories