diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index fa5789c5d..f3d1133e8 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -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 + ); } /** diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 358f53994..e3b56e255 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -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 AbstractHydrator. @@ -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(); diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index da38eb8a8..14150d0bf 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -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) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index 9de82de30..fc9693c3e 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -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; diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 02eb7d2d4..bffa50af2 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -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) diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index 83390497f..f0aa6d911 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -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); diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 6879193e5..6b19a8412 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -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. diff --git a/lib/Doctrine/ORM/Query/AbstractResult.php b/lib/Doctrine/ORM/Query/AbstractResult.php deleted file mode 100644 index 4edaeb226..000000000 --- a/lib/Doctrine/ORM/Query/AbstractResult.php +++ /dev/null @@ -1,58 +0,0 @@ -. - */ - -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 - * @author Konsta Vesterinen - * @author Roman Borschel - */ -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() - )); - } -} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/CacheHandler.php b/lib/Doctrine/ORM/Query/CacheHandler.php deleted file mode 100644 index 46eb93cde..000000000 --- a/lib/Doctrine/ORM/Query/CacheHandler.php +++ /dev/null @@ -1,146 +0,0 @@ -. - */ - -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 - * @author Konsta Vesterinen - * - * @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; - } -} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php b/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php deleted file mode 100644 index c36533c52..000000000 --- a/lib/Doctrine/ORM/Query/Exec/AbstractExecutor.php +++ /dev/null @@ -1,112 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Query\Exec; - -/** - * Base class for SQL statement executors. - * - * @author Roman Borschel - * @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); - } - -} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/QueryResult.php b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php similarity index 63% rename from lib/Doctrine/ORM/Query/QueryResult.php rename to lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php index 7f5b899c9..851c07a56 100644 --- a/lib/Doctrine/ORM/Query/QueryResult.php +++ b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -19,29 +19,36 @@ * . */ -namespace Doctrine\ORM\Query; +namespace Doctrine\ORM\Query\Exec; /** - * Doctrine_ORM_Query_QueryResult + * Base class for SQL statement executors. * - * @author Guilherme Blanco - * @author Janne Vanhala + * @author Roman Borschel * @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); } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php index 4e4474e92..3b53d5404 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -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; diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index be677f00d..12c630587 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -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; diff --git a/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php b/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php index 967ba4541..328a9727c 100644 --- a/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -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); } diff --git a/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php index 0d9396379..1479099bf 100644 --- a/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -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) { diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 87eda3ebf..0b05b3e80 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -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; } diff --git a/lib/Doctrine/ORM/Query/ParserResult.php b/lib/Doctrine/ORM/Query/ParserResult.php index 22ac0d5ca..f242616de 100644 --- a/lib/Doctrine/ORM/Query/ParserResult.php +++ b/lib/Doctrine/ORM/Query/ParserResult.php @@ -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; } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index b53a1ad9d..5e194f8ee 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -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); + } + } } diff --git a/lib/Doctrine/ORM/Query/TreeWalker.php b/lib/Doctrine/ORM/Query/TreeWalker.php index 3d2149c17..fd7c42447 100644 --- a/lib/Doctrine/ORM/Query/TreeWalker.php +++ b/lib/Doctrine/ORM/Query/TreeWalker.php @@ -1,4 +1,23 @@ . + */ 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); } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php b/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php index 2f0df4af7..54175d938 100644 --- a/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php +++ b/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -1,4 +1,23 @@ . + */ namespace Doctrine\ORM\Query; diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 38ff888a1..d55cb0f70 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -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); diff --git a/tests/Doctrine/Tests/Mocks/MockTreeWalker.php b/tests/Doctrine/Tests/Mocks/MockTreeWalker.php index ee2f532d6..809b11443 100644 --- a/tests/Doctrine/Tests/Mocks/MockTreeWalker.php +++ b/tests/Doctrine/Tests/Mocks/MockTreeWalker.php @@ -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; + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php index d3f157ae0..df57f8d34 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php @@ -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()); } diff --git a/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php b/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php index 72a59f208..57f6bb45f 100644 --- a/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php +++ b/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php @@ -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; + } } diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index c77fc4f2f..69fd3d57b 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -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(); }