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