Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / orm / lib / Doctrine / ORM / AbstractQuery.php
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19
20 namespace Doctrine\ORM;
21
22 use Doctrine\Common\Util\ClassUtils;
23 use Doctrine\Common\Collections\ArrayCollection;
24
25 use Doctrine\DBAL\Types\Type;
26 use Doctrine\DBAL\Cache\QueryCacheProfile;
27
28 use Doctrine\ORM\Query\QueryException;
29
30 /**
31  * Base contract for ORM queries. Base class for Query and NativeQuery.
32  *
33  * @link    www.doctrine-project.org
34  * @since   2.0
35  * @author  Benjamin Eberlei <kontakt@beberlei.de>
36  * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
37  * @author  Jonathan Wage <jonwage@gmail.com>
38  * @author  Roman Borschel <roman@code-factory.org>
39  * @author  Konsta Vesterinen <kvesteri@cc.hut.fi>
40  */
41 abstract class AbstractQuery
42 {
43     /* Hydration mode constants */
44     /**
45      * Hydrates an object graph. This is the default behavior.
46      */
47     const HYDRATE_OBJECT = 1;
48     /**
49      * Hydrates an array graph.
50      */
51     const HYDRATE_ARRAY = 2;
52     /**
53      * Hydrates a flat, rectangular result set with scalar values.
54      */
55     const HYDRATE_SCALAR = 3;
56     /**
57      * Hydrates a single scalar value.
58      */
59     const HYDRATE_SINGLE_SCALAR = 4;
60
61     /**
62      * Very simple object hydrator (optimized for performance).
63      */
64     const HYDRATE_SIMPLEOBJECT = 5;
65
66     /**
67      * @var \Doctrine\Common\Collections\ArrayCollection The parameter map of this query.
68      */
69     protected $parameters;
70
71     /**
72      * @var ResultSetMapping The user-specified ResultSetMapping to use.
73      */
74     protected $_resultSetMapping;
75
76     /**
77      * @var \Doctrine\ORM\EntityManager The entity manager used by this query object.
78      */
79     protected $_em;
80
81     /**
82      * @var array The map of query hints.
83      */
84     protected $_hints = array();
85
86     /**
87      * @var integer The hydration mode.
88      */
89     protected $_hydrationMode = self::HYDRATE_OBJECT;
90
91     /**
92      * @param \Doctrine\DBAL\Cache\QueryCacheProfile
93      */
94     protected $_queryCacheProfile;
95
96     /**
97      * @var boolean Boolean value that indicates whether or not expire the result cache.
98      */
99     protected $_expireResultCache = false;
100
101     /**
102      * @param \Doctrine\DBAL\Cache\QueryCacheProfile
103      */
104     protected $_hydrationCacheProfile;
105
106     /**
107      * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
108      *
109      * @param \Doctrine\ORM\EntityManager $entityManager
110      */
111     public function __construct(EntityManager $em)
112     {
113         $this->_em = $em;
114         $this->parameters = new ArrayCollection();
115     }
116
117     /**
118      * Gets the SQL query that corresponds to this query object.
119      * The returned SQL syntax depends on the connection driver that is used
120      * by this query object at the time of this method call.
121      *
122      * @return string SQL query
123      */
124     abstract public function getSQL();
125
126     /**
127      * Retrieves the associated EntityManager of this Query instance.
128      *
129      * @return \Doctrine\ORM\EntityManager
130      */
131     public function getEntityManager()
132     {
133         return $this->_em;
134     }
135
136     /**
137      * Frees the resources used by the query object.
138      *
139      * Resets Parameters, Parameter Types and Query Hints.
140      *
141      * @return void
142      */
143     public function free()
144     {
145         $this->parameters = new ArrayCollection();
146
147         $this->_hints = array();
148     }
149
150     /**
151      * Get all defined parameters.
152      *
153      * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters.
154      */
155     public function getParameters()
156     {
157         return $this->parameters;
158     }
159
160     /**
161      * Gets a query parameter.
162      *
163      * @param mixed $key The key (index or name) of the bound parameter.
164      *
165      * @return mixed The value of the bound parameter.
166      */
167     public function getParameter($key)
168     {
169         $filteredParameters = $this->parameters->filter(
170             function ($parameter) use ($key)
171             {
172                 // Must not be identical because of string to integer conversion
173                 return ($key == $parameter->getName());
174             }
175         );
176
177         return count($filteredParameters) ? $filteredParameters->first() : null;
178     }
179
180     /**
181      * Sets a collection of query parameters.
182      *
183      * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters
184      *
185      * @return \Doctrine\ORM\AbstractQuery This query instance.
186      */
187     public function setParameters($parameters)
188     {
189         // BC compatibility with 2.3-
190         if (is_array($parameters)) {
191             $parameterCollection = new ArrayCollection();
192
193             foreach ($parameters as $key => $value) {
194                 $parameter = new Query\Parameter($key, $value);
195
196                 $parameterCollection->add($parameter);
197             }
198
199             $parameters = $parameterCollection;
200         }
201
202         $this->parameters = $parameters;
203
204         return $this;
205     }
206
207     /**
208      * Sets a query parameter.
209      *
210      * @param string|integer $key The parameter position or name.
211      * @param mixed $value The parameter value.
212      * @param string $type The parameter type. If specified, the given value will be run through
213      *                     the type conversion of this type. This is usually not needed for
214      *                     strings and numeric types.
215      *
216      * @return \Doctrine\ORM\AbstractQuery This query instance.
217      */
218     public function setParameter($key, $value, $type = null)
219     {
220         $filteredParameters = $this->parameters->filter(
221             function ($parameter) use ($key)
222             {
223                 // Must not be identical because of string to integer conversion
224                 return ($key == $parameter->getName());
225             }
226         );
227
228         if (count($filteredParameters)) {
229             $parameter = $filteredParameters->first();
230             $parameter->setValue($value, $type);
231
232             return $this;
233         }
234
235         $parameter = new Query\Parameter($key, $value, $type);
236
237         $this->parameters->add($parameter);
238
239         return $this;
240     }
241
242     /**
243      * Process an individual parameter value
244      *
245      * @param mixed $value
246      * @return array
247      */
248     public function processParameterValue($value)
249     {
250         switch (true) {
251             case is_array($value):
252                 foreach ($value as $key => $paramValue) {
253                     $paramValue  = $this->processParameterValue($paramValue);
254                     $value[$key] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
255                 }
256
257                 return $value;
258
259             case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value)):
260                 return $this->convertObjectParameterToScalarValue($value);
261
262             default:
263                 return $value;
264         }
265     }
266
267     private function convertObjectParameterToScalarValue($value)
268     {
269         $class = $this->_em->getClassMetadata(get_class($value));
270
271         if ($class->isIdentifierComposite) {
272             throw new \InvalidArgumentException(
273                 "Binding an entity with a composite primary key to a query is not supported. " .
274                 "You should split the parameter into the explicit fields and bind them seperately."
275             );
276         }
277
278         $values = ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED)
279             ? $this->_em->getUnitOfWork()->getEntityIdentifier($value)
280             : $class->getIdentifierValues($value);
281
282         $value = $values[$class->getSingleIdentifierFieldName()];
283
284         if ( ! $value) {
285             throw new \InvalidArgumentException(
286                 "Binding entities to query parameters only allowed for entities that have an identifier."
287             );
288         }
289
290         return $value;
291     }
292
293     /**
294      * Sets the ResultSetMapping that should be used for hydration.
295      *
296      * @param ResultSetMapping $rsm
297      * @return \Doctrine\ORM\AbstractQuery
298      */
299     public function setResultSetMapping(Query\ResultSetMapping $rsm)
300     {
301         $this->_resultSetMapping = $rsm;
302
303         return $this;
304     }
305
306     /**
307      * Set a cache profile for hydration caching.
308      *
309      * If no result cache driver is set in the QueryCacheProfile, the default
310      * result cache driver is used from the configuration.
311      *
312      * Important: Hydration caching does NOT register entities in the
313      * UnitOfWork when retrieved from the cache. Never use result cached
314      * entities for requests that also flush the EntityManager. If you want
315      * some form of caching with UnitOfWork registration you should use
316      * {@see AbstractQuery::setResultCacheProfile()}.
317      *
318      * @example
319      * $lifetime = 100;
320      * $resultKey = "abc";
321      * $query->setHydrationCacheProfile(new QueryCacheProfile());
322      * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
323      *
324      * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
325      * @return \Doctrine\ORM\AbstractQuery
326      */
327     public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
328     {
329         if ( ! $profile->getResultCacheDriver()) {
330             $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
331             $profile = $profile->setResultCacheDriver($resultCacheDriver);
332         }
333
334         $this->_hydrationCacheProfile = $profile;
335
336         return $this;
337     }
338
339     /**
340      * @return \Doctrine\DBAL\Cache\QueryCacheProfile
341      */
342     public function getHydrationCacheProfile()
343     {
344         return $this->_hydrationCacheProfile;
345     }
346
347     /**
348      * Set a cache profile for the result cache.
349      *
350      * If no result cache driver is set in the QueryCacheProfile, the default
351      * result cache driver is used from the configuration.
352      *
353      * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
354      * @return \Doctrine\ORM\AbstractQuery
355      */
356     public function setResultCacheProfile(QueryCacheProfile $profile = null)
357     {
358         if ( ! $profile->getResultCacheDriver()) {
359             $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
360             $profile = $profile->setResultCacheDriver($resultCacheDriver);
361         }
362
363         $this->_queryCacheProfile = $profile;
364
365         return $this;
366     }
367
368     /**
369      * Defines a cache driver to be used for caching result sets and implictly enables caching.
370      *
371      * @param \Doctrine\Common\Cache\Cache $driver Cache driver
372      * @return \Doctrine\ORM\AbstractQuery
373      */
374     public function setResultCacheDriver($resultCacheDriver = null)
375     {
376         if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
377             throw ORMException::invalidResultCacheDriver();
378         }
379
380         $this->_queryCacheProfile = $this->_queryCacheProfile
381             ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
382             : new QueryCacheProfile(0, null, $resultCacheDriver);
383
384         return $this;
385     }
386
387     /**
388      * Returns the cache driver used for caching result sets.
389      *
390      * @deprecated
391      * @return \Doctrine\Common\Cache\Cache Cache driver
392      */
393     public function getResultCacheDriver()
394     {
395         if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
396             return $this->_queryCacheProfile->getResultCacheDriver();
397         }
398
399         return $this->_em->getConfiguration()->getResultCacheImpl();
400     }
401
402     /**
403      * Set whether or not to cache the results of this query and if so, for
404      * how long and which ID to use for the cache entry.
405      *
406      * @param boolean $bool
407      * @param integer $lifetime
408      * @param string $resultCacheId
409      * @return \Doctrine\ORM\AbstractQuery This query instance.
410      */
411     public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
412     {
413         if ($bool) {
414             $this->setResultCacheLifetime($lifetime);
415             $this->setResultCacheId($resultCacheId);
416
417             return $this;
418         }
419
420         $this->_queryCacheProfile = null;
421
422         return $this;
423     }
424
425     /**
426      * Defines how long the result cache will be active before expire.
427      *
428      * @param integer $lifetime How long the cache entry is valid.
429      * @return \Doctrine\ORM\AbstractQuery This query instance.
430      */
431     public function setResultCacheLifetime($lifetime)
432     {
433         $lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
434
435         $this->_queryCacheProfile = $this->_queryCacheProfile
436             ? $this->_queryCacheProfile->setLifetime($lifetime)
437             : new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl());
438
439         return $this;
440     }
441
442     /**
443      * Retrieves the lifetime of resultset cache.
444      *
445      * @deprecated
446      * @return integer
447      */
448     public function getResultCacheLifetime()
449     {
450         return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
451     }
452
453     /**
454      * Defines if the result cache is active or not.
455      *
456      * @param boolean $expire Whether or not to force resultset cache expiration.
457      * @return \Doctrine\ORM\AbstractQuery This query instance.
458      */
459     public function expireResultCache($expire = true)
460     {
461         $this->_expireResultCache = $expire;
462
463         return $this;
464     }
465
466     /**
467      * Retrieves if the resultset cache is active or not.
468      *
469      * @return boolean
470      */
471     public function getExpireResultCache()
472     {
473         return $this->_expireResultCache;
474     }
475
476     /**
477      * @return QueryCacheProfile
478      */
479     public function getQueryCacheProfile()
480     {
481         return $this->_queryCacheProfile;
482     }
483
484     /**
485      * Change the default fetch mode of an association for this query.
486      *
487      * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
488      *
489      * @param  string $class
490      * @param  string $assocName
491      * @param  int $fetchMode
492      * @return AbstractQuery
493      */
494     public function setFetchMode($class, $assocName, $fetchMode)
495     {
496         if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
497             $fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
498         }
499
500         $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
501
502         return $this;
503     }
504
505     /**
506      * Defines the processing mode to be used during hydration / result set transformation.
507      *
508      * @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
509      *                               One of the Query::HYDRATE_* constants.
510      * @return \Doctrine\ORM\AbstractQuery This query instance.
511      */
512     public function setHydrationMode($hydrationMode)
513     {
514         $this->_hydrationMode = $hydrationMode;
515
516         return $this;
517     }
518
519     /**
520      * Gets the hydration mode currently used by the query.
521      *
522      * @return integer
523      */
524     public function getHydrationMode()
525     {
526         return $this->_hydrationMode;
527     }
528
529     /**
530      * Gets the list of results for the query.
531      *
532      * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
533      *
534      * @return array
535      */
536     public function getResult($hydrationMode = self::HYDRATE_OBJECT)
537     {
538         return $this->execute(null, $hydrationMode);
539     }
540
541     /**
542      * Gets the array of results for the query.
543      *
544      * Alias for execute(null, HYDRATE_ARRAY).
545      *
546      * @return array
547      */
548     public function getArrayResult()
549     {
550         return $this->execute(null, self::HYDRATE_ARRAY);
551     }
552
553     /**
554      * Gets the scalar results for the query.
555      *
556      * Alias for execute(null, HYDRATE_SCALAR).
557      *
558      * @return array
559      */
560     public function getScalarResult()
561     {
562         return $this->execute(null, self::HYDRATE_SCALAR);
563     }
564
565     /**
566      * Get exactly one result or null.
567      *
568      * @throws NonUniqueResultException
569      * @param int $hydrationMode
570      * @return mixed
571      */
572     public function getOneOrNullResult($hydrationMode = null)
573     {
574         $result = $this->execute(null, $hydrationMode);
575
576         if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
577             return null;
578         }
579
580         if ( ! is_array($result)) {
581             return $result;
582         }
583
584         if (count($result) > 1) {
585             throw new NonUniqueResultException;
586         }
587
588         return array_shift($result);
589     }
590
591     /**
592      * Gets the single result of the query.
593      *
594      * Enforces the presence as well as the uniqueness of the result.
595      *
596      * If the result is not unique, a NonUniqueResultException is thrown.
597      * If there is no result, a NoResultException is thrown.
598      *
599      * @param integer $hydrationMode
600      * @return mixed
601      * @throws NonUniqueResultException If the query result is not unique.
602      * @throws NoResultException If the query returned no result.
603      */
604     public function getSingleResult($hydrationMode = null)
605     {
606         $result = $this->execute(null, $hydrationMode);
607
608         if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
609             throw new NoResultException;
610         }
611
612         if ( ! is_array($result)) {
613             return $result;
614         }
615
616         if (count($result) > 1) {
617             throw new NonUniqueResultException;
618         }
619
620         return array_shift($result);
621     }
622
623     /**
624      * Gets the single scalar result of the query.
625      *
626      * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
627      *
628      * @return mixed
629      * @throws QueryException If the query result is not unique.
630      */
631     public function getSingleScalarResult()
632     {
633         return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
634     }
635
636     /**
637      * Sets a query hint. If the hint name is not recognized, it is silently ignored.
638      *
639      * @param string $name The name of the hint.
640      * @param mixed $value The value of the hint.
641      * @return \Doctrine\ORM\AbstractQuery
642      */
643     public function setHint($name, $value)
644     {
645         $this->_hints[$name] = $value;
646
647         return $this;
648     }
649
650     /**
651      * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
652      *
653      * @param string $name The name of the hint.
654      * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
655      */
656     public function getHint($name)
657     {
658         return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
659     }
660
661     /**
662      * Return the key value map of query hints that are currently set.
663      *
664      * @return array
665      */
666     public function getHints()
667     {
668         return $this->_hints;
669     }
670
671     /**
672      * Executes the query and returns an IterableResult that can be used to incrementally
673      * iterate over the result.
674      *
675      * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters.
676      * @param integer $hydrationMode The hydration mode to use.
677      * @return \Doctrine\ORM\Internal\Hydration\IterableResult
678      */
679     public function iterate($parameters = null, $hydrationMode = null)
680     {
681         if ($hydrationMode !== null) {
682             $this->setHydrationMode($hydrationMode);
683         }
684
685         if ( ! empty($parameters)) {
686             $this->setParameters($parameters);
687         }
688
689         $stmt = $this->_doExecute();
690
691         return $this->_em->newHydrator($this->_hydrationMode)->iterate(
692             $stmt, $this->_resultSetMapping, $this->_hints
693         );
694     }
695
696     /**
697      * Executes the query.
698      *
699      * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters Query parameters.
700      * @param integer $hydrationMode Processing mode to be used during the hydration process.
701      * @return mixed
702      */
703     public function execute($parameters = null, $hydrationMode = null)
704     {
705         if ($hydrationMode !== null) {
706             $this->setHydrationMode($hydrationMode);
707         }
708
709         if ( ! empty($parameters)) {
710             $this->setParameters($parameters);
711         }
712
713         $setCacheEntry = function() {};
714
715         if ($this->_hydrationCacheProfile !== null) {
716             list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
717
718             $queryCacheProfile = $this->getHydrationCacheProfile();
719             $cache             = $queryCacheProfile->getResultCacheDriver();
720             $result            = $cache->fetch($cacheKey);
721
722             if (isset($result[$realCacheKey])) {
723                 return $result[$realCacheKey];
724             }
725
726             if ( ! $result) {
727                 $result = array();
728             }
729
730             $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
731                 $result[$realCacheKey] = $data;
732
733                 $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
734             };
735         }
736
737         $stmt = $this->_doExecute();
738
739         if (is_numeric($stmt)) {
740             $setCacheEntry($stmt);
741
742             return $stmt;
743         }
744
745         $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
746             $stmt, $this->_resultSetMapping, $this->_hints
747         );
748
749         $setCacheEntry($data);
750
751         return $data;
752     }
753
754     /**
755      * Get the result cache id to use to store the result set cache entry.
756      * Will return the configured id if it exists otherwise a hash will be
757      * automatically generated for you.
758      *
759      * @return array ($key, $hash)
760      */
761     protected function getHydrationCacheId()
762     {
763         $parameters = array();
764
765         foreach ($this->getParameters() as $parameter) {
766             $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
767         }
768
769         $sql                    = $this->getSQL();
770         $queryCacheProfile      = $this->getHydrationCacheProfile();
771         $hints                  = $this->getHints();
772         $hints['hydrationMode'] = $this->getHydrationMode();
773
774         ksort($hints);
775
776         return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
777     }
778
779     /**
780      * Set the result cache id to use to store the result set cache entry.
781      * If this is not explicitly set by the developer then a hash is automatically
782      * generated for you.
783      *
784      * @param string $id
785      * @return \Doctrine\ORM\AbstractQuery This query instance.
786      */
787     public function setResultCacheId($id)
788     {
789         $this->_queryCacheProfile = $this->_queryCacheProfile
790             ? $this->_queryCacheProfile->setCacheKey($id)
791             : new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl());
792
793         return $this;
794     }
795
796     /**
797      * Get the result cache id to use to store the result set cache entry if set.
798      *
799      * @deprecated
800      * @return string
801      */
802     public function getResultCacheId()
803     {
804         return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
805     }
806
807     /**
808      * Executes the query and returns a the resulting Statement object.
809      *
810      * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
811      */
812     abstract protected function _doExecute();
813
814     /**
815      * Cleanup Query resource when clone is called.
816      *
817      * @return void
818      */
819     public function __clone()
820     {
821         $this->parameters = new ArrayCollection();
822
823         $this->_hints = array();
824     }
825 }