1
0
mirror of synced 2025-02-22 07:03:13 +03:00

Merge branch 'master' into innerjoin-on-fetch-eager

Conflicts:
	lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
This commit is contained in:
Alexander 2011-10-31 22:24:16 +01:00
commit 53386e5247
57 changed files with 1637 additions and 1257 deletions

View File

@ -1,3 +1,17 @@
# ResultCache implementation rewritten
The result cache is completly rewritten and now works on the database result level, not inside the ORM AbstractQuery
anymore. This means that for result cached queries the hydration will now always be performed again, regardless of
the hydration mode. Affected areas are:
1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork
leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore.
2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result.
The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now
deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile`
instance with access to result cache driver, lifetime and cache key.
# EntityManager#getPartialReference() creates read-only entity # EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they Entities returned from EntityManager#getPartialReference() are now marked as read-only if they

View File

@ -20,7 +20,8 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type, use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\QueryException; Doctrine\ORM\Query\QueryException,
Doctrine\DBAL\Cache\QueryCacheProfile;
/** /**
* Base contract for ORM queries. Base class for Query and NativeQuery. * Base contract for ORM queries. Base class for Query and NativeQuery.
@ -91,34 +92,15 @@ abstract class AbstractQuery
protected $_hydrationMode = self::HYDRATE_OBJECT; protected $_hydrationMode = self::HYDRATE_OBJECT;
/** /**
* The locally set cache driver used for caching result sets of this query. * @param \Doctrine\DBAL\Cache\QueryCacheProfile
*
* @var CacheDriver
*/ */
protected $_resultCacheDriver; protected $_queryCacheProfile;
/**
* Boolean flag for whether or not to cache the results of this query.
*
* @var boolean
*/
protected $_useResultCache;
/**
* @var string The id to store the result cache entry under.
*/
protected $_resultCacheId;
/** /**
* @var boolean Boolean value that indicates whether or not expire the result cache. * @var boolean Boolean value that indicates whether or not expire the result cache.
*/ */
protected $_expireResultCache = false; protected $_expireResultCache = false;
/**
* @var int Result Cache lifetime.
*/
protected $_resultCacheTTL;
/** /**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>. * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
* *
@ -260,7 +242,7 @@ abstract class AbstractQuery
} }
/** /**
* Defines a cache driver to be used for caching result sets. * Defines a cache driver to be used for caching result sets and implictly enables caching.
* *
* @param Doctrine\Common\Cache\Cache $driver Cache driver * @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery * @return Doctrine\ORM\AbstractQuery
@ -270,9 +252,10 @@ abstract class AbstractQuery
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver(); throw ORMException::invalidResultCacheDriver();
} }
$this->_resultCacheDriver = $resultCacheDriver; if ($this->_queryCacheProfile) {
if ($resultCacheDriver) { $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver);
$this->_useResultCache = true; } else {
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver);
} }
return $this; return $this;
} }
@ -280,12 +263,13 @@ abstract class AbstractQuery
/** /**
* Returns the cache driver used for caching result sets. * Returns the cache driver used for caching result sets.
* *
* @deprecated
* @return Doctrine\Common\Cache\Cache Cache driver * @return Doctrine\Common\Cache\Cache Cache driver
*/ */
public function getResultCacheDriver() public function getResultCacheDriver()
{ {
if ($this->_resultCacheDriver) { if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $this->_resultCacheDriver; return $this->_queryCacheProfile->getResultCacheDriver();
} else { } else {
return $this->_em->getConfiguration()->getResultCacheImpl(); return $this->_em->getConfiguration()->getResultCacheImpl();
} }
@ -296,18 +280,17 @@ abstract class AbstractQuery
* how long and which ID to use for the cache entry. * how long and which ID to use for the cache entry.
* *
* @param boolean $bool * @param boolean $bool
* @param integer $timeToLive * @param integer $lifetime
* @param string $resultCacheId * @param string $resultCacheId
* @return Doctrine\ORM\AbstractQuery This query instance. * @return Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null) public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
{ {
$this->_useResultCache = $bool; if ($bool) {
if ($timeToLive) { $this->setResultCacheLifetime($lifetime);
$this->setResultCacheLifetime($timeToLive); $this->setResultCacheId($resultCacheId);
} } else {
if ($resultCacheId) { $this->_queryCacheProfile = null;
$this->_resultCacheId = $resultCacheId;
} }
return $this; return $this;
} }
@ -315,27 +298,33 @@ abstract class AbstractQuery
/** /**
* Defines how long the result cache will be active before expire. * Defines how long the result cache will be active before expire.
* *
* @param integer $timeToLive How long the cache entry is valid. * @param integer $lifetime How long the cache entry is valid.
* @return Doctrine\ORM\AbstractQuery This query instance. * @return Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function setResultCacheLifetime($timeToLive) public function setResultCacheLifetime($lifetime)
{ {
if ($timeToLive !== null) { if ($lifetime === null) {
$timeToLive = (int) $timeToLive; $lifetime = 0;
} else {
$lifetime = (int)$lifetime;
}
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
} else {
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
} }
$this->_resultCacheTTL = $timeToLive;
return $this; return $this;
} }
/** /**
* Retrieves the lifetime of resultset cache. * Retrieves the lifetime of resultset cache.
* *
* @deprecated
* @return integer * @return integer
*/ */
public function getResultCacheLifetime() public function getResultCacheLifetime()
{ {
return $this->_resultCacheTTL; return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
} }
/** /**
@ -360,6 +349,14 @@ abstract class AbstractQuery
return $this->_expireResultCache; return $this->_expireResultCache;
} }
/**
* @return QueryCacheProfile
*/
public function getQueryCacheProfile()
{
return $this->_queryCacheProfile;
}
/** /**
* Change the default fetch mode of an association for this query. * Change the default fetch mode of an association for this query.
* *
@ -548,7 +545,7 @@ abstract class AbstractQuery
* *
* @param array $params The query parameters. * @param array $params The query parameters.
* @param integer $hydrationMode The hydration mode to use. * @param integer $hydrationMode The hydration mode to use.
* @return IterableResult * @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/ */
public function iterate(array $params = array(), $hydrationMode = null) public function iterate(array $params = array(), $hydrationMode = null)
{ {
@ -584,28 +581,6 @@ abstract class AbstractQuery
$this->setParameters($params); $this->setParameters($params);
} }
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
list($key, $hash) = $this->getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
if ($cached === false || !isset($cached[$key])) {
// Cache miss.
$stmt = $this->_doExecute();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached[$key];
}
}
$stmt = $this->_doExecute(); $stmt = $this->_doExecute();
if (is_numeric($stmt)) { if (is_numeric($stmt)) {
@ -627,43 +602,23 @@ abstract class AbstractQuery
*/ */
public function setResultCacheId($id) public function setResultCacheId($id)
{ {
$this->_resultCacheId = $id; if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
}
return $this; return $this;
} }
/** /**
* Get the result cache id to use to store the result set cache entry. * Get the result cache id to use to store the result set cache entry if set.
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
* *
* @return array ($key, $hash) * @deprecated
* @return string
*/ */
protected function getResultCacheId() public function getResultCacheId()
{ {
if ($this->_resultCacheId) { return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
return array($this->_resultCacheId, $this->_resultCacheId);
} else {
$params = $this->_params;
foreach ($params AS $key => $value) {
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$params[$key] = $idValues;
} else {
$params[$key] = $value;
}
}
$sql = $this->getSql();
ksort($this->_hints);
$key = implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
return array($key, md5($key));
}
} }
/** /**

View File

@ -209,27 +209,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['metadataDriverImpl'] : null; $this->_attributes['metadataDriverImpl'] : null;
} }
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getResultCacheImpl()
{
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/** /**
* Gets the cache driver implementation that is used for the query cache (SQL cache). * Gets the cache driver implementation that is used for the query cache (SQL cache).
* *

View File

@ -19,14 +19,16 @@
namespace Doctrine\ORM\Event; namespace Doctrine\ORM\Event;
use \Doctrine\Common\EventSubscriber; use Doctrine\Common\EventSubscriber;
use \LogicException; use LogicException;
/** /**
* Delegate events only for certain entities they are registered for. * Delegate events only for certain entities they are registered for.
* *
* @author Benjamin Eberlei <kontakt@beberlei.de> * @link www.doctrine-project.org
* @since 2.2 * @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.2
*/ */
class EntityEventDelegator implements EventSubscriber class EntityEventDelegator implements EventSubscriber
{ {
@ -54,17 +56,23 @@ class EntityEventDelegator implements EventSubscriber
public function addEventListener($events, $entities, $listener) public function addEventListener($events, $entities, $listener)
{ {
if ($this->frozen) { if ($this->frozen) {
throw new LogicException("Cannot add event listeners after EntityEventDelegator::getSubscribedEvents() " . throw new LogicException(
"is called once. This happens when you register the delegator with the event manager."); "Cannot add event listeners after EntityEventDelegator::getSubscribedEvents() " .
"is called once. This happens when you register the delegator with the event manager."
);
} }
// Picks the hash code related to that listener // Picks the hash code related to that listener
$hash = spl_object_hash($listener); $hash = spl_object_hash($listener);
$entities = array_flip((array) $entities);
foreach ((array) $events as $event) { foreach ((array) $events as $event) {
// Overrides listener if a previous one was associated already // Overrides listener if a previous one was associated already
// Prevents duplicate listeners on same event (same instance only) // Prevents duplicate listeners on same event (same instance only)
$this->listeners[$event][$hash] = array('listener' => $listener, 'entities' => array_flip((array)$entities)); $this->listeners[$event][$hash] = array(
'listener' => $listener,
'entities' => $entities
);
} }
} }
@ -73,6 +81,7 @@ class EntityEventDelegator implements EventSubscriber
* interested in and added as a listener for these events. * interested in and added as a listener for these events.
* *
* @param Doctrine\Common\EventSubscriber $subscriber The subscriber. * @param Doctrine\Common\EventSubscriber $subscriber The subscriber.
* @param array $entities
*/ */
public function addEventSubscriber(EventSubscriber $subscriber, $entities) public function addEventSubscriber(EventSubscriber $subscriber, $entities)
{ {
@ -87,24 +96,27 @@ class EntityEventDelegator implements EventSubscriber
public function getSubscribedEvents() public function getSubscribedEvents()
{ {
$this->frozen = true; $this->frozen = true;
return array_keys($this->listeners); return array_keys($this->listeners);
} }
/** /**
* Delegate the event to an appropriate listener * Delegate the event to an appropriate listener
* *
* @param $eventName * @param string $eventName
* @param $event * @param array $args
* @return void * @return void
*/ */
public function __call($eventName, $args) public function __call($eventName, $args)
{ {
$event = $args[0]; $event = $args[0];
foreach ($this->listeners[$eventName] AS $listenerData) { foreach ($this->listeners[$eventName] AS $listenerData) {
$class = get_class($event->getEntity()); $class = get_class($event->getEntity());
if (isset($listenerData['entities'][$class])) {
$listenerData['listener']->$eventName($event); if ( ! isset($listenerData['entities'][$class])) continue;
}
$listenerData['listener']->$eventName($event);
} }
} }
} }

View File

@ -19,42 +19,59 @@
namespace Doctrine\ORM\Event; namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\EntityManager;
/** /**
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
* of entities. * of entities.
* *
* @since 2.0 * @link www.doctrine-project.org
* @since 2.0
* @author Roman Borschel <roman@code-factory.de> * @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
*/ */
class LifecycleEventArgs extends \Doctrine\Common\EventArgs class LifecycleEventArgs extends EventArgs
{ {
/** /**
* @var EntityManager * @var Doctrine\ORM\EntityManager
*/ */
private $_em; private $em;
/** /**
* @var object * @var object
*/ */
private $_entity; private $entity;
public function __construct($entity, $em) /**
* Constructor
*
* @param object $entity
* @param Doctrine\ORM\EntityManager $em
*/
public function __construct($entity, EntityManager $em)
{ {
$this->_entity = $entity; $this->entity = $entity;
$this->_em = $em; $this->em = $em;
}
public function getEntity()
{
return $this->_entity;
} }
/** /**
* @return EntityManager * Retireve associated Entity.
*
* @return object
*/
public function getEntity()
{
return $this->entity;
}
/**
* Retrieve associated EntityManager.
*
* @return Doctrine\ORM\EntityManager
*/ */
public function getEntityManager() public function getEntityManager()
{ {
return $this->_em; return $this->em;
} }
} }

View File

@ -1,9 +1,25 @@
<?php <?php
/*
* 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\Event; namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs; use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
@ -11,32 +27,36 @@ use Doctrine\ORM\EntityManager;
* Class that holds event arguments for a loadMetadata event. * Class that holds event arguments for a loadMetadata event.
* *
* @author Jonathan H. Wage <jonwage@gmail.com> * @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class LoadClassMetadataEventArgs extends EventArgs class LoadClassMetadataEventArgs extends EventArgs
{ {
/** /**
* @var ClassMetadata * @var Doctrine\ORM\Mapping\ClassMetadata
*/ */
private $classMetadata; private $classMetadata;
/** /**
* @var EntityManager * @var Doctrine\ORM\EntityManager
*/ */
private $em; private $em;
/** /**
* @param ClassMetadataInfo $classMetadata * Constructor.
* @param EntityManager $em *
* @param Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata
* @param Doctrine\ORM\EntityManager $em
*/ */
public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em) public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em)
{ {
$this->classMetadata = $classMetadata; $this->classMetadata = $classMetadata;
$this->em = $em; $this->em = $em;
} }
/** /**
* @return ClassMetadataInfo * Retrieve associated ClassMetadata.
*
* @return Doctrine\ORM\Mapping\ClassMetadataInfo
*/ */
public function getClassMetadata() public function getClassMetadata()
{ {
@ -44,7 +64,9 @@ class LoadClassMetadataEventArgs extends EventArgs
} }
/** /**
* @return EntityManager * Retrieve associated EntityManager.
*
* @return Doctrine\ORM\EntityManager
*/ */
public function getEntityManager() public function getEntityManager()
{ {

View File

@ -15,7 +15,7 @@
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\ORM\Event; namespace Doctrine\ORM\Event;
@ -23,16 +23,15 @@ namespace Doctrine\ORM\Event;
* Provides event arguments for the onClear event. * Provides event arguments for the onClear event.
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de> * @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
*/ */
class OnClearEventArgs extends \Doctrine\Common\EventArgs class OnClearEventArgs extends \Doctrine\Common\EventArgs
{ {
/** /**
* @var \Doctrine\ORM\EntityManager * @var Doctrine\ORM\EntityManager
*/ */
private $em; private $em;
@ -42,16 +41,21 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
private $entityClass; private $entityClass;
/** /**
* @param \Doctrine\ORM\EntityManager $em * Constructor.
*
* @param Doctrine\ORM\EntityManager $em
* @param string $entityClass Optional entity class
*/ */
public function __construct($em, $entityClass = null) public function __construct($em, $entityClass = null)
{ {
$this->em = $em; $this->em = $em;
$this->entityClass = $entityClass; $this->entityClass = $entityClass;
} }
/** /**
* @return \Doctrine\ORM\EntityManager * Retrieve associated EntityManager.
*
* @return Doctrine\ORM\EntityManager
*/ */
public function getEntityManager() public function getEntityManager()
{ {
@ -75,6 +79,6 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
*/ */
public function clearsAllEntities() public function clearsAllEntities()
{ {
return $this->entityClass === null; return ($this->entityClass === null);
} }
} }

View File

@ -21,37 +21,45 @@
namespace Doctrine\ORM\Event; namespace Doctrine\ORM\Event;
use Doctrine\ORM\EntityManager;
/** /**
* Provides event arguments for the preFlush event. * Provides event arguments for the preFlush event.
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de> * @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
*/ */
class OnFlushEventArgs extends \Doctrine\Common\EventArgs class OnFlushEventArgs extends \Doctrine\Common\EventArgs
{ {
/** /**
* @var EntityManager * @var Doctirne\ORM\EntityManager
*/ */
private $_em; private $em;
//private $_entitiesToPersist = array(); //private $entitiesToPersist = array();
//private $_entitiesToRemove = array(); //private $entitiesToRemove = array();
public function __construct($em) /**
* Constructor.
*
* @param Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{ {
$this->_em = $em; $this->em = $em;
} }
/** /**
* @return EntityManager * Retrieve associated EntityManager.
*
* @return Doctrine\ORM\EntityManager
*/ */
public function getEntityManager() public function getEntityManager()
{ {
return $this->_em; return $this->em;
} }
/* /*

View File

@ -17,9 +17,10 @@
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\ORM\Event; namespace Doctrine\ORM\Event;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\Common\EventArgs; use Doctrine\Common\EventArgs;
@ -27,20 +28,21 @@ use Doctrine\Common\EventArgs;
* Provides event arguments for the postFlush event. * Provides event arguments for the postFlush event.
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Daniel Freudenberger <df@rebuy.de> * @author Daniel Freudenberger <df@rebuy.de>
*/ */
class PostFlushEventArgs extends EventArgs class PostFlushEventArgs extends EventArgs
{ {
/** /**
* @var EntityManager * @var Doctrine\ORM\EntityManager
*/ */
private $em; private $em;
/** /**
* @param EntityManager $em * Constructor.
*
* @param Doctrine\ORM\EntityManager $em
*/ */
public function __construct(EntityManager $em) public function __construct(EntityManager $em)
{ {
@ -48,7 +50,9 @@ class PostFlushEventArgs extends EventArgs
} }
/** /**
* @return EntityManager * Retrieve associated EntityManager.
*
* @return Doctrine\ORM\EntityManager
*/ */
public function getEntityManager() public function getEntityManager()
{ {

View File

@ -1,4 +1,23 @@
<?php <?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\Event; namespace Doctrine\ORM\Event;
@ -8,42 +27,50 @@ use Doctrine\Common\EventArgs,
/** /**
* Class that holds event arguments for a preInsert/preUpdate event. * Class that holds event arguments for a preInsert/preUpdate event.
* *
* @author Guilherme Blanco <guilehrmeblanco@hotmail.com>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0 * @since 2.0
*/ */
class PreUpdateEventArgs extends LifecycleEventArgs class PreUpdateEventArgs extends LifecycleEventArgs
{ {
/** /**
* @var array * @var array
*/ */
private $_entityChangeSet; private $entityChangeSet;
/** /**
* Constructor.
* *
* @param object $entity * @param object $entity
* @param EntityManager $em * @param Doctrine\ORM\EntityManager $em
* @param array $changeSet * @param array $changeSet
*/ */
public function __construct($entity, $em, array &$changeSet) public function __construct($entity, EntityManager $em, array &$changeSet)
{ {
parent::__construct($entity, $em); parent::__construct($entity, $em);
$this->_entityChangeSet = &$changeSet;
}
public function getEntityChangeSet() $this->entityChangeSet = &$changeSet;
{
return $this->_entityChangeSet;
} }
/** /**
* Field has a changeset? * Retrieve entity changeset.
* *
* @return bool * @return array
*/
public function getEntityChangeSet()
{
return $this->entityChangeSet;
}
/**
* Check if field has a changeset.
*
* @return boolean
*/ */
public function hasChangedField($field) public function hasChangedField($field)
{ {
return isset($this->_entityChangeSet[$field]); return isset($this->entityChangeSet[$field]);
} }
/** /**
@ -54,9 +81,9 @@ class PreUpdateEventArgs extends LifecycleEventArgs
*/ */
public function getOldValue($field) public function getOldValue($field)
{ {
$this->_assertValidField($field); $this->assertValidField($field);
return $this->_entityChangeSet[$field][0]; return $this->entityChangeSet[$field][0];
} }
/** /**
@ -67,9 +94,9 @@ class PreUpdateEventArgs extends LifecycleEventArgs
*/ */
public function getNewValue($field) public function getNewValue($field)
{ {
$this->_assertValidField($field); $this->assertValidField($field);
return $this->_entityChangeSet[$field][1]; return $this->entityChangeSet[$field][1];
} }
/** /**
@ -80,18 +107,24 @@ class PreUpdateEventArgs extends LifecycleEventArgs
*/ */
public function setNewValue($field, $value) public function setNewValue($field, $value)
{ {
$this->_assertValidField($field); $this->assertValidField($field);
$this->_entityChangeSet[$field][1] = $value; $this->entityChangeSet[$field][1] = $value;
} }
private function _assertValidField($field) /**
* Assert the field exists in changeset.
*
* @param string $field
*/
private function assertValidField($field)
{ {
if (!isset($this->_entityChangeSet[$field])) { if ( ! isset($this->entityChangeSet[$field])) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(sprintf(
"Field '".$field."' is not a valid field of the entity ". 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.',
"'".get_class($this->getEntity())."' in PreUpdateEventArgs." $field,
); get_class($this->getEntity())
));
} }
} }
} }

View File

@ -20,6 +20,7 @@
namespace Doctrine\ORM\Id; namespace Doctrine\ORM\Id;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\ORMException; use Doctrine\ORM\ORMException;
/** /**
@ -42,44 +43,27 @@ class AssignedGenerator extends AbstractIdGenerator
*/ */
public function generate(EntityManager $em, $entity) public function generate(EntityManager $em, $entity)
{ {
$class = $em->getClassMetadata(get_class($entity)); $class = $em->getClassMetadata(get_class($entity));
$idFields = $class->getIdentifierFieldNames();
$identifier = array(); $identifier = array();
if ($class->isIdentifierComposite) {
$idFields = $class->getIdentifierFieldNames();
foreach ($idFields as $idField) {
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. foreach ($idFields as $idField) {
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
} else {
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
}
}
} else {
$idField = $class->identifier[0];
$value = $class->reflFields[$idField]->getValue($entity); $value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. if ( ! isset($value)) {
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
} else {
throw ORMException::entityMissingAssignedIdForField($entity, $idField); throw ORMException::entityMissingAssignedIdForField($entity, $idField);
} }
if (isset($class->associationMappings[$idField])) {
if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$value = current($em->getUnitOfWork()->getEntityIdentifier($value));
}
$identifier[$idField] = $value;
} }
return $identifier; return $identifier;

View File

@ -369,10 +369,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index]; $this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
} }
} else if ( ! $reflField->getValue($parentObject)) { } else if ( ! $reflField->getValue($parentObject)) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection); $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
$coll->setOwner($parentObject, $relation);
$reflField->setValue($parentObject, $coll);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
} }
} else { } else {
// PATH B: Single-valued association // PATH B: Single-valued association

View File

@ -1111,23 +1111,23 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function getIdentifierColumnNames() public function getIdentifierColumnNames()
{ {
if ($this->isIdentifierComposite) { $columnNames = array();
$columnNames = array();
foreach ($this->identifier as $idField) { foreach ($this->identifier as $idProperty) {
if (isset($this->associationMappings[$idField])) { if (isset($this->fieldMappings[$idProperty])) {
// no composite pk as fk entity assumption: $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
$columnNames[] = $this->associationMappings[$idField]['joinColumns'][0]['name'];
} else { continue;
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
}
} }
return $columnNames;
} else if(isset($this->fieldMappings[$this->identifier[0]])) { // Association defined as Id field
return array($this->fieldMappings[$this->identifier[0]]['columnName']); $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
} else { $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
// no composite pk as fk entity assumption:
return array($this->associationMappings[$this->identifier[0]]['joinColumns'][0]['name']); $columnNames = array_merge($columnNames, $assocColumnNames);
} }
return $columnNames;
} }
/** /**
@ -1904,6 +1904,42 @@ class ClassMetadataInfo implements ClassMetadata
return $this->name; return $this->name;
} }
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @param AbstractPlatform $platform
* @return array
*/
public function getQuotedIdentifierColumnNames($platform)
{
$quotedColumnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
: $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) {
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/** /**
* Gets the (possibly quoted) column name of a mapped field for safe use * Gets the (possibly quoted) column name of a mapped field for safe use
* in an SQL statement. * in an SQL statement.
@ -1914,7 +1950,9 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function getQuotedColumnName($field, $platform) public function getQuotedColumnName($field, $platform)
{ {
return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName']; return isset($this->fieldMappings[$field]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
: $this->fieldMappings[$field]['columnName'];
} }
/** /**

View File

@ -327,6 +327,8 @@ class XmlDriver extends AbstractFileDriver
if (isset($oneToManyElement['index-by'])) { if (isset($oneToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$oneToManyElement['index-by']; $mapping['indexBy'] = (string)$oneToManyElement['index-by'];
} else if (isset($oneToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
} }
$metadata->mapOneToMany($mapping); $metadata->mapOneToMany($mapping);
@ -432,8 +434,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy; $mapping['orderBy'] = $orderBy;
} }
if (isset($manyToManyElement->{'index-by'})) { if (isset($manyToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'}; $mapping['indexBy'] = (string)$manyToManyElement['index-by'];
} else if (isset($manyToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
} }
$metadata->mapManyToMany($mapping); $metadata->mapManyToMany($mapping);

View File

@ -57,17 +57,17 @@ final class NativeQuery extends AbstractQuery
*/ */
protected function _doExecute() protected function _doExecute()
{ {
$stmt = $this->_em->getConnection()->prepare($this->_sql);
$params = $this->_params; $params = $this->_params;
foreach ($params as $key => $value) { $types = $this->_paramTypes;
if (isset($this->_paramTypes[$key])) { if ($params) {
$stmt->bindValue($key, $value, $this->_paramTypes[$key]); if (is_int(key($params))) {
} else { ksort($params);
$stmt->bindValue($key, $value); ksort($types);
$params = array_values($params);
$types = array_values($types);
} }
} }
$stmt->execute();
return $stmt; return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile);
} }
} }

View File

@ -468,7 +468,7 @@ final class PersistentCollection implements Collection
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork() return $this->em->getUnitOfWork()
->getCollectionPersister($this->association) ->getCollectionPersister($this->association)
->count($this) + $this->coll->count(); ->count($this) + ($this->isDirty ? $this->coll->count() : 0);
} }
$this->initialize(); $this->initialize();
@ -672,7 +672,10 @@ final class PersistentCollection implements Collection
*/ */
public function slice($offset, $length = null) public function slice($offset, $length = null)
{ {
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { if ( ! $this->initialized &&
! $this->isDirty &&
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork() return $this->em->getUnitOfWork()
->getCollectionPersister($this->association) ->getCollectionPersister($this->association)
->slice($this, $offset, $length); ->slice($this, $offset, $length);

View File

@ -62,7 +62,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{ {
$columnName = $class->columnNames[$field]; $columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform); $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); $columnAlias = $this->getSQLColumnAlias($columnName);
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name); $this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
return $sql . ' AS ' . $columnAlias; return $sql . ' AS ' . $columnAlias;
@ -70,9 +70,8 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className) protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
{ {
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++; $columnAlias = $this->getSQLColumnAlias($joinColumnName);
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); $this->_rsm->addMetaResult('r', $columnAlias, $joinColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
} }

View File

@ -1001,7 +1001,6 @@ class BasicEntityPersister
$columnList .= $assoc2ColumnSQL; $columnList .= $assoc2ColumnSQL;
} }
} }
$first = true; $first = true;
if ($assoc['isOwningSide']) { if ($assoc['isOwningSide']) {
@ -1012,7 +1011,6 @@ class BasicEntityPersister
if ( ! $first) { if ( ! $first) {
$this->_selectJoinSql .= ' AND '; $this->_selectJoinSql .= ' AND ';
} }
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = '
. $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol; . $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol;
$first = false; $first = false;
@ -1061,8 +1059,7 @@ class BasicEntityPersister
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnAlias = $srcColumn . $this->_sqlAliasCounter++; $resultColumnName = $this->getSQLColumnAlias($srcColumn);
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $resultColumnName; . '.' . $srcColumn . ' AS ' . $resultColumnName;
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true); $this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
@ -1181,10 +1178,9 @@ class BasicEntityPersister
*/ */
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{ {
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
. '.' . $class->getQuotedColumnName($field, $this->_platform); . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); $columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]);
$this->_rsm->addFieldResult($alias, $columnAlias, $field); $this->_rsm->addFieldResult($alias, $columnAlias, $field);
@ -1493,8 +1489,8 @@ class BasicEntityPersister
$criteria = array_merge($criteria, $extraConditions); $criteria = array_merge($criteria, $extraConditions);
} }
$sql = 'SELECT 1' $sql = 'SELECT 1 '
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name) . $this->getLockTablesSql()
. ' WHERE ' . $this->_getSelectConditionSQL($criteria); . ' WHERE ' . $this->_getSelectConditionSQL($criteria);
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
@ -1519,4 +1515,19 @@ class BasicEntityPersister
return 'INNER JOIN'; return 'INNER JOIN';
} }
/**
* Gets an SQL column alias for a column name.
*
* @param string $columnName
* @return string
*/
public function getSQLColumnAlias($columnName)
{
// Trim the column alias to the maximum identifier length of the platform.
// If the alias is to long, characters are cut off from the beginning.
return $this->_platform->getSQLResultCasing(
substr($columnName . $this->_sqlAliasCounter++, -$this->_platform->getMaxIdentifierLength())
);
}
} }

View File

@ -232,6 +232,9 @@ final class Query extends AbstractQuery
protected function _doExecute() protected function _doExecute()
{ {
$executor = $this->_parse()->getSqlExecutor(); $executor = $this->_parse()->getSqlExecutor();
if ($this->_queryCacheProfile) {
$executor->setQueryCacheProfile($this->_queryCacheProfile);
}
// Prepare parameters // Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings(); $paramMappings = $this->_parserResult->getParameterMappings();
@ -523,7 +526,7 @@ final class Query extends AbstractQuery
* *
* @param array $params The query parameters. * @param array $params The query parameters.
* @param integer $hydrationMode The hydration mode to use. * @param integer $hydrationMode The hydration mode to use.
* @return IterableResult * @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/ */
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{ {

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -22,6 +20,7 @@
namespace Doctrine\ORM\Query\Exec; namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
/** /**
* Base class for SQL statement executors. * Base class for SQL statement executors.
@ -34,8 +33,16 @@ use Doctrine\DBAL\Connection;
*/ */
abstract class AbstractSqlExecutor abstract class AbstractSqlExecutor
{ {
/**
* @var array
*/
protected $_sqlStatements; protected $_sqlStatements;
/**
* @var QueryCacheProfile
*/
protected $queryCacheProfile;
/** /**
* Gets the SQL statements that are executed by the executor. * Gets the SQL statements that are executed by the executor.
* *
@ -46,11 +53,17 @@ abstract class AbstractSqlExecutor
return $this->_sqlStatements; return $this->_sqlStatements;
} }
public function setQueryCacheProfile(QueryCacheProfile $qcp)
{
$this->queryCacheProfile = $qcp;
}
/** /**
* Executes all sql statements. * Executes all sql statements.
* *
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries. * @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters. * @param array $params The parameters.
* @param array $types The parameter types.
* @return Doctrine\DBAL\Driver\Statement * @return Doctrine\DBAL\Driver\Statement
*/ */
abstract public function execute(Connection $conn, array $params, array $types); abstract public function execute(Connection $conn, array $params, array $types);

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -32,7 +30,6 @@ use Doctrine\DBAL\Connection,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org * @link http://www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
*/ */
class MultiTableDeleteExecutor extends AbstractSqlExecutor class MultiTableDeleteExecutor extends AbstractSqlExecutor
{ {
@ -102,11 +99,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
} }
/** /**
* Executes all SQL statements. * {@inheritDoc}
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/ */
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {

View File

@ -141,11 +141,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
} }
/** /**
* Executes all SQL statements. * {@inheritDoc}
*
* @param Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/ */
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
*/ */
@ -41,8 +38,11 @@ class SingleSelectExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST); $this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
} }
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {
return $conn->executeQuery($this->_sqlStatements, $params, $types); return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile);
} }
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
@ -46,6 +43,9 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
} }
} }
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {
return $conn->executeUpdate($this->_sqlStatements, $params, $types); return $conn->executeUpdate($this->_sqlStatements, $params, $types);

View File

@ -57,7 +57,7 @@ abstract class Base
public function add($arg) public function add($arg)
{ {
if ( $arg !== null ) { if ( $arg !== null || ($arg instanceof self && $arg->count() > 0) ) {
// If we decide to keep Expr\Base instances, we can use this check // If we decide to keep Expr\Base instances, we can use this check
if ( ! is_string($arg)) { if ( ! is_string($arg)) {
$class = get_class($arg); $class = get_class($arg);

View File

@ -86,20 +86,22 @@ class ResultSetMappingBuilder extends ResultSetMapping
if (isset($renamedColumns[$columnName])) { if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName]; $columnName = $renamedColumns[$columnName];
} }
$columnName = $platform->getSQLResultCasing($columnName);
if (isset($this->fieldMappings[$columnName])) { if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
} }
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName); $this->addFieldResult($alias, $columnName, $propertyName);
} }
foreach ($classMetadata->associationMappings AS $associationMapping) { foreach ($classMetadata->associationMappings AS $associationMapping) {
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($associationMapping['joinColumns'] AS $joinColumn) { foreach ($associationMapping['joinColumns'] AS $joinColumn) {
$columnName = $joinColumn['name']; $columnName = $joinColumn['name'];
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName; $renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
$renamedColumnName = $platform->getSQLResultCasing($renamedColumnName);
if (isset($this->metaMappings[$renamedColumnName])) { if (isset($this->metaMappings[$renamedColumnName])) {
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper."); throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
} }
$this->addMetaResult($alias, $platform->getSQLResultCasing($renamedColumnName), $platform->getSQLResultCasing($columnName)); $this->addMetaResult($alias, $renamedColumnName, $columnName);
} }
} }
} }

View File

@ -157,32 +157,24 @@ class SqlWalker implements TreeWalker
*/ */
public function getExecutor($AST) public function getExecutor($AST)
{ {
$isDeleteStatement = $AST instanceof AST\DeleteStatement; switch (true) {
$isUpdateStatement = $AST instanceof AST\UpdateStatement; case ($AST instanceof AST\DeleteStatement):
$primaryClass = $this->_em->getClassMetadata($AST->deleteClause->abstractSchemaName);
if ($isDeleteStatement) { return ($primaryClass->isInheritanceTypeJoined())
$primaryClass = $this->_em->getClassMetadata( ? new Exec\MultiTableDeleteExecutor($AST, $this)
$AST->deleteClause->abstractSchemaName : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
);
if ($primaryClass->isInheritanceTypeJoined()) { case ($AST instanceof AST\UpdateStatement):
return new Exec\MultiTableDeleteExecutor($AST, $this); $primaryClass = $this->_em->getClassMetadata($AST->updateClause->abstractSchemaName);
} else {
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
} else if ($isUpdateStatement) {
$primaryClass = $this->_em->getClassMetadata(
$AST->updateClause->abstractSchemaName
);
if ($primaryClass->isInheritanceTypeJoined()) { return ($primaryClass->isInheritanceTypeJoined())
return new Exec\MultiTableUpdateExecutor($AST, $this); ? new Exec\MultiTableUpdateExecutor($AST, $this)
} else { : new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
} default:
return new Exec\SingleSelectExecutor($AST, $this);
} }
return new Exec\SingleSelectExecutor($AST, $this);
} }
/** /**
@ -229,7 +221,11 @@ class SqlWalker implements TreeWalker
*/ */
public function getSQLColumnAlias($columnName) public function getSQLColumnAlias($columnName)
{ {
return $columnName . $this->_aliasCounter++; // Trim the column alias to the maximum identifier length of the platform.
// If the alias is to long, characters are cut off from the beginning.
return $this->_platform->getSQLResultCasing(
substr($columnName . $this->_aliasCounter++, -$this->_platform->getMaxIdentifierLength())
);
} }
/** /**
@ -256,10 +252,9 @@ class SqlWalker implements TreeWalker
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($class->identifier as $idField) { foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; $sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
} }
} }
@ -272,10 +267,9 @@ class SqlWalker implements TreeWalker
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($class->identifier as $idField) { foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; $sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
} }
} }
@ -535,7 +529,6 @@ class SqlWalker implements TreeWalker
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias; $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); $this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']); $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
@ -556,8 +549,7 @@ class SqlWalker implements TreeWalker
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
} }
} }
} }
@ -574,8 +566,7 @@ class SqlWalker implements TreeWalker
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
} }
} }
} }
@ -989,24 +980,19 @@ class SqlWalker implements TreeWalker
$qComp = $this->_queryComponents[$dqlAlias]; $qComp = $this->_queryComponents[$dqlAlias];
$class = $qComp['metadata']; $class = $qComp['metadata'];
if ( ! $selectExpression->fieldIdentificationVariable) { $resultAlias = ( ! $selectExpression->fieldIdentificationVariable)
$resultAlias = $fieldName; ? $fieldName
} else { : $selectExpression->fieldIdentificationVariable;
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
if ($class->isInheritanceTypeJoined()) { $tableName = ($class->isInheritanceTypeJoined())
$tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName); ? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
} else { : $class->getTableName();
$tableName = $class->getTableName();
}
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform); $columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName); $columnAlias = $this->getSQLColumnAlias($columnName);
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias; $sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) { if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
@ -1018,12 +1004,10 @@ class SqlWalker implements TreeWalker
$resultAlias = $selectExpression->fieldIdentificationVariable; $resultAlias = $selectExpression->fieldIdentificationVariable;
} }
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = $this->getSQLColumnAlias('sclr');
$sql .= $this->walkAggregateExpression($expr) . ' AS ' . $columnAlias; $sql .= $this->walkAggregateExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) { if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} }
@ -1034,12 +1018,10 @@ class SqlWalker implements TreeWalker
$resultAlias = $selectExpression->fieldIdentificationVariable; $resultAlias = $selectExpression->fieldIdentificationVariable;
} }
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = $this->getSQLColumnAlias('sclr');
$sql .= '(' . $this->walkSubselect($expr) . ') AS '.$columnAlias; $sql .= '(' . $this->walkSubselect($expr) . ') AS '.$columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) { if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} }
@ -1050,12 +1032,10 @@ class SqlWalker implements TreeWalker
$resultAlias = $selectExpression->fieldIdentificationVariable; $resultAlias = $selectExpression->fieldIdentificationVariable;
} }
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = $this->getSQLColumnAlias('sclr');
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias; $sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) { if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} }
@ -1072,8 +1052,7 @@ class SqlWalker implements TreeWalker
$resultAlias = $selectExpression->fieldIdentificationVariable; $resultAlias = $selectExpression->fieldIdentificationVariable;
} }
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = $this->getSQLColumnAlias('sclr');
if ($expr instanceof AST\Literal) { if ($expr instanceof AST\Literal) {
$sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias; $sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias;
} else { } else {
@ -1082,8 +1061,6 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) { if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} }
@ -1099,14 +1076,11 @@ class SqlWalker implements TreeWalker
$resultAlias = $selectExpression->fieldIdentificationVariable; $resultAlias = $selectExpression->fieldIdentificationVariable;
} }
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = $this->getSQLColumnAlias('sclr');
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias; $sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) { if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} }
@ -1146,8 +1120,6 @@ class SqlWalker implements TreeWalker
$sql .= $sqlTableAlias . '.' . $class->getQuotedColumnName($fieldName, $this->_platform) $sql .= $sqlTableAlias . '.' . $class->getQuotedColumnName($fieldName, $this->_platform)
. ' AS ' . $columnAlias; . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
} }
@ -1171,7 +1143,6 @@ class SqlWalker implements TreeWalker
$sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform) $sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform)
. ' AS ' . $columnAlias; . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName); $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
} }
@ -1185,7 +1156,7 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->getSQLColumnAlias($srcColumn); $columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; $sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn); $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn);
} }
} }
} }
@ -1292,83 +1263,77 @@ class SqlWalker implements TreeWalker
*/ */
public function walkSimpleSelectExpression($simpleSelectExpression) public function walkSimpleSelectExpression($simpleSelectExpression)
{ {
$sql = '';
$expr = $simpleSelectExpression->expression; $expr = $simpleSelectExpression->expression;
$sql = ' ';
if ($expr instanceof AST\PathExpression) { switch (true) {
$sql .= $this->walkPathExpression($expr); case ($expr instanceof AST\PathExpression):
} else if ($expr instanceof AST\AggregateExpression) { $sql .= $this->walkPathExpression($expr);
if ( ! $simpleSelectExpression->fieldIdentificationVariable) { break;
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; case ($expr instanceof AST\AggregateExpression):
} else if ($expr instanceof AST\Subselect) { $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++; $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; break;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if ($expr instanceof AST\Functions\FunctionNode) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++; case ($expr instanceof AST\Subselect):
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias; $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if (
$expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm ||
$expr instanceof AST\ArithmeticFactor ||
$expr instanceof AST\ArithmeticPrimary
) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias; $this->_scalarResultAliasMap[$alias] = $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if (
$expr instanceof AST\NullIfExpression ||
$expr instanceof AST\CoalesceExpression ||
$expr instanceof AST\GeneralCaseExpression ||
$expr instanceof AST\SimpleCaseExpression
) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++; $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias; break;
$this->_scalarResultAliasMap[$alias] = $columnAlias; case ($expr instanceof AST\Functions\FunctionNode):
} else { $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
// IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$first = true;
foreach ($class->identifier as $identifier) { $columnAlias = $this->getSQLColumnAlias('sclr');
if ($first) $first = false; else $sql .= ', '; $this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $tableAlias . '.' . $class->getQuotedColumnName($identifier, $this->_platform);
} $sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\ArithmeticPrimary):
case ($expr instanceof AST\Literal):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = $this->getSQLColumnAlias('sclr');
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = $this->getSQLColumnAlias('sclr');
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
break;
default: // IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$sqlParts = array();
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
$sqlParts[] = $tableAlias . '.' . $columnName;
}
$sql .= implode(', ', $sqlParts);
break;
} }
return ' ' . $sql; return $sql;
} }
/** /**
@ -1391,25 +1356,24 @@ class SqlWalker implements TreeWalker
*/ */
public function walkGroupByClause($groupByClause) public function walkGroupByClause($groupByClause)
{ {
$sql = ''; $sqlParts = array();
foreach ($groupByClause->groupByItems AS $groupByItem) { foreach ($groupByClause->groupByItems AS $groupByItem) {
if (is_string($groupByItem)) { if ( ! is_string($groupByItem)) {
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) { $sqlParts[] = $this->walkGroupByItem($groupByItem);
if ($sql != '') {
$sql .= ', '; continue;
} }
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD; foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
$sql .= $this->walkGroupByItem($groupByItem); $groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
} $groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
} else {
if ($sql != '') { $sqlParts[] = $this->walkGroupByItem($groupByItem);
$sql .= ', ';
}
$sql .= $this->walkGroupByItem($groupByItem);
} }
} }
return ' GROUP BY ' . $sql;
return ' GROUP BY ' . implode(', ', $sqlParts);
} }
/** /**
@ -1431,12 +1395,11 @@ class SqlWalker implements TreeWalker
*/ */
public function walkDeleteClause(AST\DeleteClause $deleteClause) public function walkDeleteClause(AST\DeleteClause $deleteClause)
{ {
$sql = 'DELETE FROM '; $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName); $tableName = $class->getTableName();
$sql .= $class->getQuotedTableName($this->_platform); $sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
$this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
$this->_rootAliases[] = $deleteClause->aliasIdentificationVariable; $this->_rootAliases[] = $deleteClause->aliasIdentificationVariable;
return $sql; return $sql;
@ -1450,17 +1413,14 @@ class SqlWalker implements TreeWalker
*/ */
public function walkUpdateClause($updateClause) public function walkUpdateClause($updateClause)
{ {
$sql = 'UPDATE '; $class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName); $tableName = $class->getTableName();
$sql .= $class->getQuotedTableName($this->_platform); $sql = 'UPDATE ' . $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
$this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
$this->_rootAliases[] = $updateClause->aliasIdentificationVariable; $this->_rootAliases[] = $updateClause->aliasIdentificationVariable;
$sql .= ' SET ' . implode( $sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems));
', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)
);
return $sql; return $sql;
} }
@ -1476,16 +1436,21 @@ class SqlWalker implements TreeWalker
$useTableAliasesBefore = $this->_useSqlTableAliases; $useTableAliasesBefore = $this->_useSqlTableAliases;
$this->_useSqlTableAliases = false; $this->_useSqlTableAliases = false;
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = '; $sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
$newValue = $updateItem->newValue; $newValue = $updateItem->newValue;
if ($newValue === null) { switch (true) {
$sql .= 'NULL'; case ($newValue instanceof AST\Node):
} else if ($newValue instanceof AST\Node) { $sql .= $newValue->dispatch($this);
$sql .= $newValue->dispatch($this); break;
} else {
$sql .= $this->_conn->quote($newValue); case ($newValue === null):
$sql .= 'NULL';
break;
default:
$sql .= $this->_conn->quote($newValue);
break;
} }
$this->_useSqlTableAliases = $useTableAliasesBefore; $this->_useSqlTableAliases = $useTableAliasesBefore;
@ -1502,12 +1467,14 @@ class SqlWalker implements TreeWalker
*/ */
public function walkWhereClause($whereClause) public function walkWhereClause($whereClause)
{ {
$condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : ''; $condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_rootAliases); $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_rootAliases);
if ($condSql) { if ($condSql) {
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql); return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
} else if ($discrSql) { }
if ($discrSql) {
return ' WHERE ' . $discrSql; return ' WHERE ' . $discrSql;
} }
@ -1524,11 +1491,11 @@ class SqlWalker implements TreeWalker
{ {
// Phase 2 AST optimization: Skip processment of ConditionalExpression // Phase 2 AST optimization: Skip processment of ConditionalExpression
// if only one ConditionalTerm is defined // if only one ConditionalTerm is defined
return ( ! ($condExpr instanceof AST\ConditionalExpression)) if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
? $this->walkConditionalTerm($condExpr) return $this->walkConditionalTerm($condExpr);
: implode( }
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
); return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms));
} }
/** /**
@ -1541,11 +1508,11 @@ class SqlWalker implements TreeWalker
{ {
// Phase 2 AST optimization: Skip processment of ConditionalTerm // Phase 2 AST optimization: Skip processment of ConditionalTerm
// if only one ConditionalFactor is defined // if only one ConditionalFactor is defined
return ( ! ($condTerm instanceof AST\ConditionalTerm)) if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
? $this->walkConditionalFactor($condTerm) return $this->walkConditionalFactor($condTerm);
: implode( }
' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)
); return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors));
} }
/** /**
@ -1573,7 +1540,9 @@ class SqlWalker implements TreeWalker
{ {
if ($primary->isSimpleConditionalExpression()) { if ($primary->isSimpleConditionalExpression()) {
return $primary->simpleConditionalExpression->dispatch($this); return $primary->simpleConditionalExpression->dispatch($this);
} else if ($primary->isConditionalExpression()) { }
if ($primary->isConditionalExpression()) {
$condExpr = $primary->conditionalExpression; $condExpr = $primary->conditionalExpression;
return '(' . $this->walkConditionalExpression($condExpr) . ')'; return '(' . $this->walkConditionalExpression($condExpr) . ')';
@ -1644,12 +1613,11 @@ class SqlWalker implements TreeWalker
$sql .= ' AND '; $sql .= ' AND ';
$first = true; $first = true;
foreach ($targetClass->identifier as $idField) { foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.' $sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
} }
} else { // many-to-many } else { // many-to-many
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
@ -1694,12 +1662,11 @@ class SqlWalker implements TreeWalker
$sql .= ' AND '; $sql .= ' AND ';
$first = true; $first = true;
foreach ($targetClass->identifier as $idField) { foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.' $sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
} }
} }
@ -1755,11 +1722,9 @@ class SqlWalker implements TreeWalker
$sql = $this->walkPathExpression($inExpr->pathExpression) $sql = $this->walkPathExpression($inExpr->pathExpression)
. ($inExpr->not ? ' NOT' : '') . ' IN ('; . ($inExpr->not ? ' NOT' : '') . ' IN (';
if ($inExpr->subselect) { $sql .= ($inExpr->subselect)
$sql .= $this->walkSubselect($inExpr->subselect); ? $this->walkSubselect($inExpr->subselect)
} else { : implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
$sql .= implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
}
$sql .= ')'; $sql .= ')';
@ -1834,9 +1799,9 @@ class SqlWalker implements TreeWalker
*/ */
public function walkInParameter($inParam) public function walkInParameter($inParam)
{ {
return $inParam instanceof AST\InputParameter ? return $inParam instanceof AST\InputParameter
$this->walkInputParameter($inParam) : ? $this->walkInputParameter($inParam)
$this->walkLiteral($inParam); : $this->walkLiteral($inParam);
} }
/** /**
@ -1929,23 +1894,19 @@ class SqlWalker implements TreeWalker
*/ */
public function walkComparisonExpression($compExpr) public function walkComparisonExpression($compExpr)
{ {
$sql = ''; $leftExpr = $compExpr->leftExpression;
$leftExpr = $compExpr->leftExpression;
$rightExpr = $compExpr->rightExpression; $rightExpr = $compExpr->rightExpression;
$sql = '';
if ($leftExpr instanceof AST\Node) { $sql .= ($leftExpr instanceof AST\Node)
$sql .= $leftExpr->dispatch($this); ? $leftExpr->dispatch($this)
} else { : (is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr));
$sql .= is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr);
}
$sql .= ' ' . $compExpr->operator . ' '; $sql .= ' ' . $compExpr->operator . ' ';
if ($rightExpr instanceof AST\Node) { $sql .= ($rightExpr instanceof AST\Node)
$sql .= $rightExpr->dispatch($this); ? $rightExpr->dispatch($this)
} else { : (is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr));
$sql .= is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr);
}
return $sql; return $sql;
} }
@ -1984,11 +1945,11 @@ class SqlWalker implements TreeWalker
*/ */
public function walkSimpleArithmeticExpression($simpleArithmeticExpr) public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{ {
return ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
? $this->walkArithmeticTerm($simpleArithmeticExpr) return $this->walkArithmeticTerm($simpleArithmeticExpr);
: implode( }
' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)
); return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms));
} }
/** /**
@ -2000,22 +1961,18 @@ class SqlWalker implements TreeWalker
public function walkArithmeticTerm($term) public function walkArithmeticTerm($term)
{ {
if (is_string($term)) { if (is_string($term)) {
if (isset($this->_queryComponents[$term])) { return (isset($this->_queryComponents[$term]))
$columnName = $this->_queryComponents[$term]['token']['value']; ? $this->_scalarResultAliasMap[$this->_queryComponents[$term]['token']['value']]
: $term;
return $this->_scalarResultAliasMap[$columnName];
}
return $term;
} }
// Phase 2 AST optimization: Skip processment of ArithmeticTerm // Phase 2 AST optimization: Skip processment of ArithmeticTerm
// if only one ArithmeticFactor is defined // if only one ArithmeticFactor is defined
return ( ! ($term instanceof AST\ArithmeticTerm)) if ( ! ($term instanceof AST\ArithmeticTerm)) {
? $this->walkArithmeticFactor($term) return $this->walkArithmeticFactor($term);
: implode( }
' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)
); return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors));
} }
/** /**
@ -2032,10 +1989,13 @@ class SqlWalker implements TreeWalker
// Phase 2 AST optimization: Skip processment of ArithmeticFactor // Phase 2 AST optimization: Skip processment of ArithmeticFactor
// if only one ArithmeticPrimary is defined // if only one ArithmeticPrimary is defined
return ( ! ($factor instanceof AST\ArithmeticFactor)) if ( ! ($factor instanceof AST\ArithmeticFactor)) {
? $this->walkArithmeticPrimary($factor) return $this->walkArithmeticPrimary($factor);
: ($factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '')) }
. $this->walkArithmeticPrimary($factor->arithmeticPrimary);
$sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
} }
/** /**
@ -2048,7 +2008,9 @@ class SqlWalker implements TreeWalker
{ {
if ($primary instanceof AST\SimpleArithmeticExpression) { if ($primary instanceof AST\SimpleArithmeticExpression) {
return '(' . $this->walkSimpleArithmeticExpression($primary) . ')'; return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
} else if ($primary instanceof AST\Node) { }
if ($primary instanceof AST\Node) {
return $primary->dispatch($this); return $primary->dispatch($this);
} }

View File

@ -67,7 +67,9 @@ class SchemaTool
/** /**
* Creates the database schema for the given array of ClassMetadata instances. * Creates the database schema for the given array of ClassMetadata instances.
* *
* @throws ToolsException
* @param array $classes * @param array $classes
* @return void
*/ */
public function createSchema(array $classes) public function createSchema(array $classes)
{ {
@ -75,7 +77,11 @@ class SchemaTool
$conn = $this->_em->getConnection(); $conn = $this->_em->getConnection();
foreach ($createSchemaSql as $sql) { foreach ($createSchemaSql as $sql) {
$conn->executeQuery($sql); try {
$conn->executeQuery($sql);
} catch(\Exception $e) {
throw ToolsException::schemaToolFailure($sql, $e);
}
} }
} }

View File

@ -50,7 +50,7 @@ class SchemaValidator
} }
/** /**
* Checks the internal consistency of mapping files. * Checks the internal consistency of all mapping files.
* *
* There are several checks that can't be done at runtime or are too expensive, which can be verified * There are several checks that can't be done at runtime or are too expensive, which can be verified
* with this command. For example: * with this command. For example:
@ -69,150 +69,7 @@ class SchemaValidator
$classes = $cmf->getAllMetadata(); $classes = $cmf->getAllMetadata();
foreach ($classes AS $class) { foreach ($classes AS $class) {
$ce = array(); if ($ce = $this->validateClass($class)) {
/* @var $class ClassMetadata */
foreach ($class->associationMappings AS $fieldName => $assoc) {
if (!$cmf->hasMetadataFor($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.';
}
if ($assoc['mappedBy'] && $assoc['inversedBy']) {
$ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning.";
}
$targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
/* @var $assoc AssociationMapping */
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association.";
}
if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) {
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ".
"incosistent with each other.";
}
}
if ($assoc['inversedBy']) {
if ($targetMetadata->hasField($assoc['inversedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association.";
}
if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist.";
} else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) {
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ".
"incosistent with each other.";
}
}
if ($assoc['isOwningSide']) {
if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) {
foreach ($assoc['joinTable']['joinColumns'] AS $joinColumn) {
if (!isset($class->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $class->name . "'.";
break;
}
$fieldName = $class->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $class->identifier)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
if (!isset($targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
$ce[] = "The inverse referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetClass->name . "'.";
break;
}
$fieldName = $targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetClass->identifier)) {
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
if (count($targetClass->identifier) != count($assoc['joinTable']['inverseJoinColumns'])) {
$ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to match to ALL identifier columns of the target entity '". $targetClass->name . "'";
}
if (count($class->identifier) != count($assoc['joinTable']['joinColumns'])) {
$ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
}
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($assoc['joinColumns'] AS $joinColumn) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
if (!isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetClass->name . "'.";
break;
}
$fieldName = $targetClass->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetClass->identifier)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
if (count($class->identifier) != count($assoc['joinColumns'])) {
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
}
}
}
if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
foreach ($assoc['orderBy'] AS $orderField => $orientation) {
if (!$targetClass->hasField($orderField)) {
$ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " .
$orderField . " that is not a field on the target entity " . $targetClass->name;
}
}
}
}
foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) {
if ($publicAttr->isStatic()) {
continue;
}
$ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ".
"or protected. Public fields may break lazy-loading.";
}
foreach ($class->subClasses AS $subClass) {
if (!in_array($class->name, class_parents($subClass))) {
$ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ".
"of '" . $class->name . "' but these entities are not related through inheritance.";
}
}
if ($ce) {
$errors[$class->name] = $ce; $errors[$class->name] = $ce;
} }
} }
@ -220,6 +77,165 @@ class SchemaValidator
return $errors; return $errors;
} }
/**
* Validate a single class of the current
*
* @param ClassMetadataInfo $class
* @return array
*/
public function validateClass(ClassMetadataInfo $class)
{
$ce = array();
$cmf = $this->em->getMetadataFactory();
foreach ($class->associationMappings AS $fieldName => $assoc) {
if (!$cmf->hasMetadataFor($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.';
return $ce;
}
if ($assoc['mappedBy'] && $assoc['inversedBy']) {
$ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning.";
}
$targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
/* @var $assoc AssociationMapping */
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association.";
}
if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) {
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ".
"incosistent with each other.";
}
}
if ($assoc['inversedBy']) {
if ($targetMetadata->hasField($assoc['inversedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association.";
}
if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist.";
} else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) {
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ".
"incosistent with each other.";
}
}
if ($assoc['isOwningSide']) {
if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) {
foreach ($assoc['joinTable']['joinColumns'] AS $joinColumn) {
if (!isset($class->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $class->name . "'.";
break;
}
$fieldName = $class->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $class->identifier)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
if (!isset($targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
$ce[] = "The inverse referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
break;
}
$fieldName = $targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetMetadata->identifier)) {
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) {
$ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " .
"however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $assoc['relationToTargetKeyColumns'])) .
"' are missing.";
}
if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) {
$ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to contain to ALL identifier columns of the source entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), $assoc['relationToSourceKeyColumns'])) .
"' are missing.";
}
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($assoc['joinColumns'] AS $joinColumn) {
if (!isset($targetMetadata->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
break;
}
$fieldName = $targetMetadata->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetMetadata->identifier)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
if (count($class->getIdentifierColumnNames()) != count($assoc['joinColumns'])) {
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), $assoc['joinColumns'])) .
"' are missing.";
}
}
}
if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
foreach ($assoc['orderBy'] AS $orderField => $orientation) {
if (!$targetMetadata->hasField($orderField)) {
$ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " .
$orderField . " that is not a field on the target entity " . $targetMetadata->name;
}
}
}
}
foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) {
if ($publicAttr->isStatic()) {
continue;
}
$ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ".
"or protected. Public fields may break lazy-loading.";
}
foreach ($class->subClasses AS $subClass) {
if (!in_array($class->name, class_parents($subClass))) {
$ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ".
"of '" . $class->name . "' but these entities are not related through inheritance.";
}
}
return $ce;
}
/** /**
* Check if the Database Schema is in sync with the current metadata state. * Check if the Database Schema is in sync with the current metadata state.
* *

View File

@ -1,11 +1,38 @@
<?php <?php
/*
* 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\Tools; namespace Doctrine\ORM\Tools;
use Doctrine\ORM\ORMException; use Doctrine\ORM\ORMException;
/**
* Tools related Exceptions
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ToolsException extends ORMException class ToolsException extends ORMException
{ {
public static function schemaToolFailure($sql, \Exception $e)
{
return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e);
}
public static function couldNotMapDoctrine1Type($type) public static function couldNotMapDoctrine1Type($type)
{ {
return new self("Could not map doctrine 1 type '$type'!"); return new self("Could not map doctrine 1 type '$type'!");

View File

@ -471,19 +471,21 @@ class UnitOfWork implements PropertyChangedListener
*/ */
public function computeChangeSet(ClassMetadata $class, $entity) public function computeChangeSet(ClassMetadata $class, $entity)
{ {
if ( ! $class->isInheritanceTypeNone()) {
$class = $this->em->getClassMetadata(get_class($entity));
}
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if (isset($this->readOnlyObjects[$oid])) { if (isset($this->readOnlyObjects[$oid])) {
return; return;
} }
if ( ! $class->isInheritanceTypeNone()) {
$class = $this->em->getClassMetadata(get_class($entity));
}
$actualData = array(); $actualData = array();
foreach ($class->reflFields as $name => $refProp) { foreach ($class->reflFields as $name => $refProp) {
$value = $refProp->getValue($entity); $value = $refProp->getValue($entity);
if (isset($class->associationMappings[$name]) if (isset($class->associationMappings[$name])
&& ($class->associationMappings[$name]['type'] & ClassMetadata::TO_MANY) && ($class->associationMappings[$name]['type'] & ClassMetadata::TO_MANY)
&& $value !== null && $value !== null
@ -2054,6 +2056,11 @@ class UnitOfWork implements PropertyChangedListener
// Loading the entity right here, if its in the eager loading map get rid of it there. // Loading the entity right here, if its in the eager loading map get rid of it there.
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]); unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
if (isset($this->eagerLoadingEntities[$class->rootEntityName]) &&
! $this->eagerLoadingEntities[$class->rootEntityName]) {
unset($this->eagerLoadingEntities[$class->rootEntityName]);
}
// Properly initialize any unfetched associations, if partial objects are not allowed. // Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
foreach ($class->associationMappings as $field => $assoc) { foreach ($class->associationMappings as $field => $assoc) {

View File

@ -9,6 +9,7 @@ class DDC117Article
{ {
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */ /** @Id @Column(type="integer", name="article_id") @GeneratedValue */
private $id; private $id;
/** @Column */ /** @Column */
private $title; private $title;

View File

@ -21,7 +21,7 @@ class LegacyUser
*/ */
public $_username; public $_username;
/** /**
* @Column(type="string", length=255) * @Column(type="string", length=255, name="name")
*/ */
public $_name; public $_name;
/** /**

View File

@ -23,12 +23,12 @@ class LegacyUserReference
private $_target; private $_target;
/** /**
* @column(type="string") * @column(type="string", name="description")
*/ */
private $_description; private $_description;
/** /**
* @column(type="datetime") * @column(type="datetime", name="created")
*/ */
private $_created; private $_created;

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -33,11 +31,13 @@ require_once __DIR__ . '/../../TestInit.php';
* @link http://www.doctrine-project.org * @link http://www.doctrine-project.org
* @since 2.0 * @since 2.0
*/ */
class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase class CustomTreeWalkersTest extends \Doctrine\Tests\OrmTestCase
{ {
protected function setUp() { private $_em;
$this->useModelSet('cms');
parent::setUp(); protected function setUp()
{
$this->_em = $this->_getTestEntityManager();
} }
public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed) public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed)

View File

@ -20,7 +20,9 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function tearDown() public function tearDown()
{ {
$this->_em->getConfiguration()->setEntityNamespaces(array()); if ($this->_em) {
$this->_em->getConfiguration()->setEntityNamespaces(array());
}
parent::tearDown(); parent::tearDown();
} }

View File

@ -304,6 +304,49 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized."); $this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
} }
/**
* @group DDC-1399
*/
public function testCountAfterAddThenFlush()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
$newGroup->name = "Test4";
$user->addGroup($newGroup);
$this->_em->persist($newGroup);
$this->assertFalse($user->groups->isInitialized());
$this->assertEquals(4, count($user->groups));
$this->assertFalse($user->groups->isInitialized());
$this->_em->flush();
$this->assertEquals(4, count($user->groups));
}
/**
* @group DDC-1462
*/
public function testSliceOnDirtyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
/* @var $user CmsUser */
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
$newGroup->name = "Test4";
$user->addGroup($newGroup);
$this->_em->persist($newGroup);
$qc = $this->getCurrentQueryCount();
$groups = $user->groups->slice(0, 10);
$this->assertEquals(4, count($groups));
$this->assertEquals($qc + 1, $this->getCurrentQueryCount());
}
private function loadFixture() private function loadFixture()
{ {
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser(); $user1 = new \Doctrine\Tests\Models\CMS\CmsUser();

View File

@ -27,14 +27,14 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush(); $this->_em->flush();
$readOnly->name = "Test2"; $readOnly->name = "Test2";
$readOnly->number = 4321; $readOnly->numericValue = 4321;
$this->_em->flush(); $this->_em->flush();
$this->_em->clear(); $this->_em->clear();
$dbReadOnly = $this->_em->find('Doctrine\Tests\ORM\Functional\ReadOnlyEntity', $readOnly->id); $dbReadOnly = $this->_em->find('Doctrine\Tests\ORM\Functional\ReadOnlyEntity', $readOnly->id);
$this->assertEquals("Test1", $dbReadOnly->name); $this->assertEquals("Test1", $dbReadOnly->name);
$this->assertEquals(1234, $dbReadOnly->number); $this->assertEquals(1234, $dbReadOnly->numericValue);
} }
} }
@ -51,11 +51,11 @@ class ReadOnlyEntity
/** @column(type="string") */ /** @column(type="string") */
public $name; public $name;
/** @Column(type="integer") */ /** @Column(type="integer") */
public $number; public $numericValue;
public function __construct($name, $number) public function __construct($name, $number)
{ {
$this->name = $name; $this->name = $name;
$this->number = $number; $this->numericValue = $number;
} }
} }

View File

@ -90,10 +90,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCache() public function testUseResultCache()
{ {
$cache = new \Doctrine\Common\Cache\ArrayCache(); $cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$query->useResultCache(true); $query->useResultCache(true);
$query->setResultCacheDriver($cache);
$query->setResultCacheId('testing_result_cache_id'); $query->setResultCacheId('testing_result_cache_id');
$users = $query->getResult(); $users = $query->getResult();
@ -108,11 +108,11 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCacheParams() public function testUseResultCacheParams()
{ {
$cache = new \Doctrine\Common\Cache\ArrayCache(); $cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$sqlCount = count($this->_sqlLoggerStack->queries); $sqlCount = count($this->_sqlLoggerStack->queries);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1');
$query->setParameter(1, 1); $query->setParameter(1, 1);
$query->setResultCacheDriver($cache);
$query->useResultCache(true); $query->useResultCache(true);
$query->getResult(); $query->getResult();
@ -149,10 +149,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
} }
/** /**
* @param <type> $query * @param string $query
* @depends testNativeQueryResultCaching * @depends testNativeQueryResultCaching
*/ */
public function testResultCacheDependsOnQueryHints($query) public function testResultCacheNotDependsOnQueryHints($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache); $cacheCount = $this->getCacheSize($cache);
@ -160,7 +160,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setHint('foo', 'bar'); $query->setHint('foo', 'bar');
$query->getResult(); $query->getResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache)); $this->assertEquals($cacheCount, $this->getCacheSize($cache));
} }
/** /**
@ -182,7 +182,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
* @param <type> $query * @param <type> $query
* @depends testNativeQueryResultCaching * @depends testNativeQueryResultCaching
*/ */
public function testResultCacheDependsOnHydrationMode($query) public function testResultCacheNotDependsOnHydrationMode($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache); $cacheCount = $this->getCacheSize($cache);
@ -190,7 +190,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode()); $this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
$query->getArrayResult(); $query->getArrayResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache)); $this->assertEquals($cacheCount, $this->getCacheSize($cache));
} }
/** /**

View File

@ -44,11 +44,10 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
->setParameter('author', $user) ->setParameter('author', $user)
->getResult(); ->getResult();
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author AND a.text = :text"; $dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author";
$farticle = $this->_em->createQuery($dql) $farticle = $this->_em->createQuery($dql)
->setParameter('author', $user) ->setParameter('author', $user)
->setParameter('topic', 'This is John Galt speaking!') ->setParameter('topic', 'This is John Galt speaking!')
->setParameter('text', 'Yadda Yadda!')
->getSingleResult(); ->getSingleResult();
$this->assertSame($article, $farticle); $this->assertSame($article, $farticle);
@ -70,12 +69,11 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($article); $this->_em->persist($article);
$this->_em->flush(); $this->_em->flush();
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3 AND a.text = ?4"; $dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3";
$farticle = $this->_em->createQuery($dql) $farticle = $this->_em->createQuery($dql)
->setParameter(1, 'This is John Galt speaking!') ->setParameter(1, 'This is John Galt speaking!')
->setParameter(2, $user) ->setParameter(2, $user)
->setParameter(3, $user) ->setParameter(3, $user)
->setParameter(4, 'Yadda Yadda!')
->getSingleResult(); ->getSingleResult();
$this->assertSame($article, $farticle); $this->assertSame($article, $farticle);

View File

@ -106,7 +106,7 @@ class DDC1209_3
{ {
/** /**
* @Id * @Id
* @Column(type="datetime") * @Column(type="datetime", name="somedate")
*/ */
private $date; private $date;

View File

@ -34,8 +34,8 @@ class DDC1225Test extends \Doctrine\Tests\OrmFunctionalTestCase
->setParameter(1, 0); ->setParameter(1, 0);
$this->assertEquals( $this->assertEquals(
'SELECT t0_.test_entity2_id AS test_entity2_id0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?', strtolower('SELECT t0_.test_entity2_id AS test_entity2_id0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?'),
$qb->getQuery()->getSQL() strtolower($qb->getQuery()->getSQL())
); );
} }
} }

View File

@ -21,7 +21,7 @@ class DDC1228Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1228User'), $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1228User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1228Profile'), $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1228Profile'),
)); ));
} catch(\PDOException $e) { } catch(\Exception $e) {
} }
} }
@ -90,10 +90,10 @@ class DDC1228User
public $id; public $id;
/** /**
* @column(type="string") * @Column(type="string")
* @var string * @var string
*/ */
public $name = ''; public $name = 'Bar';
/** /**
* @OneToOne(targetEntity="DDC1228Profile") * @OneToOne(targetEntity="DDC1228Profile")

View File

@ -19,7 +19,7 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_schemaTool->createSchema(array( $this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1238User'), $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1238User'),
)); ));
} catch(\PDOException $e) { } catch(\Exception $e) {
} }
} }

View File

@ -7,17 +7,17 @@ use DateTime;
require_once __DIR__ . '/../../../TestInit.php'; require_once __DIR__ . '/../../../TestInit.php';
/** /**
* @group DDC-1135 * @group DDC-1335
*/ */
class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase class DDC1335Test extends \Doctrine\Tests\OrmFunctionalTestCase
{ {
protected function setUp() protected function setUp()
{ {
parent::setUp(); parent::setUp();
try { try {
$this->_schemaTool->createSchema(array( $this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135User'), $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1335User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135Phone'), $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1335Phone'),
)); ));
$this->loadFixture(); $this->loadFixture();
} catch(\Exception $e) { } catch(\Exception $e) {
@ -27,7 +27,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testDql() public function testDql()
{ {
$dql = 'SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id'; $dql = 'SELECT u FROM ' . __NAMESPACE__ . '\DDC1335User u INDEX BY u.id';
$query = $this->_em->createQuery($dql); $query = $this->_em->createQuery($dql);
$result = $query->getResult(); $result = $query->getResult();
@ -36,7 +36,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey(2, $result); $this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result); $this->assertArrayHasKey(3, $result);
$dql = 'SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id'; $dql = 'SELECT u, p FROM '.__NAMESPACE__ . '\DDC1335User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id';
$query = $this->_em->createQuery($dql); $query = $this->_em->createQuery($dql);
$result = $query->getResult(); $result = $query->getResult();
@ -65,7 +65,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testTicket() public function testTicket()
{ {
$builder = $this->_em->createQueryBuilder(); $builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.id'); $builder->select('u')->from(__NAMESPACE__ . '\DDC1335User', 'u', 'u.id');
$dql = $builder->getQuery()->getDQL(); $dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult(); $result = $builder->getQuery()->getResult();
@ -74,13 +74,13 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey(1, $result); $this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey(2, $result); $this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result); $this->assertArrayHasKey(3, $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id', $dql); $this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1335User u INDEX BY u.id', $dql);
} }
public function testIndexByUnique() public function testIndexByUnique()
{ {
$builder = $this->_em->createQueryBuilder(); $builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email'); $builder->select('u')->from(__NAMESPACE__ . '\DDC1335User', 'u', 'u.email');
$dql = $builder->getQuery()->getDQL(); $dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult(); $result = $builder->getQuery()->getResult();
@ -89,14 +89,14 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey('foo@foo.com', $result); $this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result); $this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result); $this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.email', $dql); $this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1335User u INDEX BY u.email', $dql);
} }
public function testIndexWithJoin() public function testIndexWithJoin()
{ {
$builder = $this->_em->createQueryBuilder(); $builder = $this->_em->createQueryBuilder();
$builder->select('u','p') $builder->select('u','p')
->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email') ->from(__NAMESPACE__ . '\DDC1335User', 'u', 'u.email')
->join('u.phones', 'p', null, null, 'p.id'); ->join('u.phones', 'p', null, null, 'p.id');
$dql = $builder->getQuery()->getDQL(); $dql = $builder->getQuery()->getDQL();
@ -123,7 +123,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray()); $this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray()); $this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());
$this->assertEquals('SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql); $this->assertEquals('SELECT u, p FROM '.__NAMESPACE__ . '\DDC1335User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql);
} }
private function loadFixture() private function loadFixture()
@ -132,9 +132,9 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$p2 = array('22 xxxx-xxxx','22 yyyy-yyyy','22 zzzz-zzzz'); $p2 = array('22 xxxx-xxxx','22 yyyy-yyyy','22 zzzz-zzzz');
$p3 = array('33 xxxx-xxxx','33 yyyy-yyyy','33 zzzz-zzzz'); $p3 = array('33 xxxx-xxxx','33 yyyy-yyyy','33 zzzz-zzzz');
$u1 = new DDC1135User("foo@foo.com", "Foo",$p1); $u1 = new DDC1335User("foo@foo.com", "Foo",$p1);
$u2 = new DDC1135User("bar@bar.com", "Bar",$p2); $u2 = new DDC1335User("bar@bar.com", "Bar",$p2);
$u3 = new DDC1135User("foobar@foobar.com", "Foo Bar",$p3); $u3 = new DDC1335User("foobar@foobar.com", "Foo Bar",$p3);
$this->_em->persist($u1); $this->_em->persist($u1);
$this->_em->persist($u2); $this->_em->persist($u2);
@ -148,7 +148,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
/** /**
* @Entity * @Entity
*/ */
class DDC1135User class DDC1335User
{ {
/** /**
* @Id @Column(type="integer") * @Id @Column(type="integer")
@ -167,7 +167,7 @@ class DDC1135User
public $name; public $name;
/** /**
* @OneToMany(targetEntity="DDC1135Phone", mappedBy="user", cascade={"persist", "remove"}) * @OneToMany(targetEntity="DDC1335Phone", mappedBy="user", cascade={"persist", "remove"})
*/ */
public $phones; public $phones;
@ -178,7 +178,7 @@ class DDC1135User
$this->phones = new \Doctrine\Common\Collections\ArrayCollection(); $this->phones = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($numbers as $number) { foreach ($numbers as $number) {
$this->phones->add(new DDC1135Phone($this,$number)); $this->phones->add(new DDC1335Phone($this,$number));
} }
} }
} }
@ -186,22 +186,22 @@ class DDC1135User
/** /**
* @Entity * @Entity
*/ */
class DDC1135Phone class DDC1335Phone
{ {
/** /**
* @Id * @Id
* @Column(name="id", type="integer") * @Column(name="id", type="integer")
* @GeneratedValue(strategy="AUTO") * @GeneratedValue
*/ */
public $id; public $id;
/** /**
* @Column(name="number", type="string", nullable = false) * @Column(name="numericalValue", type="string", nullable = false)
*/ */
public $number; public $numericalValue;
/** /**
* @ManyToOne(targetEntity="DDC1135User", inversedBy="phones") * @ManyToOne(targetEntity="DDC1335User", inversedBy="phones")
* @JoinColumn(name="user_id", referencedColumnName="id", nullable = false) * @JoinColumn(name="user_id", referencedColumnName="id", nullable = false)
*/ */
public $user; public $user;
@ -209,6 +209,6 @@ class DDC1135Phone
public function __construct($user, $number) public function __construct($user, $number)
{ {
$this->user = $user; $this->user = $user;
$this->number = $number; $this->numericalValue = $number;
} }
} }

View File

@ -0,0 +1,69 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1454
*/
class DDC1454Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$pic = new DDC1454Picture();
$this->_em->getUnitOfWork()->getEntityState($pic);
}
}
/**
* @Entity
*/
class DDC1454Picture extends DDC1454File
{
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"picture" = "DDC1454Picture"})
*/
class DDC1454File
{
/**
* @Column(name="file_id", type="integer")
* @Id
*/
public $fileId;
public function __construct() {
$this->fileId = rand();
}
/**
* Get fileId
*/
public function getFileId() {
return $this->fileId;
}
}

View File

@ -25,18 +25,15 @@ class DDC331Test extends \Doctrine\Tests\OrmFunctionalTestCase
parent::setUp(); parent::setUp();
} }
/**
* @group DDC-331
*/
public function testSelectFieldOnRootEntity() public function testSelectFieldOnRootEntity()
{ {
$employee = new CompanyEmployee;
$employee->setName('Roman S. Borschel');
$employee->setSalary(100000);
$employee->setDepartment('IT');
$this->_em->persist($employee);
$this->_em->flush();
$this->_em->clear();
$q = $this->_em->createQuery('SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e'); $q = $this->_em->createQuery('SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e');
$this->assertEquals('SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id', $q->getSql()); $this->assertEquals(
strtolower('SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id'),
strtolower($q->getSql())
);
} }
} }

View File

@ -18,7 +18,10 @@ class DDC448Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testIssue() public function testIssue()
{ {
$q = $this->_em->createQuery("select b from ".__NAMESPACE__."\\DDC448SubTable b where b.connectedClassId = ?1"); $q = $this->_em->createQuery("select b from ".__NAMESPACE__."\\DDC448SubTable b where b.connectedClassId = ?1");
$this->assertEquals('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.connectedClassId AS connectedClassId2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?', $q->getSQL()); $this->assertEquals(
strtolower('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.connectedClassId AS connectedClassId2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?'),
strtolower($q->getSQL())
);
} }
} }

View File

@ -18,7 +18,10 @@ class DDC493Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testIssue() public function testIssue()
{ {
$q = $this->_em->createQuery("select u, c.data from ".__NAMESPACE__."\\DDC493Distributor u JOIN u.contact c"); $q = $this->_em->createQuery("select u, c.data from ".__NAMESPACE__."\\DDC493Distributor u JOIN u.contact c");
$this->assertEquals('SELECT d0_.id AS id0, d1_.data AS data1, d0_.discr AS discr2, d0_.contact AS contact3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id', $q->getSQL()); $this->assertEquals(
strtolower('SELECT d0_.id AS id0, d1_.data AS data1, d0_.discr AS discr2, d0_.contact AS contact3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id'),
strtolower($q->getSQL())
);
} }
} }

View File

@ -18,7 +18,10 @@ class DDC513Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testIssue() public function testIssue()
{ {
$q = $this->_em->createQuery("select u from ".__NAMESPACE__."\\DDC513OfferItem u left join u.price p"); $q = $this->_em->createQuery("select u from ".__NAMESPACE__."\\DDC513OfferItem u left join u.price p");
$this->assertEquals('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.price AS price2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id', $q->getSQL()); $this->assertEquals(
strtolower('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.price AS price2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id'),
strtolower($q->getSQL())
);
} }
} }

View File

@ -30,7 +30,10 @@ class DDC698Test extends \Doctrine\Tests\OrmFunctionalTestCase
$sql = $qb->getQuery()->getSQL(); $sql = $qb->getQuery()->getSQL();
$this->assertEquals('SELECT p0_.privilegeID AS privilegeID0, p0_.name AS name1, r1_.roleID AS roleID2, r1_.name AS name3, r1_.shortName AS shortName4 FROM Privileges p0_ LEFT JOIN RolePrivileges r2_ ON p0_.privilegeID = r2_.privilegeID LEFT JOIN Roles r1_ ON r1_.roleID = r2_.roleID', $sql); $this->assertEquals(
strtolower('SELECT p0_.privilegeID AS privilegeID0, p0_.name AS name1, r1_.roleID AS roleID2, r1_.name AS name3, r1_.shortName AS shortName4 FROM Privileges p0_ LEFT JOIN RolePrivileges r2_ ON p0_.privilegeID = r2_.privilegeID LEFT JOIN Roles r1_ ON r1_.roleID = r2_.roleID'),
strtolower($sql)
);
} }
} }

View File

@ -19,7 +19,10 @@ class DDC719Test extends \Doctrine\Tests\OrmFunctionalTestCase
{ {
$q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY'); $q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY');
$this->assertEquals('SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0', $q->getSQL()); $this->assertEquals(
strtolower('SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'),
strtolower($q->getSQL())
);
} }
} }

View File

@ -34,10 +34,10 @@ class DDC949Test extends \Doctrine\Tests\OrmFunctionalTestCase
$true = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => true)); $true = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => true));
$false = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => false)); $false = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => false));
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $true); $this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $true, "True model not found");
$this->assertTrue($true->booleanField, "True Boolean Model should be true."); $this->assertTrue($true->booleanField, "True Boolean Model should be true.");
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $false); $this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $false, "False model not found");
$this->assertFalse($false->booleanField, "False Boolean Model should be false."); $this->assertFalse($false->booleanField, "False Boolean Model should be false.");
} }
} }

View File

@ -604,7 +604,24 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
. ' WHERE EXISTS (' . ' WHERE EXISTS ('
. 'SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id' . 'SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id'
. ')' . ')'
);
}
public function testExistsExpressionWithSimpleSelectReturningScalar()
{
$this->assertSqlGeneration(
// DQL
// The result of this query consists of all employees whose spouses are also employees.
'SELECT DISTINCT emp FROM Doctrine\Tests\Models\CMS\CmsEmployee emp
WHERE EXISTS (
SELECT 1
FROM Doctrine\Tests\Models\CMS\CmsEmployee spouseEmp
WHERE spouseEmp = emp.spouse)',
// SQL
'SELECT DISTINCT c0_.id AS id0, c0_.name AS name1 FROM cms_employees c0_'
. ' WHERE EXISTS ('
. 'SELECT 1 AS sclr2 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id'
. ')'
); );
} }
@ -729,12 +746,12 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->assertSqlGeneration( $this->assertSqlGeneration(
"SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true", "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true",
"SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = true" "SELECT b0_.id AS id0, b0_.booleanField AS booleanfield1 FROM boolean_model b0_ WHERE b0_.booleanField = true"
); );
$this->assertSqlGeneration( $this->assertSqlGeneration(
"SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false", "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false",
"SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = false" "SELECT b0_.id AS id0, b0_.booleanField AS booleanfield1 FROM boolean_model b0_ WHERE b0_.booleanField = false"
); );
$this->_em->getConnection()->setDatabasePlatform($oldPlat); $this->_em->getConnection()->setDatabasePlatform($oldPlat);
@ -877,7 +894,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->assertSqlGeneration( $this->assertSqlGeneration(
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", "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 ". "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", "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE",
array(Query::HINT_LOCK_MODE => \Doctrine\DBAL\LockMode::PESSIMISTIC_READ) array(Query::HINT_LOCK_MODE => \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)
); );
@ -1251,6 +1268,39 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
array(Query::HINT_FORCE_PARTIAL_LOAD => false) array(Query::HINT_FORCE_PARTIAL_LOAD => false)
); );
} }
/**
* @group DDC-1384
*/
public function testAliasDoesNotExceedPlatformDefinedLength()
{
$this->assertSqlGeneration(
'SELECT m FROM ' . __NAMESPACE__ . '\\DDC1384Model m',
"SELECT d0_.aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalker_fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo AS fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0 FROM DDC1384Model d0_"
);
}
/**
* @group DDC-331
* @group DDC-1384
*/
public function testIssue331()
{
$this->assertSqlGeneration(
'SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e',
'SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id'
);
}
/**
* @group DDC-1435
*/
public function testForeignKeyAsPrimaryKeySubselect()
{
$this->assertSqlGeneration(
"SELECT s FROM Doctrine\Tests\Models\DDC117\DDC117Article s WHERE EXISTS (SELECT r FROM Doctrine\Tests\Models\DDC117\DDC117Reference r WHERE r.source = s)",
"SELECT d0_.article_id AS article_id0, d0_.title AS title1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)"
);
}
} }
@ -1281,3 +1331,15 @@ class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
$parser->match(\Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS); $parser->match(\Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS);
} }
} }
/**
* @Entity
*/
class DDC1384Model
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
protected $aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalker_fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo;
}

View File

@ -71,4 +71,88 @@ class SchemaValidatorTest extends \Doctrine\Tests\OrmTestCase
)); ));
$this->validator->validateMapping(); $this->validator->validateMapping();
} }
/**
* @group DDC-1439
*/
public function testInvalidManyToManyJoinColumnSchema()
{
$class1 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity1');
$class2 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity2');
$ce = $this->validator->validateClass($class1);
$this->assertEquals(
array(
"The inverse join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the target entity 'Doctrine\Tests\ORM\Tools\InvalidEntity2', however 'key4' are missing.",
"The join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the source entity 'Doctrine\Tests\ORM\Tools\InvalidEntity1', however 'key2' are missing."
),
$ce
);
}
/**
* @group DDC-1439
*/
public function testInvalidToOneJoinColumnSchema()
{
$class1 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity1');
$class2 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity2');
$ce = $this->validator->validateClass($class2);
$this->assertEquals(
array(
"The referenced column name 'id' does not have a corresponding field with this column name on the class 'Doctrine\Tests\ORM\Tools\InvalidEntity1'.",
"The join columns of the association 'assoc' have to match to ALL identifier columns of the source entity 'Doctrine\Tests\ORM\Tools\InvalidEntity2', however 'key3, key4' are missing."
),
$ce
);
}
}
/**
* @Entity
*/
class InvalidEntity1
{
/**
* @Id @Column
*/
protected $key1;
/**
* @Id @Column
*/
protected $key2;
/**
* @ManyToMany (targetEntity="InvalidEntity2")
* @JoinTable (name="Entity1Entity2",
* joinColumns={@JoinColumn(name="key1", referencedColumnName="key1")},
* inverseJoinColumns={@JoinColumn(name="key3", referencedColumnName="key3")}
* )
*/
protected $entity2;
}
/**
* @Entity
*/
class InvalidEntity2
{
/**
* @Id @Column
* @GeneratedValue(strategy="AUTO")
*/
protected $key3;
/**
* @Id @Column
* @GeneratedValue(strategy="AUTO")
*/
protected $key4;
/**
* @ManyToOne(targetEntity="InvalidEntity1")
*/
protected $assoc;
} }