Doctrine multiple flush commit once - mysql

I do a lot of search without success.
I would like to understand the flush() process in this situation.
//Edit updateOptions
public function updateOptions($values){
setOption('country',$values['country']);
setOption('city',$values['city']);
setOption('adress',$values['adress']);
setOption('CP',$values['CP']);
setOption('country_code',$values['country_code']);
}
function setOption($name, $value){
$em = $this->getEntityManager('option.manager');
$option = $this->getOption($name); //Entity Options
$option->setValue($value);
$em->persist($option);
$em->flush();
}
When I look the mysql.log or the profiler, i found this:
START TRANSACTION
UPDATE options SET value = 'France' WHERE name = 'country';
COMMIT
START TRANSACTION
SAVEPOINT DOCTRINE2_SAVEPOINT_2
UPDATE options SET value = 'Paris' WHERE name = 'city';
RELEASE SAVEPOINT DOCTRINE2_SAVEPOINT_2
SAVEPOINT DOCTRINE2_SAVEPOINT_2
UPDATE options SET value = 'Rue de Rivoli' WHERE name = 'adress';
RELEASE SAVEPOINT DOCTRINE2_SAVEPOINT_2
SAVEPOINT DOCTRINE2_SAVEPOINT_2
UPDATE options SET value = '75001' WHERE name = 'CP';
RELEASE SAVEPOINT DOCTRINE2_SAVEPOINT_2
SAVEPOINT DOCTRINE2_SAVEPOINT_2
UPDATE options SET value = '33' WHERE name = 'country_code';
RELEASE SAVEPOINT DOCTRINE2_SAVEPOINT_2
ROLLBACK
Only the first one is updated/committed, I get it, but i don't see why the next are rolled back?
This situation also occur if I use setOption() inside a loop for example.
A help would be great.
Thanks in advance.

Don not call persist() and flush() each time. I assume that could be the reason. Since you don not explicit tell EM to start a transaction it probably tries to guess it by combination of persist() and flush()
try following:
extend your updateOptions method
public function updateOptions ()
{
$em = $this->getEntityManager( 'option.manager' );
$em->beginTransaction();
setOption( 'country', 'France' );
setOption( 'city', 'Paris' );
setOption( 'adress', 'Rue de Rivoli' );
setOption( 'CP', '75001 ' );
setOption( 'country_code', '33' );
$em->flush(); //just notif EM there're records to UPDATE
//$success = false;
try
{
$em->commit();
//$success = true;
}
catch ( \Exception $ex )
{
$em->rollback();
// my fav is followinn: in DEV re-throw exception so you can inspect all in symfony-debug-bar
// in prod just additional emergency log (monolog + swiftmailer) so you get an email
if( $this->get( 'kernel' )->getEnvironment() !== 'prod' )
{
throw $ex;
}
$this->get( 'logger' )->addEmergency( 'Oh nooo! Not again :/' );
}
//return $success;
}
function setOption($name, $value){
$option = $this->getOption($name); //Entity Options
$option->setValue($value);
}

Related

Run 3 query only if previous success [duplicate]

As far as I understood transaction starts once we call $mysqli->autocommit(FALSE); statement and ends after calling $mysqli->commit(); command like in the example below.
<?php
//Start transaction
$mysqli->autocommit(FALSE);
$mysqli->query('UPDATE `table` SET `col`=2');
$mysqli->query('UPDATE `table1` SET `col1`=3;');
$mysqli->commit();
//End transaction
//Executing other queries without transaction control
$mysqli->query("Select * from table1");
$mysqli->query("Update table1 set col1=2");
//End of executing other queries without transaction control
//Start transaction
$mysqli->autocommit(FALSE);
$mysqli->query('UPDATE `table` SET `col`=2');
$mysqli->query('UPDATE `table1` SET `col1`=3;');
$mysqli->commit();
//End transaction
?>
Have I understood correctly? If not could you please correct me, because it is actually my first time using transactions in real life.
Thank you.
Update Novembre 2020: #Dharman gave a better answer with more details about transactions in mysqli, just check it instead: https://stackoverflow.com/a/63764001/569101 👇
Well according to the php doc, you're right.
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$mysqli->query("CREATE TABLE Language LIKE CountryLanguage");
/* set autocommit to off */
$mysqli->autocommit(FALSE);
/* Insert some values */
$mysqli->query("INSERT INTO Language VALUES ('DEU', 'Bavarian', 'F', 11.2)");
$mysqli->query("INSERT INTO Language VALUES ('DEU', 'Swabian', 'F', 9.4)");
/* commit transaction */
$mysqli->commit();
/* drop table */
$mysqli->query("DROP TABLE Language");
/* close connection */
$mysqli->close();
?>
In the example above:
the CREATE TABLE is auto committed because it's the default behaviour.
the INSERT INTO aren't auto committed because of the autocommit(FALSE).
the DROP TABLE is auto committed because the autocommit(FALSE) was reset by the ->commit();.
j0k is mainly right, except in the drop table.
The auto commit is not turned on with the ->commit()
Instead, the DROP TABLE is a DDL query, and DDL queries are always implicitly committed and will commit all your previously non committed work.
So, if you did not commit the work, the DDL query would force this commit.
How to use transactions in mysqli?
Prerequisite
In order for the transactions to behave properly you should enable exception error reporting. Otherwise mysqli will not report errors and the transaction will not be performed correctly. Alternatively, you could manually check each query, but that is not recommended. To connect properly with mysqli use the following 3 lines:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'pass', 'dbname');
$mysqli->set_charset('utf8mb4'); // always set the charset
Transactions only work with transactional tables. Make sure that your table storage engine supports transactions. For example, MyISAM ignores the commit/rollback.
Transactions
There are two possible ways to create a transaction using mysqli. By default all queries/statements are committed as soon as they are performed. You can either switch autocommit off or use a one-time-only transaction.
Transactions are committed to the database in the following situations:
when calling commit
after setting autocommit=1
when starting another transaction
when performing DDL query
and in a few other situations. For more information see Statements That Cause an Implicit Commit
Using autocommit(false)
If you turn autocommit off, you decide when you want to commit, but calling commit() does not switch autocommit back on.
//Start transaction
$mysqli->autocommit(false);
$mysqli->query('INSERT INTO director(name) VALUE("Steven Spielberg")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Jurassic Park';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
$mysqli->commit();
// Changes are committed, but autocommit is not switched back on
// Following queries are still transactional.
// They will not be committed unless you call commit or switch autocommit back on
$mysqli->query('INSERT INTO director(name) VALUE("James Cameron")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Titanic';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
$mysqli->autocommit(true);
// All queries are committed and everything that follows will be immediately committed.
Using begin_transaction()
You can start a one-time-only transaction using begin_transaction(). This does not set autocommit=false so when you call commit() you end the transaction without starting a new one.
//Start transaction
$mysqli->begin_transaction();
$mysqli->query('INSERT INTO director(name) VALUE("Steven Spielberg")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Jurassic Park';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
$mysqli->commit();
// Changes are committed and the transaction has ended
// Following queries will be committed one by one as soon as they are peformed.
$mysqli->query('INSERT INTO director(name) VALUE("James Cameron")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Titanic';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
Performing DDL statements
Some SQL statements trigger an explicit commit but do not affect the value of autocommit.
//Start transaction
$mysqli->autocommit(false);
$mysqli->query('INSERT INTO director(name) VALUE("Steven Spielberg")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Jurassic Park';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
// The following will call commit but it will not set autocommit=true
$mysqli->query('TRUNCATE TABLE movie_genre');
// if you want to switch autocommit back on, you have to call:
$mysqli->autocommit(true);
Rollback
If an exception occurs then PHP will end execution of the script and the code will never reach the commit statement. However, in some situations, you might want to roll back the transaction explicitly, for example to avoid calling commit accidentally somewhere else in the code.
Here is an example of what such a transaction would look like. The second query tries to insert into a non-existent table which means that mysqli will throw an exception. Instead of letting PHP script die, we catch the exception and roll back the transaction. The value 4 will never be inserted into the database because both queries were rolled back.
try {
// Start transaction
$mysqli->begin_transaction();
$mysqli->query('INSERT INTO some_table(col2) VALUE(4)');
$mysqli->query('INSERT INTO does_not_exist(col2) VALUE(4)');
// Commit changes
$mysqli->commit();
} catch (\Throwable $e) {
// Something went wrong. Rollback
$mysqli->rollback();
// Rethrow the exception so that PHP does not continue
// with the execution and the error can be logged in the error_log
throw $e;
}
Prepare SQL statement ONCE, and then execute it SEVERAL times:
<?php
$Mysqli = new mysqli("host","user","pass","base");
// check connection
if(mysqli_connect_errno())
{
printf("Connect failed: %s\n",mysqli_connect_error());
exit();
}
// some data for db insertion
$countries=['Austria','Belgia','Croatia','Denmark','Estonia'];
// explicitly begin DB transaction
$Mysqli->begin_transaction();
// prepare statement (for multiple inserts) only once
$stmt=$Mysqli->prepare("INSERT INTO table(column) VALUES(?)");
// bind (by reference) prepared statement with variable $country
$stmt->bind_param('s',$country);
// load value from array into referenced variable $country
foreach($countries as $country)
{
//execute prep stat more times with new values
//$country is binded (referenced) by statement
//each execute will get new $country value
if(!$stmt->execute())
{
// rollback if prep stat execution fails
$Mysqli->rollback();
// exit or throw an exception
exit();
}
}
// close prepared statement
$stmt->close();
// commit transaction
$Mysqli->commit();
// close connection
$Mysqli->close();
?>
You think that commit automatically switches autocommit back to true?
A comment in the PHP Doc says NO!

Updating record from PHP Client library for Exact Online

I had used PHP Client library for Exact Online.
I need to store the record, based on the condition if it exists or not. Since records are saving successfully. But unfortunately records are not updating.
$customer = [
'address' => 'No.22/N, 91 Cross, XYZ Street, ABC Road',
'address2' => 'DEF',
'city' => 'GHI',
'customerid' => '999',
'country' => 'DE',
'name' => 'Nishanth',
'zipcode' => '123456'
];
// Create a new account
$account->AddressLine1 = $customer['address'];
$account->AddressLine2 = $customer['address2'];
$account->City = $customer['city'];
$account->Code = $customer['customerid'];
$account->Country = $customer['country'];
$account->IsSales = 'true';
$account->Name = $customer['name'];
$account->Postcode = $customer['zipcode'];
$account->Email = 'nishanth#gmail.com';
$account->Status = 'C';
From the above piece of code, based on the condition the record needs to be updated or saved from the below coding snippets. Followed two approaches:
I Approach:
if($AccInfo)
{ // Update
$AccInfo->AddressLine1 = $customer['address'];
$AccInfo->AddressLine2 = $customer['address2'];
$AccInfo->City = $customer['city'];
$updateAcc = $AccInfo->update();
}
else
{ // Save
$savedAcc = $Accounts->save();
}
Result:
Warning: Attempt to assign property 'AddressLine1' of non-object in E:\xampp\htdocs\exact-php-client-master\example\example.php on line 506
Warning: Attempt to assign property 'AddressLine2' of non-object in E:\xampp\htdocs\exact-php-client-master\example\example.php on line 507
Warning: Attempt to assign property 'City' of non-object in E:\xampp\htdocs\exact-php-client-master\example\example.php on line 508
Fatal error: Uncaught Error: Call to a member function update() on array in E:\xampp\htdocs\exact-php-client-master\example\example.php:510 Stack trace: #0 {main} thrown in E:\..\..\exact-php-client-master\example\example.php on line 510
II Approach:
if($AccInfo)
{ // Update
$updateAcc = $Accounts->update();
}
else
{ // Save
$savedAcc = $Accounts->save();
}
Result:
Picqer\Financials\Exact\ApiException : Error 400: Bad Request - Error in query syntax.
How should we need to update the records to Exact Online Dashboard?
Finally I solved the issue by writing custom methods
$AccInfo = $Accounts->filter("Email eq 'nishanthjay#gmail.com'");
if($AccInfo)
{ // Update
$updateAcc = $Accounts->customUpdate($AccInfo[0]->ID);
echo '<pre>'; print_r($updateAcc);
}
else
{ // Save
$savedAcc = $Accounts->save();
}
I had written my own methods from ..\src\Picqer\Financials\Exact\Persistance\Storable.php
public function customUpdate($primaryKey='')
{
$this->fill($this->update2($primaryKey));
return $this;
}
public function update2($primaryKey='')
{
return $this->connection()->put($this->url() . "(guid'$primaryKey')", $this->json());
}
For any one who knows exactly how to update to an ExactOnline. You are always welcome to answer to the posted question via built-In function call known as update().

Add or update MySQL

I don't know how to proceed to do the following thing on my specific table.
Let's say I have the following table param, with 3 columns tck, label, value . tck is my primary key.
The data are coming once everyday. I would like to update the value of the existing tck, and if the data sent contain a new tck, I would like to add it to the table...
I hope I'm clear enough... Thank you for your help.
The code I'm using is the following one :
<?php try {
$bdd = new PDO('mysql:host='.$_ENV['localhost'].';dbname=eip_tasks','root'); } catch(Exception $e) {
die('Erreur : '.$e->getMessage()); }
$data = $_POST['field1'];
$phpArray = json_decode($data, true); foreach ($phpArray as $u) {
//$req = $bdd->prepare('INSERT INTO param (tck, label, value) VALUES(:tck, :label, :value)');
$req = $bdd->prepare('UPDATE param SET value=:value WHERE tck=:tck');
$req->execute(array(
':tck'=>$u['tck'],
':value'=>$u['value']
)); } ?>
Here is the code I'm using now :
<?php
try
{
$bdd = new PDO('mysql:host='.$_ENV['localhost'].';dbname=eip_tasks','root');
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}
$data = $_POST['field1'];
$phpArray = json_decode($data, true);
$sourceTck = array();
foreach ($phpArray as $u) {array_push($sourceTck, $u['tck']);
$req = $bdd->prepare("INSERT INTO param (tck, label, value) VALUES (:tck, :label, :value) ON DUPLICATE KEY UPDATE value=:value");
$req->execute(
array(
':tck'=>$u['tck'],
':label'=>$u['label'],
':value'=>$u['value']
)
);
}
if(count($sourceTck) > 0){
$sourceTckClause = implode("," , $sourceTck);
$req = $bdd->prepare("DELETE FROM param WHERE tck NOT IN ($sourceTckClause)");
$req->execute();
}
?>
Use ON DUPLICATE KEY syntax to update the row instead of insert (if tck was exist):
$req = $bdd->prepare("
INSERT INTO param (tck, label, value)
VALUES
(:tck, :label, :value)
ON DUPLICATE KEY UPDATE value=:value
");
Update: Also don't forget to bind :label. As your comment, To delete a data which would be in the table and not in the source, You should push source tck values to an array and then run a delete query where NOT IN your array:
$sourceTck = array();
foreach ($phpArray as $u) {
array_push($sourceTck, $u['tck']);
$req = $bdd->prepare("
INSERT INTO param (tck, label, value)
VALUES
(:tck, :label, :value)
ON DUPLICATE KEY UPDATE value=:value
");
$req->execute(
array(':tck'=>$u['tck'], ':label'=>$u['label'], ':value'=>$u['value'])
);
}
I found this answer useful to prepare and bind tck values, But for small solution, you can convert array values to int to prevent sql injection in this case:
if(count($sourceTck) > 0){
$sourceTckClause = implode("," , array_map('intval', $sourceTck));
$bdd->query("DELETE FROM param WHERE tck NOT IN ($sourceTckClause)");
}
What you want to do is called an upsert or merge. MySQL does support it.
INSERT INTO param VALUES (:tck, :label, :value)
ON DUPLICATE KEY UPDATE value = :value
-- change :label as well? , label = :label

insert query through php doesn't update MySQL database

I have user input coming in from a form (possible null fields - yes, the database is set up to work with null values), and I need to insert the information into a table in the database.
Just some more information: I'm making this application in Titanium
This is the code that sends all the parameters to the PHP file:
saveButton.addEventListener('click', function() {
var xhr = Ti.Network.createHTTPClient();
var url = ""; //url is in here; I just took it out for privacy
xhr.open("GET", url);
var params = {
query : "enterAsset",
barcode : barcodeTextField.value, //all these textfields are editable by the user
assetClass : assetClassTextField.value,
manufacturer : manufacturerTextField.value,
model : modelTextField.value,
serialNum : serialNumTextField.value,
custodian : custodianTextField.value,
status : statusTextField.value,
loginName : LOGIN_NAME,
divisionID : DIVISION_ID,
dateBuy : dateBoughtTextField.value,
priceBuy : priceTextField.value,
dateInSvc : dateInServiceTextField.value,
dateLastSvc : dateLastServiceTextField.value,
};
xhr.onload = function() {
alert("Successful entry"); //this alert does appear when the button is pressed
};
xhr.send(params);
});
This is the code in the PHP file after connecting to the database:
$query = $_GET['query'];
switch($query) { //this switch statement exists so we can access the database for multiple queries from the same .php file. We know it works because the "login" query works just fine.
case "data":
//unimportant stuff is in here
break;
case "clients":
//more irrelevant stuff in here
break;
case "login": //this works, but it's not trying to insert anything
$username = $_GET['username'];
$password = $_GET['password'];
$stmt4 = $con->prepare('CALL Get_user_auth(:username,:password)');
$stmt4->bindParam(':username',$username,PDO::PARAM_STR);
$stmt4->bindParam(':password',$password,PDO::PARAM_STR);
$stmt4->execute();
$results = $stmt4->fetchAll(PDO::FETCH_ASSOC);
$stmt4->closeCursor();
break;
case "search":
//more irrelevant stuff in here
break;
case "enterAsset":
$barcode = '12345';
$assetClass = 'test';
$manufacturer = 'test';
$model = 'test';
$serialNum = 'test';
$custodian = 'test';
$locationID = '1';
$status = 'test';
$dateBuy = 'test';
$priceBuy = 'test';
$dateInSvc = 'test';
$dateLastSvc = 'test';
$loginName = 'jane';
$divisionID = '1';
$stmt6 = $con->prepare('CALL Enter_new_asset(:divisionID,:barcode,:assetClass,:manufacturer,:model,:serialNum,:custodian,:status,:locationID,:dateBuy,:priceBuy,:dateInSvc,:dateLastSvc,:loginName)');
$stmt6->bindParam(':divisionID',$divisionID,PDO::PARAM_INT,11);
$stmt6->bindParam(':barcode',$barcode,PDO::PARAM_STR,128);
$stmt6->bindParam(':assetClass',$assetClass,PDO::PARAM_STR,10);
$stmt6->bindParam(':manufacturer',$manufacturer,PDO::PARAM_STR,10);
$stmt6->bindParam(':model',$model,PDO::PARAM_STR,10);
$stmt6->bindParam(':serialNum',$serialNum,PDO::PARAM_STR,20);
$stmt6->bindParam(':custodian',$custodian,PDO::PARAM_STR,20);
$stmt6->bindParam(':status',$status,PDO::PARAM_STR,10);
$stmt6->bindParam(':locationID',$locationID,PDO::PARAM_INT,11);
$stmt6->bindParam(':dateBuy',$dateBuy,PDO::PARAM_STR,13);
$stmt6->bindParam(':priceBuy',$priceBuy,PDO::PARAM_STR,10);
$stmt6->bindParam(':dateInSvc',$dateInSvc,PDO::PARAM_STR,13);
$stmt6->bindParam(':dateLastSvc',$dateLastSvc,PDO::PARAM_STR,13);
$stmt6->bindParam(':loginName',$loginName,PDO::PARAM_STR,20);
$stmt6->execute();
$stmt6->closeCursor();
break;
default:
$results = "FAIL.";
break;
}
This returns "null" when I specify the "enterAsset" query, which is expected because it shouldn't be returning anything.
The stored query for Enter_new_asset is:
INSERT INTO TBL_ASSET_DATA (Division_ID, Barcode_Tag, Asset_Class, Manufacturer, Model, Serial_Num, Custodian, Status, Location_ID, Date_buy, Price_buy, Date_in_svc, Date_last_svc, Updated_by)
VALUES(divisionID,barcode, assetClass, manufacturer, model, serialNum, custodian, status, locationID, dateBuy, priceBuy, dateInSvc, dateLastSvc, loginName)
EDIT: I tried hardcoding in some values for the variables, and now I'm getting this error: Parse error: syntax error, unexpected '';' (T_CONSTANT_ENCAPSED_STRING) in /homepages/21/d265224452/htdocs/brillient_wordpress/AMproxy.php on line 90
This is the code at line 90:
$stmt6 = $con->prepare('CALL Enter_new_asset(:divisionID,:barcode,:assetClass,:manufacturer,:model,:serialNum,:custodian,:status,:locationID,:dateBuy,:priceBuy,:dateInSvc,:dateLastSvc,:loginName)');
My question is: Why is the database not updating with the entered information? A new entry is not appearing in the TBL_ASSET_DATA. Other questions with this issue seem to be using MySQLi or the deprecated mysql commands, and this is using PDO.
Thank you for your help in advance.
EDIT I got my database to update by replacing
$stmt6 = $con->prepare('CALL Enter_new_asset(:divisionID,:barcode,:assetClass,:manufacturer,:model,:serialNum,:custodian,:status,:locationID,:dateBuy,:priceBuy,:dateInSvc,:dateLastSvc,:loginName)');
with:
$sql = "INSERT INTO TBL_ASSET_DATA(Division_ID, Barcode_Tag, Asset_Class, Manufacturer, Model, Serial_Num, Custodian, Status, Location_ID, Date_buy, Price_buy, Date_in_svc, Date_last_svc, Updated_by) VALUES(:divisionID,:barcode, :assetClass, :manufacturer, :model, :serialNum, :custodian, :status, :locationID, :dateBuy, :priceBuy, :dateInSvc, :dateLastSvc, :loginName)";
$stmt6 = $con->prepare($sql);
but I'm wondering whether this is secure.
You seem to be missing a quote to close your string here:
$dateLastSvc = 'test;
This is making you code behave incorrectly. You can easily pick ushc typo errors if using an editor with context highlighting. For example, I could pick this up right away in looking at the highlighting that even Stack Overflow does.

Invalid PDO query does not return an error

The second SQL statement below returns an error in phpMyAdmin:
SET #num=2000040;
INSERT INTO artikel( artikel_nr, lieferant_nr, bezeichnung_1, bezeichnung_1 )
SELECT #num := #num +1 AS anum, 70338, f2, f3
FROM import
WHERE id >1
MySQL says:
#1110 - Column 'bezeichnung_1' specified twice
All correct. But when I run the queries in Symfony 1.4 with this function:
// run sql query
// http://erisds.co.uk/symfony/snippet-creating-debugging-complex-sql-queries-in-symfony
// http://stackoverflow.com/questions/5434702/php-quick-refactoring
// param $sql: the query to run
// param $silent: bool if errors should be ignored
// throws: pdo error info if statement failed and $silent=false
// returns: pdo-statement (use for looping over result rows and error messages)
public static function runQuery($sql, $silent=false)
{
$conn = Propel::getConnection();
$pdo_statement = $conn->prepare($sql);
$error = null;
try
{
$pdo_statement->execute();
}
catch (Exception $e)
{
$error = $e->getMessage();
}
if ( !$error )
{
$pdo_error = $pdo_statement->errorInfo();
$error = $pdo_error[2];
}
if ( !$silent && $error ) throw new Exception($error);
return $pdo_statement;
}
no error is thrown. The two SQL statements must be submitted at the same time since they depend on each other. The faulty query is constructed from user input. I need to get that error back, otherwise I can't tell if the database was changed, and I can't tell the user about it.
Do you know why PDO doesn't complain about the invalid statement, and if it can't be made to do so, how to get the success/failure information?
BTW the query does update the database if there are no duplicate columns.
Here's the link to the PDOStatement class: http://www.php.net/manual/en/class.pdostatement.php
This is expected behavior. Since there are two statements and the first one is valid, you have to use nextRowset()
try
{
$pdo_statement->execute();
while ($pdo_statement->nextRowset()) {/* https://bugs.php.net/bug.php?id=61613 */};
}
Source: bugs.php.net/bug.php?id=61613
By default PDOStatement::execute() doesn't throw any exception, it simply returns false on error. You have to set error handling to PDO::ERRMODE_EXCEPTION through db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION).
If you have the option to use mysqli instead of PDO for the multi query, you can use mysqli_multi_query. As error handling is a little complex, here is my function:
/**
* Executes multiple queries at once (separated by semicolons) and discards the results
*
* #param string $sql
* #throws RuntimeException if a query failed
*/
function multi_query($sql) {
$mysqli = new mysqli('host', 'user', 'password', 'database');
//Execute all queries
$index = 1;
if ($mysqli->multi_query($sql)) {
do {
// next_result() fails if store_result() is not called
if ($result = $mysqli->store_result()) {
$result->free();
}
$has_next = $mysqli->more_results();
if (!$has_next)
return; // all queries successfully executed
$index++;
} while ($mysqli->next_result());
}
// At this point, either the multi_query() has returned false - which is
// when the first query failed - or more_results() was true, while next_result()
// returned false - which is when a different query failed.
$error = $mysqli->error;
if (!$error)
$error = $mysqli->errno ? "errno $mysqli->errno" : '(unknown error)';
throw new RuntimeException("mysqli query $index failed: $error");
}