Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
beberlei committed May 15, 2010
2 parents 3b01277 + 28b0307 commit e958396
Show file tree
Hide file tree
Showing 26 changed files with 1,063 additions and 22 deletions.
42 changes: 42 additions & 0 deletions lib/Doctrine/DBAL/LockMode.php
@@ -0,0 +1,42 @@
<?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\DBAL;

/**
* 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>
*/
class LockMode
{
const NONE = 0;
const OPTIMISTIC = 1;
const PESSIMISTIC_READ = 2;
const PESSIMISTIC_WRITE = 4;

final private function __construct() { }
}
39 changes: 38 additions & 1 deletion lib/Doctrine/DBAL/Platforms/AbstractPlatform.php
Expand Up @@ -488,11 +488,48 @@ public function getCosExpression($value)
return 'COS(' . $value . ')';
}

public function getForUpdateSql()
public function getForUpdateSQL()
{
return 'FOR UPDATE';
}

/**
* Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
*
* @param string $fromClause
* @param int $lockMode
* @return string
*/
public function appendLockHint($fromClause, $lockMode)
{
return $fromClause;
}

/**
* 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;
Expand Down
5 changes: 5 additions & 0 deletions lib/Doctrine/DBAL/Platforms/DB2Platform.php
Expand Up @@ -513,4 +513,9 @@ public function getSQLResultCasing($column)
{
return strtoupper($column);
}

public function getForUpdateSQL()
{
return ' WITH RR USE AND KEEP UPDATE LOCKS';
}
}
28 changes: 28 additions & 0 deletions lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php
Expand Up @@ -483,4 +483,32 @@ public function getTruncateTableSQL($tableName, $cascade = false)
{
return 'TRUNCATE TABLE '.$tableName;
}

/**
* MsSql uses Table Hints for locking strategies instead of the ANSI SQL FOR UPDATE like hints.
*
* @return string
*/
public function getForUpdateSQL()
{
return '';
}

/**
* @license LGPL
* @author Hibernate
* @param string $fromClause
* @param int $lockMode
* @return string
*/
public function appendLockHint($fromClause, $lockMode)
{
if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) {
return $fromClause . " WITH (UPDLOCK, ROWLOCK)";
} else if ( $lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ ) {
return $fromClause . " WITH (HOLDLOCK, ROWLOCK)";
} else {
return $fromClause;
}
}
}
5 changes: 5 additions & 0 deletions lib/Doctrine/DBAL/Platforms/MySqlPlatform.php
Expand Up @@ -583,4 +583,9 @@ public function createsExplicitIndexForForeignKeys()
{
return true;
}

public function getReadLockSQL()
{
return 'LOCK IN SHARE MODE';
}
}
5 changes: 5 additions & 0 deletions lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php
Expand Up @@ -637,4 +637,9 @@ public function getTruncateTableSQL($tableName, $cascade = false)
{
return 'TRUNCATE '.$tableName.' '.($cascade)?'CASCADE':'';
}

public function getReadLockSQL()
{
return 'FOR SHARE';
}
}
5 changes: 5 additions & 0 deletions lib/Doctrine/DBAL/Platforms/SqlitePlatform.php
Expand Up @@ -428,4 +428,9 @@ static public function udfLocate($str, $substr, $offset = 0)
}
return 0;
}

public function getForUpdateSql()
{
return '';
}
}
20 changes: 18 additions & 2 deletions lib/Doctrine/ORM/EntityManager.php
Expand Up @@ -318,11 +318,13 @@ public function flush()
*
* @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);
}

/**
Expand Down Expand Up @@ -478,6 +480,20 @@ public function copy($entity, $deep = false)
throw new \BadMethodCallException("Not implemented.");
}

/**
* 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 = null)
{
$this->_unitOfWork->lock($entity, $lockMode, $lockVersion);
}

/**
* Gets the repository for an entity class.
*
Expand Down
30 changes: 26 additions & 4 deletions lib/Doctrine/ORM/EntityRepository.php
Expand Up @@ -87,23 +87,45 @@ public function clear()
* 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);
}
}

/**
Expand Down
37 changes: 37 additions & 0 deletions lib/Doctrine/ORM/LockMode.php
@@ -0,0 +1,37 @@
<?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>
*/
class LockMode extends \Doctrine\DBAL\LockMode
{

}
11 changes: 11 additions & 0 deletions lib/Doctrine/ORM/OptimisticLockException.php
Expand Up @@ -24,6 +24,7 @@
* that uses optimistic locking through a version field fails.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
*/
class OptimisticLockException extends ORMException
Expand All @@ -49,4 +50,14 @@ public static function lockFailed($entity)
{
return new self("The optimistic lock on an entity failed.", $entity);
}

public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion)
{
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
}

public static function notVersioned($entityName)
{
return new self("Cannot obtain optimistic lock on unversioned entity " . $entityName, null);
}
}

0 comments on commit e958396

Please sign in to comment.