PHP mysqli prepared statement for stored procedure with out parameter - php

I have a stored procedure IsUserPresent like:
DELIMITER $$
CREATE PROCEDURE IsUserPresent(
in userid varchar (150),
out isPresent bit
)
BEGIN
SET isPresent=0;
SELECT COUNT(*)
INTO isPresent
FROM users_table
WHERE users_table.userid=userid;
END$$
and I want to call it from PHP using mysqli prepared statement. I am doing it following code snippet but it gives me warning.
$connect=&ConnectDB();
$stmt=$connect->prepare("CALL IsUserPresent(?,?)");
$stmt->bind_param('si',$uid,$userCount);
$stmt->execute();
$toRet = $userCount!=0;
Disconnect($connect);
return $toRet;
Warnings are as follow:
Premature end of data (mysqlnd_wireprotocol.c:1112)
Warning: mysqli_stmt::execute(): RSET_HEADER packet 1 bytes shorter than expected
Warning: mysqli_stmt::execute(): Error reading result set's header

The way stored procedures work with prepared statements is a bit more complicated. 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.
So you could do it with
$connect=&ConnectDB();
// bind the first parameter to the session variable #uid
$stmt = $connect->prepare('SET #uid := ?');
$stmt->bind_param('s', $uid);
$stmt->execute();
// bind the second parameter to the session variable #userCount
$stmt = $connect->prepare('SET #userCount := ?');
$stmt->bind_param('i', $userCount);
$stmt->execute();
// execute the stored Procedure
$result = $connect->query('call IsUserPresent(#uid, #userCount)');
// getting the value of the OUT parameter
$r = $connect->query('SELECT #userCount as userCount');
$row = $r->fetch_assoc();
$toRet = ($row['userCount'] != 0);
Remark:
I recommend to rewrite this procedure as a function with one IN parameter that returns INT.

Should be a comment, but due to code formatting posting as answer.
Can't comment on the PHP code, I'm no programmer, but your procedure should be more like this:
DELIMITER $$
CREATE PROCEDURE IsUserPresent(
in p_userId varchar (150),
out p_isPresent bit
)
BEGIN
SELECT EXISTS (SELECT 1 FROM users_table WHERE user_table.userid = p_userId)
INTO p_isPresent;
END$$
Use exists(), since it stops as soon as an entry is found. count() continues to look for records, although this isn't necessary.
And you named a parameter the same as your column name. This is confusing for MySQL and should be avoided at all costs. Good practice is, to prefix parameters with p_ and variables with v_ and/or some indications of what type the variable or parameter is.
For better readability I also changed the paramter names to camel case.
Oh, and finally always include error messages in questions.

Related

PHP PDO UPDATE statement with subquery

I'm trying to create a query using PDO, where the query includes a subquery. The code isn't working. Using the workbench, I can do the query and it does perform.
I feel like there is nuance here when it comes to deriving a table while using PDO.
$turn = 1;
$phase = -1;
$status = "waiting";
$gameid = 1;
$stmt = $this->connection->prepare("
UPDATE playerstatus
SET
turn = :turn,
phase = :phase,
status = :status,
value = value + (SELECT reinforce FROM games where id = :gameid)
WHERE
gameid = :gameid
");
$stmt->bindParam(":turn", $turn);
$stmt->bindParam(":phase", $phase);
$stmt->bindParam(":status", $status);
$stmt->bindParam(":gameid", $gameid);
$stmt->execute();
I tried a multitude of adjustments, it simply fails upon executing.
EDIT error:
Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number
A known (but not well documented) limitation with PDO named placholders: the same bind placeholder can't be used more than one time in a statement. Workaround is to use distinct bind placeholder names.
(This limitation in PDO may have been addressed in a later versions(?). I think the root cause is that "behind the scenes", PDO is replacing the named placeholders with positional notation question marks. This problem is not restricted to just UPDATE statements, this same problem plagues all PDO SQL statements using named placeholders.)
Also, not related to the problem, I recommend using bindValue in place of bindParam.
Change the bind placeholder name to be distinct/unique. Shown here, changing one of the occurrences of :gameid to :gameid2
value = value + (SELECT reinforce FROM games where id = :gameid)
WHERE
gameid = :gameid2
^
And we need to supply a value for each bind placeholder. Which means we need to add another line. With bindValue, we can reference the same variable without needing make a copy of it.
$stmt->bindValue(":gameid", $gameid);
$stmt->bindValue(":gameid2", $gameid);
^

PDO: Database reports an error: SQLSTATE[HY000]: General error: 2031 [duplicate]

I'm getting this annoying error and although I have an idea of why I'm getting it, I can't for the life of me find a solution to it.
if ($limit) {
$sth->bindValue(':page', $page - 1, PDO::PARAM_INT);
$sth->bindValue(':entries_per_page', $page * $entries_per_page, PDO::PARAM_INT);
}
$sth->execute($criteria);
Query contains placeholders (:placeholder). But to add those LIMIT placeholders, I need to use the manual method (bindValue) because otherwise the engine will turn them into strings.
I'm not getting the Invalid number of parameters error, so all placeholders have been bound correctly (I assume).
Query:
SELECT `articles`.*, `regional_municipalities`.`name` AS `regional_municipality_name`,
`_atc_codes`.`code` AS `atc_code`, `_atc_codes`.`name` AS `substance`
FROM `articles`
LEFT JOIN `_atc_codes`
ON (`_atc_codes`.`id` = `articles`.`atc_code`)
JOIN `regional_municipalities`
ON (`regional_municipalities`.`id` = `articles`.`regional_municipality`)
WHERE TRUE AND `articles`.`strength` = :strength
GROUP BY `articles`.`id`
ORDER BY `articles`.`id`
LIMIT :page, :entries_per_page
All placeholder values reside in $criteria, except for the last two LIMIT, which I manually bind with bindValue().
This same error 2031 can be issued when one bind two values with the same parameter name, like in:
$sth->bindValue(':colour', 'blue');
$sth->bindValue(':colour', 'red');
..so, beware.
You cannot use ->bind* and ->execute($params). Use either or; if you pass parameters to execute(), those will make PDO forget the parameters already bound via ->bind*.
This exception also appears if you try to run a query with placeholders instead of preparing a statment such as
$stmt = $db->query('SELECT * FROM tbl WHERE ID > ?');
instead of
$stmt = $db->prepare('SELECT * FROM tbl WHERE ID > ?');
From the manual:
public bool PDOStatement::execute ([ array $input_parameters ] )
Execute the prepared statement. If the prepared statement included
parameter markers, you must either:
call PDOStatement::bindParam() to bind PHP variables to the parameter markers: bound variables pass their value as input and
receive the output value, if any, of their associated parameter
markers
or pass an array of input-only parameter values
You need to pick a method. You cannot mix both.
It's not exactly an answer, but this error also happens if you try to use a word with a hyphen as placeholders, for example:
$sth->bindValue(':page-1', $page1);
So better use
$sth->bindValue(':page_1', $page1);
This happens if you have mismatching parameters. For example:
$q = $db->prepare("select :a, :b");
$q->execute([":a"=>"a"]);
The exception also happens (at least in MySQL/PDO) when your SQL tries to UPDATE an AUTO_INCREMENT field.

using STORED PROCEDURES: different results in mysqli->query("CALL select_procedure") VS mysqli->query("SELECT ...")

I am migrating all my mysqli queries to STORED PROCEDURE.
It should be as easy as changing one line in the mysqli call, howver, the two following codes give different results:
Regular query, which works correctly:
$query = $this->mysqli->query("SELECT DISTINCT ID FROM user
WHERE
MATCH (name) AGAINST ('* *$sanitized* *') ");
if ($query) {
$nrows = $query -> num_rows;
if ($nrows > 0) {
$searchResult = 'We found '. $nrows .' results';
}
}
CALL to PROCEDURE, which returns a "fetch_array() on boolean" error:
$query = $this->mysqli->query("CALL myfunction('.$sanitized.')");
where the procedures is defined as:
DELIMITER $$
CREATE PROCEDURE myfunction (sanitized VARCHAR(124))
BEGIN
SELECT DISTINCT ID FROM user
WHERE
MATCH (name) AGAINST ('* *sanitized* *');
END
$$
DELIMITER ;
I can't find a solution and it seems that no one has a similar issue in this forum.
consider Prepared Statements used with concat() as they often are.
DROP PROCEDURE if exists myStoredProc101;
DELIMITER $$
CREATE PROCEDURE myStoredProc101
( pSanitized VARCHAR(124)
)
BEGIN
set #mySql:=concat("SELECT DISTINCT ID FROM user where match(name) against ('* *",pSanitized,"* *')");
PREPARE stmt1 FROM #mySql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END
$$
DELIMITER ;
Your stored proc had no chance of working as it wasn't even using your parameter. What you did was bury something inside of a string literal. Also, varchar(124) is a bit odd :p
About the only success people have with prepared statements is with using a User Variable (with an #) versus failed attempts of using Local Variables (from DECLARE). So, that may save you a few hours of head banging in the future.
From the PHP Manual Page Stored Procedures:
Handling result sets
Stored procedures can return result sets. Result sets returned from a
stored procedure cannot be fetched correctly using mysqli_query. The
mysqli_query function combines statement execution and fetching the
first result set into a buffered result set, if any. However, there
are additional stored procedure result sets hidden from the user which
cause mysqli_query to fail returning the user expected result sets.
Result sets returned from a stored procedure are fetched using
mysqli_real_query or mysqli_multi_query. Both functions allow fetching
any number of result sets returned by a statement, such as CALL.
Failing to fetch all result sets returned by a stored procedure causes
an error.
As for calling the stored proc from mysqli, please take a look at the Answer from Pablo Tobar. It does not look especially pleasant with many variables, but that seems to be where it is at. Spoiler Alert: use mysql variables, not PHP variables.
Granted, Pablo was not returning a resultset, but rather writing to an OUT var in the stored proc. Perhaps you need to do what he did for the IN parameters, and call multi_query(), then a store_result(), then a fetch_all() (in short, the PHP reference a page up).
Alternatively, a call would be made as done by Palladium here.
In either case, case must be taken to avoid the known vulnerability of passing SQL Injection over to stored procedure routines.

Calling stored procedure with Out parameter using PDO

I've been using PDO for awhile now and am refactoring a project so that it uses stored procs instead of inline SQL. I am getting an error that I can't explain.I am using PHP version 5.3.5 and MySQL version 5.0.7.
I'm just trying to get a basic stored proc with an output to work. Here is the stored proc:
DELIMITER //
CREATE PROCEDURE `proc_OUT` (OUT var1 VARCHAR(100))
BEGIN
SET var1 = 'This is a test';
END //
Here is the code I am using to call the proc, $db is an instance of PDO:
$stmt = $db->prepare("CALL proc_OUT(?)");
$stmt->bindParam(1, $return_value, PDO::PARAM_STR, 4000);
// call the stored procedure
$stmt->execute();
echo $returnvalue;
Simple right? However, it results in the following error:
exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1414 OUT or INOUT argument 1 for routine mydb.proc_OUT is not a variable or NEW pseudo-variable in BEFORE trigger
If I call the proc directly like so:
CALL proc_OUT(#res);
SELECT #res;
it works as expected which leads me to believe that there is a problem with how it is being called with PHP, however I can't seem to find what the issue is. I am following the instructions in the manual but am still getting this error. Could anyone suggest what I could be doing wrong? Any advice would be very much appreciated. Thanks much!
It would seem that there is a bug at work here, best solution I've found is this:
http://www.php.net/manual/en/pdo.prepared-statements.php#101993
From the comment at the link above:
$dbh->query("CALL SomeStoredProcedure($someInParameter1, $someInParameter2, #someOutParameter)");
$dbh->query("SELECT #someOutParameter");
// OR, if you want very much to use PDO.Prepare(),
// insert "SELECT #someOutParameter" in your stored procedure and then use:
$stmt = $dbh->prepare("CALL SomeStoredProcedure(?, ?)");
$stmt ->execute(array($someInParameter1, $someInParameter2));
See also this: https://stackoverflow.com/a/4502524/815386
Got it! Just add a
SELECT #outputparam;
at the end of the stored procedure, where #outputparam is the name used for the param in the stored procedure definition. If you cannot edit the stored procedure, you should do a second query, for SELECT #outputparam, with PHP PDO to get the output param value.
Tip: If you're using the deprecated DBLib to connect to SQL Server and you modified the stored procedure as suggested, you'll also need to tweak your syntax to get the output param value in the calling PHP script:
$out = 0;
$sth = $db->prepare("DECLARE #myout INT; EXECUTE mysp :firstparam, :secondparam, #myout OUTPUT;"); // the DECLARE trick is needed with DBLib
$sth->bindParam(':firstparam', $firstparam, PDO::PARAM_INT);
$sth->execute();
$sth->bindColumn(1, $out, PDO::PARAM_INT);
$sth->fetch(PDO::FETCH_BOUND);
var_dump($out); // works
You need to specify that your parameter is IN/OUT style like PHP web site example :
http://php.net/manual/en/pdo.prepared-statements.php example #5
<?php
$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)");
$value = 'hello';
$stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
// call the stored procedure
$stmt->execute();
print "procedure returned $value\n";

MySQL stored procedure caused `Commands out of sync`

Call procedure works all right in MySQL terminal, but in PHP, caused Commands out of sync; you can't run this command nowCommands out of sync; you can't run this command now
My procedure is
delimiter $$
create procedure getMostSimilar (IN vU_ID INT, IN voffset INT, IN vsize INT)
BEGIN
set #offset = voffset;
set #size = vsize;
set #uid = vU_ID;
prepare SimilarStmt from
"SELECT U_ID, getSimilarity(U_ID, ?) AS similar FROM Answer WHERE U_ID != ? GROUP BY U_ID ORDER BY similar DESC LIMIT ?, ?";
execute SimilarStmt using #uid, #uid, #offset, #size;
deallocate prepare SimilarStmt;
END
$$
where getSimilarity is a function.
In PHP:
function getMostSimilar($U_ID, $offset, $size){
$query = sprintf("CALL getMostSimilar(%s, %s, %s)",
$U_ID, $offset, $size);
$result = mysql_query($query);
print mysql_error();
if (!$result){
return $query;
}
$ans = array();
$len = 0;
while($row = mysql_fetch_assoc($result)){
$ans[$len] = $row;
$len++;
}
return $ans;
}
What should I do now? Thanks!
There seems to be a nasty bug (or feature) that is manifested when calling a stored procedure that returns a result set.. I.e. a stored procedure that ends with a select statement without an INTO clause (see example below).
The mysqli driver (probably) returns 2 result sets. The first is the one returned from the stored procedure and the second a dummy, empty result set. It is like a multiple query command was issued. One solution to this (that does not break on usual (e.g. SELECT) queries), is to consume this dummy result set after processing the legit one (the first).
Example PHP code
function do_query($con, $sql)
{
$result = mysqli_query($con, $sql);
if ($result === true) {
return true;
}
while ($row = mysqli_fetch_assoc($result)) {
// process rows
}
// Hack for procedures returning second dummy result set
while (mysqli_more_results($con)) {
mysqli_next_result($con);
// echo "* DUMMY RS \n";
}
}
Example stored procedure:
CREATE PROCEDURE selectStaleHeaders()
NOT DETERMINISTIC
SELECT TT.*
FROM one_pretty_table AS TT
LEFT JOIN another AS AN on TT.fk_id = AN.id
WHERE TT.id IS NULL;
C.5.2.14. Commands out of sync If you
get Commands out of sync; you can't
run this command now in your client
code, you are calling client functions
in the wrong order.
This can happen, for example, if you
are using mysql_use_result() and try
to execute a new query before you have
called mysql_free_result(). It can
also happen if you try to execute two
queries that return data without
calling mysql_use_result() or
mysql_store_result() in between.
http://dev.mysql.com/doc/refman/5.0/en/commands-out-of-sync.html
I think you need to rewrite the getMostSimilar stored procedure, instead of using prepare and execute (which I thinks is fooling mysql) if you use the parameters in the procedure like in this example I think your error will be fixed.
This "bug" was happening to me with extremely simple procedure even inside phpmyadmin. The reason was that I was declaring general variables (without the # prefix). I changed my variables to user-defined variables prefixed with # and it was solved.
i know it is late and there is already an answer, but i was getting the same message for a whole different reason, so i will leave my solution here:
it was actually a very silly error. It was just a typo in a parameter name.
My function had a parameter named preferable:
create function call_client (pereferable int, client_id int) returns int
in the function body, i was using the parameter preferable with the wrong name:
if prefered = 1 then
...
end if;
once i changed prefered for preferable it started working again.

Categories