PHP - Can't get a MySQL stored procedure OUT parameter - php

I'm trying to get a value from a string declared inside a stored procedure, so that I can use it in my PHP-code.
The procedure:
DELIMITER //
CREATE PROCEDURE InsertName
(IN NewName varchar(50), OUT NewID char(25))
BEGIN
DECLARE NewID CHAR(25) DEFAULT (SELECT CONCAT((SELECT unix_timestamp()), (SELECT FLOOR(RAND() * 1000000000000000))));
INSERT INTO User
VALUES (NewID, NewName);
END;
//
DELIMITER ;
The PHP-code that calls the procedure:
$name = $_POST['name'];
$sth = $pdo->prepare("CALL InsertName('$name', #idcode)");
$sth->execute();
$sth->closeCursor();
$sth = $pdo->query("SELECT #idcode AS idcode");
$newid = $sth->fetch(PDO::FETCH_ASSOC);
$idcode=$newid['idcode'];
echo $idcode . " blarg";
The page I'm trying to display only shows " blarg", and nothing else. Something is wrong when fetching the OUT parameter. I have also tried with
SELECT NewID;
in the procedure, it still gives the same result.

Related

PHP parameter binding for mysql insert if not exist

I am reading and referencing different posts on how to insert if not exists. This is a good thread but I need some additional help.
I need to add a new record with about 10 columns in it, but only insert it if two columns don't match. I also need to do parameter binding on it.
$query = "INSERT INTO Customers (col1,col2,col3,col4,col5,col6)
SELECT * FROM (SELECT ?,?,?,?,?,?) AS tmp
WHERE NOT EXISTS (
SELECT col1 from Customers WHERE uid=? AND pid=?
) LIMIT 1;"
$results = dbQuery($query,(array($val1,$val2,$val3,$val4,$val5,$val6,$uid,$pid)) ;
What am I doing wrong here?
And here is dbQuery call:
function dbQuery($query,$data) {
global $pdo ;
$stmt = $pdo->prepare($query);
$result = $stmt->execute($data) ;
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$error = $stmt->errorInfo() ;
if ($result == false) {
var_dump($error) ;
}
return array($result,$error,$rows) ;
}
"?" parameter placeholders aren't named, they're passed by position, so you can't use them more than once.
You could pass uid/pwd twice; but better is to used named parameters passed as a map
(https://www.php.net/manual/en/pdostatement.execute.php)
$query = "INSERT INTO Customers (uid,pid,col3,col4,col5,col6)
SELECT * FROM (SELECT :uid, :pid, :val3, :val4, :val5, :val6) AS tmp
WHERE NOT EXISTS (
SELECT * from Customers WHERE uid = :uid AND pid = :pid
)";
$results = dbQuery($query,(array("uid"=>$uid, "pid"=>$pid, "val3"=>$val3, "val4"=>$val4, "val5"=>$val5, "val6"->$val6)));
Note using the parameter array means all the parameters come through as strings, which may not be what you want. You would have to use bindParam to set specifically typed values.
Best of all would be a stored procedure that allows you to define parameters, use proper binding, and prevent any accidental datatype mismatches.

How to return value from stored procedure using PDO on MSSQL database

I have a stored procedure that when called updates few tables and eventually returns an integer value.
When I call this stored procedure using SQL Pro tool, I get back a result as expected. The SQL that is auto-generated by the tool is this;
DECLARE #return_value int
EXEC #return_value =
dbo.GetNextReference
#c_tableName = 'prp',
#c_offYear = 'rcs14'
SELECT
'Return Value' = #return_value
However, I can't seem to get the same results or any results when I try to execute this using PHP PDO driver.
This is my code so far;
$conn = $this->getPDO();
$sql = "CALL GetNextReference (? , ?)";
$stmt = $conn->prepare($sql);
$tbl = 'prp';
$year = "rcs14";
$stmt->execute([$tbl, $year]);
$results = $stmt->fetchAll();
The statement executes without any errors but the results come back as an empty array.
What am I missing?
Sorry, I can't post the actual stored procedure as I am not permitted.
If I understand your question correctly and if you want to check the result of stored procedure execution, you may try with this:
<?php
# Connection
$server = 'server\instance,port';
$database = 'database';
$uid = 'user';
$pwd = 'password';
# Statement
try {
$conn = new PDO("sqlsrv:server=$server;Database=$database", $uid, $pwd);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch( PDOException $e ) {
die( "Error connecting to SQL Server" );
}
try {
$sql = "{? = call GetNextReference (? , ?)}";
# This should work also.
#$sql = "exec ? = GetNextReference (? , ?)";
$spresult = 0;
$tbl = 'prp';
$year = "rcs14";
$stmt = $conn->prepare($sql);
$stmt->bindParam(1, $spresult, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
$stmt->bindParam(2, $tbl);
$stmt->bindParam(3, $year);
$stmt->execute();
# Next line for single resultset
#$results = $stmt->fetchAll();
# Multiple resultsets
do {
$results = $stmt->fetchAll();
print_r($results, true);
} while ($stmt->nextRowset());
} catch( PDOException $e ) {
die( "Error connecting to SQL Server" );
}
$stmt = null;
$conn = null;
echo 'Stored procedure return value : '.$spresult."</br>";
?>
Op has asked for an example of an OUTPUT parameter. it doesn't specifically answer their question, however, is far too long for a comment:
USE Sandbox;
GO
--Sample Table
CREATE TABLE dbo.TestTable (ID int IDENTITY(1,1),
SomeString varchar(20));
GO
--Sample proc
CREATE PROC dbo.TestSP #SomeString varchar(20), #ID int OUTPUT AS
--You cannot OUTPUT from an INSERT into a scalar variable, so we need a table variable
DECLARE #IDt table(ID int);
INSERT INTO dbo.TestTable (SomeString)
OUTPUT inserted.ID
INTO #IDt
SELECT #SomeString;
--Now set the scalar OUTPUT parameter to the value in the table variable
SET #ID = (SELECT ID FROM #IDt); --this works, as the SP is designed for only one row insertion
GO
DECLARE #SomeString varchar(20) = 'abc', #ID int;
EXEC dbo.TestSP #SomeString = #SomeString,
#ID = #ID OUTPUT; --ID now has the value of the IDENTITY column
--We can check here:
SELECT #ID AS OutputID;
SELECT *
FROM dbo.TestTable;
GO
--Clean up
DROP PROC dbo.TestSP;
DROP TABLE dbo.TestTable;
GO

MySQL Stored Procedure using Input Variable in With Clause

Slowly working through change MSSQL database to MySQL and the final problem is as follows :-
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_GetUserInRole`(
IN $EMail nvarchar(256),
IN $RoleName nvarchar(256),
INOUT $ReturnStatus bit)
BEGIN
DECLARE $UserId char(38);
DECLARE $RoleId char(38);
DECLARE $this_count INT;
SET $UserId = (SELECT UserId FROM server.`user_Data` WHERE EMail = $EMail);
SET $RoleId = (SELECT RoleId FROM server.`user_Roles` WHERE RoleName = $RoleName);
SET $this_count = (SELECT COUNT(*) FROM user_UsersInRoles WHERE UserId = $UserId AND RoleId = $RoleId);
IF ($this_count > 0) THEN
SET $ReturnStatus = 1;
ELSE
SET $ReturnStatus = 0;
END IF;
END
Always returns 0, even when I know the count is equal to 1. I believe that there is an issue with the input variables not being recognized properly by the Where clause in both Select UserId and Select RoleId statements, but I can't see what I'm doing differently to the various helps I've found.
Any help, as always, is greatly appreciated.
EDIT
I have since tried running a simple insert command
CREATE DEFINER=`root`#`localhost` PROCEDURE `test`()
BEGIN
INSERT INTO server.`test_table`
VALUES('test', 'test2');
END
And this hasn't worked either. This leads me to believe that it's actually more of a problem with the PDO call.
$command = "EXEC test";
$stpro = $conn->prepare($command);
$returnvalue = $stpro->execute();
I know that $conn works as straight sql calls in my php definitely work.
EDIT 2
So, turns out EXEC should have been CALL. This now works with the test. As soon as I put any parameters into it though it stops working. Any thoughts?
No need of $Email. Make your parameter declaration like
IN email nvarchar(256)
Change your select inside procedure as below
FROM
SET $UserId = (SELECT UserId FROM server.`user_Data` WHERE EMail = $EMail);
TO
SET #UserId := (SELECT UserId FROM server.`user_Data` WHERE EMail = email);
with that, your procedure should look like
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_GetUserInRole`(
IN email nvarchar(256),
IN rolename nvarchar(256),
INOUT retstatus bit)
BEGIN
SET #UserId := (SELECT UserId FROM server.`user_Data` WHERE EMail = email);
SET #RoleId := (SELECT RoleId FROM server.`user_Roles`
WHERE RoleName = rolename);
SET #this_count := (SELECT COUNT(*) FROM user_UsersInRoles
WHERE UserId = #UserId AND RoleId = #RoleId);
IF (#this_count > 0) THEN
SET retstatus = 1;
ELSE
SET retstatus = 0;
END IF;
END$$
DELIMITER ;
Okay, so at this this where I am at the moment #meda. Thought I'd post as a separate "answer" to avoid any confusion.
The current Procedure is :-
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_GetUserInRole`(
IN email nvarchar(256),
IN rolename nvarchar(256),
OUT returnstatus int)
BEGIN
SELECT #UserId := UserId FROM server.`user_data` WHERE EMail = email;
SELECT #RoleId := RoleId FROM server.`user_roles` WHERE RoleName = rolename;
SELECT #this_count := COUNT(*) FROM server.`user_usersinroles` WHERE UserId = #UserId AND RoleId = #RoleId;
IF (#this_count > 0) THEN
SET #returnstatus = 1;
ELSE
SET #returnstatus = 0;
END IF;
END
This is launched by the php :-
$command = "CALL user_GetUserInRole (?, ?, ?)";
$role = "Admin";
$stpro = $conn->prepare($command);
$stpro->bindParam(1, $_SESSION['vaild_user']);
$stpro->bindParam(2, $role);
$stpro->bindParam(3, $returnvalue, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT);
$success = $stpro->execute();
if($success)
{
$returnvalue = $stpro->fetch(PDO::FETCH_ASSOC);
}
If I remove the OUT returnstatus int from the Procedure and the third parameter from the php it runs perfectly and if I add an Insert command for debug purposes in the procedure I am getting the correct answers from the queries.
However, as soon as the OUT is returned to the statement the whole thing falls over and $success = $stpro->execute(); returns "false" every time.
Okay, so in case anyone else runs into this problem, I finally have the answer. The problem is that PDO does not allow you to bind parameters to OUT variables properly. As a result you need to run two queries in order for the CALL to run properly. This allows the variable to be read though the $output arrange and away you go.
$role = "Admin";
$command = "CALL user_GetUserInRole ('" . $_SESSION['vaild_user'] . "', '" . $role . "', #returnvalue)";
$conn->query($command);
$output = $conn->query("SELECT #returnvalue")->fetch(PDO::FETCH_ASSOC);
if ($output["#returnvalue"] == 1)
{
$admin = true;
}
PDOStatement::execute will return a boolean value based on success or failure of the stored procedure call.
So $returnvalue is not really what you think, try this instead:
$command = "EXEC test ?";
$param = "123";
$stpro = $conn->prepare($command);
$stpro ->bindParam(1, $param, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 12);
$success = $stpro->execute();
if($success){
$returnvalue = $stpro->fetch(PDO::FETCH_ASSOC);
var_dump($returnvalue);
}else{
echo "execute() failed!";
}

PHP, PDO Stored Procedure return nothing or unchanged value

i am using php with mysql.
My stored proc returns values via out parameter via Toad4MySQL but when it comes to php Pdo, it does not capture the return value.
here's my code
$validusername= 'x';
$validemail = 'x';
$validkey = 'x';
$query = $pdo->prepare("Call InsertNewUser(:fullname, :usernamex, :email, :password, :con_password, #:validusername, #:validemail, #:validkey); ");
$query->bindParam(':fullname', $fullname , PDO::PARAM_STR);
$query->bindParam(':usernamex', $usernamex , PDO::PARAM_STR);
$query->bindParam(':email', $email, PDO::PARAM_STR);
$query->bindParam(':password', $password, PDO::PARAM_STR);
$query->bindParam(':con_password', $con_password, PDO::PARAM_STR);
$query->bindParam(':validusername', $validusername, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT);
$query->bindParam(':validemail', $validemail , PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT);
$query->bindParam(':validkey', $validkey , PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT);
$results = $query-> execute();
echo $validemail ; # returns x but not the value that i wanted.
Can anyone help me with this puzzle ?
My Stored Procedure code:
DROP PROCEDURE IF EXISTS InsertNewUser;
CREATE PROCEDURE xxx.`InsertNewUser`(
fullname varchar(255),
username varchar(255),
email_address varchar(255),
password varchar(255),
con_password varchar(255),
out validusername varchar(10),
out validemail varchar(10),
out validkey varchar(155)
)
BEGIN
declare xcnt_1 int;
declare xcnt_2 int;
declare xkey varchar(155);
set xkey = MD5(RAND());
select count(*) into xcnt_1 from user where user.email_address = email_address;
select count(*) into xcnt_2 from user where user.username = username;
if xcnt_1 > 0 then
set validemail = 'false';
else
set validemail = 'true';
end if;
if xcnt_2 > 0 then
set validusername = 'false';
else
set validusername = 'true';
end if;
if xcnt_1 = 0 and xcnt_2 = 0 then
set validkey = xkey;
INSERT INTO user
(user.fullname, user.username, user.email_address, user.password, user.con_password, user.md5validate)
VALUES
(fullname, username, email_address, password, con_password, xkey);
else
set validkey = 'false';
end if;
commit;
END;
ah... finally solved this problem :
for some unknown reasons, i have to change the call statement to :
Call InsertNewUser(:fullname, :usernamex, :email, :password, :con_password, #validusername_global, #validemail_global, #validkey_global);
and use this line of codes to get the data...
$SQL = ' select #validusername_global as validusername, #validemail_global as validemail, #validkey_global as validkey';
$query = $pdo->query($SQL);
while($row = $query->fetch(PDO::FETCH_ASSOC))
{
print_r($row);
}
Executing the PDO prepared statement won't have any effect on $validemail.
After $results = $query->execute();, try adding print_r($query->fetchAll()); to get a better sense of what is being returned from MySQL to PHP when your PHP statement runs the MySQL stored procedure.
Keep in mind that $results is just going to be a boolean indicating whether or not the statement could be executed.
From the PDO manual:
int $length
Length of the data type.
To indicate that a parameter is an OUT
parameter from a stored procedure, you
must explicitly set the length.
http://php.net/manual/en/pdostatement.bindparam.php
You have omitted this parameter, which would explain why it is not being used as an OUT parameter.
if you set $validemail=x and dont manipulate/change nothing with $validemail in later code then obvioulsy echo $validemail will returns x.? whats' wrong with that? either u missed to paste some code
Try wrapping your prepare statement, bind parameters and execute in try, catch blocks and see if PDO spits out any sort of error.

Calling stored procedure in a prepared statement multiple times

I'm using PHP to process some XML that I'm getting from a web service and to insert the results into a database. I've created a stored procedure to insert the records and I'm using PDO to prepare a statement that I'd then like to execute multiple times, something like this:
$st = $this->db->prepare('CALL AddClient(:Name, :Id, :Priority, :Territory, :DateCreated, :Result)');
$st->bindParam(':Name', $Name, PDO::PARAM_STR);
$st->bindParam(':Id', $Id, PDO::PARAM_INT);
$st->bindParam(':Priority', $Priority, PDO::PARAM_STR);
$st->bindParam(':Territory', $Territory, PDO::PARAM_STR);
$st->bindParam(':DateCreated', $dateFormat, PDO::PARAM_STR);
$st->bindParam(':Result', $result, PDO::PARAM_STR);
foreach ($this->xml->AccountsDataSet->MainRecord as $Account) {
$Name = (string) $Account->AcctDesc;
$Id = (int) $Account->AcctID;
if ($num = (int)$Account->CDF309607) {
$Priority = self::$priorityFields[$num];
} else {
$Priority = '';
}
if (! $Territory = (string) $Account->AcctState) {
$Territory = '';
}
$date = new DateTime($Account->AcctCreationDate, new DateTimeZone('UTC'));
$dateFormat = $date->format('Y-m-d H:i:s');
$st->execute();
echo $result . '<br />';
}
In this example ':Result' would be a output value from the procedure. I cannot make this work at all though. If I omit the out put parameter both in the procedure code and PHP then one row gets inserted but subsequent calls in the loop fail. To make this work I have to prepare the statement fresh each time in the loop, then binding the parameters to the statement again. I still cannot make this work with an output parameter in the procedure and have to Select a value in the procedure to get some return value to PHP to process.
Does anyone know where I'm going wrong with this? Ideally I'd like to prepare the statement once and then loop through the results, executing it each time with fresh data. Are there any tricks that I'm missing to make all of this work? I'm developing on Windows 7 with Apache 2.2.17, PHP 5.3.5 and MySQL 5.5.8.
EDIT:
Well, the answer to executing the statement multiple times in the loop seems to be to call the PDOStatement::closeCursor() method each time after calling PDOStatement::execute(). Still no idea on how to get the value of an output parameter returned to PHP though.
EDIT
Here's the code for the procedure:
CREATE
PROCEDURE foo.AddClient(IN ClientName VARCHAR(255), IN Client_Id INT UNSIGNED, IN Priority VARCHAR(255), IN Territory VARCHAR(100), IN DateCreated DATETIME, OUT Result VARCHAR(100))
COMMENT 'Procedure to add a new client to the database'
BEGIN
#Declare variables.
DECLARE Priority_Id, Territory_Id TINYINT UNSIGNED DEFAULT NULL;
# Check if a Priority is defined. If so get the id, if not add it and get the id
IF LENGTH(Priority) > 0 THEN
SELECT
PriorityId
INTO
Priority_Id
FROM
client_priority
WHERE
Name = Priority;
IF Priority_Id IS NULL THEN
INSERT INTO client_priority (Name) VALUES (Priority);
SET Priority_Id = LAST_INSERT_ID();
END IF;
END IF;
#Check if a Territory is defined. If so get the id, if not add it and get the id.
IF LENGTH(Territory) > 0 THEN
SELECT
TerritoryId
INTO
Territory_Id
FROM
territories
WHERE
Name = Territory;
IF Territory_Id IS NULL THEN
INSERT INTO territories (Name) VALUES (Territory);
SET Territory_Id = LAST_INSERT_ID();
END IF;
END IF;
#Add the details of the client.
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '23000'
SET Result = 'Client already exists'; # Error handler in case client with the same name already exists.
INSERT INTO clients
(ClientId, Name, PriorityId, TerritoryId, DateCreatedSalesNet) VALUES (Client_Id, ClientName, Priority_Id, Territory_Id, DateCreated);
SET Result = 'Client Inserted';
END;
END
The fact is you can't access you result like this, see these posts :
http://bugs.mysql.com/bug.php?id=25970
http://dev.mysql.com/doc/refman/5.0/en/call.html
So you will need to store the result into a MySQL var, then you can display it, like this :
<?php
// change
$st = $this->db->prepare('CALL AddClient(:Name, :Id, :Priority, :Territory, :DateCreated, :Result)');
// to
$st = $this->db->prepare('CALL AddClient(:Name, :Id, :Priority, :Territory, :DateCreated, #result)');
// remove this line
$st->bindParam(':Result', $result, PDO::PARAM_STR);
// change
$st->execute();
echo $result . '<br />';
// to
$st->execute();
echo $this->db->query('SELECT #result')->fetchColumn() . '<br />';
Try adding a string length to the output parameter:
$st->bindParam(':Result', $result, PDO::PARAM_STR, 4000);
Try to add PDO::PARAM_INPUT_OUTPUT:
$st->bindParam(':Result', $result, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);

Categories