Using PDO, I am trying to run two prepared statements within a transaction. I only want the second prepared statement to run if the first one executes successfully and vice versa.
//Open Database Connection
$dbh = new PDO("mysql:host=$host;dbname=$dbname", $user, $pwd);
//Set Error Handling
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//Run First Prepared Statement
$sql = 'UPDATE listers SET
first_name = :first_name,
last_name = :last_name,
address = :address,
apt = :apt,
city = :city,
state = :state,
zip_1 = :zip_1,
phone_prefix = :phone_prefix,
phone_first = :phone_first,
phone_last = :phone_last
WHERE lister_id = :lister_id';
try {
$dbh->beginTransaction();
$result = $dbh->prepare($sql);
$result->bindParam(':first_name', $_POST['first_name'], PDO::PARAM_STR);
$result->bindParam(':last_name', $_POST['last_name'], PDO::PARAM_STR);
$result->bindParam(':address', $_POST['address'], PDO::PARAM_STR);
$result->bindParam(':apt', $_POST['apt'], PDO::PARAM_STR);
$result->bindParam(':city', $_POST['city'], PDO::PARAM_STR);
$result->bindParam(':state', $_POST['state'], PDO::PARAM_STR);
$result->bindParam(':zip_1', $_POST['zip_1'], PDO::PARAM_INT);
$result->bindParam(':phone_prefix', $phone_1_prefix, PDO::PARAM_INT);
$result->bindParam(':phone_first', $phone_1_first, PDO::PARAM_INT);
$result->bindParam(':phone_last', $phone_1_last, PDO::PARAM_INT);
$result->bindParam(':lister_id', $_GET['lister_id'], PDO::PARAM_INT);
$result->execute();
//Run Second Prepared Statement
$current_date = time(); //Current Date/time in Unix format
$sql = 'INSERT INTO hidddstory_details SET
history_id = :history_id,
listing_type = "lister",
date_added = :date_added,
listing_id = :listing_id';
$result = $dbh->prepare($sql);
$result->bindParam(':history_id', $account_edited_admin, PDO::PARAM_INT);
$result->bindParam(':date_added', $current_date, PDO::PARAM_INT);
$result->bindParam(':listing_id', $row['lister_id'], PDO::PARAM_INT);
$result->execute();
$dbh->commit();
} catch(PDOException $e) {
$error = '<div id="error"><p class="error_message">The account could not be edited due to a system error. We apologize for any inconvenience.</p></div>';
$dbh->rollBack();
echo errorHandle($e);
}
When I load the page without any syntax errors, it runs the prepared statements correctly. When I purposefully add some letters to the history_details table name, as you can see above, it displays the error message relating to the incorrect table name, as it should. Unfortunately though, it doesn't rollback the first prepared statement, so when I check the listers table, it has actually updated that table, which it shouldn't.
How can I run multiple prepared statements in a transaction?
Examine the storage engine for the table you are trying to perform transactions on to ensure that it in fact supports transactions. As far as I know InnoDB is on the only format that currently supports transactions. Engines that do not support transactions will silently do nothing, no errors will be issued nor will any rollback be done.
Related
I have the following PHP script that executes an sql update query, how can I prevent it from being executed until the two POST values are not empty?
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$delivery_time = addslashes($_POST['delivery_time']);
$customer = addslashes($_POST['customer']);
$sql = "UPDATE Equipment SET time = '$time', customer = '$customer' WHERE status ='Broken' ";
// Prepare statement
$stmt = $conn->prepare($sql);
// execute the query
$stmt->execute();
// echo a message to say the UPDATE succeeded
echo $stmt->rowCount() . " records UPDATED successfully";
}
catch(PDOException $e)
{
echo $sql . "<br>" . $e->getMessage();
}
$conn = null;
Warning: You are wide open to SQL Injections and should really use parameterized prepared statements instead of manually building your queries. They are provided by PDO or by MySQLi. Never trust any kind of input, especially that which comes from the client side. Even when your queries are executed only by trusted users, you are still in risk of corrupting your data.
To avoid sending the query until $_POST variables are supplied check for their existence with if statement and isset.
if (isset($_POST['delivery_time'], $_POST['customer'])) {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // switch off emulated prepares, but you should add charset=utf8mb4 in the contructor above too
$sql = "UPDATE Equipment SET `time` = ?, `customer` = ? WHERE `status` ='Broken' ";
// Prepare statement
$stmt = $conn->prepare($sql);
// execute the query
$stmt->execute([
$_POST['delivery_time'],
$_POST['customer']
]);
// echo a message to say the UPDATE succeeded
echo $stmt->rowCount() . " records UPDATED successfully";
}
I've been looking at this for the last hour and cannot see why this wont work? It is carried out in a bootstrap modal via AJAX.
For some reason SupplierUpdate will not get updated in the database. (It is a timestamp)
$conn = new PDO("mysql:host=$hostname; dbname=$username;charset=utf8", $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "UPDATE Suppliers SET UpdateTime=:UpdateTime, Code1=:Code1, Code1Desc=:Code1Desc, Code2=:Code2, Code2Desc=:Code2Desc, Code3=:Code3, Code3Desc=:Code3Desc WHERE UserID ='$UserID'";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':Code1', $_POST['Code1'], PDO::PARAM_STR);
$stmt->bindParam(':Code1Desc', $_POST['Code1Desc'], PDO::PARAM_STR);
$stmt->bindParam(':Code2', $_POST['Code2'], PDO::PARAM_STR);
$stmt->bindParam(':Code2Desc', $_POST['Code2Desc'], PDO::PARAM_STR);
$stmt->bindParam(':Code3', $_POST['Code3'], PDO::PARAM_STR);
$stmt->bindParam(':Code3Desc', $_POST['Code3Desc'], PDO::PARAM_STR);
$stmt->bindParam(':UpdateTime', $_POST['Update'], PDO::PARAM_STR);
$stmt->execute();
<input type="text" name="SupplierUpdate" id="SupplierUpdate" value="<?php echo $timestamp ?>">
Your input type name="SupplierUpdate"
But i dont see you are binding it with this name
you are using $_POST['Update']
you may try to debug it like below...
if($conn){
$stmt = $conn->prepare($sql);
}
else{
echo "not connected";
}
Then, set your date format forcefully like,
$upd_tim = date('Y-m-d H:i:s', strtotime($_POST['Update']));
$stmt->bindParam(':UpdateTime', $upd_tim, PDO::PARAM_STR);
Then, after the execute(),
echo $stmt->rowCount();
I know of two ways to use PDO in PHP to update a MySQL database record. Please could someone explain which one I should use for better security and the difference and I am a little confused.
Method One:
$user = "root";
$pass = "";
$dbh = new PDO('mysql:host=somehost;dbname=somedb', $user, $pass);
$sql = "UPDATE coupons SET
coupon_code = :coupon_code,
valid_from = :valid_from,
valid_to = :valid_to,
discount_percentage = :discount_percentage,
discount_amount = :discount_amount,
calculationType = :calculationType,
limit = :limit
WHERE coupon_code = :coupon";
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':coupon_code', $_POST['coupon_code'], PDO::PARAM_STR);
$stmt->bindParam(':valid_from', $_POST['$valid_from'], PDO::PARAM_STR);
$stmt->bindParam(':valid_to', $_POST['valid_to'], PDO::PARAM_STR);
$stmt->bindParam(':discount_percentage', $_POST['discount_percentage'], PDO::PARAM_STR);
$stmt->bindParam(':discount_amount', $_POST['discount_amount'], PDO::PARAM_STR);
$stmt->bindParam(':calculationType', $_POST['calculationType'], PDO::PARAM_STR);
$stmt->bindParam(':limit', $_POST['limit'], PDO::PARAM_STR);
$stmt->bindParam(':coupon', $_POST['coupon_code'], PDO::PARAM_STR);
$stmt->execute();
Method Two:
$dbtype="somedbtype";
$dbhost="somehost";
$dbname="somedb";
$dbuser="someuser";
$dbpass= "somepass";
$conn = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass);
$title = 'PHP Pattern';
$author = 'Imanda';
$id = 3;
$sql = "UPDATE books
SET title=?, author=?
WHERE id=?";
$q = $conn->prepare($sql);
$q->execute(array($title,$author,$id));
From what I can see, method two does not bind the data, rather insert it directly into the query as an array type. Does this make the script more susceptible to SQL injection or other security risks?
The only difference between the two is that if you pass the array in to the execute function rather than calling bindParam yourself, it treats all parameters as PDO::PARAM_STR automatically, whereas in calling bindParam yourself you could bind them as integers, etc.
From the docs:
input_parameters
An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.
You can also see from the examples there that you can use named parameters (e.g. :limit) when passing the array into the execute function. You don't have to just put ?. In that case you give the array a key:
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
It's mostly a matter of preference. Both protect you from injection.
Though I think it's much easier to force data type with bind(), where as using execute(array()) will be using strings.
I have two stored procedures in my database Postgres, both have the same name but the difference are the parameters.
procedure1(::string, ::integer, ::string, ::integer)
procedure1(::string, ::integer, ::integer)
In PDO doing bindParam correct, is coming STR, INT, INT but the prepere always performs procedure1.
How do I get him to understand what I call the procedure2?
Some information for more help? I clear? thanks
EDIT ===
...
$bounds = null; // forced for debug
if(!is_null($bounds)){
$query = "SELECT procedure1(:name, :domain, :geo, :userid)";
$stmt = $db->prepare($query);
$stmt->bindParam('name', $name, PDO::PARAM_STR);
$stmt->bindParam('domain', $idDomain, PDO::PARAM_INT);
$stmt->bindParam('geo', $geoString, PDO::PARAM_STR);
$stmt->bindParam('userid', $userId, PDO::PARAM_INT);
}else{
$query = "SELECT procedure1(:name, :domain, :userid)";
$stmt = $db->prepare($query);
$stmt->bindParam('name', $name, PDO::PARAM_STR);
$stmt->bindParam('domain', $idDomain, PDO::PARAM_INT);
$stmt->bindParam('userid', $userId, PDO::PARAM_INT);
}
$result = $stmt->execute();
...
The error it gives is that he is running a procedure that requires four parameters
Try changing your $query statements to explicitly tell PDO the types, and to avoid extra code switch to bindValue (PDO uses the PARAM flags to format SQL, not to cast data types):
$bounds = null; // forced for debug
if(!is_null($bounds)){
$query = "SELECT procedure1(:name::VARCHAR, :domain::INTEGER, :geo::VARCHAR, :userid::INTEGER)";
$stmt = $db->prepare($query);
$stmt->bindValue('name', $name);
$stmt->bindValue('domain', $idDomain);
$stmt->bindValue('geo', $geoString);
$stmt->bindValue('userid', $userId);
}else{
$query = "SELECT procedure1(:name::VARCHAR, :domain::INTEGER, :userid::INTEGER)";
$stmt = $db->prepare($query);
$stmt->bindValue('name', $name);
$stmt->bindValue('domain', $idDomain);
$stmt->bindValue('userid', $userId);
}
$result = $stmt->execute();
The current error when running this from the command line is "Call to a member function bindParam() on a non-object" which I've worked out to being a problem with the variable $orderPO. Something does not like non-numeric characters which led me to the bindParam PARAM_STR business which does not work either. The database fields are both varchar 50.
My search skills are failing me. I know this must be posted somewhere about a million times but I can't seem to find it. I am completely open to doing this another way if someone has a better idea.
Current attempt code:
try
{
$orderNum = '123456';
$orderPO = '123456-A';
$dbh = new PDO("mysql:host=localhost;dbname=dbname", 'someuser', 'somepass');
$stm = $dbh->prepare("insert into some_table (order_number, order_po)");
$stm->bindParam(':order_number', $orderNum, PDO::PARAM_STR);
$stm->bindParam(':order_po', $orderPO, PDO::PARAM_STR);
$stm->execute();
print_r($stm);
print_r($dbh);
$arr = $stm->errorInfo();
print_r($arr);
$stm->closeCursor();
$dbh = null;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
In order to bind parameters using PDO, you will need to use placeholders, like this:
$stm = $dbh->prepare("
INSERT INTO `some_table` SET
`order_number` = :order_number,
`order_po` = :order_po
");
$stm->bindParam(':order_number', $orderNum, PDO::PARAM_STR);
$stm->bindParam(':order_po', $orderPO, PDO::PARAM_STR);
Notice the inclusion of the : character before the named placeholder. I also added column names to your query.
Read further and see examples: PDO bindParam
The correct syntax is
$stm = $dbh->prepare("insert into some_table (order_number, order_po) VALUES (?, ?)");
$stm->bindParam(1,$orderNum);
$stm->bindParam(2,$orderPO);
include the questions marks, the numbers in the bindParam call refer to which question mark you're binding the parameter to
You are trying to use bindparam, but bind param matches ? not cursors :. You have not included any parameters or values.
Also, you are missing your VALUES statement within the query, which is causing the query to fail. This is why you get the "Call to a member function bindParam() on a non-object"
To use the :value syntax, use bindValue, not bindParam. to use bindParam, switch the :value to ? in your query and number them in order is your execute array.
try
{
$orderNum = '123456';
$orderPO = '123456-A';
$dbh = new PDO("mysql:host=localhost;dbname=dbname", 'someuser', 'somepass');
$stm = $dbh->prepare("insert into some_table (order_number, order_po) VALUES (:order_number, :order_po)");
$stm->bindvalue(':order_number', $orderNum, PDO::PARAM_STR);
$stm->bindvalue(':order_po', $orderPO, PDO::PARAM_STR);
$stm->execute();
print_r($stm);
print_r($dbh);
$arr = $stm->errorInfo();
print_r($arr);
$stm->closeCursor();
$dbh = null;
}
catch(PDOException $e)
{
echo $e->getMessage();
}