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

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.

Related

Incomplete execution of stored procedure from 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;
}

Upload image/longblob to mysql database using stored procedure,php and mysqli

I am attempting to upload a image to my database, i know it is bad practice to upload images to the database. I have it working by running the query in my php class (DB Handler). Now I am attempting to use a stored procedure and can't seem to find my error in my stored procedure. Apologies if my question seems vague.
DB Handler.php
public static function Business_Logo_Upload($image)
{
$sp = 'CALL uspBusines_Logo_Upload(?)';
return DBHelper::BlobUpload($sp,$image);
}
DBHelper.php
public static function BlobUpload($query,$image)
{
//Open Connection
$conn = DB::Connect();
//Prepare Query
$call = $conn->prepare($query);
$null = null;
$call->bind_param('b', $null);
$call->send_long_data(0, file_get_contents($image));
$call->execute();
//Close Connection
mysqli_close($conn);
//Return Result
return $call;
}
MYSQL Stored Procedure
CREATE DEFINER=`BIT1`#`%` PROCEDURE `uspBusines_Logo_Upload`(IN logo LONGBLOB)
BEGIN
DECLARE errno INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET CURRENT DIAGNOSTICS CONDITION 1 errno = MYSQL_ERRNO;
SELECT errno AS MYSQL_ERROR;
ROLLBACK;
END;
START TRANSACTION;
SET autocommit=0;
UPDATE business
SET business.Logo = logo
WHERE business.BusinessID = 1;
END
CREATE DEFINER=`BIT1`#`%` PROCEDURE `uspBusines_Logo_Upload`(IN logo LONGBLOB)
BEGIN
UPDATE business
SET business.Logo = logo
WHERE business.BusinessID = 1;
END
This is the solution. Had to remove the transaction.

MySQL Stored procedure, PDO::fetch() throws general error on null return

The problem is that I'm using a stored procedure to handle validation of my accounts. (Please note that this is a dumbed down version of the procedure and method and is only used for demonstration purposes). Note: Account authentication is not the original purpose of this method, once again just for example.
Here's my Example MySQL procedure:
BEGIN
DECLARE rc INT(11);
DECLARE new VARCHAR(40);
SET new = uuid();
UPDATE accounts SET session_key = new WHERE account_id = id_in AND session_key = session_in;
SELECT ROW_COUNT() into rc;
IF(rc > 0) THEN
SELECT new AS `session_key`;
END IF;
END
Here's the PHP related code that goes with it:
private static function authenticate() {
$connection = Database::getConnection();
$account = 19;
$session = "cc73e13b-2983-11e5-8ade-00163e603fb4";
$statement = $connection->prepare("CALL AUTH_SESSION(:account, :session);");
$statement->bindParam(":account", $account);
$statement->bindParam(":session", $session);
if($statement->execute()) {
if($row = $statement->fetch()) {
echo 'valid session';
} else {
echo 'invalid session';
}
} else {
echo 'failed to execute query';
}
}
When the session_key and account_id are correct, the procedure prints out (or SELECTS) the new session_key so that PHP can obtain it. However when one of the two values are not correct the SELECT statement is never called.
I figured this would be fine. If a row is returned->get the session key (Success) and if the row isn't returned, the validation failed, but it apparently doesn't work that way.
The error returned is as follows:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error'
Just to re-iterate, this error is only thrown when (!(rc>0)).
You can only use fetch() if the query returns a result set, i.e. the stored procedure has to return the result of a SELECT.
You could have the procedure return an empty result set when rc > 0 is false.
BEGIN
DECLARE rc INT(11);
DECLARE new VARCHAR(40);
SET new = uuid();
UPDATE accounts SET session_key = new WHERE account_id = id_in AND session_key = session_in;
SELECT ROW_COUNT() into rc;
SELECT new AS session_key
FROM dual
WHERE rc > 0;
END
When you call fetch() it will return false if the result set is empty, otherwise it will return an array containing the session key.

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();
}
?>

User defined MySQL function not accessible with PHP PDO connection

I've got a trivial MySQL function:
DELIMITER $$
DROP FUNCTION IF EXISTS `mydb`.`CALC` $$
CREATE FUNCTION `mydb`.`CALC_IT`(Flag VARCHAR(1), One FLOAT, Other FLOAT)
RETURNS FLOAT
BEGIN
IF One IS NULL THEN RETURN 0; END IF;
IF Other IS NULL THEN RETURN 0; END IF;
IF Flag = 'Y' THEN
RETURN Other - One;
ELSE
RETURN Other
END IF;
END $$
DELIMITER ;
And it's called in a query from PHP using a PDO connection:
$query = 'SELECT CALC_IT(`Flag`, `One`, `Two`) FROM `mydb`.`table` WHERE `Condition` = 1';
$db = new PDO('mysql:host=localhost', 'user', 'pass');
$stmt = $db->prepare($query);
if (!$stmt->execute()) {
var_dump($stmt->errorInfo());
}
But, it reports the following:
array
0 => string '42000' (length=5)
1 => int 1305
2 => string 'FUNCTION CALC_IT does not exist' (length=37)
And, if you try it with the legacy Mysql code, it works:
$db = mysql_connect('localhost', 'user', 'pass');
$result = mysql_query($query);
if (mysql_error()) {
var_dump(mysql_error());
}
The query also works if you try it with any other mysql client.
So why doesn't some user defined MySQL functions work in PHP's PDO library?
new PDO('mysql:host=localhost', 'user', 'pass');
Missing dbname=mydb?
it will be mydb.CALC_IT
Yep, that's a possibility. There are advantages and disadvantages to selecting a database at connect-time rather than explicitly specifying it in the query. If you're only using a single database on the server it tends to be easier to say so at connect-time and handle the possible access-rights errors then, but if you're doing cross-db work the explicit way “mydb.CALC_IT” will be necessary.

Categories