-
-
Save doctrinebot/ca86c14e7e14e6a6d2da to your computer and use it in GitHub Desktop.
Attachments to Doctrine Jira Issue DDC-178 - https://github.com/doctrine/doctrine2/issues/2432
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Property changes on: tests | |
___________________________________________________________________ | |
Added: svn:ignore | |
+ mysql.xml | |
coverage.xml | |
oracle.xml | |
.postgres.xml.swp | |
postgres.xml | |
run-all.sh | |
sqlite.xml | |
Index: tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | |
=================================================================== | |
--- tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php (revision 6884) | |
+++ tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php (working copy) | |
@@ -15,11 +15,14 @@ | |
$this->_em = $this->_getTestEntityManager(); | |
} | |
- public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed) | |
+ public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed, array $queryHints=array()) | |
{ | |
try { | |
$query = $this->_em->createQuery($dqlToBeTested); | |
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); | |
+ foreach ($queryHints AS $name => $value) { | |
+ $query->setHint($name, $value); | |
+ } | |
parent::assertEquals($sqlToBeConfirmed, $query->getSql()); | |
$query->free(); | |
} catch (Doctrine_Exception $e) { | |
@@ -462,6 +465,31 @@ | |
$this->_em->getConnection()->setDatabasePlatform($oldPlat); | |
} | |
+ | |
+ public function testPessimisticLockQueryHint() | |
+ { | |
+ $this->assertSqlGeneration( | |
+ "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", | |
+ "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ". | |
+ "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", | |
+ array(Query::HINT_LOCK_MODE => \Doctrine\ORM\LockMode::PESSIMISTIC) | |
+ ); | |
+ } | |
+ | |
+ public function testPessimisticNoWaitLockQueryHint() | |
+ { | |
+ $oldPlat = $this->_em->getConnection()->getDatabasePlatform(); | |
+ $this->_em->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform); | |
+ | |
+ $this->assertSqlGeneration( | |
+ "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", | |
+ "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ". | |
+ "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE NOWAIT", | |
+ array(Query::HINT_LOCK_MODE => \Doctrine\ORM\LockMode::PESSIMISTIC_NOWAIT) | |
+ ); | |
+ | |
+ $this->_em->getConnection()->setDatabasePlatform($oldPlat); | |
+ } | |
/* Not yet implemented, needs more thought | |
Index: lib/Doctrine/DBAL/Platforms/OraclePlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/OraclePlatform.php (revision 6884) | |
+++ lib/Doctrine/DBAL/Platforms/OraclePlatform.php (working copy) | |
@@ -34,14 +34,6 @@ | |
class OraclePlatform extends AbstractPlatform | |
{ | |
/** | |
- * Constructor. | |
- */ | |
- public function __construct() | |
- { | |
- parent::__construct(); | |
- } | |
- | |
- /** | |
* return string to call a function to get a substring inside an SQL statement | |
* | |
* Note: Not SQL92, but common functionality. | |
@@ -617,4 +609,9 @@ | |
{ | |
return false; | |
} | |
+ | |
+ public function getForUpdateNoWaitSql() | |
+ { | |
+ return 'FOR UPDATE NOWAIT'; | |
+ } | |
} | |
Index: lib/Doctrine/DBAL/Platforms/SqlitePlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/SqlitePlatform.php (revision 6884) | |
+++ lib/Doctrine/DBAL/Platforms/SqlitePlatform.php (working copy) | |
@@ -456,4 +456,9 @@ | |
{ | |
return 'sqlite'; | |
} | |
+ | |
+ public function getForUpdateSql() | |
+ { | |
+ return ''; | |
+ } | |
} | |
Index: lib/Doctrine/DBAL/Platforms/AbstractPlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/AbstractPlatform.php (revision 6884) | |
+++ lib/Doctrine/DBAL/Platforms/AbstractPlatform.php (working copy) | |
@@ -460,6 +460,11 @@ | |
return 'FOR UPDATE'; | |
} | |
+ public function getForUpdateNoWaitSql() | |
+ { | |
+ return $this->getForUpdateSql(); | |
+ } | |
+ | |
public function getDropDatabaseSql($database) | |
{ | |
return 'DROP DATABASE ' . $database; | |
Index: lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php (revision 6884) | |
+++ lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php (working copy) | |
@@ -34,16 +34,6 @@ | |
class PostgreSqlPlatform extends AbstractPlatform | |
{ | |
/** | |
- * Constructor. | |
- * Creates a new PostgreSqlPlatform. | |
- */ | |
- public function __construct() | |
- { | |
- parent::__construct(); | |
- } | |
- | |
- | |
- /** | |
* Returns the md5 sum of a field. | |
* | |
* Note: Not SQL92, but common functionality | |
@@ -784,4 +774,9 @@ | |
{ | |
return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; | |
} | |
+ | |
+ public function getForUpdateNoWaitSql() | |
+ { | |
+ return 'FOR UPDATE NOWAIT'; | |
+ } | |
} | |
Index: lib/Doctrine/ORM/Query.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Query.php (revision 6901) | |
+++ lib/Doctrine/ORM/Query.php (working copy) | |
@@ -83,6 +83,16 @@ | |
const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration'; | |
/** | |
+ * @var string | |
+ */ | |
+ const HINT_LOCK_MODE = 'doctrine.lockMode'; | |
+ | |
+ /** | |
+ * @var string | |
+ */ | |
+ const HINT_LOCK_VERSION = 'doctrine.lockVersion'; | |
+ | |
+ /** | |
* @var integer $_state The current state of this query. | |
*/ | |
private $_state = self::STATE_CLEAN; | |
Index: lib/Doctrine/ORM/Query/QueryException.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Query/QueryException.php (revision 6901) | |
+++ lib/Doctrine/ORM/Query/QueryException.php (working copy) | |
@@ -66,6 +66,7 @@ | |
/** | |
* @param Doctrine\ORM\Mapping\AssociationMapping $assoc | |
+ * @return QueryException | |
*/ | |
public static function iterateWithFetchJoinCollectionNotAllowed($assoc) | |
{ | |
@@ -74,4 +75,12 @@ | |
"in class ".$assoc->sourceEntityName." assocation ".$assoc->sourceFieldName | |
); | |
} | |
+ | |
+ /** | |
+ * @return QueryException | |
+ */ | |
+ public static function aquiredOptimisticLockWihtoutVersion() | |
+ { | |
+ return new self("Aquireing an optimistic lock without specifying the 'doctrine.lockVersion' query hint."); | |
+ } | |
} | |
\ No newline at end of file | |
Index: lib/Doctrine/ORM/Query/SqlWalker.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Query/SqlWalker.php (revision 6901) | |
+++ lib/Doctrine/ORM/Query/SqlWalker.php (working copy) | |
@@ -334,6 +334,14 @@ | |
$sql, $q->getMaxResults(), $q->getFirstResult() | |
); | |
+ if (($lockMode = $q->getHint(Query::HINT_LOCK_MODE)) !== false) { | |
+ if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC) { | |
+ $sql .= " " . $this->_platform->getForUpdateSql(); | |
+ } else if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_NOWAIT) { | |
+ $sql .= " " . $this->_platform->getForUpdateNoWaitSql(); | |
+ } | |
+ } | |
+ | |
return $sql; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Property changes on: . | |
___________________________________________________________________ | |
Modified: svn:ignore | |
- build | |
+ reports | |
logs | |
build | |
dist | |
Index: tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | |
=================================================================== | |
--- tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php (revision 7525) | |
+++ tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php (working copy) | |
@@ -15,12 +15,15 @@ | |
$this->_em = $this->_getTestEntityManager(); | |
} | |
- public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed) | |
+ public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed, array $queryHints = array()) | |
{ | |
try { | |
$query = $this->_em->createQuery($dqlToBeTested); | |
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true) | |
->useQueryCache(false); | |
+ foreach ($queryHints AS $name => $value) { | |
+ $query->setHint($name, $value); | |
+ } | |
parent::assertEquals($sqlToBeConfirmed, $query->getSql()); | |
$query->free(); | |
} catch (\Exception $e) { | |
@@ -584,4 +587,60 @@ | |
"SELECT c0_.name AS name0, (SELECT COUNT(c1_.phonenumber) AS dctrn__1 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr1 FROM cms_users c0_ WHERE c0_.name = 'jon'" | |
); | |
} | |
+ | |
+ public function testPessimisticWriteLockQueryHint() | |
+ { | |
+ if ($this->_em->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { | |
+ $this->markTestSkipped('SqLite does not support Row locking at all.'); | |
+ } | |
+ | |
+ $this->assertSqlGeneration( | |
+ "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", | |
+ "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ". | |
+ "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", | |
+ array(Query::HINT_LOCK_MODE => \Doctrine\ORM\LockMode::PESSIMISTIC_WRITE) | |
+ ); | |
+ } | |
+ | |
+ public function testPessimisticReadLockQueryHintPostgreSql() | |
+ { | |
+ if (!($this->_em->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\PostgreSqlPlatform)) { | |
+ $this->markTestSkipped('Only runs on PostgreSql'); | |
+ } | |
+ | |
+ $this->assertSqlGeneration( | |
+ "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", | |
+ "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ". | |
+ "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR SHARE", | |
+ array(Query::HINT_LOCK_MODE => \Doctrine\ORM\LockMode::PESSIMISTIC_READ) | |
+ ); | |
+ } | |
+ | |
+ public function testPessimisticReadLockQueryHintMySql() | |
+ { | |
+ if (!($this->_em->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform)) { | |
+ $this->markTestSkipped('Only runs on MySql'); | |
+ } | |
+ | |
+ $this->assertSqlGeneration( | |
+ "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", | |
+ "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ". | |
+ "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR SHARE", | |
+ array(Query::HINT_LOCK_MODE => \Doctrine\ORM\LockMode::PESSIMISTIC_READ) | |
+ ); | |
+ } | |
+ | |
+ public function testPessimisticReadLockQueryHintOracle() | |
+ { | |
+ if (!($this->_em->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\OraclePlatform)) { | |
+ $this->markTestSkipped('Only runs on Oracle'); | |
+ } | |
+ | |
+ $this->assertSqlGeneration( | |
+ "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", | |
+ "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ". | |
+ "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", | |
+ array(Query::HINT_LOCK_MODE => \Doctrine\ORM\LockMode::PESSIMISTIC_READ) | |
+ ); | |
+ } | |
} | |
Index: lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php (revision 7525) | |
+++ lib/Doctrine/DBAL/Platforms/MySqlPlatform.php (working copy) | |
@@ -666,4 +666,9 @@ | |
{ | |
return true; | |
} | |
+ | |
+ public function getReadLockSQL() | |
+ { | |
+ return 'LOCK IN SHARE MODE'; | |
+ } | |
} | |
Index: lib/Doctrine/DBAL/Platforms/SqlitePlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/SqlitePlatform.php (revision 7525) | |
+++ lib/Doctrine/DBAL/Platforms/SqlitePlatform.php (working copy) | |
@@ -428,4 +428,9 @@ | |
} | |
return 0; | |
} | |
+ | |
+ public function getForUpdateSql() | |
+ { | |
+ return ''; | |
+ } | |
} | |
Index: lib/Doctrine/DBAL/Platforms/AbstractPlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/AbstractPlatform.php (revision 7525) | |
+++ lib/Doctrine/DBAL/Platforms/AbstractPlatform.php (working copy) | |
@@ -488,11 +488,36 @@ | |
return 'COS(' . $value . ')'; | |
} | |
- public function getForUpdateSql() | |
+ public function getForUpdateSQL() | |
{ | |
return 'FOR UPDATE'; | |
} | |
+ /** | |
+ * Get the sql snippet to append to any SELECT statement which locks rows in shared read lock. | |
+ * | |
+ * This defaults to the ASNI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database | |
+ * vendors allow to lighten this constraint up to be a real read lock. | |
+ * | |
+ * @return string | |
+ */ | |
+ public function getReadLockSQL() | |
+ { | |
+ return $this->getForUpdateSQL(); | |
+ } | |
+ | |
+ /** | |
+ * Get the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. | |
+ * | |
+ * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ASNI SQL standard. | |
+ * | |
+ * @return string | |
+ */ | |
+ public function getWriteLockSQL() | |
+ { | |
+ return $this->getForUpdateSQL(); | |
+ } | |
+ | |
public function getDropDatabaseSQL($database) | |
{ | |
return 'DROP DATABASE ' . $database; | |
Index: lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php | |
=================================================================== | |
--- lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php (revision 7525) | |
+++ lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php (working copy) | |
@@ -637,4 +637,9 @@ | |
{ | |
return 'TRUNCATE '.$tableName.' '.($cascade)?'CASCADE':''; | |
} | |
+ | |
+ public function getReadLockSQL() | |
+ { | |
+ return 'FOR SHARE'; | |
+ } | |
} | |
Index: lib/Doctrine/ORM/LockMode.php | |
=================================================================== | |
--- lib/Doctrine/ORM/LockMode.php (revision 0) | |
+++ lib/Doctrine/ORM/LockMode.php (revision 0) | |
@@ -0,0 +1,40 @@ | |
+<?php | |
+/* | |
+ * $Id$ | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ * | |
+ * This software consists of voluntary contributions made by many individuals | |
+ * and is licensed under the LGPL. For more information, see | |
+ * <http://www.doctrine-project.org>. | |
+*/ | |
+ | |
+namespace Doctrine\ORM; | |
+ | |
+/** | |
+ * Contains all ORM LockModes | |
+ * | |
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL | |
+ * @link www.doctrine-project.com | |
+ * @since 1.0 | |
+ * @version $Revision$ | |
+ * @author Benjamin Eberlei <kontakt@beberlei.de> | |
+ * @author Roman Borschel <roman@code-factory.org> | |
+ */ | |
+final class LockMode | |
+{ | |
+ const NONE = 0; | |
+ const OPTIMISTIC = 1; | |
+ const PESSIMISTIC_READ = 2; | |
+ const PESSIMISTIC_WRITE = 4; | |
+} | |
\ No newline at end of file | |
Property changes on: lib/Doctrine/ORM/LockMode.php | |
___________________________________________________________________ | |
Added: svn:keywords | |
+ Id,Revision | |
Added: svn:eol-style | |
+ LF | |
Index: lib/Doctrine/ORM/TransactionRequiredException.php | |
=================================================================== | |
--- lib/Doctrine/ORM/TransactionRequiredException.php (revision 0) | |
+++ lib/Doctrine/ORM/TransactionRequiredException.php (revision 0) | |
@@ -0,0 +1,40 @@ | |
+<?php | |
+/* | |
+ * $Id$ | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ * | |
+ * This software consists of voluntary contributions made by many individuals | |
+ * and is licensed under the LGPL. For more information, see | |
+ * <http://www.doctrine-project.org>. | |
+*/ | |
+ | |
+namespace Doctrine\ORM; | |
+ | |
+/** | |
+ * Is thrown when a transaction is required for the current operation, but there is none open. | |
+ * | |
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL | |
+ * @link www.doctrine-project.com | |
+ * @since 1.0 | |
+ * @version $Revision$ | |
+ * @author Benjamin Eberlei <kontakt@beberlei.de> | |
+ * @author Roman Borschel <roman@code-factory.org> | |
+ */ | |
+class TransactionRequiredException extends ORMException | |
+{ | |
+ static public function transactionRequired() | |
+ { | |
+ return new self('An open transaction is required for this operation.'); | |
+ } | |
+} | |
\ No newline at end of file | |
Property changes on: lib/Doctrine/ORM/TransactionRequiredException.php | |
___________________________________________________________________ | |
Added: svn:keywords | |
+ Id,Revision | |
Added: svn:eol-style | |
+ LF | |
Index: lib/Doctrine/ORM/Query.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Query.php (revision 7525) | |
+++ lib/Doctrine/ORM/Query.php (working copy) | |
@@ -98,6 +98,11 @@ | |
const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration'; | |
/** | |
+ * @var string | |
+ */ | |
+ const HINT_LOCK_MODE = 'doctrine.lockMode'; | |
+ | |
+ /** | |
* @var integer $_state The current state of this query. | |
*/ | |
private $_state = self::STATE_CLEAN; | |
@@ -492,6 +497,39 @@ | |
} | |
/** | |
+ * Set the lock mode for this Query. | |
+ * | |
+ * @see Doctrine\ORM\LockMode | |
+ * @param int $lockMode | |
+ * @return Query | |
+ */ | |
+ public function setLockMode($lockMode) | |
+ { | |
+ if ($lockMode == LockMode::PESSIMISTIC_READ || $lockMode == LockMode::PESSIMISTIC_WRITE) { | |
+ if (!$this->_em->getConnection()->isTransactionActive()) { | |
+ throw TransactionRequiredException::transactionRequired(); | |
+ } | |
+ } | |
+ | |
+ $this->setHint(self::HINT_LOCK_MODE, $lockMode); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Get the current lock mode for this query. | |
+ * | |
+ * @return int | |
+ */ | |
+ public function getLockMode() | |
+ { | |
+ $lockMode = $this->getHint(self::HINT_LOCK_MODE); | |
+ if (!$lockMode) { | |
+ return LockMode::NONE; | |
+ } | |
+ return $lockMode; | |
+ } | |
+ | |
+ /** | |
* Generate a cache id for the query cache - reusing the Result-Cache-Id generator. | |
* | |
* The query cache | |
Index: lib/Doctrine/ORM/UnitOfWork.php | |
=================================================================== | |
--- lib/Doctrine/ORM/UnitOfWork.php (revision 7525) | |
+++ lib/Doctrine/ORM/UnitOfWork.php (working copy) | |
@@ -1632,6 +1632,48 @@ | |
} | |
/** | |
+ * Acquire a lock on the given entity. | |
+ * | |
+ * @param object $entity | |
+ * @param int $lockMode | |
+ * @param int $lockVersion | |
+ */ | |
+ public function lock($entity, $lockMode, $lockVersion = null) | |
+ { | |
+ $entityName = get_class($entity); | |
+ $class = $this->_em->getClassMetadata($entityName); | |
+ | |
+ if ($lockMode == LockMode::OPTIMISTIC) { | |
+ if (!$class->isVersioned) { | |
+ throw OptimisticLockException::notVersioned($entityName); | |
+ } | |
+ | |
+ if ($lockVersion != null) { | |
+ $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); | |
+ if ($entityVersion != $lockVersion) { | |
+ throw OptimisticLockException::lockFailed(); | |
+ } | |
+ } | |
+ } else if ($lockMode == LockMode::PESSIMISTIC_READ || $lockMode == LockMode::PESSIMISTIC_WRITE) { | |
+ | |
+ if (!$this->_em->getConnection()->isTransactionActive()) { | |
+ throw TransactionRequiredException::transactionRequired(); | |
+ } | |
+ | |
+ if ($this->getEntityState($entity) == self::STATE_MANAGED) { | |
+ $oid = spl_object_hash($entity); | |
+ | |
+ $this->getEntityPersister($class->name)->lock( | |
+ array_combine($class->getIdentifierColumnNames(), $this->_entityIdentifiers[$oid]), | |
+ $entity | |
+ ); | |
+ } else { | |
+ throw new \InvalidArgumentException("Entity is not MANAGED."); | |
+ } | |
+ } | |
+ } | |
+ | |
+ /** | |
* Gets the CommitOrderCalculator used by the UnitOfWork to order commits. | |
* | |
* @return Doctrine\ORM\Internal\CommitOrderCalculator | |
Index: lib/Doctrine/ORM/EntityManager.php | |
=================================================================== | |
--- lib/Doctrine/ORM/EntityManager.php (revision 7525) | |
+++ lib/Doctrine/ORM/EntityManager.php (working copy) | |
@@ -288,11 +288,13 @@ | |
* | |
* @param string $entityName | |
* @param mixed $identifier | |
+ * @param int $lockMode | |
+ * @param int $lockVersion | |
* @return object | |
*/ | |
- public function find($entityName, $identifier) | |
+ public function find($entityName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null) | |
{ | |
- return $this->getRepository($entityName)->find($identifier); | |
+ return $this->getRepository($entityName)->find($identifier, $lockMode, $lockVersion); | |
} | |
/** | |
@@ -448,6 +450,20 @@ | |
} | |
/** | |
+ * Acquire a lock on the given entity. | |
+ * | |
+ * @param object $entity | |
+ * @param int $lockMode | |
+ * @param int $lockVersion | |
+ * @throws OptimisticLockException | |
+ * @throws PessimisticLockException | |
+ */ | |
+ public function lock($entity, $lockMode, $lockVersion) | |
+ { | |
+ $this->_unitOfWork->lock($entity, $lockMode, $lockVersion); | |
+ } | |
+ | |
+ /** | |
* Gets the repository for an entity class. | |
* | |
* @param string $entityName The name of the Entity. | |
Index: lib/Doctrine/ORM/EntityRepository.php | |
=================================================================== | |
--- lib/Doctrine/ORM/EntityRepository.php (revision 7525) | |
+++ lib/Doctrine/ORM/EntityRepository.php (working copy) | |
@@ -92,23 +92,45 @@ | |
* Finds an entity by its primary key / identifier. | |
* | |
* @param $id The identifier. | |
- * @param int $hydrationMode The hydration mode to use. | |
+ * @param int $lockMode | |
+ * @param int $lockVersion | |
* @return object The entity. | |
*/ | |
- public function find($id) | |
+ public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) | |
{ | |
// Check identity map first | |
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) { | |
+ if ($lockMode != LockMode::NONE) { | |
+ $this->_em->lock($entity, $lockMode, $lockVersion); | |
+ } | |
+ | |
return $entity; // Hit! | |
} | |
if ( ! is_array($id) || count($id) <= 1) { | |
- //FIXME: Not correct. Relies on specific order. | |
+ // @todo FIXME: Not correct. Relies on specific order. | |
$value = is_array($id) ? array_values($id) : array($id); | |
$id = array_combine($this->_class->identifier, $value); | |
} | |
- return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); | |
+ if ($lockMode == LockMode::NONE) { | |
+ return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); | |
+ } else if ($lockMode == LockMode::OPTIMISTIC) { | |
+ if (!$this->_class->isVersioned) { | |
+ throw OptimisticLockException::notVersioned($this->_entityName); | |
+ } | |
+ $entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); | |
+ | |
+ $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion); | |
+ | |
+ return $entity; | |
+ } else { | |
+ if (!$this->_em->getConnection()->isTransactionActive()) { | |
+ throw TransactionRequiredException::transactionRequired(); | |
+ } | |
+ | |
+ return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode); | |
+ } | |
} | |
/** | |
Index: lib/Doctrine/ORM/OptimisticLockException.php | |
=================================================================== | |
--- lib/Doctrine/ORM/OptimisticLockException.php (revision 7525) | |
+++ lib/Doctrine/ORM/OptimisticLockException.php (working copy) | |
@@ -36,4 +36,9 @@ | |
{ | |
return new self("The optimistic lock failed."); | |
} | |
+ | |
+ public static function notVersioned($className) | |
+ { | |
+ return new self("Cannot obtain optimistic lock on unversioned entity ".$className); | |
+ } | |
} | |
\ No newline at end of file | |
Index: lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php (revision 7531) | |
+++ lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php (working copy) | |
@@ -235,7 +235,7 @@ | |
/** | |
* {@inheritdoc} | |
*/ | |
- protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null) | |
+ protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null, $lockMode = 0) | |
{ | |
$idColumns = $this->_class->getIdentifierColumnNames(); | |
$baseTableAlias = $this->_getSQLTableAlias($this->_class); | |
Index: lib/Doctrine/ORM/Persisters/StandardEntityPersister.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Persisters/StandardEntityPersister.php (revision 7525) | |
+++ lib/Doctrine/ORM/Persisters/StandardEntityPersister.php (working copy) | |
@@ -423,11 +423,12 @@ | |
* a new entity is created. | |
* @param $assoc The association that connects the entity to load to another entity, if any. | |
* @param array $hints Hints for entity creation. | |
+ * @param int $lockMode | |
* @return The loaded entity instance or NULL if the entity/the data can not be found. | |
*/ | |
- public function load(array $criteria, $entity = null, $assoc = null, array $hints = array()) | |
+ public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0) | |
{ | |
- $sql = $this->_getSelectEntitiesSQL($criteria, $assoc); | |
+ $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, null, $lockMode); | |
$params = array_values($criteria); | |
$stmt = $this->_conn->executeQuery($sql, $params); | |
$result = $stmt->fetch(PDO::FETCH_ASSOC); | |
@@ -641,9 +642,10 @@ | |
* @param array $criteria | |
* @param AssociationMapping $assoc | |
* @param string $orderBy | |
+ * @param int $lockMode | |
* @return string | |
*/ | |
- protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null) | |
+ protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null, $lockMode = 0) | |
{ | |
// Construct WHERE conditions | |
$conditionSql = ''; | |
@@ -671,10 +673,17 @@ | |
); | |
} | |
+ $lockSql = ''; | |
+ if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_READ) { | |
+ $lockSql = ' ' . $this->_platform->getReadLockSql(); | |
+ } else if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_WRITE) { | |
+ $lockSql = ' ' . $this->_platform->getWriteLockSql(); | |
+ } | |
+ | |
return 'SELECT ' . $this->_getSelectColumnListSQL() | |
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' | |
. $this->_getSQLTableAlias($this->_class) | |
- . ($conditionSql ? ' WHERE ' . $conditionSql : '') . $orderBySql; | |
+ . ($conditionSql ? ' WHERE ' . $conditionSql : '') . $orderBySql . $lockSql; | |
} | |
/** | |
@@ -912,4 +921,43 @@ | |
return $tableAlias; | |
} | |
+ | |
+ /** | |
+ * Lock all rows of this entity matching the given criteria with the specified pessimistic lock mode | |
+ * | |
+ * @param array $criteria | |
+ * @param int $lockMode | |
+ * @return void | |
+ */ | |
+ public function lock(array $criteria, $lockMode) | |
+ { | |
+ // @todo Extract method to remove duplicate code from _getSelectEntitiesSQL()? | |
+ $conditionSql = ''; | |
+ foreach ($criteria as $field => $value) { | |
+ if ($conditionSql != '') { | |
+ $conditionSql .= ' AND '; | |
+ } | |
+ | |
+ if (isset($this->_class->columnNames[$field])) { | |
+ $conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform); | |
+ } else if (isset($this->_class->fieldNames[$field])) { | |
+ $conditionSql .= $this->_class->getQuotedColumnName($this->_class->fieldNames[$field], $this->_platform); | |
+ } else if ($assoc !== null) { | |
+ $conditionSql .= $field; | |
+ } else { | |
+ throw ORMException::unrecognizedField($field); | |
+ } | |
+ $conditionSql .= ' = ?'; | |
+ } | |
+ | |
+ if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_READ) { | |
+ $lockSql = $this->_platform->getReadLockSql(); | |
+ } else if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_WRITE) { | |
+ $lockSql = $this->_platform->getWriteLockSql(); | |
+ } | |
+ | |
+ return 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' | |
+ . $this->_getSQLTableAlias($this->_class) | |
+ . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; | |
+ } | |
} | |
Index: lib/Doctrine/ORM/Query/SqlWalker.php | |
=================================================================== | |
--- lib/Doctrine/ORM/Query/SqlWalker.php (revision 7530) | |
+++ lib/Doctrine/ORM/Query/SqlWalker.php (working copy) | |
@@ -371,6 +371,25 @@ | |
$sql, $this->_query->getMaxResults(), $this->_query->getFirstResult() | |
); | |
+ if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) { | |
+ if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_READ) { | |
+ $sql .= " " . $this->_platform->getReadLockSQL(); | |
+ } else if ($lockMode == \Doctrine\ORM\LockMode::PESSIMISTIC_WRITE) { | |
+ $sql .= " " . $this->_platform->getWriteLockSQL(); | |
+ } else if ($lockMode == \Doctrine\ORM\LockMode::OPTIMISTIC) { | |
+ $versionedClassFound = false; | |
+ foreach ($this->_selectedClasses AS $class) { | |
+ if ($class->isVersioned) { | |
+ $versionedClassFound = true; | |
+ } | |
+ } | |
+ | |
+ if (!$versionedClassFound) { | |
+ throw \Doctrine\ORM\OptimisticLockException::lockFailed(); | |
+ } | |
+ } | |
+ } | |
+ | |
return $sql; | |
} | |
Index: lib/Doctrine/ORM/PessimisticLockException.php | |
=================================================================== | |
--- lib/Doctrine/ORM/PessimisticLockException.php (revision 0) | |
+++ lib/Doctrine/ORM/PessimisticLockException.php (revision 0) | |
@@ -0,0 +1,40 @@ | |
+<?php | |
+/* | |
+ * $Id$ | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ * | |
+ * This software consists of voluntary contributions made by many individuals | |
+ * and is licensed under the LGPL. For more information, see | |
+ * <http://www.doctrine-project.org>. | |
+*/ | |
+ | |
+namespace Doctrine\ORM; | |
+ | |
+/** | |
+ * Pessimistic Lock Exception | |
+ * | |
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL | |
+ * @link www.doctrine-project.com | |
+ * @since 1.0 | |
+ * @version $Revision$ | |
+ * @author Benjamin Eberlei <kontakt@beberlei.de> | |
+ * @author Roman Borschel <roman@code-factory.org> | |
+ */ | |
+class PessimisticLockException extends ORMException | |
+{ | |
+ public static function lockFailed() | |
+ { | |
+ return new self("The pessimistic lock failed."); | |
+ } | |
+} | |
\ No newline at end of file | |
Property changes on: lib/Doctrine/ORM/PessimisticLockException.php | |
___________________________________________________________________ | |
Added: svn:keywords | |
+ Id,Revision | |
Added: svn:eol-style | |
+ LF | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment