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();
}