Incomplete execution of stored procedure from PHP - php

Hi I have a stored procedure with a while inside which receives 2 parameters for the condition of the while. I make it to work like a for. If I executed it from SQL it works perfectly, but If I call it from PHP, the while doesn't respect the condition and suddenly ends.
The while insert an iterator number in each row like
1,a,b
2,a,b
3,a,b
etc. If I set the iterations from 1 to 1000 the loop stop without a reason in different interaction in every execution of the SP.
I have a try/catch and I can't detect the error.
I'm sure all the parameters are right.
Can someone help me to detect why the stored procedure can't end correctly?
I'm using PDO to access to SQL Server.
Store Procedure:
ALTER procedure [dbo].[sp_AddSealsFromPurchase]
#initialFolio int,
#finalFolio int,
#userDelivery_ID varchar(50),
#userReceives_ID varchar(50),
#color nvarchar(10),
#supplier_ID int,
#requisition_ID int,
#office_ID int,
#reference nvarchar(50)
as
begin
declare #now datetime;
set #now = getdate();
while #initialFolio<=#finalFolio Begin
begin try
INSERT into Seals (SealNo,SealType,Color,Supplier_ID,[Source],Office_ID,Requisition_ID,Reference)
Values (#initialFolio,1,#color,#supplier_ID,1,#office_ID,#requisition_ID,#reference)
INSERT INTO StatusSeal(Seal_ID,Status_ID,UserDelivery_ID,UserReceives_ID,StatusSeal_Date,UserDeliveryType,UserReceivesType)
Values(##Identity,1,#userDelivery_ID,#userReceives_ID,#now,1,1)
set #initialFolio = #initialFolio +1;
end try
begin catch
set #initialFolio = #initialFolio +1;
end catch
end
end
PHP:
function AddSealsFromPurchase($Data){
session_start();
$cnx=$this->connectSqlSrv();
$sth=$cnx->prepare("Exec sp_AddSealsFromPurchase ?,?,?,?,?,?,?,?,?");
$sth->bindParam(1, $Data["InitialFolio"]);
$sth->bindParam(2, $Data["FinalFolio"]);
$sth->bindParam(3, $Data["UserDelivery"]);
$sth->bindParam(4, $_SESSION['SESSINFOSEAL']['User_Usr']);
$sth->bindParam(5, $Data["Color"]);
$sth->bindParam(6, $Data["Supplier_ID"]);
$sth->bindParam(7, $Data["Requisition_ID"]);
$sth->bindParam(8, $_SESSION['SESSINFOSEAL']['Ofice_ID']);
$sth->bindParam(9, $Data["Reference"]);
session_write_close();
$retval=$this->ExecuteNoQuery($sth);
return json_encode($retval);
}
public function connectSqlSrv(){
try{
$dbCnx = new PDO("sqlsrv:Server=$this->server;Database=$this->db", $this->usr, $this->psw);
$dbCnx->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbCnx;
}
catch(PDOException $e){
echo $e;
die();
return null;
}
}
public function ExecuteNoQuery($sth)
{
$r=array('data'=>false,
'error'=>false,
'r'=>'');
try {
$sth->execute();
if ($r['r'] = $sth->rowCount()) {
$r['data'] = true;
}
} catch (PDOException $e) {
$r['error']=true;
$r['r'] = $e->getMessage();
}
return $r;
}

Related

MySQL or PHP keeps returning a null [duplicate]

This question already has an answer here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
Closed 3 years ago.
I am trying to execute a query through parameters using mysqli. It works normally for other procedures, but for this specific procedure, it keeps returning a null.
I cross-referenced every single detail I could find, but for some strange reason, it does not like what it receives.
I tested the query using Workbench and it seems to work without an issue, but it does not execute via php for this brain scratcher.
The Stored Procedure that does the insert and it receives 10 parameters.
PROCEDURE `usp_Advert`(IN `advertID` VARCHAR(50),
IN `sellerID` VARCHAR(100),
IN `make` VARCHAR(30),
IN `model` VARCHAR(30),
IN `description` varchar(100),
IN `mileage` int(11),
IN `color` VARCHAR(100),
IN `price` int(8),
IN `cityID` int(11),
IN `year` int(4))
BEGIN
Declare errorNo int;
Declare Exit Handler for sqlexception
Begin
Get current diagnostics condition 1 errorNo = Mysql_errno;
select errorNo as MYSQL_ERROR;
Rollback;
end;
start transaction;
set autocommit=0;
set #now := curdate();
if((select count(a.advertID) from `vas`.advert as a where a.advertID=`advertID`)>=1)then
select true as `tryagain`;
else
insert into vas.advert
(`advertID`,
`sellerID`,
`vehicleMake`,
`vehicleModel`,
`advertDescription`,
`vehicleMileage`,
`vehicleColor`,
`sellingPrice`,
`cityID`,
`advertDate`,
`advertExpireDate`,
`vehicleYear`)
VALUES
(`advertID`,
`sellerID`,
`make`,
`model`,
`description`,
`mileage`,
`color`,
`price`,
`cityID`,
#now,
null,
`year`);
END IF;
IF (row_count()>0) then
select true;
end if;
COMMIT WORK;
end
The Database connection
namespace DAL;
class DB
{
// Local setup
private static $dbHost = '127.0.0.1';
private static $dbUser = 'username';
private static $dbPass = 'password';
private static $dbName = 'db';
protected static function Connect()
{
return mysqli_connect(self::$dbHost, self::$dbUser, self::$dbPass, self::$dbName);
}
}
The Helper that does a selection with parameters.
<?php
namespace DAL;
require_once '../DAL/DBHelper.php';
class DBHandler
{
public static function SelectParam($query, array $param)
{
//Variables
$arrayType = null;
//Format Calculation Loop
foreach ($param as $element) {
if (is_string($element)) {
$arrayType .= 's';
} else if (is_int($element)) {
$arrayType .= 'i';
} else if (is_double($element)) {
$arrayType .= 'd';
}
else if(is_bool($element))
{
$arrayType .='b';
}
}
//Open Connection
$conn = DB::Connect();
//Prepare Query
//Somewhere here it breaks, but I cannot find the answer.
$call = mysqli_prepare($conn, $query);
//Bind Query
call_user_func_array(array($call, 'bind_param'), array_merge(array($arrayType),$param));
//Execute Query
mysqli_stmt_execute($call);
//Fetch Result
$result = mysqli_stmt_get_result($call);
//Fill Row
$row = mysqli_fetch_all($result, MYSQLI_ASSOC);
//Close Connection
mysqli_close($conn);
//Return Result
return $row;
}
}
The Handler that parses the query and parameters to the Helper.
<?php
namespace DAL;
require_once '../DAL/DBHelper.php';
class DBHandler
{
public static function Advertise($advertID,$sellerID,$vehicleMake,$vehicleModel,$description,$price,$mileage,$color,$cityID, $vehicleYear)
{
$sp='CALL usp_Advert(?,?,?,?,?,?,?,?,?,?)';
$param = array(&$advertID,&$sellerID,&$vehicleMake,&$vehicleModel,&$description,&$price,&$mileage,&$color,&$cityID,&$vehicleYear);
return DBHelper::SelectParam($sp,$param);
}
}
The Business logic layer that actually sends the values through...
<?php
require_once '../BLL/CORS_Headers.php';
require_once '../DAL/DBHandler.php';
use DAL\DBHandler;
//$json=json_decode(file_get_contents('php://input'));
$json_encode(DBHandler::Advertise('dsknfdsnnfd','email#address.com','Toyota','Regius','it is cool',10000,10000,'Blue',1,2006));
?>
The values does make their way to the DBHelper, but it keeps returning a null when it is trying to bind the values to the parameters.
I tried altering tiny bits of code, but the result is still the same. All the other calls work, but this one decides not to.
Figured it out
My referencing was just just swapped in my BLL
Instead of...
json_encode(DBHandler::Advertise('dsknfdsnnfd','email#address.com','Toyota','Regius','it is cool',10000,10000,'Blue',1,2006));
It should have been...
json_encode(DBHandler::Advertise('dsknfdsnnfd','email#address.com','Toyota','Regius','WORK',10000,'Blue',10000,1,1998));

Prepared PDO MySQL statement commits but changes do not stick

I am having a problem where a prepared MySQL stored procedure call runs fine in a transaction, and I see the expected results from the stored procedure, but the changes do not appear to be saving to the actual database.
The PHP side of things looks like this:
$options = array();
$db = new PDO("mysql:host=localhost;dbname=mydb", "myuser", "mypass", $options);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// ..... .... ... .. .
$response["error"] = true;
if ($db->beginTransaction() == true)
{
try
{
$stmt = $db->prepare("call Layout_Row_Add(:pageid, :position);");
// $jason->page_id
$stmt->bindValue(':pageid', (int)$jason->page_id, PDO::PARAM_INT);
// $jason->position
$stmt->bindValue(':position', (int)$jason->position, PDO::PARAM_INT);
$stmt->execute();
$response["dbg1"] = $jason->page_id;
$response["dbg2"] = $jason->position;
$response["intrans1"] = $db->inTransaction();
$row = $stmt->fetch();
$db->commit();
$response["intrans2"] = $db->inTransaction();
$response["new_row_id"] = $row["NewRowId"];
$response["error"] = false;
}
catch (PDOException $e)
{
$db->rollBack();
$response["errortext"] = "PDO exception: " . $e->getMessage();
}
catch (Exception $exc)
{
$db->rollBack();
$response["errortext"] = "Exception: " . $e->getMessage();
}
}
else
{
$response["errortext"] = "Couldn't start transaction";
}
The $response variable gets encoded into JSON and sent back to the browser, which gets this:
error false
dbg1 1
dbg2 3
intrans1 true
intrans2 false
new_row_id 21
Everything looks exactly like it should, new_row_id is at its expected value meaning the autoincrement field ticked up, and the debug fields and transaction info is as expected.
However, doing a select * in MySQL Workbench doesn't return any of these rows that were supposedly added by the procedure. Running the procedure itself in MySQL Workbench works fine, as in, the commit actually sticks. Here's the procedure itself:
CREATE DEFINER=`myuser`#`myhost` PROCEDURE `Layout_Row_Add`(PageId int, Position int)
BEGIN
declare NewRowId int unsigned default 0;
update pages_layout_rows set ordinal = ordinal + 1 where page_id = PageId and ordinal >= Position;
insert into pages_layout_rows (page_id, ordinal) values (PageId, Position);
set NewRowId = last_insert_id();
select NewRowId;
END
The table is set to InnoDB, so transaction support should be available. I don't really know what to try next.
Found it - it looks like if you don't consume all the resultsets, the transaction appears to get rolled back in the end anyway. A stored procedure call adds an empty resultset as the last resultset, so that's what's happening.
// ...
$row = $stmt->fetch();
// let's consume all resultsets
while($stmt->nextRowset() && $stmt->columnCount());
$sol->db->commit();
// ...

Transactions in php function or in mysql stored procedure?

What is better way to begin a transaction?
Inside procedures or PHP functions?
For example I calling MySQL procedure like this:
function sendLeaguesToDb(){
$leagues = "";
try{
$this->PDO->beginTransaction();
$stmt = $this->PDO->prepare("call insupd_Leagues(:id,:name,:country,:sport_id,:his_data,:fixtures,:livescore,
:numofmatches,:latestmatch)");
$leagues=$this->soccer->GetAllLeagues();
foreach($leagues as $key=>$value){
$stmt->bindParam(':id',$value->Id);
$stmt->bindParam(':name',$value->Name);
$stmt->bindParam(':country',$value->Country);
$stmt->bindParam(':sport_id',$value->Sport_Id);
$stmt->bindParam(':his_data',$value->Historical_Data);
$stmt->bindParam(':fixtures',$value->Fixtures);
$stmt->bindParam(':livescore',$value->Livescore);
$stmt->bindParam(':numofmatches',$value->NumberOfMatches);
$stmt->bindParam(':latestmatch',$value->LatestMatch);
$stmt->execute();
$this->PDO->commit();
}
}
catch(XMLSoccerException $e){
echo "XMLSoccerException: ".$e->getMessage();
}
catch(PDOException $e){
echo "PDOException: ".$e->getMessage();
$this->PDO->rollback();
}
}
Is this good way if I want to send/get data fastest possible every minute/hour?
It depends on what you're trying to achieve.
If you want to see all the inserts as an 'atomic operation' you are doing right, as if one call to the SP fails, the rollback will undo all the changes made from the previous calls
If, otherwise, you want to "isolate" every single SP call, assuring that if it succedes the results are stored in the DB, you have to start and end the transaction inside the SP
I think the preferred solution is the first
EDIT: one thing i'm noting now: the commit should be after the for :
try{
$this->PDO->beginTransaction();
$stmt = $this->PDO->prepare("call insupd_Leagues(:id,:name,:country,:sport_id,:his_data,:fixtures,:livescore,
:numofmatches,:latestmatch)");
$leagues=$this->soccer->GetAllLeagues();
foreach($leagues as $key=>$value){
$stmt->bindParam(':id',$value->Id);
$stmt->bindParam(':name',$value->Name);
$stmt->bindParam(':country',$value->Country);
$stmt->bindParam(':sport_id',$value->Sport_Id);
$stmt->bindParam(':his_data',$value->Historical_Data);
$stmt->bindParam(':fixtures',$value->Fixtures);
$stmt->bindParam(':livescore',$value->Livescore);
$stmt->bindParam(':numofmatches',$value->NumberOfMatches);
$stmt->bindParam(':latestmatch',$value->LatestMatch);
$stmt->execute();
}
//move your commit here
$this->PDO->commit();
}

pdo defining a function an then using it in a select statement

My query is somewhat like this :
CREATE FUNCTION func ... ;
SELECT * FROM ....;
I'm using php's PDO which doesn't allow more than 1 statements in a $pdo->query() , so instead I decided that I'd split the query into two, so I execute the CREATE FUNCTION query using $pdo->exec() and then use $pdo->query() on the select statement.
However, I get the error on execution of select statement that FUNCTION database.func does not exist. How do I solve this? when I run it in phpmyadmin as a single query it works fine
Edit : PHP code :
class MyClass {
function connectPDO($user,$pass,$chartset="utf8"){
//simple function for making a new PDO object and mysql connection
$dbname = "my_db";
try {
//creating new pdo
$pdo = new PDO('mysql:host=localhost;dbname='.$dbname, $user,$pass);
//attributes set to throw errors and exceptions which can be caught, can be changed
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//fetch associative arrays
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
//default chartset
$pdo->exec('SET NAMES "'.$chartset.'"');
} catch (PDOException $e) {
//on some error
$output = "Ay caramba! there's something wrong here!.<br>";
echo $output.$e->getMessage();
exit();
}
$this->db = $pdo;
}
}
$object = new MyClass();
$object->connectPDO("user","pass");
$sqlPreliminary = "
DROP FUNCTION IF EXISTS myFunc;
DELIMITER //
CREATE FUNCTION myFunc (id INT)
RETURNS DECIMAL
BEGIN
RETURN id + 1;
END //
DELIMITER ;
";
$sqlFinal = "
SELECT id, myFunc(id) AS plusOne FROM table;
";
$object->db->exec($sqlPreliminary);
var_dump($object->db->query($sqlFinal)->fetchAll(PDO::FETCH_ASSOC));
Create a user defined function using PDO simply works by exec, no delimiter is required. Here $db is PDO instance.
$db->exec('CREATE FUNCTION ConvertHTMLToText(str LONGTEXT CHARSET utf8)
RETURNS LONGTEXT CHARSET utf8
BEGIN
DECLARE start, end INT DEFAULT 1;
LOOP
SET start = LOCATE("<", str, start);
IF (!start) THEN RETURN str; END IF;
SET end = LOCATE(">", str, start);
IF (!end) THEN SET end = start; END IF;
SET str = TRIM(INSERT(str, start, end - start + 1, ""));
END LOOP;
END');
EDIT:
This is multiple statement. The first part is DROP FUNCTION MyFunc IF EXISTS and the second is CREATE FUNCTION.
This was my previous answer, which was correct, but OP changed the code:
You don't say what $object->db is:
You call connectPDO("user","pass"); and then use $object->db->exec($sqlPreliminary);. $object should be global in function body. At the moment it is not, so it is a local variable, which is lost after end of the function. This $object in rest of the code is something different.
I have faced the same problem. When I removed " DELIMITER // " (also //
DELIMITER ; at the end), it worked. I think DELIMITER is no longer required with PHP PDO.
$sqlPreliminary = "
DROP FUNCTION IF EXISTS myFunc;
CREATE FUNCTION myFunc (id INT)
RETURNS DECIMAL
BEGIN
RETURN id + 1;
END
";
I tested this and worked.

How to detect a rollback in MySQL stored procedure?

I'm trying to figure out a way to detect an occurrence of rollback in a MySQL stored procedure so I could handle the situation accordingly from a PHP script, but so far I can not find any solution.
My stored procedure looks like this:
delimiter |
create procedure multi_inserts(
IN var1 int(11),
.
.
.
IN string1 text
)
BEGIN
declare exit handler for sqlexception rollback;
declare exit handler for sqlwarning rollback;
START TRANSACTION;
insert into table1(a,b,c,d) values(var1,var2,var3,var4);
insert into table2(e,f,g) values(var5,var6,string1);
COMMIT;
END
delimiter ;
I did a rollback test on this procedure and it did rollback but I got no false.
I want my stored procedure to throw some kind of error message if the transaction failed, so I could handle it like this:
$result = mysql_query($procedure);
if(!$result)
{
//rollback occured do something
}
Is there a way to detect rollback in MySQL?
Am I missing something?
Any reply will be appreciated.
Thanks for reading.
Thanks to your advices I fixed this problem. Here's what I did:
Stored Procedure
delimiter |
create procedure multi_inserts(
IN var1 int(11),
.
.
.
IN string1 text
)
BEGIN
declare exit handler for sqlexception sqlwarning
BEGIN
rollback;
select -1;
END;
START TRANSACTION;
insert into table1(a,b,c,d) values(var1,var2,var3,var4);
insert into table2(e,f,g) values(var5,var6,string1);
COMMIT;
END
delimiter ;
If I use out variable instead of select -1, it gives me this error:
OUT or INOUT argument is not a
variable or NEW pseudo-variable in
BEFORE trigger
I don't know what did I wrong, but I couldn't fix this problem.
PHP script
$result=mysqli_query($con,$procedure);
if(is_object($result))
{
//rollback happened do something!
}
If the SP is successful it throws true.
You can add an output param and then set it to the value you want in your exit handlers.
Here's an example using your proc:
delimiter $$
create procedure multi_inserts(
IN var1 int(11),
.
.
.
IN string1 text,
OUT p_return_code tinyint unsigned
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;
START TRANSACTION;
insert into table1(a,b,c,d) values(var1,var2,var3,var4);
insert into table2(e,f,g) values(var5,var6,string1);
COMMIT;
-- SUCCESS
set p_return_code = 0;
END $$
delimiter ;
You would usually do this PHP-side if you wanted to catch errors. Read http://php.net/manual/en/pdo.transactions.php for more information.
Hey do one thing, use OUTPUT variable and return 1 or 0 as result form SP and do what ever you want on this flag.
<?php
try {
$user='root';
$pass='';
$dbh = new PDO('mysql:host=localhost;dbname=dbname', $user, $pass,
array(PDO::ATTR_PERSISTENT => true));
echo "Connected\n";
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();
}
catch (Exception $e) {
$dbh->rollBack();
echo "Failed: " . $e->getMessage();
}
?>

Categories