Commit bbbe2a3e by Carsten Brandt

Added support for transaction isolation levels

fixes #3220
parent 9637fa1d
......@@ -68,6 +68,7 @@ Yii Framework 2 Change Log
- Enh #3132: `yii\rbac\PhpManager` now supports more compact data file format (qiangxue)
- Enh #3154: Added validation error display for `GridView` filters (ivan-kolmychek)
- Enh #3196: Masked input upgraded to use jquery.inputmask plugin with more features. (kartik-v)
- Enh #3220: Added support for setting transation isolation levels (cebe)
- Enh #3222: Added `useTablePrefix` option to the model generator for Gii (horizons2)
- Enh #3230: Added `yii\filters\AccessControl::user` to support access control with different actors (qiangxue)
- Enh #3232: Added `export()` and `exportAsString()` methods to `yii\helpers\BaseVarDumper` (klimov-paul)
......
......@@ -410,16 +410,18 @@ class Connection extends Component
/**
* Starts a transaction.
* @param string|null $isolationLevel The isolation level to use for this transaction.
* See [[Transaction::begin()]] for details.
* @return Transaction the transaction initiated
*/
public function beginTransaction()
public function beginTransaction($isolationLevel = null)
{
$this->open();
if (($transaction = $this->getTransaction()) === null) {
$transaction = $this->_transaction = new Transaction(['db' => $this]);
}
$transaction->begin();
$transaction->begin($isolationLevel);
return $transaction;
}
......
......@@ -340,6 +340,19 @@ abstract class Schema extends Object
}
/**
* Sets the isolation level of the current transaction.
* @param string $level The transaction isolation level to use for this transaction.
* This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], [[Transaction::REPEATABLE_READ]]
* and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific syntax to be used
* after `SET TRANSACTION ISOLATION LEVEL`.
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
*/
public function setTransactionIsolationLevel($level)
{
$this->db->createCommand("SET TRANSACTION ISOLATION LEVEL $level;")->execute();
}
/**
* Quotes a string value for use in a query.
* Note that if the parameter is not a string, it will be returned without change.
* @param string $str string to be quoted
......
......@@ -39,6 +39,27 @@ use yii\base\InvalidConfigException;
class Transaction extends \yii\base\Object
{
/**
* A constant representing the transaction isolation level `READ UNCOMMITTED`.
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
*/
const READ_UNCOMMITTED = 'READ UNCOMMITTED';
/**
* A constant representing the transaction isolation level `READ COMMITTED`.
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
*/
const READ_COMMITTED = 'READ COMMITTED';
/**
* A constant representing the transaction isolation level `REPEATABLE READ`.
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
*/
const REPEATABLE_READ = 'REPEATABLE READ';
/**
* A constant representing the transaction isolation level `SERIALIZABLE`.
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
*/
const SERIALIZABLE = 'SERIALIZABLE';
/**
* @var Connection the database connection that this transaction is associated with.
*/
public $db;
......@@ -47,6 +68,7 @@ class Transaction extends \yii\base\Object
*/
private $_level = 0;
/**
* Returns a value indicating whether this transaction is active.
* @return boolean whether this transaction is active. Only an active transaction
......@@ -59,9 +81,15 @@ class Transaction extends \yii\base\Object
/**
* Begins a transaction.
* @param string|null $isolationLevel The [isolation level][] to use for this transaction.
* This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but
* also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.
* If not specified (`null`) the isolation level will not be set explicitly and the DBMS default will be used.
*
* [isolation level]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
* @throws InvalidConfigException if [[db]] is `null`.
*/
public function begin()
public function begin($isolationLevel = null)
{
if ($this->db === null) {
throw new InvalidConfigException('Transaction::db must be set.');
......@@ -69,7 +97,10 @@ class Transaction extends \yii\base\Object
$this->db->open();
if ($this->_level == 0) {
Yii::trace('Begin transaction', __METHOD__);
if ($isolationLevel !== null) {
$this->db->getSchema()->setTransactionIsolationLevel($isolationLevel);
}
Yii::trace('Begin transaction' . ($isolationLevel ? ' with isolation level ' . $isolationLevel : ''), __METHOD__);
$this->db->pdo->beginTransaction();
$this->_level = 1;
......@@ -141,4 +172,25 @@ class Transaction extends \yii\base\Object
throw new Exception('Roll back failed: nested transaction not supported.');
}
}
/**
* Sets the transaction isolation level for this transaction.
*
* This method can be used to set the isolation level while the transaction is already active.
* However this is not supported by all DBMS so you might rather specify the isolation level directly
* when calling [[begin()]].
* @param string $level The transaction isolation level to use for this transaction.
* This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but
* also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.
* @throws Exception if the transaction is not active
* @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
*/
public function setIsolationLevel($level)
{
if (!$this->getIsActive()) {
throw new Exception('Failed to set isolation level: transaction was inactive.');
}
Yii::trace('Setting transaction isolation level to ' . $level, __METHOD__);
$this->db->getSchema()->setTransactionIsolationLevel($level);
}
}
......@@ -7,8 +7,10 @@
namespace yii\db\sqlite;
use yii\base\NotSupportedException;
use yii\db\TableSchema;
use yii\db\ColumnSchema;
use yii\db\Transaction;
/**
* Schema is the class for retrieving metadata from a SQLite (2/3) database.
......@@ -249,4 +251,27 @@ class Schema extends \yii\db\Schema
return $column;
}
/**
* Sets the isolation level of the current transaction.
* @param string $level The transaction isolation level to use for this transaction.
* This can be either [[Transaction::READ_UNCOMMITTED]] or [[Transaction::SERIALIZABLE]].
* @throws \yii\base\NotSupportedException when unsupported isolation levels are used.
* SQLite only supports SERIALIZABLE and READ UNCOMMITTED.
* @see http://www.sqlite.org/pragma.html#pragma_read_uncommitted
*/
public function setTransactionIsolationLevel($level)
{
switch($level)
{
case Transaction::SERIALIZABLE:
$this->db->createCommand("PRAGMA read_uncommitted = False;")->execute();
break;
case Transaction::READ_UNCOMMITTED:
$this->db->createCommand("PRAGMA read_uncommitted = True;")->execute();
break;
default:
throw new NotSupportedException(get_class($this) . ' only supports transaction isolation levels READ UNCOMMITTED and SERIALIZABLE.');
}
}
}
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