Commit e4bd3b59 by Alexander Makarov

Merge pull request #3378 from yiisoft/db-integrity-exception

Database IntegrityException
parents b4914412 11cded9b
......@@ -11,6 +11,7 @@ use yii\base\Object;
use yii\caching\Cache;
use Yii;
use yii\caching\GroupDependency;
use yii\db\Exception;
/**
* Schema represents the Sphinx schema information.
......@@ -499,4 +500,22 @@ class Schema extends Object
return $column;
}
/**
* Handles database error
*
* @param \Exception $e
* @param string $rawSql SQL that produced exception
* @throws Exception
*/
public function handleException(\Exception $e, $rawSql)
{
if ($e instanceof Exception) {
throw $e;
} else {
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int) $e->getCode(), $e);
}
}
}
......@@ -27,6 +27,7 @@ Yii Framework 2 Change Log
- Bug #3368: Fix for comparing numeric attributes in JavaScript (technixp)
- Bug: Fixed inconsistent return of `\yii\console\Application::runAction()` (samdark)
- Enh #2264: `CookieCollection::has()` will return false for expired or removed cookies (qiangxue)
- Enh #2435: `yii\db\IntegrityException` is now thrown on database integrity errors instead of general `yii\db\Exception` (samdark)
- Enh #2837: Error page now shows arguments in stack trace method calls (samdark)
- Enh #2906: Added support for using conditional comments for js and css files registered through asset bundles and Html helper (exromany, qiangxue)
- Enh #2942: Added truncate and truncateWord methods (Alex-Code, samdark)
......
......@@ -284,13 +284,7 @@ class Command extends \yii\base\Component
return $n;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
if ($e instanceof Exception) {
throw $e;
} else {
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int) $e->getCode(), $e);
}
$this->db->getSchema()->handleException($e, $rawSql);
}
}
......@@ -423,13 +417,7 @@ class Command extends \yii\base\Component
return $result;
} catch (\Exception $e) {
Yii::endProfile($token, 'yii\db\Command::query');
if ($e instanceof Exception) {
throw $e;
} else {
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int) $e->getCode(), $e);
}
$this->db->getSchema()->handleException($e, $rawSql);
}
}
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* Exception represents an exception that is caused by violation of DB constraints.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class IntegrityException extends Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return 'Integrity constraint violation';
}
}
......@@ -73,6 +73,14 @@ abstract class Schema extends Object
private $_builder;
/**
* @var array map of DB errors and corresponding exceptions
* If left part is found in DB error message exception class from the right part is used.
*/
public $exceptionMap = [
'SQLSTATE[23' => 'yii\db\IntegrityException',
];
/**
* Loads the metadata for the specified table.
* @param string $name table name
* @return TableSchema DBMS-dependent table metadata, null if the table does not exist.
......@@ -470,4 +478,29 @@ abstract class Schema extends Object
return 'string';
}
}
/**
* Handles database error
*
* @param \Exception $e
* @param string $rawSql SQL that produced exception
* @throws Exception
*/
public function handleException(\Exception $e, $rawSql)
{
if ($e instanceof Exception) {
throw $e;
} else {
$exceptionClass = '\yii\db\Exception';
foreach ($this->exceptionMap as $error => $class) {
if (strpos($e->getMessage(), $error) !== false) {
$exceptionClass = $class;
}
}
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new $exceptionClass($message, $errorInfo, (int) $e->getCode(), $e);
}
}
}
......@@ -65,6 +65,14 @@ class Schema extends \yii\db\Schema
];
/**
* @var array map of DB errors and corresponding exceptions
* If left part is found in DB error message exception class from the right part is used.
*/
public $exceptionMap = [
'Operation would have caused one or more unique constraint violations' => 'yii\db\IntegrityException',
];
/**
* @inheritdoc
*/
public function releaseSavepoint($name)
......
......@@ -287,4 +287,16 @@ class CommandTest extends DatabaseTestCase
public function testDropIndex()
{
}
public function testIntegrityViolation()
{
$this->setExpectedException('\yii\db\IntegrityException');
$db = $this->getConnection();
$sql = 'INSERT INTO profile(id, description) VALUES (123, \'duplicate\')';
$command = $db->createCommand($sql);
$command->execute();
$command->execute();
}
}
<?php
namespace yii\tests\unit\framework\db\pgsql;
use yiiunit\framework\db\CommandTest;
class PostgreSQLCommandTest extends CommandTest
{
public $driverName = 'pgsql';
public function testAutoQuoting()
{
$db = $this->getConnection(false);
$sql = 'SELECT [[id]], [[t.name]] FROM {{customer}} t';
$command = $db->createCommand($sql);
$this->assertEquals('SELECT "id", "t"."name" FROM "customer" t', $command->sql);
}
public function testBindParamValue()
{
$this->markTestIncomplete('TODO: impement it');
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment