Sorry for this beginners question and i'm not a PHP developer, but now i'm trying to learn it.
i want to add record in MySQL data base and i'm using transactions lock.
my code is as below.
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
$waiting = true;
while($waiting) {
try {
// save border data
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute();
sleep(1);
$x=$conn->commit();
echo "x value-".$x;
echo "Success";
$waiting = false;
}
catch (PDOException $e){
echo "Failled :".$PhotoID."-".$PhotoID;
if(stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {
// This should be specific to SQLite, sleep for 0.25 seconds
// and try again. We do have to commit the open transaction first though
$conn->commit();
usleep(250000);
} else {
$conn->rollBack();
throw $e;
}
}
}
in here as output it gives,
x value-1 Success
but actually this record doesn't add to the database.
My Questions:
Even the commit is successful(output 1) how does it not added to the database?
how can i check whether record is added to database? ( Is there any way to find it without write select statement?
As I understand, you expect that PDOException will be thrown when statement is failed to execute. But as I can see, exception is not thrown by default in such cases.
See how you can change that here
Suppose in your case you should have a code like this:
$conn = new PDO($connection_string);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // this will force PDO to throw exception when SQL statement fails instead of simply setting an error.
Suppose this will work fine for you.
Please note that you should not use
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
Instead of that, you should use parameters binding:
$SqlQuery="INSERT INTO tab_photo VALUES(:PhotoID,:ProjectId,:Day,:barCode,:photoName,:PhotoXml)";
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute(array(':PhotoID' => $PhotoID, ':ProjectId' => $ProjectId, ....));
sleep(1);
See this for more details.
Related
$droptable = 'DROP TABLE cars';
$resultd = $db->prepare($droptable);
$resultd->execute();
$printresult = $resultd->fetchAll();
echo "<pre>";
print_r($printresult);
echo "</pre>";
I am trying to test something with PDO and created an execute query for the drop table query, but I could not see anything on the screen. Am I making some fundamental programming fundamental or there will be a way around?
fetchAll will not work, as there are not records generated.
$resultd->debugDumpParams(); // print to screen
/* to push the debug to a var */
ob_start();
$content = ob_get_contents();
ob_end_clean();
/* or to just get the error message */
if($resultd->execute())
echo " Table deleted ";
else
print_r($sql->errorInfo());
DROP is a DDL (Data Definition Language) statement, it can't possibly return anything.
The statement can succeed or fail. You could manually check the result of every database method call, but you're better off configuring PDO to throw exceptions on error.
It's also unnecessary to prepare the statement since there aren't parameters to bind.
Putting all this together:
$db = new \PDO($dsn, $user, $pass, [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
$droptable = 'DROP TABLE cars';
$db->query($droptable);
You'll get PDOException if something goes wrong or the code will just continue running otherwise. If you really expect the removal to fail now and then during normal operations, you can capture the exception as usual:
try {
$db->query($droptable);
echo 'Table dropped successfully';
} catch(\PDOException $e) {
echo 'Could not drop table: ', $e->getMessage();
}
Please take a look at my code:
try {
// db connection here
$stm = $dbh->prepare("INSERT INTO mytable(id,token) values(NULL,$token)")->execute();
} catch(PDOException $e){
if ( $stm ){
echo 'inserting fails';
} else {
echo 'something else is wrong';
}
}
-- `token` column is unique
Current outputs:
The row inserted successfully.
It prints something else is wrong error for both {duplicate entry} and {SQL syntax}
Expected outputs:
The row inserted successfully.
It prints inserting fails error for {duplicate entry}
It prints something else is wrong error for {SQL syntax}
Ok, if I write my code like following (without chaining), then expected output happens:
$stm = $dbh->prepare("INSERT INTO mytable(id,token) values(NULL,$token)");
$stm->execute();
Well I want to know, when can I chain those PDO statements?
An exception can only be thrown in either the prepare or execute methods. Either of those is going to happen before $stm =. In other words, if an exception is going to be thrown, the assignment to $stm is always going to be skipped, meaning the variable doesn't exist at all in your catch block. Therefore it can only evaluate to false, and will in fact produce a notice about being undefined.
Read the PDO documentation http://php.net/manual/en/book.pdo.php and look at the return values. You can only chain when an object is returned such as a statement or resultset.
Execute (http://php.net/manual/en/pdostatement.execute.php) returns a boolean, not an object so we know it cannot be chained.
Prepare (http://php.net/manual/en/pdo.prepare.php) returns a statement object, so we can use the return statement to chain on another method call.
Think of it like this:
$stmt = $dbh->prepare("..sql..");
$bool = $stmt->execute();
This can translate into:
$bool = $dbh->prepare("..sql..")->execute();
As the return from ->prepare() is the the $stmt.
The reason you aren't getting your expected output is that the way you have it written, any time you get a PDOException, $stm can never be true. If either the prepare or the execute fails, then $stm will be undefined.
I originally thought that you could fix this by removing the check for execute success from the catch block, but I was mistaken. You cannot get your expected output while still chaining the methods.
try {
$success = $dbh->prepare("INSERT INTO mytable(id,token) values(NULL,$token)")->execute();
if (!$success) {
// This can never be reached. If your have set PDO::ERRMODE_EXCEPTION, then either
// the query is successful and $success === true, or the prepare or the execute
// failed, and an exception will be thrown
echo 'inserting fails';
}
} catch(PDOException $e){
echo 'something else is wrong';
}
Just for the record. To answer the question the guy tried to ask.
A code from my article on PDO (also fixing an SQL injection):
try {
$dbh->prepare("INSERT INTO mytable(token) values(?)")->execute([$token]);
} catch (PDOException $e) {
if ($e->getCode() == 1062) {
// insert failed due to duplicate key error
echo "duplicate token";
} else {
// insert failed due to any other error
throw $e;
}
}
I do know that PDO does not support multiple queries getting executed in one statement. I've been Googleing and found few posts talking about PDO_MYSQL and PDO_MYSQLND.
PDO_MySQL is a more dangerous
application than any other traditional
MySQL applications. Traditional MySQL
allows only a single SQL query. In
PDO_MySQL there is no such limitation,
but you risk to be injected with
multiple queries.
From: Protection against SQL Injection using PDO and Zend Framework (June 2010; by Julian)
It seems like PDO_MYSQL and PDO_MYSQLND do provide support for multiple queries, but I am not able to find more information about them. Were these projects discontinued? Is there any way now to run multiple queries using PDO.
As I know, PDO_MYSQLND replaced PDO_MYSQL in PHP 5.3. Confusing part is that name is still PDO_MYSQL. So now ND is default driver for MySQL+PDO.
Overall, to execute multiple queries at once you need:
PHP 5.3+
mysqlnd
Emulated prepared statements. Make sure PDO::ATTR_EMULATE_PREPARES is set to 1 (default). Alternatively you can avoid using prepared statements and use $pdo->exec directly.
Using exec
$db = new PDO("mysql:host=localhost;dbname=test", 'root', '');
// works regardless of statements emulation
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
$sql = "
DELETE FROM car;
INSERT INTO car(name, type) VALUES ('car1', 'coupe');
INSERT INTO car(name, type) VALUES ('car2', 'coupe');
";
$db->exec($sql);
Using statements
$db = new PDO("mysql:host=localhost;dbname=test", 'root', '');
// works not with the following set to 0. You can comment this line as 1 is default
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
$sql = "
DELETE FROM car;
INSERT INTO car(name, type) VALUES ('car1', 'coupe');
INSERT INTO car(name, type) VALUES ('car2', 'coupe');
";
$stmt = $db->prepare($sql);
$stmt->execute();
A note:
When using emulated prepared statements, make sure you have set proper encoding (that reflects actual data encoding) in DSN (available since 5.3.6). Otherwise there can be a slight possibility for SQL injection if some odd encoding is used.
After half a day of fiddling with this, found out that PDO had a bug where...
--
//This would run as expected:
$pdo->exec("valid-stmt1; valid-stmt2;");
--
//This would error out, as expected:
$pdo->exec("non-sense; valid-stmt1;");
--
//Here is the bug:
$pdo->exec("valid-stmt1; non-sense; valid-stmt3;");
It would execute the "valid-stmt1;", stop on "non-sense;" and never throw an error. Will not run the "valid-stmt3;", return true and lie that everything ran good.
I would expect it to error out on the "non-sense;" but it doesn't.
Here is where I found this info:
Invalid PDO query does not return an error
Here is the bug:
https://bugs.php.net/bug.php?id=61613
So, I tried doing this with mysqli and haven't really found any solid answer on how it works so I thought I's just leave it here for those who want to use it..
try{
// db connection
$mysqli = new mysqli("host", "user" , "password", "database");
if($mysqli->connect_errno){
throw new Exception("Connection Failed: [".$mysqli->connect_errno. "] : ".$mysqli->connect_error );
exit();
}
// read file.
// This file has multiple sql statements.
$file_sql = file_get_contents("filename.sql");
if($file_sql == "null" || empty($file_sql) || strlen($file_sql) <= 0){
throw new Exception("File is empty. I wont run it..");
}
//run the sql file contents through the mysqli's multi_query function.
// here is where it gets complicated...
// if the first query has errors, here is where you get it.
$sqlFileResult = $mysqli->multi_query($file_sql);
// this returns false only if there are errros on first sql statement, it doesn't care about the rest of the sql statements.
$sqlCount = 1;
if( $sqlFileResult == false ){
throw new Exception("File: '".$fullpath."' , Query#[".$sqlCount."], [".$mysqli->errno."]: '".$mysqli->error."' }");
}
// so handle the errors on the subsequent statements like this.
// while I have more results. This will start from the second sql statement. The first statement errors are thrown above on the $mysqli->multi_query("SQL"); line
while($mysqli->more_results()){
$sqlCount++;
// load the next result set into mysqli's active buffer. if this fails the $mysqli->error, $mysqli->errno will have appropriate error info.
if($mysqli->next_result() == false){
throw new Exception("File: '".$fullpath."' , Query#[".$sqlCount."], Error No: [".$mysqli->errno."]: '".$mysqli->error."' }");
}
}
}
catch(Exception $e){
echo $e->getMessage(). " <pre>".$e->getTraceAsString()."</pre>";
}
A quick-and-dirty approach:
function exec_sql_from_file($path, PDO $pdo) {
if (! preg_match_all("/('(\\\\.|.)*?'|[^;])+/s", file_get_contents($path), $m))
return;
foreach ($m[0] as $sql) {
if (strlen(trim($sql)))
$pdo->exec($sql);
}
}
Splits at reasonable SQL statement end points. There is no error checking, no injection protection. Understand your use before using it. Personally, I use it for seeding raw migration files for integration testing.
Like thousands of people, I'm looking for this question:
Can run multiple queries simultaneously, and if there was one error, none would run
I went to this page everywhere
But although the friends here gave good answers, these answers were not good for my problem
So I wrote a function that works well and has almost no problem with sql Injection.
It might be helpful for those who are looking for similar questions so I put them here to use
function arrayOfQuerys($arrayQuery)
{
$mx = true;
$conn->beginTransaction();
try {
foreach ($arrayQuery AS $item) {
$stmt = $conn->prepare($item["query"]);
$stmt->execute($item["params"]);
$result = $stmt->rowCount();
if($result == 0)
$mx = false;
}
if($mx == true)
$conn->commit();
else
$conn->rollBack();
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
return $mx;
}
for use(example):
$arrayQuery = Array(
Array(
"query" => "UPDATE test SET title = ? WHERE test.id = ?",
"params" => Array("aa1", 1)
),
Array(
"query" => "UPDATE test SET title = ? WHERE test.id = ?",
"params" => Array("bb1", 2)
)
);
arrayOfQuerys($arrayQuery);
and my connection:
try {
$options = array(
//For updates where newvalue = oldvalue PDOStatement::rowCount() returns zero. You can use this:
PDO::MYSQL_ATTR_FOUND_ROWS => true
);
$conn = new PDO("mysql:host=$servername;dbname=$database", $username, $password, $options);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "Error connecting to SQL Server: " . $e->getMessage();
}
Note:
This solution helps you to run multiple statement together,
If an incorrect a statement occurs, it does not execute any other statement
PDO does support this (as of 2020). Just do a query() call on a PDO object as usual, separating queries by ; and then nextRowset() to step to the next SELECT result, if you have multiple. Resultsets will be in the same order as the queries. Obviously think about the security implications - so don't accept user supplied queries, use parameters, etc. I use it with queries generated by code for example.
$statement = $connection->query($query);
do {
$data[] = $statement->fetchAll(PDO::FETCH_ASSOC);
} while ($statement->nextRowset());
Try this function : multiple queries and multiple values insertion.
function employmentStatus($Status) {
$pdo = PDO2::getInstance();
$sql_parts = array();
for($i=0; $i<count($Status); $i++){
$sql_parts[] = "(:userID, :val$i)";
}
$requete = $pdo->dbh->prepare("DELETE FROM employment_status WHERE userid = :userID; INSERT INTO employment_status (userid, status) VALUES ".implode(",", $sql_parts));
$requete->bindParam(":userID", $_SESSION['userID'],PDO::PARAM_INT);
for($i=0; $i<count($Status); $i++){
$requete->bindParam(":val$i", $Status[$i],PDO::PARAM_STR);
}
if ($requete->execute()) {
return true;
}
return $requete->errorInfo();
}
Tried following code
$db = new PDO("mysql:host={$dbhost};dbname={$dbname};charset=utf8", $dbuser, $dbpass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Then
try {
$db->query('SET NAMES gbk');
$stmt = $db->prepare('SELECT * FROM 2_1_paidused WHERE NumberRenamed = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
}
catch (PDOException $e){
echo "DataBase Errorz: " .$e->getMessage() .'<br>';
}
catch (Exception $e) {
echo "General Errorz: ".$e->getMessage() .'<br>';
}
And got
DataBase Errorz: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '/*' LIMIT 1' at line 1
If added $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); after $db = ...
Then got blank page
If instead SELECT tried DELETE, then in both cases got error like
DataBase Errorz: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '* FROM 2_1_paidused WHERE NumberRenamed = '¿\' OR 1=1 /*' LIMIT 1' at line 1
So my conclusion that no injection possible...
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();
}
try {
$query = 'UPDATE keywords SET value = :keyvalue WHERE keyword = :keyname AND document_id = :docId';
$pdo = _openConnection();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->beginTransaction();
$pdoStatement = $pdo->prepare($query);
foreach ($keywords as $keyname => $keyval) {
$pdoStatement->bindParam(':docId', $id, PDO::PARAM_STR);
$pdoStatement->bindParam(':keyname', $keyname, PDO::PARAM_STR);
$pdoStatement->bindParam(':keyvalue', $keyval, PDO::PARAM_STR);
$pdoStatement->execute();
}
$res = $pdo->commit();
var_dump('retornando true', $res);
return true;
} catch (PDOException $e) {
$pdo->rollBack();
echo $e->getMessage();
return false;
}
The sentence updates a given row identified by KEYWORDNAME and DOCUMENT_ID.
I am sending a wrong keyword name (non-existent) but existent document id.
Shouldn't it throw an exception for record not found and rollback the operation?
It is always succeeding and returning true (also I see the var_dump)
PS: this is the last portion of the code.
As far as the database (and thus PDO) is concerned - this is not an error. You performed an update statement, and it successfully updated 0 rows.
If you want to handle this as an error, you'd have to do it manually:
$res = $pdo->commit();
if ($pdo->rowCount() == 0) {
# some exception treatment
}
No, you are simply updating 0 rows when the WHERE condition is not matched. Updating, selecting, etc. 0 rows is not an error, these are normal database operations.
Check the rowCount() to see how many rows are updated and handle that accordingly.
A query which doesn't match any records is NOT an error. It's just an empty result set, which is a perfectly valid result.
The only time you'd get an exception from the query is if there was an actual problem with the query itself, the connection to the db, etc... e.g. A syntax error in the DB, connection failure, permission denied on whatever table(s) you're accessing, etc...