Coverage for Doctrine_Query_Abstract

Back to coverage report

1 <?php
2 /*
3  *  $Id: Query.php 1393 2007-05-19 17:49:16Z zYne $
4  *
5  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16  *
17  * This software consists of voluntary contributions made by many individuals
18  * and is licensed under the LGPL. For more information, see
19  * <http://www.phpdoctrine.org>.
20  */
21
22 /**
23  * Doctrine_Query_Abstract
24  *
25  * @package     Doctrine
26  * @subpackage  Query
27  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
28  * @link        www.phpdoctrine.org
29  * @since       1.0
30  * @version     $Revision: 1393 $
31  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
32  * @todo        See {@link Doctrine_Query} 
33  */
34 abstract class Doctrine_Query_Abstract
35 {
36     /**
37      * QUERY TYPE CONSTANTS
38      */
39
40     /**
41      * constant for SELECT queries
42      */
43     const SELECT = 0;
44
45     /**
46      * constant for DELETE queries
47      */
48     const DELETE = 1;
49
50     /**
51      * constant for UPDATE queries
52      */
53     const UPDATE = 2;
54
55     /**
56      * constant for INSERT queries
57      */
58     const INSERT = 3;
59
60     /**
61      * constant for CREATE queries
62      */
63     const CREATE = 4;
64     
65     /** @todo document the query states (and the transitions between them). */
66     /**
67      * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
68      */
69     const STATE_CLEAN  = 1;
70     
71     /**
72      * A query object is in state DIRTY when it has DQL parts that have not yet been 
73      * parsed/processed.
74      */
75     const STATE_DIRTY  = 2;
76     
77     /**
78      * A query is in DIRECT state when ... ?
79      */
80     const STATE_DIRECT = 3;
81     
82     /**
83      * A query object is on LOCKED state when ... ?
84      */
85     const STATE_LOCKED = 4;
86     
87     /**
88      * @var array  Table alias map. Keys are SQL aliases and values DQL aliases. 
89      */
90     protected $_tableAliasMap = array();
91     
92     /**
93      * @var Doctrine_View  The view object used by this query, if any.
94      */
95     protected $_view;
96     
97     /**
98      * @var integer $_state   The current state of this query.
99      */
100     protected $_state = Doctrine_Query::STATE_CLEAN;
101
102     /**
103      * @var array $params  The parameters of this query.
104      */
105     protected $_params = array('join' => array(),
106                                'where' => array(),
107                                'set' => array(),
108                                'having' => array());
109     
110     /* Caching properties */
111     /** 
112      * @var Doctrine_Cache_Interface  The cache driver used for caching result sets.
113      */
114     protected $_resultCache; 
115     /**
116      * @var boolean $_expireResultCache  A boolean value that indicates whether or not
117      *                                   expire the result cache.
118      */
119     protected $_expireResultCache = false;
120     protected $_resultCacheTTL;
121     
122     /** 
123      * @var Doctrine_Cache_Interface  The cache driver used for caching queries.
124      */
125     protected $_queryCache;
126     protected $_expireQueryCache = false;
127     protected $_queryCacheTTL;
128     
129     
130     /**
131      * @var Doctrine_Connection  The connection used by this query object.
132      */
133     protected $_conn;
134     
135     
136     /**
137      * @var array $_sqlParts  The SQL query string parts. Filled during the DQL parsing process.
138      */
139     protected $_sqlParts = array(
140             'select'    => array(),
141             'distinct'  => false,
142             'forUpdate' => false,
143             'from'      => array(),
144             'set'       => array(),
145             'join'      => array(),
146             'where'     => array(),
147             'groupby'   => array(),
148             'having'    => array(),
149             'orderby'   => array(),
150             'limit'     => false,
151             'offset'    => false,
152             );
153             
154     /**
155      * @var array $_dqlParts                an array containing all DQL query parts
156      */
157     protected $_dqlParts = array(
158                             'from'      => array(),
159                             'select'    => array(),
160                             'forUpdate' => false,
161                             'set'       => array(),
162                             'join'      => array(),
163                             'where'     => array(),
164                             'groupby'   => array(),
165                             'having'    => array(),
166                             'orderby'   => array(),
167                             'limit'     => array(),
168                             'offset'    => array(),
169                             );
170             
171     
172     /**
173      * @var array $_queryComponents   Two dimensional array containing the components of this query,
174      *                                informations about their relations and other related information.
175      *                                The components are constructed during query parsing.
176      *
177      *      Keys are component aliases and values the following:
178      *
179      *          table               table object associated with given alias
180      *
181      *          relation            the relation object owned by the parent
182      *
183      *          parent              the alias of the parent
184      *
185      *          agg                 the aggregates of this component
186      *
187      *          map                 the name of the column / aggregate value this
188      *                              component is mapped to a collection
189      */
190     protected $_queryComponents = array();
191     
192     /**
193      * @var integer $type                   the query type
194      *
195      * @see Doctrine_Query::* constants
196      */
197     protected $_type = self::SELECT;
198     
199     /**
200      * @var Doctrine_Hydrator   The hydrator object used to hydrate query results.
201      */
202     protected $_hydrator;
203     
204     /**
205      * @var Doctrine_Query_Tokenizer  The tokenizer that is used during the query parsing process.
206      */
207     protected $_tokenizer;
208     
209     /**
210      * @var Doctrine_Query_Parser  The parser that is used for query parsing.
211      */
212     protected $_parser;
213     
214     /**
215      * @var array $_tableAliasSeeds         A simple array keys representing table aliases and values
216      *                                      table alias seeds. The seeds are used for generating short table
217      *                                      aliases.
218      */
219     protected $_tableAliasSeeds = array();
220     
221     /**
222      * @var array $_options                 an array of options
223      */
224     protected $_options    = array(
225                             'fetchMode'      => Doctrine::FETCH_RECORD
226                             );
227     
228     /**
229      * @var array $_enumParams              an array containing the keys of the parameters that should be enumerated
230      */
231     protected $_enumParams = array();
232     
233     /**
234      * @var boolean
235      */
236     protected $_isLimitSubqueryUsed = false;
237     
238     protected $_pendingSetParams = array();
239     
240     /**
241      * Constructor.
242      *
243      * @param Doctrine_Connection  The connection object the query will use.
244      * @param Doctrine_Hydrator_Abstract  The hydrator that will be used for generating result sets.
245      */
246     public function __construct(Doctrine_Connection $connection = null,
247             Doctrine_Hydrator_Abstract $hydrator = null)
248     {
249         if ($connection === null) {
250             $connection = Doctrine_Manager::getInstance()->getCurrentConnection();
251         }
252         if ($hydrator === null) {
253             $hydrator = new Doctrine_Hydrator();
254         }
255         $this->_conn = $connection;
256         $this->_hydrator = $hydrator;
257         $this->_tokenizer = new Doctrine_Query_Tokenizer();
258     }
259     
260     /**
261      * setOption
262      *
263      * @param string $name      option name
264      * @param string $value     option value
265      * @return Doctrine_Query   this object
266      */
267     public function setOption($name, $value)
268     {
269         if ( ! isset($this->_options[$name])) {
270             throw new Doctrine_Query_Exception('Unknown option ' . $name);
271         }
272         $this->_options[$name] = $value;
273     }
274     
275     /**
276      * hasTableAlias
277      * whether or not this object has given tableAlias
278      *
279      * @param string $tableAlias    the table alias to be checked
280      * @return boolean              true if this object has given alias, otherwise false
281      * @deprecated
282      */
283     public function hasTableAlias($sqlTableAlias)
284     {
285         return $this->hasSqlTableAlias($sqlTableAlias);
286     }
287     
288     /**
289      * hasSqlTableAlias
290      * whether or not this object has given tableAlias
291      *
292      * @param string $tableAlias    the table alias to be checked
293      * @return boolean              true if this object has given alias, otherwise false
294      */
295     public function hasSqlTableAlias($sqlTableAlias)
296     {
297         return (isset($this->_tableAliasMap[$sqlTableAlias]));
298     }
299     
300     /**
301      * getTableAliases
302      * returns all table aliases
303      *
304      * @return array        table aliases as an array
305      * @deprecated
306      */
307     public function getTableAliases()
308     {
309         return $this->getTableAliasMap();
310     }
311     
312     /**
313      * getTableAliasMap
314      * returns all table aliases
315      *
316      * @return array        table aliases as an array
317      */
318     public function getTableAliasMap()
319     {
320         return $this->_tableAliasMap;
321     }
322     
323     /**
324      * getQueryPart
325      * gets a query part from the query part array
326      *
327      * @param string $name          the name of the query part to be set
328      * @param string $part          query part string
329      * @throws Doctrine_Query_Exception   if trying to set unknown query part
330      * @return Doctrine_Query_Abstract  this object
331      * @deprecated
332      */
333     public function getQueryPart($part)
334     {
335         return $this->getSqlQueryPart($part);
336     }
337     
338     /**
339      * getSqlQueryPart
340      * gets an SQL query part from the SQL query part array
341      *
342      * @param string $name          the name of the query part to be set
343      * @param string $part          query part string
344      * @throws Doctrine_Query_Exception   if trying to set unknown query part
345      * @return Doctrine_Hydrate     this object
346      */
347     public function getSqlQueryPart($part)
348     {
349         if ( ! isset($this->_sqlParts[$part])) {
350             throw new Doctrine_Query_Exception('Unknown SQL query part ' . $part);
351         }
352         return $this->_sqlParts[$part];
353     }
354     
355     /**
356      * setQueryPart
357      * sets a query part in the query part array
358      *
359      * @param string $name          the name of the query part to be set
360      * @param string $part          query part string
361      * @throws Doctrine_Query_Exception   if trying to set unknown query part
362      * @return Doctrine_Hydrate     this object
363      * @deprecated
364      */
365     public function setQueryPart($name, $part)
366     {
367         return $this->setSqlQueryPart($name, $part);
368     }
369     
370     /**
371      * setSqlQueryPart
372      * sets an SQL query part in the SQL query part array
373      *
374      * @param string $name          the name of the query part to be set
375      * @param string $part          query part string
376      * @throws Doctrine_Query_Exception   if trying to set unknown query part
377      * @return Doctrine_Hydrate     this object
378      */
379     public function setSqlQueryPart($name, $part)
380     {
381         if ( ! isset($this->_sqlParts[$name])) {
382             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
383         }
384
385         if ($name !== 'limit' && $name !== 'offset') {
386             if (is_array($part)) {
387                 $this->_sqlParts[$name] = $part;
388             } else {
389                 $this->_sqlParts[$name] = array($part);
390             }
391         } else {
392             $this->_sqlParts[$name] = $part;
393         }
394
395         return $this;
396     }
397     
398     /**
399      * addQueryPart
400      * adds a query part in the query part array
401      *
402      * @param string $name          the name of the query part to be added
403      * @param string $part          query part string
404      * @throws Doctrine_Query_Exception   if trying to add unknown query part
405      * @return Doctrine_Hydrate     this object
406      * @deprecated
407      */
408     public function addQueryPart($name, $part)
409     {
410         return $this->addSqlQueryPart($name, $part);
411     }
412     
413     /**
414      * addSqlQueryPart
415      * adds an SQL query part to the SQL query part array
416      *
417      * @param string $name          the name of the query part to be added
418      * @param string $part          query part string
419      * @throws Doctrine_Query_Exception   if trying to add unknown query part
420      * @return Doctrine_Hydrate     this object
421      */
422     public function addSqlQueryPart($name, $part)
423     {
424         if ( ! isset($this->_sqlParts[$name])) {
425             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
426         }
427         if (is_array($part)) {
428             $this->_sqlParts[$name] = array_merge($this->_sqlParts[$name], $part);
429         } else {
430             $this->_sqlParts[$name][] = $part;
431         }
432         return $this;
433     }
434     
435     /**
436      * removeQueryPart
437      * removes a query part from the query part array
438      *
439      * @param string $name          the name of the query part to be removed
440      * @throws Doctrine_Query_Exception   if trying to remove unknown query part
441      * @return Doctrine_Hydrate     this object
442      * @deprecated
443      */
444     public function removeQueryPart($name)
445     {
446         return $this->removeSqlQueryPart($name);
447     }
448     
449     /**
450      * removeSqlQueryPart
451      * removes a query part from the query part array
452      *
453      * @param string $name          the name of the query part to be removed
454      * @throws Doctrine_Query_Exception   if trying to remove unknown query part
455      * @return Doctrine_Hydrate     this object
456      */
457     public function removeSqlQueryPart($name)
458     {
459         if ( ! isset($this->_sqlParts[$name])) {
460             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
461         }
462         
463         if ($name == 'limit' || $name == 'offset') {
464             $this->_sqlParts[$name] = false;
465         } else {
466             $this->_sqlParts[$name] = array();
467         }
468         
469         return $this;
470     }
471     
472     /**
473      * setView
474      * sets a database view this query object uses
475      * this method should only be called internally by doctrine
476      *
477      * @param Doctrine_View $view       database view
478      * @return void
479      */
480     public function setView(Doctrine_View $view)
481     {
482         $this->_view = $view;
483     }
484     
485     /**
486      * getView
487      * returns the view associated with this query object (if any)
488      *
489      * @return Doctrine_View        the view associated with this query object
490      */
491     public function getView()
492     {
493         return $this->_view;
494     }
495     
496     /**
497      * limitSubqueryUsed
498      *
499      * @return boolean
500      */
501     public function isLimitSubqueryUsed()
502     {
503         return $this->_isLimitSubqueryUsed;
504     }
505     
506     /**
507      * convertEnums
508      * convert enum parameters to their integer equivalents
509      *
510      * @return array    converted parameter array
511      */
512     public function convertEnums($params)
513     {
514         $table = $this->getRoot();
515         foreach ($this->_pendingSetParams as $fieldName => $value) {
516             $e = explode('.', $fieldName);
517             $fieldName = isset($e[1]) ? $e[1]:$e[0];
518             $columnName = $table->getColumnName($fieldName);
519             if ($table->getTypeOf($columnName) == 'enum') {
520                 $this->addEnumParam($value, $table, $columnName);
521             }
522         }
523
524         foreach ($this->_enumParams as $key => $values) {
525             if (isset($params[$key])) {
526                 if ( ! empty($values)) {
527                     $params[$key] = $values[0]->enumIndex($values[1], $params[$key]);
528                 }
529             }
530         }
531         return $params;
532     }
533     
534     /**
535      * applyInheritance
536      * applies column aggregation inheritance to DQL / SQL query
537      *
538      * @return string
539      */
540     public function applyInheritance()
541     {
542         // get the inheritance maps
543         $array = array();
544
545         foreach ($this->_queryComponents as $componentAlias => $data) {
546             $tableAlias = $this->getSqlTableAlias($componentAlias);
547             $array[$tableAlias][] = $data['table']->inheritanceMap;
548         }
549
550         // apply inheritance maps
551         $str = '';
552         $c = array();
553
554         $index = 0;
555         foreach ($array as $tableAlias => $maps) {
556             $a = array();
557
558             // don't use table aliases if the query isn't a select query
559             if ($this->_type !== Doctrine_Query::SELECT) {
560                 $tableAlias = '';
561             } else {
562                 $tableAlias .= '.';
563             }
564
565             foreach ($maps as $map) {
566                 $b = array();
567                 foreach ($map as $field => $value) {
568                     $identifier = $this->_conn->quoteIdentifier($tableAlias . $field);
569
570                     if ($index > 0) {
571                         $b[] = '(' . $identifier . ' = ' . $this->_conn->quote($value)
572                              . ' OR ' . $identifier . ' IS NULL)';
573                     } else {
574                         $b[] = $identifier . ' = ' . $this->_conn->quote($value);
575                     }
576                 }
577
578                 if ( ! empty($b)) {
579                     $a[] = implode(' AND ', $b);
580                 }
581             }
582
583             if ( ! empty($a)) {
584                 $c[] = implode(' AND ', $a);
585             }
586             $index++;
587         }
588
589         $str .= implode(' AND ', $c);
590
591         return $str;
592     }
593     
594     /**
595      * getTableAlias
596      * some database such as Oracle need the identifier lengths to be < ~30 chars
597      * hence Doctrine creates as short identifier aliases as possible
598      *
599      * this method is used for the creation of short table aliases, its also
600      * smart enough to check if an alias already exists for given component (componentAlias)
601      *
602      * @param string $componentAlias    the alias for the query component to search table alias for
603      * @param string $tableName         the table name from which the table alias is being created
604      * @return string                   the generated / fetched short alias
605      * @deprecated
606      */
607     public function getTableAlias($componentAlias, $tableName = null)
608     {
609         return $this->getSqlTableAlias($componentAlias, $tableName);
610     }
611     
612     /**
613      * getSqlTableAlias
614      * some database such as Oracle need the identifier lengths to be < ~30 chars
615      * hence Doctrine creates as short identifier aliases as possible
616      *
617      * this method is used for the creation of short table aliases, its also
618      * smart enough to check if an alias already exists for given component (componentAlias)
619      *
620      * @param string $componentAlias    the alias for the query component to search table alias for
621      * @param string $tableName         the table name from which the table alias is being created
622      * @return string                   the generated / fetched short alias
623      */
624     public function getSqlTableAlias($componentAlias, $tableName = null)
625     {
626         $alias = array_search($componentAlias, $this->_tableAliasMap);
627
628         if ($alias !== false) {
629             return $alias;
630         }
631
632         if ($tableName === null) {
633             throw new Doctrine_Query_Exception("Couldn't get short alias for " . $componentAlias);
634         }
635
636         return $this->generateTableAlias($componentAlias, $tableName);
637     }
638     
639     /**
640      * generateNewTableAlias
641      * generates a new alias from given table alias
642      *
643      * @param string $tableAlias    table alias from which to generate the new alias from
644      * @return string               the created table alias
645      * @deprecated
646      */
647     public function generateNewTableAlias($oldAlias)
648     {
649         return $this->generateNewSqlTableAlias($oldAlias);
650     }
651     
652     /**
653      * generateNewSqlTableAlias
654      * generates a new alias from given table alias
655      *
656      * @param string $tableAlias    table alias from which to generate the new alias from
657      * @return string               the created table alias
658      */
659     public function generateNewSqlTableAlias($oldAlias)
660     {
661         if (isset($this->_tableAliasMap[$oldAlias])) {
662             // generate a new alias
663             $name = substr($oldAlias, 0, 1);
664             $i    = ((int) substr($oldAlias, 1));
665
666             if ($i == 0) {
667                 $i = 1;
668             }
669
670             $newIndex  = ($this->_tableAliasSeeds[$name] + $i);
671
672             return $name . $newIndex;
673         }
674
675         return $oldAlias;
676     }
677     
678     /**
679      * getTableAliasSeed
680      * returns the alias seed for given table alias
681      *
682      * @param string $tableAlias    table alias that identifies the alias seed
683      * @return integer              table alias seed
684      * @deprecated
685      */
686     public function getTableAliasSeed($sqlTableAlias)
687     {
688         return $this->getSqlTableAliasSeed($sqlTableAlias);
689     }
690     
691     /**
692      * getSqlTableAliasSeed
693      * returns the alias seed for given table alias
694      *
695      * @param string $tableAlias    table alias that identifies the alias seed
696      * @return integer              table alias seed
697      */
698     public function getSqlTableAliasSeed($sqlTableAlias)
699     {
700         if ( ! isset($this->_tableAliasSeeds[$sqlTableAlias])) {
701             return 0;
702         }
703         return $this->_tableAliasSeeds[$sqlTableAlias];
704     }
705     
706     /**
707      * hasAliasDeclaration
708      * whether or not this object has a declaration for given component alias
709      *
710      * @param string $componentAlias    the component alias the retrieve the declaration from
711      * @return boolean
712      */
713     public function hasAliasDeclaration($componentAlias)
714     {
715         return isset($this->_queryComponents[$componentAlias]);
716     }
717     
718     /**
719      * getAliasDeclaration
720      * get the declaration for given component alias
721      *
722      * @param string $componentAlias    the component alias the retrieve the declaration from
723      * @return array                    the alias declaration
724      * @deprecated
725      */
726     public function getAliasDeclaration($componentAlias)
727     {
728         return $this->getQueryComponent($componentAlias);
729     }
730     
731     /**
732      * getQueryComponent
733      * get the declaration for given component alias
734      *
735      * @param string $componentAlias    the component alias the retrieve the declaration from
736      * @return array                    the alias declaration
737      */
738     public function getQueryComponent($componentAlias)
739     {
740         if ( ! isset($this->_queryComponents[$componentAlias])) {
741             throw new Doctrine_Query_Exception('Unknown component alias ' . $componentAlias);
742         }
743
744         return $this->_queryComponents[$componentAlias];
745     }
746     
747     /**
748      * copyAliases
749      * copy aliases from another Hydrate object
750      *
751      * this method is needed by DQL subqueries which need the aliases
752      * of the parent query
753      *
754      * @param Doctrine_Hydrate $query   the query object from which the
755      *                                  aliases are copied from
756      * @return Doctrine_Hydrate         this object
757      */
758     public function copyAliases(Doctrine_Query_Abstract $query)
759     {
760         $this->_tableAliasMap = $query->_tableAliasMap;
761         $this->_queryComponents     = $query->_queryComponents;
762         $this->_tableAliasSeeds = $query->_tableAliasSeeds;
763         return $this;
764     }
765     
766     /**
767      * getRootAlias
768      * returns the alias of the the root component
769      *
770      * @return array
771      */
772     public function getRootAlias()
773     {
774         if ( ! $this->_queryComponents) {
775           $this->getSql();
776         }
777         reset($this->_queryComponents);
778
779         return key($this->_queryComponents);
780     }
781     
782     /**
783      * getRootDeclaration
784      * returns the root declaration
785      *
786      * @return array
787      */
788     public function getRootDeclaration()
789     {
790         $map = reset($this->_queryComponents);
791         return $map;
792     }
793     
794     /**
795      * getRoot
796      * returns the root component for this object
797      *
798      * @return Doctrine_Table       root components table
799      */
800     public function getRoot()
801     {
802         $map = reset($this->_queryComponents);
803
804         if ( ! isset($map['table'])) {
805             throw new Doctrine_Query_Exception('Root component not initialized.');
806         }
807
808         return $map['table'];
809     }
810     
811     /**
812      * generateTableAlias
813      * generates a table alias from given table name and associates 
814      * it with given component alias
815      *
816      * @param string $componentAlias    the component alias to be associated with generated table alias
817      * @param string $tableName         the table name from which to generate the table alias
818      * @return string                   the generated table alias
819      * @deprecated
820      */
821     public function generateTableAlias($componentAlias, $tableName)
822     {
823         return $this->generateSqlTableAlias($componentAlias, $tableName);
824     }
825     
826     /**
827      * generateSqlTableAlias
828      * generates a table alias from given table name and associates 
829      * it with given component alias
830      *
831      * @param string $componentAlias    the component alias to be associated with generated table alias
832      * @param string $tableName         the table name from which to generate the table alias
833      * @return string                   the generated table alias
834      */
835     public function generateSqlTableAlias($componentAlias, $tableName)
836     {
837         preg_match('/([^_])/', $tableName, $matches);
838         $char = strtolower($matches[0]);
839
840         $alias = $char;
841
842         if ( ! isset($this->_tableAliasSeeds[$alias])) {
843             $this->_tableAliasSeeds[$alias] = 1;
844         }
845
846         while (isset($this->_tableAliasMap[$alias])) {
847             if ( ! isset($this->_tableAliasSeeds[$alias])) {
848                 $this->_tableAliasSeeds[$alias] = 1;
849             }
850             $alias = $char . ++$this->_tableAliasSeeds[$alias];
851         }
852
853         $this->_tableAliasMap[$alias] = $componentAlias;
854
855         return $alias;
856     }
857     
858     /**
859      * getComponentAlias
860      * get component alias associated with given table alias
861      *
862      * @param string $sqlTableAlias    the SQL table alias that identifies the component alias
863      * @return string               component alias
864      */
865     public function getComponentAlias($sqlTableAlias)
866     {
867         if ( ! isset($this->_tableAliasMap[$sqlTableAlias])) {
868             throw new Doctrine_Query_Exception('Unknown table alias ' . $sqlTableAlias);
869         }
870         return $this->_tableAliasMap[$sqlTableAlias];
871     }
872     
873     /**
874      * _execute 
875      * 
876      * @param array $params 
877      * @return PDOStatement  The executed PDOStatement.
878      */
879     protected function _execute($params)
880     {
881         $params = $this->_conn->convertBooleans($params);
882
883         if ( ! $this->_view) {
884             if ($this->_queryCache || $this->_conn->getAttribute(Doctrine::ATTR_QUERY_CACHE)) {
885                 $queryCacheDriver = $this->getQueryCacheDriver();
886                 // calculate hash for dql query
887                 $dql = $this->getDql(); 
888                 $hash = md5($dql . 'DOCTRINE_QUERY_CACHE_SALT');
889                 $cached = $queryCacheDriver->fetch($hash);
890                 if ($cached) {
891                     $query = $this->_constructQueryFromCache($cached);
892                 } else {
893                     $query = $this->getSqlQuery($params);
894                     $serializedQuery = $this->getCachedForm($query);
895                     $queryCacheDriver->save($hash, $serializedQuery, $this->_queryCacheTTL);
896                 }
897             } else {
898                 $query = $this->getSqlQuery($params);
899             }
900         } else {
901             $query = $this->_view->getSelectSql();
902         }
903
904         $params = $this->convertEnums($params);
905
906         if ($this->isLimitSubqueryUsed() &&
907                 $this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') {
908             $params = array_merge($params, $params);
909         }
910
911         if ($this->_type !== self::SELECT) {
912             return $this->_conn->exec($query, $params);
913         }
914
915         $stmt = $this->_conn->execute($query, $params);
916         return $stmt;
917     }
918
919     /**
920      * execute
921      * executes the query and populates the data set
922      *
923      * @param string $params
924      * @return Doctrine_Collection            the root collection
925      */
926     public function execute($params = array(), $hydrationMode = null)
927     {
928         $params = array_merge($this->_params['join'],
929                               $this->_params['set'], 
930                               $this->_params['where'],
931                               $this->_params['having'], 
932                               $params);
933         
934         if ($this->_resultCache) {
935             $cacheDriver = $this->getResultCacheDriver();
936
937             $dql = $this->getDql();
938             // calculate hash for dql query
939             $hash = md5($dql . var_export($params, true));
940
941             $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
942
943             if ($cached === false) {
944                 // cache miss
945                 $stmt = $this->_execute($params);
946                 $this->_hydrator->setQueryComponents($this->_queryComponents);
947                 $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap,
948                         Doctrine::HYDRATE_ARRAY);
949
950                 $cached = $this->getCachedForm($result);
951                 $cacheDriver->save($hash, $cached, $this->_resultCacheTTL);
952                 return $result;
953             } else {
954                 return $this->_constructQueryFromCache($cached);
955             }
956         } else {
957             $stmt = $this->_execute($params);
958
959             if (is_integer($stmt)) {
960                 return $stmt;
961             }
962             
963             $this->_hydrator->setQueryComponents($this->_queryComponents);
964             return $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap, $hydrationMode);
965         }
966     }
967     
968     /**
969      * Constructs the query from the cached form.
970      * 
971      * @param string  The cached query, in a serialized form.
972      * @return array  The custom component that was cached together with the essential
973      *                query data. This can be either a result set (result caching)
974      *                or an SQL query string (query caching).
975      */
976     protected function _constructQueryFromCache($cached)
977     {
978         $cached = unserialize($cached);
979         $this->_tableAliasMap = $cached[2];
980         $customComponent = $cached[0];
981
982         $queryComponents = array();
983         $cachedComponents = $cached[1];
984         foreach ($cachedComponents as $alias => $components) {
985             $e = explode('.', $components[0]);
986             if (count($e) === 1) {
987                 $queryComponents[$alias]['table'] = $this->_conn->getTable($e[0]);
988             } else {
989                 $queryComponents[$alias]['parent'] = $e[0];
990                 $queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getRelation($e[1]);
991                 $queryComponents[$alias]['table'] = $queryComponents[$alias]['relation']->getTable();
992             }
993             if (isset($components[1])) {
994                 $queryComponents[$alias]['agg'] = $components[1];
995             }
996             if (isset($components[2])) {
997                 $queryComponents[$alias]['map'] = $components[2];
998             }
999         }
1000         $this->_queryComponents = $queryComponents;
1001         
1002         return $customComponent;
1003     }
1004     
1005     /**
1006      * getCachedForm
1007      * returns the cached form of this query for given resultSet
1008      *
1009      * @param array $resultSet
1010      * @return string           serialized string representation of this query
1011      */
1012     public function getCachedForm($customComponent = null)
1013     {
1014         $componentInfo = array();
1015
1016         foreach ($this->getQueryComponents() as $alias => $components) {
1017             if ( ! isset($components['parent'])) {
1018                 $componentInfo[$alias][] = $components['table']->getComponentName();
1019             } else {
1020                 $componentInfo[$alias][] = $components['parent'] . '.' . $components['relation']->getAlias();
1021             }
1022             if (isset($components['agg'])) {
1023                 $componentInfo[$alias][] = $components['agg'];
1024             }
1025             if (isset($components['map'])) {
1026                 $componentInfo[$alias][] = $components['map'];
1027             }
1028         }
1029         
1030         return serialize(array($customComponent, $componentInfo, $this->getTableAliasMap()));
1031     }
1032     
1033     /**
1034      * addSelect
1035      * adds fields to the SELECT part of the query
1036      *
1037      * @param string $select        Query SELECT part
1038      * @return Doctrine_Query
1039      */
1040     public function addSelect($select)
1041     {
1042         return $this->_addDqlQueryPart('select', $select, true);
1043     }
1044     
1045     /** 
1046      * addTableAlias
1047      * adds an alias for table and associates it with given component alias
1048      *
1049      * @param string $componentAlias    the alias for the query component associated with given tableAlias
1050      * @param string $tableAlias        the table alias to be added
1051      * @return Doctrine_Hydrate
1052      * @deprecated
1053      */
1054     public function addTableAlias($tableAlias, $componentAlias)
1055     {
1056         return $this->addSqlTableAlias($tableAlias, $componentAlias);
1057     }
1058     
1059     /** 
1060      * addSqlTableAlias
1061      * adds an SQL table alias and associates it a component alias
1062      *
1063      * @param string $componentAlias    the alias for the query component associated with given tableAlias
1064      * @param string $tableAlias        the table alias to be added
1065      * @return Doctrine_Query_Abstract
1066      */
1067     public function addSqlTableAlias($sqlTableAlias, $componentAlias)
1068     {
1069         $this->_tableAliasMap[$sqlTableAlias] = $componentAlias;
1070         return $this;
1071     }
1072
1073     /**
1074      * addFrom
1075      * adds fields to the FROM part of the query
1076      *
1077      * @param string $from        Query FROM part
1078      * @return Doctrine_Query
1079      */
1080     public function addFrom($from)
1081     {
1082         return $this->_addDqlQueryPart('from', $from, true);
1083     }
1084
1085     /**
1086      * addWhere
1087      * adds conditions to the WHERE part of the query
1088      *
1089      * @param string $where         Query WHERE part
1090      * @param mixed $params         an array of parameters or a simple scalar
1091      * @return Doctrine_Query
1092      */
1093     public function addWhere($where, $params = array())
1094     {
1095         if (is_array($params)) {
1096             $this->_params['where'] = array_merge($this->_params['where'], $params);
1097         } else {
1098             $this->_params['where'][] = $params;
1099         }
1100         return $this->_addDqlQueryPart('where', $where, true);
1101     }
1102
1103     /**
1104      * whereIn
1105      * adds IN condition to the query WHERE part
1106      *
1107      * @param string $expr          the operand of the IN
1108      * @param mixed $params         an array of parameters or a simple scalar
1109      * @param boolean $not          whether or not to use NOT in front of IN
1110      * @return Doctrine_Query
1111      */
1112     public function whereIn($expr, $params = array(), $not = false)
1113     {
1114         $params = (array) $params;
1115
1116         // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
1117         if (!count($params))
1118           return $this;
1119
1120         $a = array();
1121         foreach ($params as $k => $value) {
1122             if ($value instanceof Doctrine_Expression) {
1123                 $value = $value->getSql();
1124                 unset($params[$k]);
1125             } else {
1126                 $value = '?';
1127             }
1128             $a[] = $value;
1129         }
1130
1131         $this->_params['where'] = array_merge($this->_params['where'], $params);
1132
1133         $where = $expr . ($not === true ? ' NOT ':'') . ' IN (' . implode(', ', $a) . ')';
1134
1135         return $this->_addDqlQueryPart('where', $where, true);
1136     }
1137
1138     /**
1139      * whereNotIn
1140      * adds NOT IN condition to the query WHERE part
1141      *
1142      * @param string $expr          the operand of the NOT IN
1143      * @param mixed $params         an array of parameters or a simple scalar
1144      * @return Doctrine_Query
1145      */
1146     public function whereNotIn($expr, $params = array())
1147     {
1148         return $this->whereIn($expr, $params, true);
1149     } 
1150
1151     /**
1152      * addGroupBy
1153      * adds fields to the GROUP BY part of the query
1154      *
1155      * @param string $groupby       Query GROUP BY part
1156      * @return Doctrine_Query
1157      */
1158     public function addGroupBy($groupby)
1159     {
1160         return $this->_addDqlQueryPart('groupby', $groupby, true);
1161     }
1162
1163     /**
1164      * addHaving
1165      * adds conditions to the HAVING part of the query
1166      *
1167      * @param string $having        Query HAVING part
1168      * @param mixed $params         an array of parameters or a simple scalar
1169      * @return Doctrine_Query
1170      */
1171     public function addHaving($having, $params = array())
1172     {
1173         if (is_array($params)) {
1174             $this->_params['having'] = array_merge($this->_params['having'], $params);
1175         } else {
1176             $this->_params['having'][] = $params;
1177         }
1178         return $this->_addDqlQueryPart('having', $having, true);
1179     }
1180
1181     /**
1182      * addOrderBy
1183      * adds fields to the ORDER BY part of the query
1184      *
1185      * @param string $orderby       Query ORDER BY part
1186      * @return Doctrine_Query
1187      */
1188     public function addOrderBy($orderby)
1189     {
1190         return $this->_addDqlQueryPart('orderby', $orderby, true);
1191     }
1192
1193     /**
1194      * select
1195      * sets the SELECT part of the query
1196      *
1197      * @param string $select        Query SELECT part
1198      * @return Doctrine_Query
1199      */
1200     public function select($select)
1201     {
1202         return $this->_addDqlQueryPart('select', $select);
1203     }
1204
1205     /**
1206      * distinct
1207      * Makes the query SELECT DISTINCT.
1208      *
1209      * @param bool $flag            Whether or not the SELECT is DISTINCT (default true).
1210      * @return Doctrine_Query
1211      */
1212     public function distinct($flag = true)
1213     {   
1214         $this->_sqlParts['distinct'] = (bool) $flag;
1215         return $this;
1216     }
1217
1218     /**
1219      * forUpdate
1220      * Makes the query SELECT FOR UPDATE.
1221      *
1222      * @param bool $flag            Whether or not the SELECT is FOR UPDATE (default true).
1223      * @return Doctrine_Query
1224      */
1225     public function forUpdate($flag = true)
1226     {
1227         $this->_sqlParts[self::FOR_UPDATE] = (bool) $flag;
1228         return $this;
1229     }
1230
1231     /**
1232      * delete
1233      * sets the query type to DELETE
1234      *
1235      * @return Doctrine_Query
1236      */
1237     public function delete()
1238     {
1239         $this->_type = self::DELETE;
1240         return $this;
1241     }
1242
1243     /**
1244      * update
1245      * sets the UPDATE part of the query
1246      *
1247      * @param string $update        Query UPDATE part
1248      * @return Doctrine_Query
1249      */
1250     public function update($update)
1251     {
1252         $this->_type = self::UPDATE;
1253         return $this->_addDqlQueryPart('from', $update);
1254     }
1255
1256     /**
1257      * set
1258      * sets the SET part of the query
1259      *
1260      * @param string $update        Query UPDATE part
1261      * @return Doctrine_Query
1262      */
1263     public function set($key, $value, $params = null)
1264     {
1265         if (is_array($key)) {
1266             foreach ($key as $k => $v) {
1267                 $this->set($k, '?', array($v));                               
1268             }
1269             return $this;
1270         } else {
1271             if ($params !== null) {
1272                 if (is_array($params)) {
1273                     $this->_params['set'] = array_merge($this->_params['set'], $params);
1274                 } else {
1275                     $this->_params['set'][] = $params;
1276                 }
1277             }
1278
1279             $this->_pendingSetParams[$key] = $value;
1280
1281             return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
1282         }
1283     }
1284
1285     /**
1286      * from
1287      * sets the FROM part of the query
1288      *
1289      * @param string $from          Query FROM part
1290      * @return Doctrine_Query
1291      */
1292     public function from($from)
1293     {
1294         return $this->_addDqlQueryPart('from', $from);
1295     }
1296
1297     /**
1298      * innerJoin
1299      * appends an INNER JOIN to the FROM part of the query
1300      *
1301      * @param string $join         Query INNER JOIN
1302      * @return Doctrine_Query
1303      */
1304     public function innerJoin($join, $params = array())
1305     {
1306         if (is_array($params)) {
1307             $this->_params['join'] = array_merge($this->_params['join'], $params);
1308         } else {
1309             $this->_params['join'][] = $params;
1310         }
1311
1312         return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
1313     }
1314
1315     /**
1316      * leftJoin
1317      * appends a LEFT JOIN to the FROM part of the query
1318      *
1319      * @param string $join         Query LEFT JOIN
1320      * @return Doctrine_Query
1321      */
1322     public function leftJoin($join, $params = array())
1323     {
1324         if (is_array($params)) {
1325             $this->_params['join'] = array_merge($this->_params['join'], $params);
1326         } else {
1327             $this->_params['join'][] = $params;
1328         }
1329
1330         return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
1331     }
1332
1333     /**
1334      * groupBy
1335      * sets the GROUP BY part of the query
1336      *
1337      * @param string $groupby      Query GROUP BY part
1338      * @return Doctrine_Query
1339      */
1340     public function groupBy($groupby)
1341     {
1342         return $this->_addDqlQueryPart('groupby', $groupby);
1343     }
1344
1345     /**
1346      * where
1347      * sets the WHERE part of the query
1348      *
1349      * @param string $join         Query WHERE part
1350      * @param mixed $params        an array of parameters or a simple scalar
1351      * @return Doctrine_Query
1352      */
1353     public function where($where, $params = array())
1354     {
1355         $this->_params['where'] = array();
1356         if (is_array($params)) {
1357             $this->_params['where'] = $params;
1358         } else {
1359             $this->_params['where'][] = $params;
1360         }
1361
1362         return $this->_addDqlQueryPart('where', $where);
1363     }
1364
1365     /**
1366      * having
1367      * sets the HAVING part of the query
1368      *
1369      * @param string $having       Query HAVING part
1370      * @param mixed $params        an array of parameters or a simple scalar
1371      * @return Doctrine_Query
1372      */
1373     public function having($having, $params = array())
1374     {
1375         $this->_params['having'] = array();
1376         if (is_array($params)) {
1377             $this->_params['having'] = $params;
1378         } else {
1379             $this->_params['having'][] = $params;
1380         }
1381         
1382         return $this->_addDqlQueryPart('having', $having);
1383     }
1384
1385     /**
1386      * orderBy
1387      * sets the ORDER BY part of the query
1388      *
1389      * @param string $orderby      Query ORDER BY part
1390      * @return Doctrine_Query
1391      */
1392     public function orderBy($orderby)
1393     {
1394         return $this->_addDqlQueryPart('orderby', $orderby);
1395     }
1396
1397     /**
1398      * limit
1399      * sets the Query query limit
1400      *
1401      * @param integer $limit        limit to be used for limiting the query results
1402      * @return Doctrine_Query
1403      */
1404     public function limit($limit)
1405     {
1406         return $this->_addDqlQueryPart('limit', $limit);
1407     }
1408
1409     /**
1410      * offset
1411      * sets the Query query offset
1412      *
1413      * @param integer $offset       offset to be used for paginating the query
1414      * @return Doctrine_Query
1415      */
1416     public function offset($offset)
1417     {
1418         return $this->_addDqlQueryPart('offset', $offset);
1419     }
1420     
1421     /**
1422      * getSql
1423      * shortcut for {@link getSqlQuery()}.
1424      *
1425      * @return string   sql query string
1426      */
1427     public function getSql()
1428     {
1429         return $this->getSqlQuery();
1430     }
1431     
1432     /**
1433      * clear
1434      * resets all the variables
1435      *
1436      * @return void
1437      */
1438     protected function clear()
1439     {
1440         $this->_sqlParts = array(
1441                     'select'    => array(),
1442                     'distinct'  => false,
1443                     'forUpdate' => false,
1444                     'from'      => array(),
1445                     'set'       => array(),
1446                     'join'      => array(),
1447                     'where'     => array(),
1448                     'groupby'   => array(),
1449                     'having'    => array(),
1450                     'orderby'   => array(),
1451                     'limit'     => false,
1452                     'offset'    => false,
1453                     );
1454     }
1455     
1456     public function setHydrationMode($hydrationMode)
1457     {
1458         $this->_hydrator->setHydrationMode($hydrationMode);
1459         return $this;
1460     }
1461     
1462     /**
1463      * @deprecated
1464      */
1465     public function getAliasMap()
1466     {
1467         return $this->_queryComponents;
1468     }
1469     
1470     /**
1471      * Gets the components of this query.
1472      */
1473     public function getQueryComponents()
1474     {
1475         return $this->_queryComponents;
1476     }
1477     
1478     /**
1479      * Return the SQL parts.
1480      *
1481      * @return array The parts
1482      * @deprecated
1483      */
1484     public function getParts()
1485     {
1486         return $this->getSqlParts();
1487     }
1488     
1489     /**
1490      * Return the SQL parts.
1491      *
1492      * @return array The parts
1493      */
1494     public function getSqlParts()
1495     {
1496         return $this->_sqlParts;
1497     }
1498     
1499     /**
1500      * getType
1501      *
1502      * returns the type of this query object
1503      * by default the type is Doctrine_Query_Abstract::SELECT but if update() or delete()
1504      * are being called the type is Doctrine_Query_Abstract::UPDATE and Doctrine_Query_Abstract::DELETE,
1505      * respectively
1506      *
1507      * @see Doctrine_Query_Abstract::SELECT
1508      * @see Doctrine_Query_Abstract::UPDATE
1509      * @see Doctrine_Query_Abstract::DELETE
1510      *
1511      * @return integer      return the query type
1512      */
1513     public function getType()
1514     {
1515         return $this->_type;
1516     }
1517     
1518     /**
1519      * useCache
1520      *
1521      * @param Doctrine_Cache_Interface|bool $driver      cache driver
1522      * @param integer $timeToLive                        how long the cache entry is valid
1523      * @return Doctrine_Hydrate         this object
1524      * @deprecated Use useResultCache()
1525      */
1526     public function useCache($driver = true, $timeToLive = null)
1527     {
1528         return $this->useResultCache($driver, $timeToLive);
1529     }
1530     
1531     /**
1532      * useResultCache
1533      *
1534      * @param Doctrine_Cache_Interface|bool $driver      cache driver
1535      * @param integer $timeToLive                        how long the cache entry is valid
1536      * @return Doctrine_Hydrate         this object
1537      */
1538     public function useResultCache($driver = true, $timeToLive = null)
1539     {
1540         if ($driver !== null && $driver !== true && ! ($driver instanceOf Doctrine_Cache_Interface)){
1541             $msg = 'First argument should be instance of Doctrine_Cache_Interface or null.';
1542             throw new Doctrine_Query_Exception($msg);
1543         }
1544         $this->_resultCache = $driver;
1545
1546         return $this->setResultCacheLifeSpan($timeToLive);
1547     }
1548     
1549     /**
1550      * useQueryCache
1551      *
1552      * @param Doctrine_Cache_Interface|bool $driver      cache driver
1553      * @param integer $timeToLive                        how long the cache entry is valid
1554      * @return Doctrine_Hydrate         this object
1555      */
1556     public function useQueryCache(Doctrine_Cache_Interface $driver, $timeToLive = null)
1557     {
1558         $this->_queryCache = $driver;
1559         return $this->setQueryCacheLifeSpan($timeToLive);
1560     }
1561     
1562     /**
1563      * expireCache
1564      *
1565      * @param boolean $expire       whether or not to force cache expiration
1566      * @return Doctrine_Hydrate     this object
1567      * @deprecated Use expireResultCache()
1568      */
1569     public function expireCache($expire = true)
1570     {
1571         return $this->expireResultCache($expire);
1572     }
1573     
1574     /**
1575      * expireCache
1576      *
1577      * @param boolean $expire       whether or not to force cache expiration
1578      * @return Doctrine_Hydrate     this object
1579      */
1580     public function expireResultCache($expire = true)
1581     {
1582         $this->_expireResultCache = true;
1583         return $this;
1584     }
1585     
1586     /**
1587      * expireQueryCache
1588      *
1589      * @param boolean $expire       whether or not to force cache expiration
1590      * @return Doctrine_Hydrate     this object
1591      */
1592     public function expireQueryCache($expire = true)
1593     {
1594         $this->_expireQueryCache = true;
1595         return $this;
1596     }
1597     
1598     /**
1599      * setCacheLifeSpan
1600      *
1601      * @param integer $timeToLive   how long the cache entry is valid
1602      * @return Doctrine_Hydrate     this object
1603      * @deprecated Use setResultCacheLifeSpan()
1604      */
1605     public function setCacheLifeSpan($timeToLive)
1606     {
1607         return $this->setResultCacheLifeSpan($timeToLive);
1608     }
1609     
1610     /**
1611      * setResultCacheLifeSpan
1612      *
1613      * @param integer $timeToLive   how long the cache entry is valid
1614      * @return Doctrine_Hydrate     this object
1615      */
1616     public function setResultCacheLifeSpan($timeToLive)
1617     {
1618         if ($timeToLive !== null) {
1619             $timeToLive = (int) $timeToLive;
1620         }
1621         $this->_resultCacheTTL = $timeToLive;
1622
1623         return $this;
1624     }
1625     
1626     /**
1627      * setQueryCacheLifeSpan
1628      *
1629      * @param integer $timeToLive   how long the cache entry is valid
1630      * @return Doctrine_Hydrate     this object
1631      */
1632     public function setQueryCacheLifeSpan($timeToLive)
1633     {
1634         if ($timeToLive !== null) {
1635             $timeToLive = (int) $timeToLive;
1636         }
1637         $this->_queryCacheTTL = $timeToLive;
1638
1639         return $this;
1640     }
1641     
1642     /**
1643      * getCacheDriver
1644      * returns the cache driver associated with this object
1645      *
1646      * @return Doctrine_Cache_Interface|boolean|null    cache driver
1647      * @deprecated Use getResultCacheDriver()
1648      */
1649     public function getCacheDriver()
1650     {
1651         return $this->getResultCacheDriver();
1652     }
1653     
1654     /**
1655      * getResultCacheDriver
1656      * returns the cache driver used for caching result sets
1657      *
1658      * @return Doctrine_Cache_Interface|boolean|null    cache driver
1659      */
1660     public function getResultCacheDriver()
1661     {
1662         if ($this->_resultCache instanceof Doctrine_Cache_Interface) {
1663             return $this->_resultCache;
1664         } else {
1665             return $this->_conn->getResultCacheDriver();
1666         }
1667     }
1668     
1669     /**
1670      * getQueryCacheDriver
1671      * returns the cache driver used for caching queries
1672      *
1673      * @return Doctrine_Cache_Interface|boolean|null    cache driver
1674      */
1675     public function getQueryCacheDriver()
1676     {
1677         if ($this->_queryCache instanceof Doctrine_Cache_Interface) {
1678             return $this->_queryCache;
1679         } else {
1680             return $this->_conn->getQueryCacheDriver();
1681         }
1682     }
1683     
1684     /**
1685      * getConnection
1686      *
1687      * @return Doctrine_Connection
1688      */
1689     public function getConnection()
1690     {
1691         return $this->_conn;
1692     }
1693     
1694     /** 
1695      * Adds a DQL part to the internal parts collection.
1696      * 
1697      * @param string $queryPartName  The name of the query part.
1698      * @param string $queryPart      The actual query part to add.
1699      * @param boolean $append        Whether to append $queryPart to already existing
1700      *                               parts under the same $queryPartName. Defaults to FALSE
1701      *                               (previously added parts with the same name get overridden).
1702      */
1703     protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
1704     {
1705         if ($append) {
1706             $this->_dqlParts[$queryPartName][] = $queryPart;
1707         } else {
1708             $this->_dqlParts[$queryPartName] = array($queryPart);
1709         }
1710         
1711         $this->_state = Doctrine_Query::STATE_DIRTY;
1712         return $this;     
1713     }
1714     
1715     /**
1716      * _processDqlQueryPart
1717      * parses given query part
1718      *
1719      * @param string $queryPartName     the name of the query part
1720      * @param array $queryParts         an array containing the query part data
1721      * @return Doctrine_Query           this object
1722      * @todo Better description. "parses given query part" ??? Then wheres the difference
1723      *       between process/parseQueryPart? I suppose this does something different.
1724      */
1725     protected function _processDqlQueryPart($queryPartName, $queryParts)
1726     {
1727         $this->removeSqlQueryPart($queryPartName);
1728
1729         if (is_array($queryParts) && ! empty($queryParts)) {
1730             foreach ($queryParts as $queryPart) {
1731                 $parser = $this->_getParser($queryPartName);
1732                 $sql = $parser->parse($queryPart);
1733                 if (isset($sql)) {
1734                     if ($queryPartName == 'limit' || $queryPartName == 'offset') {
1735                         $this->setSqlQueryPart($queryPartName, $sql);
1736                     } else {
1737                         $this->addSqlQueryPart($queryPartName, $sql);
1738                     }
1739                 }
1740             }
1741         }
1742     }
1743     
1744     /**
1745      * _getParser
1746      * parser lazy-loader
1747      *
1748      * @throws Doctrine_Query_Exception     if unknown parser name given
1749      * @return Doctrine_Query_Part
1750      * @todo Doc/Description: What is the parameter for? Which parsers are available?
1751      */
1752     protected function _getParser($name)
1753     {
1754         if ( ! isset($this->_parsers[$name])) {
1755             $class = 'Doctrine_Query_' . ucwords(strtolower($name));
1756
1757             Doctrine::autoload($class);
1758
1759             if ( ! class_exists($class)) {
1760                 throw new Doctrine_Query_Exception('Unknown parser ' . $name);
1761             }
1762
1763             $this->_parsers[$name] = new $class($this, $this->_tokenizer);
1764         }
1765
1766         return $this->_parsers[$name];
1767     }
1768     
1769     /**
1770      * Gets the SQL query that corresponds to this query object.
1771      * The returned SQL syntax depends on the connection driver that is used 
1772      * by this query object at the time of this method call.
1773      *
1774      * @param array $params
1775      */
1776     abstract public function getSqlQuery($params = array());
1777     
1778     /**
1779      * parseDqlQuery
1780      * parses a dql query
1781      *
1782      * @param string $query         query to be parsed
1783      * @return Doctrine_Query_Abstract  this object
1784      */
1785     abstract public function parseDqlQuery($query);
1786     
1787     /**
1788      * @deprecated
1789      */
1790     public function parseQuery($query)
1791     {
1792         return $this->parseDqlQuery($query);
1793     }
1794     
1795     /**
1796      * @deprecated
1797      */
1798     public function getQuery($params = array())
1799     {
1800         return $this->getSqlQuery($params);
1801     }
1802 }