I'm refactoring some code which consists in multiple insert queries, to do so I'm converting every insert method that looks like this:
$code = Yii::$app->db->createCommand()
->insert('code', $codeData
)->execute();
To something like this:
$codeQuery = Yii::$app->db->createCommand()
->insert('code', $codeData);
return $codeQuery;
And in an another method which executes the transaction:
$transaction = Yii::$app->db->beginTransaction();
try{
Yii::$app->db->createCommand($codeQuery)->execute();
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollBack();
throw $e;
} catch (\Throwable $e) {
$transaction->rollBack();
throw $e;
}
But I got the error:
Object of class yii\db\Command could not be converted to string
This post: post doesn't really helped me.
I'm doubtful if that correct choice is to rewrite every single insert command in something that the transaction method can execute, or converting directly my current insert command output in something viable.
If you already had assigned $codeQuery = Yii::$app->db->createCommand()->insert('code', $codeData) than you don't need to pass $codeQuery into another createCommand method. This method can read string of sql as variable.
If you want to transform command into string, make a conversion:
$str = $codeQuery->getRawSql(); // get sql statement
$res = $codeQuery->execute(); // execute sql statement
Related
Currently I'm testing a execution of a query using laravel 5.4.
I'm trying to print is a query was executed on a transaction, for example:
\DB::enableQueryLog();
try {
\DB::beginTransaction();
$item = new Item();
$item->name = 'some name';
// ...
$item->save();
\DB::commit();
dump(\DB::getQueryLog());
} catch (\Exception $e) {
But query log only show the information of the sql statement, but not tells me complete transaction sql:
I want to print the execution with the transaction.
How do I show the error mysql throws when I try to insert data into a custom table and the insert fails?
For example, below a bit of code that should(will) fail with an SQL error.
$insert = "some insert sql statement that will fail";
$myquery = $modx->query($insert);
if(!$myquery){
echo 'error occurred! <br>';
}
How do I return what the error actually was [i.e. column mismatch, unique id exists etc.]?
There is a little bit easier way to track your custom xpdo request.
$c = $modx->newQuery('modResource');
$c->where(array(
'id1' => 1
));
// print request for control
print_r($c->toSQL());
$s = $c->prepare();
$s->execute();
print_r($s->errorInfo());
After execution we can catch an error:
Array ( [0] => 42S22 [1] => 1054 [2] => Unknown column 'modResource.id1' in 'where clause' )
It's all because xpdo use pdo and controls execution with it's help. Some code from xpdo source:
/**
* #see http://php.net/manual/en/function.pdo-errorinfo.php
*/
public function errorInfo() {
if (!$this->connect()) {
return false;
}
return $this->pdo->errorInfo();
}
Based on the examples in the xPDO Getting Started guide, $modx in this context appears to be a class extending PDO and the result resource object $myquery is likely a PDOStatement object.
You can therefore set an exception error mode on $modx as you would with a normal PDO object.
$modx->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
to force it to throw errors on failure. By default, PDO errors silently; its error modes are documented here.
Correction: Looking into the xPDO source it does not extend PDO, but rather contains a PDO object as a property and implement PDO methods, passing them through to its connection property. So the setAttribute() call will be passed through to the underlying PDO object and should work accordingly.
The xPDO constructor extends functionality from a normal PDO constructor slightly, and accepts an array of options in the 5th parameter where you may set the error mode, rather than setting it later via setAttribute():
$xpdo = new xPDO($dsn, $user, $password, [], [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION]);
Whichever method you choose to set it, you may wrap your code in a try/catch block to catch exceptions of type PDOException on error:
try {
$insert = "some insert sql statement that will fail";
$myquery = $modx->query($insert);
}
catch (PDOException $e) {
echo 'error occurred! ' . $e->getMessage() . '<br>';
}
You may also more simply set the errormode to PDO::ERRMODE_WARNING and PHP will instead just emit E_WARNING messages, which unlike exceptions, are non-fatal.
I was able to verify all of this works as expected by setting up a quick test with xPDO.
public function smart_query($query, $options = null, $bindoptions = null)
{
// Code to Reconnect incase of timeout
try {
$this->db->query('SELECT * FROM templates');
}
catch (PDOException $e)
{
echo $e;
$pdooptions = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->db = new PDO("mysql:host=localhost;dbname=$this->database", "$this->username", "$this->password", $pdooptions);
}
$this->statement = $this->db->prepare($query);
if($bindoptions != null)
{
$this->bind($bindoptions);
}
$this->execute();
if($options != null)
{
// Return Single Row
if($options['rows'] == 1)
{
return $this->statement->fetch(PDO::FETCH_ASSOC);
}
// Return Multiple Rows
elseif($options['rows'] != 1)
{
return $this->statement->fetchAll(PDO::FETCH_ASSOC);
}
}
}
I've saw this code today, and got really confused.
It looks like he is trying to process a simple query, before doing the actual query.
Why is he checking if the connection is still open?
I thought that PDO only destroys it's connection upon script finishing executing automatically?
Is that correct to check if it's open or closed?
This implements a form of lazy loading.
This first time a query is executed through this class/function, the database connection may not be established yet. This is the purpose of this check, so that the consumer (you) does not have to mind about it.
The connection is then stored in the $this->db class member, for future reuse when you call this method again in the course of your script (and yes, this connection will stay open until the script ends -- unless it is closed explicitely beforehand, of course).
For information, this check is slightly inefficient. A simple $this->db->query('SELECT 1') would suffice, without the need to read a table at all.
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");
}
I've run into an issue that I'm hoping to get a little help on. I'm using the following:
Kohana 3.0.7
PostgreSQL 8.4
Transactions in PostgreSQL using
$db->query(NULL, 'BEGIN', FALSE)
$db->query(NULL, 'ROLLBACK', FALSE);
$db->query(NULL, 'COMMIT', FALSE);
The issue is that when I send a query to the database that results in a postgres error within a transaction my system freezes up. When I send the same query to the database without wrapping it in a transaction the PDO error is reported back just as expected. Here is an exmaple:
This first example works fine and duplicate key value violates unique constraint "pk_test_table" error is returned:
$query = DB::query(Database::INSERT, 'INSERT INTO test_table (test_table_id, test_table_val) VALUES (:id, :value)';
$query->param(':id', 1);
$query->param(':value', "test value");
try
{
$result = $query->execute($db);
}
catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
}
This second example causes my system to freeze (I can't tell if it's an infinite loop, or some other freeze):
$db->query(NULL, 'BEGIN', FALSE);
$query = DB::query(Database::INSERT, 'INSERT INTO test_table (test_table_id, test_table_val) VALUES (:id, :value)';
$query->param(':id', 1);
$query->param(':value', "test value");
try
{
$result = $query->execute($db);
}
catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
}
$db->query(NULL, 'ROLLBACK', FALSE);
As you can see the only difference is that the second example is wrapped in a transaction.
Any ideas on what is going on? Any suggestions for things to try?
I found a way to work around the issue. Not sure if this is a bug in PDO or some other part of the tool set but what I'm doing to work around is the following:
$exception_exists = FALSE;
$db->query(NULL, 'BEGIN', FALSE);
$query = DB::query(Database::INSERT, 'INSERT INTO test_table (test_table_id, test_table_val) VALUES (:id, :value)';
$query->param(':id', 1);
$query->param(':value', "test value");
try
{
$result = $query->execute($db);
}
catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
$exception_exists = TRUE;
}
if (!$exception_exists)
{
$db->query(NULL, 'ROLLBACK', FALSE);
}
By adding the variable $exception_exists in the catch I can then act of that if there is no exception. If there is an exception and I try to ROLLBACK or COMMIT then I get the freezing behavior.
This works for now but I wouldn't call it elegant.