mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 14:26:54 +08:00
Merge pull request #3378 from yiisoft/db-integrity-exception
Database IntegrityException
This commit is contained in:
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
25
framework/db/IntegrityException.php
Normal file
25
framework/db/IntegrityException.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
@ -72,6 +72,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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,14 @@ class Schema extends \yii\db\Schema
|
||||
'enum' => self::TYPE_STRING,
|
||||
];
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
23
tests/unit/framework/db/pgsql/PostgreSQLCommandTest.php
Normal file
23
tests/unit/framework/db/pgsql/PostgreSQLCommandTest.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user