I have several stored procedures that I am calling via PDO in PHP. I was hoping to be able to handle errors by performing a ROLLBACK, but I still want to be able to use PHP to retrieve and handle the last error in the procedure. I have tryed using PDO::errorCode() and PDO::errorInfo(), but that does seem to be a legitimate solution, I think because I am already handling the errors in my stored procedures.
When I call one of the stored procedures via command line and then call SHOW ERRORS I get a nice result set with the error status, code and message, but if I call SHOW ERRORS in PDO after executing the stored procedure, I get no results. I also get no result from SHOW ERRORS in command line if I call show errors inside the stored procedure.
I would use GET DIAGNOSTICS, but the MySQL server I am developing for is on a hosted cPanel that I don't have control over updating and it is version 5.5.
Is there some other option I could use or another route I should be taking?
Like I said, I have several stored procedures I want to handle errors, but I can't even get this to work on a simple stored procedure:
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
SELECT *
FROM bunnies;
END
Update: My PHP code was in an object, so I copied and simplified the code to post per Barmar's request and when I tried the simplified code, I found that SHOW ERRORS does indeed work with PDO when prepared and executed after the function is prepared and executed.
My object was a little complicated (I wrote it a while back before I knew much about PHP OOP), so I simplified it as well and now it works! I think the connection was being closed in between calls and now that the code is simpler, I am having no problems with calling SHOW ERRORS in it.
Here's the simplified PHP code I used to test, in case anyone has had issues getting this to work:
$host = '***';
$user = '***';
$pass = '***';
$schema = '***';
$connection = new PDO('mysql:host=' . $host . ';dbname=' . $schema . ';', $user, $pass);
$statement = $connection->prepare('CALL test()');
$statement->execute();
$statement = $connection->prepare('SHOW ERRORS');
$statement->execute();
echo var_dump($statement->fetchAll());
$statement = null;
$statement = null;
$connection = null;
Make sure you use the same PDO connection object to execute the original queries and to retrieve the errors. Each connection has its own error state.
Related
I am converting PHP code from mysql_ to mysqli_. My DB connection is in a separate file, named "conn.inc" in a parent folder of my regular code. The code in it is ::
function GetDBConn($host="localhost", $user="mydb", $pass="mypass", $db="mydb") {
return $dbconn = #mysqli_connect($host, $user, $pass, $db);
mysqli_close($dbconn);
}
In my code files, I have
include_once ("../conn.inc"); .
I have code like -
$AuditInsertQ = mysqli_query(GetDBConn(),"INSERT INTO audit (userid, notes) VALUES (\"".$userid."\", \"".$notes."\")") or die("Error inserting row to Audit: ".mysqli_error($dbconn));
When I run the code, I get a message that ::
PHP Notice: Undefined variable: dbconn in C:\...
All of the examples I have seen have the DB connection in the same file as the code it was referencing. How do I reference the DB connection when it is in a different file; I thought the "include_once" was the way...?
There is so much wrong in this code I don't even know where to start.
no need for mysqli_error() at all
no need for a function like GetDBConn()
mysqli_close() right after connect makes no sense. Thanks to return operator, it never gets called though
die() is harmful
# operator is harmful
the file extension for conn.inc is harmful
the way you are adding variables to your query is most harmful of them all
I know it's hard to find a good tutorial. Internet is full of crappy outdated information. I am writing good tutorials, but Google don't know they are good and don't show them to you. Well at least I can give it to you here in my answer.
Three things you must understand about modern mysqli
mysqli can report its errors automatically, no need for mysqli_error()
a connection must be made only once, it means there is no use for a function like this
no variable should be added to the query directly. You have to use prepared statements with placeholders for the purpose.
In order to fix your code,
please read this post. It doesn't explain why you should use prepared statements but take my word for it
then read my tutorial on how to connect with mysqi properly
rename your file to conn.php or anyone will be able to see your database credentals
Then rewrite your code to
include_once ("../conn.php");
$stmt = $mysqli->prepare("INSERT INTO audit (userid, notes) VALUES (?,?)");
$stmt->bind_param("ss", $userid,$notes);
$stmt->execute();
For the explanation on what is going on in this code please see my tutorial on how to run an INSERT query with mysqli
In you function GetDBConn it return a mysql resource, not define a $dbconn variable for you.
Use something like mysqli_query($dbconn = GetDBConn(),"INSERT INTO audit ....
Note
function GetDBConn($host="localhost", $user="mydb", $pass="mypass", $db="mydb") {
return $dbconn = #mysqli_connect($host, $user, $pass, $db);
mysqli_close($dbconn);
}
$dbconn is only avaliable in the function, it cannot be accessed outside the function, so define it here is useless.
mysqli_close($dbconn); will never reached.
I am attempting to create new tables every time I post to this method, but for some reason I can not figure out why it dies.
<?php
$host = "127.0.0.1";
$username = 'cotten3128';
$pwd = 'pwd';
$database = "student_cotten3128";
$pin = $_REQUEST['pinSent'];
$words = $_REQUEST['resultSent'];
$tableName = $pin;
$db = new mysqli($host, $username, $pwd, $database);
if ($sql = $db->prepare("CREATE TABLE $pin (id INT(11) AUTO_INCREMENT);")) {
$sql->execute();
$sql->close();
}else{
echo $mysql->error;
die('Could not create table');
}
for($i=0;$i<count($words);$i++){
if($sql = $db->prepare("INSERT INTO ".$pin.$words[$i].";")) {
$sql->execute();
$sql->close();
}else{
echo $mysql->error;
die("Could not add data to table");
}
}
mysqli_close();
?>
Any help or insight would be greatly appreciated.
The intention of my post is to help you finding the issue by yourself. As you did not added much information I assume my post is helpful for you.
Based on the code you have shared I guess you mean one of your called die() functions is executed.
Wrong function call
As Jay Blancherd mentioned mysql_close is the wrong function. You rather have to use mysqli_close as you created a mysqli instance.
Beside of that mysql_* is deprecated and should not be used anymore.
Debugging Steps
Not only for this case but in general you should ask yourself:
Is there an error message available? (Frontend output, error log file, ...)
YES:
What's the message about?
Is it an error you can search for? E.g. via a search engine or the corresponding documentation?
Look up in the bug tracker (if available), by the software developer of the software you are using, and if it has not been reported yet report the issue.
NO: (if none error message available OR you cannot search for it as it is a custom error message)
Search in the files of the software you are using for the error message and start a core-debugging.
STILL NO SOLUTION?:
Ask on stackoverflow.com e.g. and tell your issue and the steps you have performed to find and fix the bug. Post only as much code as necessary plus use a proper format.
Debugging in your case:
In order to narrow down the scope. Which of the die() is executed? Depending on that echo the query to execute just before it actually is executed. Then copy the SQL query to an SQL editor and look at it syntax. After that you probably know the problem already.
I'm trying to get ADODB caching to work. I have a php script where i define the DB connection.
global $conn;
$conn = new COM ("ADODB.Connection");
$connStr = "PROVIDER-SQLOLEDB;SERVER=;UID=;PWD=;DATABASE=);
$conn->open($connStr);
I left the unnecessary details out of the picture.
Then in some other script i import the connection.php, and then try to make a normal query.
$query = "SELECT * from table where some_id = 21540 and other_id = BOGUS_INFO"
$rs = $GLOBALS['conn']->CacheExecute(60,$query);
This returns Uncaught exception 'com_exception'.. ADODB.Connection Arguments are of the wrong type,are out of acceptable range, or are in conflict with another.
I'm baffled because the next line of code works flawlessly.
$rs = $GLOBALS['conn']->execute($query); //OK!
Any ideeas?
I also tried CacheGetOne but i get the same error.
Could it be from the way i defined this thing below? (it's literally like that in my code)
$GLOBALS['ADODB_CACHE_DIR']=$_SERVER['DOCUMENT_ROOT'].'/../cache/adodb';
Well after alot of hassle, i kinda found an answer by choosing another way of doing things. I downloaded the latest ADODB build. Inserted it in my project, and modified files accordingly:
The connection.php changed to:
require('PATH/adodb.inc.php');
require('PATH/adodb-csvlib.inc.php');//read somewhere that i need this for the caching executes
$GLOBALS['ADODB_CACHE_DIR'] = $_SERVER['DOCUMENT_ROOT'].'/cache/adodb';
global $ADODB_CACHE_DIR; //don't know which one adodb usese really to identify cache directory so for safety - both
$ADODB_CACHE_DIR = $_SERVER['DOCUMENT_ROOT'].'/cache/adodb';
$conn = NewADOConnection('mssqlnative');//i tried first with mssql simple but script terminated execution on execute() attempt.. no error.. no nothing.. no output .. strange
$conn->Connect($myServer, $myUser, $myPass, $myDb);
After that i had to fiddle a bit with the code because,
$rs = $conn->CacheExecute(time,query)
returns Adodbrecordset_array_mssqlnative Object, and not an array, and, in my code i used to display and manipulate values as
while (!$rs->EOF) {
$rs['row']->value;
$rs->MoveNext();
}
and now they should be
$rs->fields['row'];
Another tricky thing was getting the fields array to be associated to the names of the columns in my query, but after a short search i discovered
$GLOBALS['conn']->SetFetchMode(ADODB_FETCH_ASSOC);
and voila! Everything works, even the caching.
It took script execution times with this bare optimisation from 1 sec to 0.1 or even 0.005 sometimes.
I'm a beginner programmer, and I've installed XAMPP with the intention of learning a bit of PHP. I have a working knowledge of SQL.
I've been following the PHP tutorial at w3schools. The problem I am currently having is this: I'm using the script here to create a database and a table within it. What I'm using is almost verbatim, except I've replaced the user with "root" and I've deleted the password.
After running the script through the browser, the database my_db appears in the datafile for mysql in XAMPP.
However, there is no sign of a table, and when I try to select the table, I get
Table 'my_db.persons' doesn't exist
What is going on? Is there something wrong with the code I took verbatim, or is it something with permissions?
It's weird that the database is created but not a table...
What has happenned is that your "CREATE TABLE" will have failed.
Get into the habit of checking for errors after EVERY mySQL query (in your code): there are some times you can ignore errors, but it's rare. So program alonghte lines of:
Create query: $sql =
Set parameters: $aParams = (or bind paramters)
Execute query
If errors
If debug: Show error and query
If live: Log error and query
I'm not giving the solution is code, as one problem with the tutorial you follow is that it uses mysql_() functions that are going to be depreciated shortly. You should use PDO (PHP Database Objects) or mysqli() functions otherwise your code will not work in a few releases time.
With PDO, you can set error handling to use exceptions, and you wrap every call in try {} catch {} and this makes the habit of catching and reporting errors very easy.
$sql = 'CREATE TABLE....';
$aParams = array(
':param_name' => $param_value,
':param_name2' => $param_value2
);
try {
$stmnt = $db->prepare($sql);
$stmnt->execute($aParams);
$stmnt = null;
} catch (Exception $e) {
// Error log here; $e contins line of error and the actual error, you have $sql and $aParams
LogDBError($e, $sql, $aParams);
}
I'm trying to get into PDO details. So I coded this:
$cn = getConnection();
// get table sequence
$comando = "call p_generate_seq('bitacora')";
$id = getValue($cn, $comando);
//$comando = 'INSERT INTO dsa_bitacora (id, estado, fch_creacion) VALUES (?, ?, ?)';
$comando = 'INSERT INTO dsa_bitacora (id, estado, fch_creacion) VALUES (:id, :estado, :fch_creacion)';
$parametros = array (
':id'=> (int)$id,
':estado'=>1,
':fch_creacion'=>date('Y-m-d H:i:s')
);
execWithParameters($cn, $comando, $parametros);
my getValue function works fine, and I get the next sequence for the table. But when I get into execWithParameters, i get this exception:
PDOException: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in D:\Servidor\xampp_1_7_1\htdocs\bitacora\func_db.php on line 77
I tried to modify the connection attributes but it doesn't work.
These are my core db functions:
function getConnection() {
try {
$cn = new PDO("mysql:host=$host;dbname=$bd", $usuario, $clave, array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
));
$cn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
return $cn;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
function getValue($cn, $comando) {
$resul = $cn->query($comando);
if (!$resul) return null;
while($res = $resul->fetch()) {
$retorno = $res[0][0];
break;
}
return $retorno;
}
function execWithParameters($cn, $comando, $parametros) {
$q = $cn->prepare($comando);
$q->execute($parametros);
if ($q->errorInfo() != null) {
$e = $q->errorInfo();
echo $e[0].':'.$e[1].':'.$e[2];
}
}
Somebody who can shed a light for this? PD. Please do not suggest doing autonumeric id, cause i am porting from another system.
The issue is that mysql only allows for one outstanding cursor at a given time. By using the fetch() method and not consuming all the pending data, you are leaving a cursor open.
The recommended approach is to consume all the data using the fetchAll() method.
An alternative is to use the closeCursor() method.
If you change this function, I think you will be happier:
<?php
function getValue($cn, $comando) {
$resul = $cn->query($comando);
if (!$resul) return null;
foreach ($resul->fetchAll() as $res) {
$retorno = $res[0];
break;
}
return $retorno;
}
?>
I don't think PDOStatement::closeCursor() would work if you're not doing a query that returns data (i.e. an UPDATE, INSERT, etc).
A better solution is to simply unset() your PDOStatement object after calling PDOStatement::execute():
$stmt = $pdo->prepare('UPDATE users SET active = 1');
$stmt->execute();
unset($stmt);
The problem seems to be---I'm not too familiar with PDO--- that after your getValue call returns, the query is still bound to the connection (You only ever ask for the first value, yet the connection returns several, or expects to do so).
Perhaps getValue can be fixed by adding
$resul->closeCursor();
before the return.
Otherwise, if queries to getValue will always return a single (or few enough) value, it seems that using fetchAll will be preferred.
I just spend 15 minutes googling all around the internet, and viewed at least 5 different Stackoverflow questions, some who claimed my bug apparently arose from the wrong version of PHP, wrong version of MySQL library or any other magical black-box stuff...
I changed all my code into using "fetchAll" and I even called closeCursor() and unset() on the query object after each and every query. I was honestly getting desperate! I also tried the MYSQL_ATTR_USE_BUFFERED_QUERY flag, but it did not work.
FINALLY I threw everything out the window and looked at the PHP error, and tracked the line of code where it happened.
SELECT AVG((original_bytes-new_bytes)/original_bytes) as saving
FROM (SELECT original_bytes, new_bytes FROM jobs ORDER BY id DESC LIMIT 100) AS t1
Anyway, the problem happened because my original_bytes and new_bytes both where unsigned bigints, and that meant that if I ever had a job where the new_bytes where actually LARGER than the original_bytes, then I would have a nasty MySQL "out of range" error. And that just happened randomly after running my minification service for a little while.
Why the hell I got this weird MySQL error instead of just giving me the plain error, is beyond me! It actually showed up in SQLBuddy (lightweight PHPMyAdmin) when I ran the raw query.
I had PDO exceptions on, so it should have just given me the MySQL error.
Never mind, the bottom line is:
If you ever get this error, be sure to check that your raw MySQL is actually correct and STILL working!!!
A friend of mine had very much the same problem with the xampp 1.7.1 build. After replacing xampp/php/* by the 5.2.9-2 php.net build and copying all necessary files to xampp/apache/bin it worked fine.
If you're using XAMPP 1.7.1, you just need to upgrade to 1.7.2.