
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

