[2.0] Further cleanups. Started eager loading support.
This commit is contained in:
parent
b3d110bac4
commit
49434b0322
@ -423,6 +423,8 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function execute($params = array(), $hydrationMode = null)
|
||||
{
|
||||
// If there are still pending insertions in the UnitOfWork we need to flush
|
||||
// in order to guarantee a correct result.
|
||||
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
|
||||
$this->_em->flush();
|
||||
}
|
||||
@ -442,25 +444,24 @@ abstract class AbstractQuery
|
||||
if ($cached === false) {
|
||||
// Cache miss.
|
||||
$result = $this->_doExecute($params);
|
||||
$queryResult = CacheHandler::fromResultSet($this, $result);
|
||||
$cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL);
|
||||
$cacheDriver->save($hash, serialize($result), $this->_resultCacheTTL);
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
// Cache hit.
|
||||
$queryResult = CacheHandler::fromCachedResult($this, $cached);
|
||||
|
||||
return $queryResult->getResultSet();
|
||||
return unserialize($cached);
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute($params);
|
||||
|
||||
if (is_integer($stmt)) {
|
||||
if (is_numeric($stmt)) {
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_resultSetMapping);
|
||||
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +55,9 @@ abstract class AbstractHydrator
|
||||
|
||||
/** @var Statement The statement that provides the data to hydrate. */
|
||||
protected $_stmt;
|
||||
|
||||
/** @var array The query hints. */
|
||||
protected $_hints;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.
|
||||
@ -90,10 +93,11 @@ abstract class AbstractHydrator
|
||||
* @param object $resultSetMapping
|
||||
* @return mixed
|
||||
*/
|
||||
public function hydrateAll($stmt, $resultSetMapping)
|
||||
public function hydrateAll($stmt, $resultSetMapping, array $hints = array())
|
||||
{
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
$result = $this->_hydrateAll();
|
||||
$this->_cleanup();
|
||||
|
@ -23,6 +23,7 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
@ -39,7 +40,6 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/* Class entries */
|
||||
private $_ce = array();
|
||||
private $_discriminatorMap = array();
|
||||
|
||||
/*
|
||||
* The following parts are reinitialized on every hydration run.
|
||||
*/
|
||||
@ -61,7 +61,8 @@ class ObjectHydrator extends AbstractHydrator
|
||||
protected function _prepare()
|
||||
{
|
||||
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
|
||||
$this->_allowPartialObjects = $this->_em->getConfiguration()->getAllowPartialObjects();
|
||||
$this->_allowPartialObjects = $this->_em->getConfiguration()->getAllowPartialObjects()
|
||||
|| isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]);
|
||||
$this->_identifierMap = array();
|
||||
$this->_resultPointers = array();
|
||||
$this->_idTemplate = array();
|
||||
@ -225,7 +226,14 @@ class ObjectHydrator extends AbstractHydrator
|
||||
return key($coll);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets an entity instance.
|
||||
*
|
||||
* @param $data The instance data.
|
||||
* @param $dqlAlias The DQL alias of the entity's class.
|
||||
* @return object The entity.
|
||||
*/
|
||||
private function getEntity(array $data, $dqlAlias)
|
||||
{
|
||||
$className = $this->_rsm->aliasMap[$dqlAlias];
|
||||
@ -234,25 +242,28 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$className = $this->_discriminatorMap[$className][$data[$discrColumn]];
|
||||
unset($data[$discrColumn]);
|
||||
}
|
||||
$entity = $this->_uow->createEntity($className, $data);
|
||||
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
if ( ! $this->_allowPartialObjects) {
|
||||
foreach ($this->_ce[$className]->associationMappings as $field => $assoc) {
|
||||
if ( ! isset($this->_fetchedAssociations[$className][$field])) {
|
||||
if ($assoc->isOneToOne()) {
|
||||
$joinColumns = array();
|
||||
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
|
||||
$joinColumns[$srcColumn] = $data[$assoc->joinColumnFieldNames[$srcColumn]];
|
||||
}
|
||||
if ($assoc->isLazilyFetched()) {
|
||||
$joinColumnsValues = array();
|
||||
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
|
||||
$joinColumnsValues[$srcColumn] = $data[$assoc->joinColumnFieldNames[$srcColumn]];
|
||||
}
|
||||
// Inject proxy
|
||||
$proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnsValues);
|
||||
$this->_ce[$className]->reflFields[$field]->setValue($entity, $proxy);
|
||||
$this->_ce[$className]->reflFields[$field]->setValue($entity,
|
||||
$this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumns));
|
||||
} else {
|
||||
//TODO: Schedule for eager fetching
|
||||
// Eager load
|
||||
//TODO: Allow more efficient and configurable batching of these loads
|
||||
$assoc->load($entity, new $className, $this->_em, $joinColumns);
|
||||
}
|
||||
} else {
|
||||
//TODO: Eager load
|
||||
// Inject collection
|
||||
$this->_ce[$className]->reflFields[$field]
|
||||
->setValue($entity, new PersistentCollection(
|
||||
@ -280,6 +291,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$class->reflFields[$property]->setValue($entity1, $entity2);
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($entity1), $property, $entity2);
|
||||
$relation = $class->associationMappings[$property];
|
||||
|
||||
if ($relation->isOneToOne()) {
|
||||
$targetClass = $this->_ce[$relation->targetEntityName];
|
||||
if ($relation->isOwningSide) {
|
||||
|
@ -29,7 +29,7 @@ final class Entity extends \Doctrine\Common\Annotations\Annotation {
|
||||
final class InheritanceType extends \Doctrine\Common\Annotations\Annotation {}
|
||||
final class DiscriminatorColumn extends \Doctrine\Common\Annotations\Annotation {
|
||||
public $name;
|
||||
//public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
public $type;
|
||||
public $length;
|
||||
}
|
||||
@ -43,7 +43,7 @@ final class GeneratedValue extends \Doctrine\Common\Annotations\Annotation {
|
||||
final class Version extends \Doctrine\Common\Annotations\Annotation {}
|
||||
final class JoinColumn extends \Doctrine\Common\Annotations\Annotation {
|
||||
public $name;
|
||||
//public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
public $referencedColumnName;
|
||||
public $unique = false;
|
||||
public $nullable = true;
|
||||
|
@ -161,7 +161,7 @@ class OneToOneMapping extends AssociationMapping
|
||||
* @param object $targetEntity the entity to load data in
|
||||
* @param EntityManager $em
|
||||
* @param array $joinColumnValues values of the join columns of $sourceEntity. There are no fields
|
||||
* to store this data in $sourceEntity
|
||||
* to store this data in $sourceEntity
|
||||
*/
|
||||
public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues)
|
||||
{
|
||||
@ -179,10 +179,12 @@ class OneToOneMapping extends AssociationMapping
|
||||
$conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn];
|
||||
}
|
||||
}
|
||||
if ($targetClass->hasInverseAssociationMapping($this->sourceFieldName)) {
|
||||
$targetClass->setFieldValue(
|
||||
$targetEntity,
|
||||
$targetClass->inverseMappings[$this->sourceFieldName]->getSourceFieldName(),
|
||||
|
||||
$targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity);
|
||||
|
||||
if ($targetEntity !== null && $targetClass->hasInverseAssociationMapping($this->sourceFieldName)) {
|
||||
$targetClass->setFieldValue($targetEntity,
|
||||
$targetClass->inverseMappings[$this->sourceFieldName]->sourceFieldName,
|
||||
$sourceEntity);
|
||||
}
|
||||
} else {
|
||||
@ -195,10 +197,11 @@ class OneToOneMapping extends AssociationMapping
|
||||
$conditions[$sourceKeyColumn] = $joinColumnValues[$targetKeyColumn];
|
||||
}
|
||||
}
|
||||
|
||||
$targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity);
|
||||
|
||||
$targetClass->setFieldValue($targetEntity, $this->mappedByFieldName, $sourceEntity);
|
||||
}
|
||||
|
||||
$em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity);
|
||||
}
|
||||
|
||||
protected function _getPrivateValue(ClassMetadata $class, $entity, $column)
|
||||
|
@ -405,7 +405,8 @@ class StandardEntityPersister
|
||||
*
|
||||
* @param array $criteria The criteria by which to load the entity.
|
||||
* @param object $entity The entity to load the data into. If not specified,
|
||||
* a new entity is created.
|
||||
* a new entity is created.
|
||||
* @return The loaded entity instance or NULL if the entity/the data can not be found.
|
||||
*/
|
||||
public function load(array $criteria, $entity = null)
|
||||
{
|
||||
@ -413,7 +414,14 @@ class StandardEntityPersister
|
||||
$stmt->execute(array_values($criteria));
|
||||
$data = array();
|
||||
$joinColumnValues = array();
|
||||
foreach ($stmt->fetch(Connection::FETCH_ASSOC) as $column => $value) {
|
||||
$result = $stmt->fetch(Connection::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($result as $column => $value) {
|
||||
if (isset($this->_class->fieldNames[$column])) {
|
||||
$fieldName = $this->_class->fieldNames[$column];
|
||||
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
|
||||
@ -422,7 +430,6 @@ class StandardEntityPersister
|
||||
$joinColumnValues[$column] = $value;
|
||||
}
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
if ($entity === null) {
|
||||
$entity = $this->_em->getUnitOfWork()->createEntity($this->_entityName, $data);
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\ORM\Query\CacheHandler;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
|
||||
@ -38,17 +37,43 @@ use Doctrine\ORM\Query\QueryException;
|
||||
*/
|
||||
final class Query extends AbstractQuery
|
||||
{
|
||||
/* Query STATES */
|
||||
/**
|
||||
* A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
|
||||
*/
|
||||
const STATE_CLEAN = 1;
|
||||
|
||||
/**
|
||||
* A query object is in state DIRTY when it has DQL parts that have not yet been
|
||||
* parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
|
||||
* is called.
|
||||
*/
|
||||
const STATE_DIRTY = 2;
|
||||
|
||||
/* Query HINTS */
|
||||
/**
|
||||
* The refresh hint turns any query into a refresh query with the result that
|
||||
* any local changes in entities are overridden with the fetched values.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_REFRESH = 'doctrine.refresh';
|
||||
/**
|
||||
* The forcePartialLoad query hint forces a particular query to return
|
||||
* partial objects when partial objects in general are disallowed in the
|
||||
* configuration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
|
||||
/**
|
||||
* The includeMetaColumns query hint causes meta columns like foreign keys and
|
||||
* discriminator columns to be selected and returned as part of the query result.
|
||||
*
|
||||
* This hint does only apply to non-object queries.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
|
||||
|
||||
/**
|
||||
* @var integer $_state The current state of this query.
|
||||
|
@ -1,58 +0,0 @@
|
||||
<?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\Query;
|
||||
|
||||
use Doctrine\Common\DoctrineException;
|
||||
|
||||
/**
|
||||
* Doctrine_ORM_Query_AbstractResult
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @version $Revision: 1393 $
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
abstract class AbstractResult
|
||||
{
|
||||
/**
|
||||
* @var mixed $_data The actual data to be stored. Can be an array, a string or an integer.
|
||||
*/
|
||||
protected $_data;
|
||||
|
||||
/**
|
||||
* Returns this object in serialized format, revertable using fromCached*.
|
||||
*
|
||||
* @return string Serialized cached item.
|
||||
*/
|
||||
public function toCachedForm()
|
||||
{
|
||||
return serialize(array(
|
||||
$this->_data,
|
||||
$this->getQueryComponents(),
|
||||
$this->getTableAliasMap(),
|
||||
$this->getEnumParams()
|
||||
));
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id: Cache.php 3938 2008-03-06 19:36:50Z romanb $
|
||||
*
|
||||
* 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\Query;
|
||||
|
||||
/**
|
||||
* Doctrine\ORM\Query\CacheHandler
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @version $Revision: 1393 $
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
*
|
||||
* @todo Re-document this class
|
||||
*/
|
||||
abstract class CacheHandler
|
||||
{
|
||||
/**
|
||||
* Static factory method. Receives a Doctrine_ORM_Query object and generates
|
||||
* the object after processing queryComponents. Table aliases are retrieved
|
||||
* directly from Doctrine_ORM_Query_Parser.
|
||||
*
|
||||
* @param mixed $result Data to be stored.
|
||||
* @param Doctrine_ORM_Query_ParserResult $parserResult Parser results that enables to have important data retrieved.
|
||||
*/
|
||||
public static function fromResultSet($result, $parserResult)
|
||||
{
|
||||
$queryComponents = array();
|
||||
|
||||
foreach ($parserResult->getQueryComponents() as $alias => $components) {
|
||||
if ( ! isset($components['parent'])) {
|
||||
$queryComponents[$alias][] = $components['mapper']->getComponentName();
|
||||
//$queryComponents[$alias][] = $components['mapper']->getComponentName();
|
||||
} else {
|
||||
$queryComponents[$alias][] = $components['parent'] . '.' . $components['relation']->getAlias();
|
||||
}
|
||||
|
||||
if (isset($components['agg'])) {
|
||||
$queryComponents[$alias][] = $components['agg'];
|
||||
}
|
||||
|
||||
if (isset($components['map'])) {
|
||||
$queryComponents[$alias][] = $components['map'];
|
||||
}
|
||||
}
|
||||
|
||||
return new QueryResult(
|
||||
$result,
|
||||
$queryComponents,
|
||||
$parserResult->getTableAliasMap(),
|
||||
$parserResult->getEnumParams()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method. Receives a Doctrine_ORM_Query object and a cached data.
|
||||
* It handles the cache and generates the object after processing queryComponents.
|
||||
* Table aliases are retrieved from cache.
|
||||
*
|
||||
* @param Doctrine_ORM_Query $query Doctrine_ORM_Query_Object related to this cache item.
|
||||
* @param mixed $cached Cached data.
|
||||
*/
|
||||
public static function fromCachedResult($query, $cached = false)
|
||||
{
|
||||
$cached = unserialize($cached);
|
||||
|
||||
return new QueryResult(
|
||||
$cached[0],
|
||||
self::_getQueryComponents($cached[1]),
|
||||
$cached[2],
|
||||
$cached[3]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method. Receives a Doctrine_ORM_Query object and a cached data.
|
||||
* It handles the cache and generates the object after processing queryComponents.
|
||||
* Table aliases are retrieved from cache.
|
||||
*
|
||||
* @param Doctrine_ORM_Query $query Doctrine_ORM_Query_Object related to this cache item.
|
||||
* @param mixed $cached Cached data.
|
||||
*/
|
||||
public static function fromCachedQuery($query, $cached = false)
|
||||
{
|
||||
$cached = unserialize($cached);
|
||||
|
||||
return new ParserResult(
|
||||
$cached[0],
|
||||
self::_getQueryComponents($cached[1]),
|
||||
$cached[2],
|
||||
$cached[3]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @nodoc
|
||||
*/
|
||||
protected static function _getQueryComponents($query, $cachedQueryComponents)
|
||||
{
|
||||
$queryComponents = array();
|
||||
|
||||
foreach ($cachedQueryComponents as $alias => $components) {
|
||||
$e = explode('.', $components[0]);
|
||||
|
||||
if (count($e) === 1) {
|
||||
$queryComponents[$alias]['mapper'] = $query->getConnection()->getMapper($e[0]);
|
||||
$queryComponents[$alias]['table'] = $queryComponents[$alias]['mapper']->getTable();
|
||||
} else {
|
||||
$queryComponents[$alias]['parent'] = $e[0];
|
||||
$queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getAssociation($e[1]);
|
||||
$queryComponents[$alias]['mapper'] = $query->getConnection()->getMapper($queryComponents[$alias]['relation']->getTargetEntityName());
|
||||
$queryComponents[$alias]['table'] = $queryComponents[$alias]['mapper']->getTable();
|
||||
}
|
||||
|
||||
if (isset($v[1])) {
|
||||
$queryComponents[$alias]['agg'] = $components[1];
|
||||
}
|
||||
|
||||
if (isset($v[2])) {
|
||||
$queryComponents[$alias]['map'] = $components[2];
|
||||
}
|
||||
}
|
||||
|
||||
return $queryComponents;
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
<?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.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\Exec;
|
||||
|
||||
/**
|
||||
* Base class for SQL statement executors.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link http://www.phpdoctrine.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
*/
|
||||
abstract class AbstractExecutor implements \Serializable
|
||||
{
|
||||
protected $_sqlStatements;
|
||||
|
||||
public function __construct(\Doctrine\ORM\Query\AST\Node $AST, $sqlWalker)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL statements that are executed by the executor.
|
||||
*
|
||||
* @return array All the SQL update statements.
|
||||
*/
|
||||
public function getSqlStatements()
|
||||
{
|
||||
return $this->_sqlStatements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all sql statements.
|
||||
*
|
||||
* @param Doctrine_Connection $conn The database connection that is used to execute the queries.
|
||||
* @param array $params The parameters.
|
||||
*/
|
||||
abstract public function execute(\Doctrine\DBAL\Connection $conn, array $params);
|
||||
|
||||
/**
|
||||
* Factory method.
|
||||
* Creates an appropriate sql executor for the given AST.
|
||||
*
|
||||
* @param Doctrine_ORM_Query_AST $AST The root node of the AST.
|
||||
* @return Doctrine_ORM_Query_SqlExecutor_Abstract The executor that is suitable for the given AST.
|
||||
*/
|
||||
public static function create(\Doctrine\ORM\Query\AST\Node $AST, $sqlWalker)
|
||||
{
|
||||
$isDeleteStatement = $AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement;
|
||||
$isUpdateStatement = $AST instanceof \Doctrine\ORM\Query\AST\UpdateStatement;
|
||||
|
||||
if ($isDeleteStatement) {
|
||||
$primaryClass = $sqlWalker->getEntityManager()->getClassMetadata(
|
||||
$AST->getDeleteClause()->getAbstractSchemaName()
|
||||
);
|
||||
if ($primaryClass->isInheritanceTypeJoined()) {
|
||||
return new MultiTableDeleteExecutor($AST, $sqlWalker);
|
||||
} else {
|
||||
return new SingleTableDeleteUpdateExecutor($AST, $sqlWalker);
|
||||
}
|
||||
} else if ($isUpdateStatement) {
|
||||
$primaryClass = $sqlWalker->getEntityManager()->getClassMetadata(
|
||||
$AST->getUpdateClause()->getAbstractSchemaName()
|
||||
);
|
||||
if ($primaryClass->isInheritanceTypeJoined()) {
|
||||
return new MultiTableUpdateExecutor($AST, $sqlWalker);
|
||||
} else {
|
||||
return new SingleTableDeleteUpdateExecutor($AST, $sqlWalker);
|
||||
}
|
||||
} else {
|
||||
return new SingleSelectExecutor($AST, $sqlWalker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the sql statements of the executor.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->_sqlStatements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstructs the executor with it's sql statements.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->_sqlStatements = unserialize($serialized);
|
||||
}
|
||||
|
||||
}
|
@ -19,29 +19,36 @@
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
namespace Doctrine\ORM\Query\Exec;
|
||||
|
||||
/**
|
||||
* Doctrine_ORM_Query_QueryResult
|
||||
* Base class for SQL statement executors.
|
||||
*
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link http://www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
*/
|
||||
class QueryResult extends AbstractResult
|
||||
abstract class AbstractSqlExecutor
|
||||
{
|
||||
protected $_data;
|
||||
|
||||
protected $_sqlStatements;
|
||||
|
||||
/**
|
||||
* Returns cached resultset.
|
||||
* Gets the SQL statements that are executed by the executor.
|
||||
*
|
||||
* @return array Resultset.
|
||||
* @return array All the SQL update statements.
|
||||
*/
|
||||
public function getResultSet()
|
||||
public function getSqlStatements()
|
||||
{
|
||||
return $this->_data;
|
||||
return $this->_sqlStatements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all sql statements.
|
||||
*
|
||||
* @param Doctrine_Connection $conn The database connection that is used to execute the queries.
|
||||
* @param array $params The parameters.
|
||||
*/
|
||||
abstract public function execute(\Doctrine\DBAL\Connection $conn, array $params);
|
||||
}
|
@ -33,7 +33,7 @@ use Doctrine\ORM\Query\AST;
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
*/
|
||||
class MultiTableDeleteExecutor extends AbstractExecutor
|
||||
class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
{
|
||||
private $_createTempTableSql;
|
||||
private $_dropTempTableSql;
|
||||
|
@ -33,7 +33,7 @@ use Doctrine\ORM\Query\AST;
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
*/
|
||||
class MultiTableUpdateExecutor extends AbstractExecutor
|
||||
class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
{
|
||||
private $_createTempTableSql;
|
||||
private $_dropTempTableSql;
|
||||
|
@ -30,11 +30,10 @@ namespace Doctrine\ORM\Query\Exec;
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
*/
|
||||
class SingleSelectExecutor extends AbstractExecutor
|
||||
class SingleSelectExecutor extends AbstractSqlExecutor
|
||||
{
|
||||
public function __construct(\Doctrine\ORM\Query\AST\SelectStatement $AST, $sqlWalker)
|
||||
{
|
||||
parent::__construct($AST, $sqlWalker);
|
||||
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,10 @@ use Doctrine\ORM\Query\AST;
|
||||
* @since 2.0
|
||||
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
|
||||
*/
|
||||
class SingleTableDeleteUpdateExecutor extends AbstractExecutor
|
||||
class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
|
||||
{
|
||||
public function __construct(AST\Node $AST, $sqlWalker)
|
||||
{
|
||||
parent::__construct($AST, $sqlWalker);
|
||||
if ($AST instanceof AST\UpdateStatement) {
|
||||
$this->_sqlStatements = $sqlWalker->walkUpdateStatement($AST);
|
||||
} else if ($AST instanceof AST\DeleteStatement) {
|
||||
|
@ -189,7 +189,7 @@ class Parser
|
||||
);
|
||||
|
||||
// Assign an SQL executor to the parser result
|
||||
$this->_parserResult->setSqlExecutor(Exec\AbstractExecutor::create($AST, $sqlWalker));
|
||||
$this->_parserResult->setSqlExecutor($sqlWalker->getExecutor($AST));
|
||||
|
||||
return $this->_parserResult;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class ParserResult
|
||||
*
|
||||
* @param AbstractExecutor $executor
|
||||
*/
|
||||
public function setSqlExecutor(\Doctrine\ORM\Query\Exec\AbstractExecutor $executor)
|
||||
public function setSqlExecutor($executor)
|
||||
{
|
||||
$this->_sqlExecutor = $executor;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\AST;
|
||||
use Doctrine\Common\DoctrineException;
|
||||
@ -33,6 +34,7 @@ use Doctrine\Common\DoctrineException;
|
||||
* @since 2.0
|
||||
* @todo Code review for identifier quoting.
|
||||
* @todo Code review for schema usage with table names.
|
||||
* (Prepend schema name to tables IF schema is defined AND platform supports them)
|
||||
*/
|
||||
class SqlWalker implements TreeWalker
|
||||
{
|
||||
@ -51,7 +53,7 @@ class SqlWalker implements TreeWalker
|
||||
private $_em;
|
||||
/** The Connection of the EntityManager. */
|
||||
private $_conn;
|
||||
/** The parsed Query instance. */
|
||||
/** The Query instance. */
|
||||
private $_query;
|
||||
private $_dqlToSqlAliasMap = array();
|
||||
/** Map of all components/classes that appear in the DQL query. */
|
||||
@ -135,8 +137,9 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= $AST->getOrderByClause() ? $this->walkOrderByClause($AST->getOrderByClause()) : '';
|
||||
|
||||
$q = $this->getQuery();
|
||||
$sql = $this->getConnection()->getDatabasePlatform()
|
||||
->modifyLimitQuery($sql, $q->getMaxResults(), $q->getFirstResult());
|
||||
$sql = $this->getConnection()->getDatabasePlatform()->modifyLimitQuery(
|
||||
$sql, $q->getMaxResults(), $q->getFirstResult()
|
||||
);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
@ -163,17 +166,15 @@ class SqlWalker implements TreeWalker
|
||||
);
|
||||
}
|
||||
|
||||
//if ($this->_query->getHydrationMode() == \Doctrine\ORM\Query::HYDRATE_OBJECT) {
|
||||
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
|
||||
$rootClass = $this->_em->getClassMetadata($class->rootEntityName);
|
||||
$tblAlias = $this->getSqlTableAlias($rootClass->getTableName() . $dqlAlias);
|
||||
$tblAlias = $this->getSqlTableAlias($rootClass->getTableName(), $dqlAlias);
|
||||
$discrColumn = $rootClass->discriminatorColumn;
|
||||
$columnAlias = $this->getSqlColumnAlias($discrColumn['name']);
|
||||
$sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias;
|
||||
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
|
||||
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
@ -195,7 +196,7 @@ class SqlWalker implements TreeWalker
|
||||
$this->_currentRootAlias = $dqlAlias;
|
||||
$class = $rangeDecl->getClassMetadata();
|
||||
|
||||
$sql .= $class->getTableName() . ' ' . $this->getSqlTableAlias($class->getTableName() . $dqlAlias);
|
||||
$sql .= $class->getTableName() . ' ' . $this->getSqlTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
@ -246,7 +247,7 @@ class SqlWalker implements TreeWalker
|
||||
$dqlAlias = $expr->getIdentificationVariable();
|
||||
$qComp = $this->_queryComponents[$dqlAlias];
|
||||
$columnName = $qComp['metadata']->getColumnName($parts[0]);
|
||||
$sql = $this->getSqlTableAlias($qComp['metadata']->getTableName() . $dqlAlias) . '.' . $columnName;
|
||||
$sql = $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' . $columnName;
|
||||
$sql .= $orderByItem->isAsc() ? ' ASC' : ' DESC';
|
||||
return $sql;
|
||||
}
|
||||
@ -285,8 +286,8 @@ class SqlWalker implements TreeWalker
|
||||
$targetQComp = $this->_queryComponents[$joinedDqlAlias];
|
||||
|
||||
$targetTableName = $targetQComp['metadata']->getTableName();
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetTableName . $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($sourceQComp['metadata']->getTableName() . $joinAssocPathExpr->getIdentificationVariable());
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetTableName, $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($sourceQComp['metadata']->getTableName(), $joinAssocPathExpr->getIdentificationVariable());
|
||||
|
||||
// Ensure we got the owning side, since it has all mapping info
|
||||
if ( ! $targetQComp['relation']->isOwningSide()) {
|
||||
@ -385,7 +386,7 @@ class SqlWalker implements TreeWalker
|
||||
$this->_selectedClasses[$dqlAlias] = $class;
|
||||
}
|
||||
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->getTableName() . $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->getTableName(), $dqlAlias);
|
||||
$columnName = $class->getColumnName($fieldName);
|
||||
$columnAlias = $this->getSqlColumnAlias($columnName);
|
||||
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
|
||||
@ -436,7 +437,7 @@ class SqlWalker implements TreeWalker
|
||||
$tableName = $class->primaryTable['name'];
|
||||
}
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
$sqlTableAlias = $this->getSqlTableAlias($tableName . $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias);
|
||||
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
|
||||
$sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) . ' AS ' . $columnAlias;
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName);
|
||||
@ -450,7 +451,7 @@ class SqlWalker implements TreeWalker
|
||||
continue;
|
||||
}
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
$sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'] . $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias);
|
||||
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
|
||||
$sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) . ' AS ' . $columnAlias;
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName);
|
||||
@ -464,7 +465,7 @@ class SqlWalker implements TreeWalker
|
||||
$this->_em->getClassMetadata($subclassName)->fieldMappings
|
||||
);
|
||||
}
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->getTableName() . $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->getTableName(), $dqlAlias);
|
||||
foreach ($fieldMappings as $fieldName => $mapping) {
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
|
||||
@ -472,7 +473,9 @@ class SqlWalker implements TreeWalker
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName);
|
||||
}
|
||||
|
||||
if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) {
|
||||
// Append foreign keys if necessary.
|
||||
if ( ! $this->_em->getConfiguration()->getAllowPartialObjects() &&
|
||||
! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
|
||||
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
|
||||
@ -539,7 +542,7 @@ class SqlWalker implements TreeWalker
|
||||
$firstIdentificationVarDecl = $identificationVarDecls[0];
|
||||
$rangeDecl = $firstIdentificationVarDecl->getRangeVariableDeclaration();
|
||||
$sql .= $rangeDecl->getClassMetadata()->getTableName() . ' '
|
||||
. $this->getSqlTableAlias($rangeDecl->getClassMetadata()->getTableName() . $rangeDecl->getAliasIdentificationVariable());
|
||||
. $this->getSqlTableAlias($rangeDecl->getClassMetadata()->getTableName(), $rangeDecl->getAliasIdentificationVariable());
|
||||
|
||||
foreach ($firstIdentificationVarDecl->getJoinVariableDeclarations() as $joinVarDecl) {
|
||||
$sql .= $this->walkJoinVariableDeclaration($joinVarDecl);
|
||||
@ -589,7 +592,7 @@ class SqlWalker implements TreeWalker
|
||||
// FIXME: Composite key support, or select all columns? Does that make
|
||||
// in a subquery?
|
||||
$class = $this->_queryComponents[$expr]['metadata'];
|
||||
$sql .= ' ' . $this->getSqlTableAlias($class->getTableName() . $expr) . '.';
|
||||
$sql .= ' ' . $this->getSqlTableAlias($class->getTableName(), $expr) . '.';
|
||||
$sql .= $class->getColumnName($class->identifier[0]);
|
||||
}
|
||||
return $sql;
|
||||
@ -613,7 +616,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sql .= $aggExpression->getFunctionName() . '(';
|
||||
if ($aggExpression->isDistinct()) $sql .= 'DISTINCT ';
|
||||
$sql .= $this->getSqlTableAlias($qComp['metadata']->getTableName() . $dqlAlias) . '.' . $columnName;
|
||||
$sql .= $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' . $columnName;
|
||||
$sql .= ')';
|
||||
return $sql;
|
||||
}
|
||||
@ -644,7 +647,7 @@ class SqlWalker implements TreeWalker
|
||||
$dqlAlias = $pathExpr->getIdentificationVariable();
|
||||
$qComp = $this->_queryComponents[$dqlAlias];
|
||||
$columnName = $qComp['metadata']->getColumnName($parts[0]);
|
||||
return $this->getSqlTableAlias($qComp['metadata']->getTableName() . $dqlAlias) . '.' . $columnName;
|
||||
return $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' . $columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -803,7 +806,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
$discrColumn = $class->discriminatorColumn;
|
||||
if ($this->_useSqlTableAliases) {
|
||||
$sql .= $this->getSqlTableAlias($class->getTableName() . $dqlAlias) . '.';
|
||||
$sql .= $this->getSqlTableAlias($class->getTableName(), $dqlAlias) . '.';
|
||||
}
|
||||
$sql .= $discrColumn['name'] . ' IN (' . implode(', ', $values) . ')';
|
||||
}
|
||||
@ -886,7 +889,7 @@ class SqlWalker implements TreeWalker
|
||||
if ($assoc->isOneToMany()) {
|
||||
$targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetClass->primaryTable['name']);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'] . $dqlAlias);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias);
|
||||
|
||||
$sql .= $this->_conn->quoteIdentifier($targetClass->primaryTable['name'])
|
||||
. ' ' . $targetTableAlias . ' WHERE ';
|
||||
@ -908,7 +911,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
} else { // many-to-many
|
||||
$targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'] . $dqlAlias);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias);
|
||||
$joinTable = $assoc->isOwningSide ? $assoc->joinTable : $targetClass->associationMappings[$assoc->mappedByFieldName]->joinTable;
|
||||
$joinTableAlias = $this->getSqlTableAlias($joinTable['name']);
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetClass->primaryTable['name']);
|
||||
@ -1218,9 +1221,9 @@ class SqlWalker implements TreeWalker
|
||||
if ($this->_useSqlTableAliases) {
|
||||
if ($class->isInheritanceTypeJoined() && isset($class->fieldMappings[$fieldName]['inherited'])) {
|
||||
$sql .= $this->getSqlTableAlias($this->_em->getClassMetadata(
|
||||
$class->fieldMappings[$fieldName]['inherited'])->getTableName() . $dqlAlias) . '.';
|
||||
$class->fieldMappings[$fieldName]['inherited'])->getTableName(), $dqlAlias) . '.';
|
||||
} else {
|
||||
$sql .= $this->getSqlTableAlias($class->getTableName() . $dqlAlias) . '.';
|
||||
$sql .= $this->getSqlTableAlias($class->getTableName(), $dqlAlias) . '.';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1247,8 +1250,9 @@ class SqlWalker implements TreeWalker
|
||||
* @param string $dqlAlias The DQL alias.
|
||||
* @return string Generated table alias.
|
||||
*/
|
||||
public function getSqlTableAlias($tableName)
|
||||
public function getSqlTableAlias($tableName, $dqlAlias = '')
|
||||
{
|
||||
$tableName .= $dqlAlias;
|
||||
if ( ! isset($this->_dqlToSqlAliasMap[$tableName])) {
|
||||
$this->_dqlToSqlAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->_tableAliasCounter++ . '_';
|
||||
}
|
||||
@ -1279,23 +1283,24 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the SQL JOINs, that are necessary for Class Table Inheritance,
|
||||
* Generates the SQL JOINs that are necessary for Class Table Inheritance
|
||||
* for the given class.
|
||||
*
|
||||
* @param ClassMetadata $class
|
||||
* @param string $dqlAlias
|
||||
* @param ClassMetadata $class The class for which to generate the joins.
|
||||
* @param string $dqlAlias The DQL alias of the class.
|
||||
* @return string The SQL.
|
||||
*/
|
||||
private function _generateClassTableInheritanceJoins($class, $dqlAlias)
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
$baseTableAlias = $this->getSqlTableAlias($class->primaryTable['name'] . $dqlAlias);
|
||||
$baseTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias);
|
||||
$idColumns = $class->getIdentifierColumnNames();
|
||||
|
||||
// INNER JOIN parent class tables
|
||||
foreach ($class->parentClasses as $parentClassName) {
|
||||
$parentClass = $this->_em->getClassMetadata($parentClassName);
|
||||
$tableAlias = $this->getSqlTableAlias($parentClass->primaryTable['name'] . $dqlAlias);
|
||||
$tableAlias = $this->getSqlTableAlias($parentClass->primaryTable['name'], $dqlAlias);
|
||||
$sql .= ' INNER JOIN ' . $parentClass->primaryTable['name'] . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
foreach ($idColumns as $idColumn) {
|
||||
@ -1307,7 +1312,7 @@ class SqlWalker implements TreeWalker
|
||||
// LEFT JOIN subclass tables
|
||||
foreach ($class->subClasses as $subClassName) {
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
$tableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'] . $dqlAlias);
|
||||
$tableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias);
|
||||
$sql .= ' LEFT JOIN ' . $subClass->primaryTable['name'] . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
foreach ($idColumns as $idColumn) {
|
||||
@ -1318,4 +1323,37 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an executor that can be used to execute the result of this walker.
|
||||
*
|
||||
* @return AbstractExecutor
|
||||
*/
|
||||
public function getExecutor($AST)
|
||||
{
|
||||
$isDeleteStatement = $AST instanceof AST\DeleteStatement;
|
||||
$isUpdateStatement = $AST instanceof AST\UpdateStatement;
|
||||
|
||||
if ($isDeleteStatement) {
|
||||
$primaryClass = $this->_em->getClassMetadata(
|
||||
$AST->getDeleteClause()->getAbstractSchemaName()
|
||||
);
|
||||
if ($primaryClass->isInheritanceTypeJoined()) {
|
||||
return new Exec\MultiTableDeleteExecutor($AST, $this);
|
||||
} else {
|
||||
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
|
||||
}
|
||||
} else if ($isUpdateStatement) {
|
||||
$primaryClass = $this->_em->getClassMetadata(
|
||||
$AST->getUpdateClause()->getAbstractSchemaName()
|
||||
);
|
||||
if ($primaryClass->isInheritanceTypeJoined()) {
|
||||
return new Exec\MultiTableUpdateExecutor($AST, $this);
|
||||
} else {
|
||||
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
|
||||
}
|
||||
} else {
|
||||
return new Exec\SingleSelectExecutor($AST, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,23 @@
|
||||
<?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\Query;
|
||||
|
||||
@ -333,4 +352,11 @@ interface TreeWalker
|
||||
* @return string The SQL.
|
||||
*/
|
||||
function walkPathExpression($pathExpr);
|
||||
|
||||
/**
|
||||
* Gets an executor that can be used to execute the result of this walker.
|
||||
*
|
||||
* @return AbstractExecutor
|
||||
*/
|
||||
function getExecutor($AST);
|
||||
}
|
@ -1,4 +1,23 @@
|
||||
<?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\Query;
|
||||
|
||||
|
@ -1476,8 +1476,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if (isset($this->_identityMap[$class->rootEntityName][$idHash])) {
|
||||
$entity = $this->_identityMap[$class->rootEntityName][$idHash];
|
||||
$oid = spl_object_hash($entity);
|
||||
$overrideLocalChanges = false;
|
||||
//$overrideLocalChanges = isset($hints['doctrine.refresh']) && $hints['doctrine.refresh'] === true;
|
||||
$overrideLocalChanges = isset($hints[Query::HINT_REFRESH]);
|
||||
} else {
|
||||
$entity = new $className;
|
||||
$oid = spl_object_hash($entity);
|
||||
|
@ -4,6 +4,14 @@ namespace Doctrine\Tests\Mocks;
|
||||
|
||||
class MockTreeWalker extends \Doctrine\ORM\Query\TreeWalkerAdapter
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets an executor that can be used to execute the result of this walker.
|
||||
*
|
||||
* @return AbstractExecutor
|
||||
*/
|
||||
public function getExecutor($AST)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional
|
||||
$result = $query->getResultList();
|
||||
$customer = $result[0];
|
||||
|
||||
$this->assertNull($customer->getMentor());
|
||||
$this->assertTrue($customer->getCart() instanceof ECommerceCart);
|
||||
$this->assertEquals('paypal', $customer->getCart()->getPayment());
|
||||
}
|
||||
|
@ -276,5 +276,63 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
|
||||
$e = microtime(true);
|
||||
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Times for comparison:
|
||||
*
|
||||
* [romanb: 10000 rows => 0.7 seconds]
|
||||
*
|
||||
* MAXIMUM TIME: 1 second
|
||||
*/
|
||||
public function testSimpleQueryScalarHydrationPerformance()
|
||||
{
|
||||
$rsm = new ResultSetMapping;
|
||||
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
|
||||
$rsm->addFieldResult('u', 'u__id', 'id');
|
||||
$rsm->addFieldResult('u', 'u__status', 'status');
|
||||
$rsm->addFieldResult('u', 'u__username', 'username');
|
||||
$rsm->addFieldResult('u', 'u__name', 'name');
|
||||
|
||||
// Faked result set
|
||||
$resultSet = array(
|
||||
//row1
|
||||
array(
|
||||
'u__id' => '1',
|
||||
'u__status' => 'developer',
|
||||
'u__username' => 'romanb',
|
||||
'u__name' => 'Roman',
|
||||
),
|
||||
array(
|
||||
'u__id' => '1',
|
||||
'u__status' => 'developer',
|
||||
'u__username' => 'romanb',
|
||||
'u__name' => 'Roman',
|
||||
),
|
||||
array(
|
||||
'u__id' => '2',
|
||||
'u__status' => 'developer',
|
||||
'u__username' => 'romanb',
|
||||
'u__name' => 'Roman',
|
||||
)
|
||||
);
|
||||
|
||||
for ($i = 4; $i < 10000; ++$i) {
|
||||
$resultSet[] = array(
|
||||
'u__id' => $i,
|
||||
'u__status' => 'developer',
|
||||
'u__username' => 'jwage',
|
||||
'u__name' => 'Jonathan',
|
||||
);
|
||||
}
|
||||
|
||||
$stmt = new HydratorMockStatement($resultSet);
|
||||
$hydrator = new \Doctrine\ORM\Internal\Hydration\ScalarHydrator($this->_em);
|
||||
|
||||
$this->setMaxRunningTime(1);
|
||||
$s = microtime(true);
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
$e = microtime(true);
|
||||
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,8 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
|
||||
}
|
||||
|
||||
$parser = new \Doctrine\ORM\Query\Parser($query);
|
||||
//$parser->setSqlTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker);
|
||||
// We do NOT test SQL construction here. That only unnecessarily slows down the tests!
|
||||
$parser->setSqlTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker);
|
||||
|
||||
return $parser->parse();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user