Coverage for Doctrine_Connection

Back to coverage report

1 <?php
2 /*
3  *  $Id: Connection.php 2829 2007-10-13 17:06:26Z 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.com>.
20  */
21 Doctrine::autoload('Doctrine_Configurable');
22 /**
23  * Doctrine_Connection
24  *
25  * A wrapper layer on top of PDO / Doctrine_Adapter
26  *
27  * Doctrine_Connection is the heart of any Doctrine based application.
28  *
29  * 1. Event listeners
30  *    An easy to use, pluggable eventlistener architecture. Aspects such as
31  *    logging, query profiling and caching can be easily implemented through
32  *    the use of these listeners
33  *
34  * 2. Lazy-connecting
35  *    Creating an instance of Doctrine_Connection does not connect
36  *    to database. Connecting to database is only invoked when actually needed
37  *    (for example when query() is being called) 
38  *
39  * 3. Convenience methods
40  *    Doctrine_Connection provides many convenience methods such as fetchAll(), fetchOne() etc.
41  *
42  * 4. Modular structure
43  *    Higher level functionality such as schema importing, exporting, sequence handling etc.
44  *    is divided into modules. For a full list of connection modules see 
45  *    Doctrine_Connection::$_modules
46  *
47  * @package     Doctrine
48  * @subpackage  Connection
49  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
50  * @link        www.phpdoctrine.com
51  * @since       1.0
52  * @version     $Revision: 2829 $
53  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
54  * @author      Lukas Smith <smith@pooteeweet.org> (MDB2 library)
55  */
56 abstract class Doctrine_Connection extends Doctrine_Configurable implements Countable, IteratorAggregate
57 {
58     /**
59      * @var $dbh                                the database handler
60      */
61     protected $dbh;
62     /**
63      * @var array $tables                       an array containing all the initialized Doctrine_Table objects
64      *                                          keys representing Doctrine_Table component names and values as Doctrine_Table objects
65      */
66     protected $tables           = array();
67     /**
68      * @var string $driverName                  the name of this connection driver
69      */
70     protected $driverName;
71     /**
72      * @var boolean $isConnected                whether or not a connection has been established
73      */
74     protected $isConnected      = false;
75     /**
76      * @var array $supported                    an array containing all features this driver supports,
77      *                                          keys representing feature names and values as
78      *                                          one of the following (true, false, 'emulated')
79      */
80     protected $supported        = array();
81     /**
82      * @var array $pendingAttributes            An array of pending attributes. When setting attributes
83      *                                          no connection is needed. When connected all the pending
84      *                                          attributes are passed to the underlying adapter (usually PDO) instance.
85      */
86     protected $pendingAttributes  = array();
87     /**
88      * @var array $modules                      an array containing all modules
89      *              transaction                 Doctrine_Transaction driver, handles savepoint and transaction isolation abstraction
90      *
91      *              expression                  Doctrine_Expression driver, handles expression abstraction
92      *
93      *              dataDict                    Doctrine_DataDict driver, handles datatype abstraction
94      *
95      *              export                      Doctrine_Export driver, handles db structure modification abstraction (contains
96      *                                          methods such as alterTable, createConstraint etc.)
97      *              import                      Doctrine_Import driver, handles db schema reading
98      *
99      *              sequence                    Doctrine_Sequence driver, handles sequential id generation and retrieval
100      *
101      *              unitOfWork                  Doctrine_Connection_UnitOfWork handles many orm functionalities such as object
102      *                                          deletion and saving
103      *
104      *              formatter                   Doctrine_Formatter handles data formatting, quoting and escaping
105      *
106      * @see Doctrine_Connection::__get()
107      * @see Doctrine_DataDict
108      * @see Doctrine_Expression
109      * @see Doctrine_Export
110      * @see Doctrine_Transaction
111      * @see Doctrine_Sequence
112      * @see Doctrine_Connection_UnitOfWork
113      * @see Doctrine_Formatter
114      */
115     private $modules = array('transaction' => false,
116                              'expression'  => false,
117                              'dataDict'    => false,
118                              'export'      => false,
119                              'import'      => false,
120                              'sequence'    => false,
121                              'unitOfWork'  => false,
122                              'formatter'   => false,
123                              'util'        => false,
124                              );
125     /**
126      * @var array $properties               an array of connection properties
127      */
128     protected $properties = array('sql_comments'        => array(array('start' => '--', 'end' => "\n", 'escape' => false),
129                                                                  array('start' => '/*', 'end' => '*/', 'escape' => false)),
130                                   'identifier_quoting'  => array('start' => '"', 'end' => '"','escape' => '"'),
131                                   'string_quoting'      => array('start' => "'",
132                                                                  'end' => "'",
133                                                                  'escape' => false,
134                                                                  'escape_pattern' => false),
135                                   'wildcards'           => array('%', '_'),
136                                   'varchar_max_length'  => 255,
137                                   );
138     /**
139      * @var array $serverInfo
140      */
141     protected $serverInfo = array();
142     
143     protected $options    = array();
144     /**
145      * @var array $availableDrivers         an array containing all availible drivers
146      */
147     private static $availableDrivers    = array(
148                                         'Mysql',
149                                         'Pgsql',
150                                         'Oracle',
151                                         'Informix',
152                                         'Mssql',
153                                         'Sqlite',
154                                         'Firebird'
155                                         );
156     protected $_count = 0;
157
158     /**
159      * the constructor
160      *
161      * @param Doctrine_Manager $manager                 the manager object
162      * @param PDO|Doctrine_Adapter_Interface $adapter   database driver
163      */
164     public function __construct(Doctrine_Manager $manager, $adapter, $user = null, $pass = null)
165     {
166         if (is_object($adapter)) {
167             if ( ! ($adapter instanceof PDO) && ! in_array('Doctrine_Adapter_Interface', class_implements($adapter))) {
168                 throw new Doctrine_Connection_Exception('First argument should be an instance of PDO or implement Doctrine_Adapter_Interface');
169             }
170             $this->dbh = $adapter;
171
172             $this->isConnected = true;
173
174         } elseif (is_array($adapter)) {
175             $this->pendingAttributes[Doctrine::ATTR_DRIVER_NAME] = $adapter['scheme'];
176
177             $this->options['dsn']      = $adapter['dsn'];
178             $this->options['username'] = $adapter['user'];
179             $this->options['password'] = $adapter['pass'];
180             
181             $this->options['other'] = array();  
182             if (isset($adapter['other'])) {
183                 $this->options['other'] = array(Doctrine::ATTR_PERSISTENT => $adapter['persistent']);
184             }
185
186         }
187
188         $this->setParent($manager);
189
190         $this->setAttribute(Doctrine::ATTR_CASE, Doctrine::CASE_NATURAL);
191         $this->setAttribute(Doctrine::ATTR_ERRMODE, Doctrine::ERRMODE_EXCEPTION);
192
193         $this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this);
194     }
195     /**
196      * getAttribute
197      * retrieves a database connection attribute
198      *
199      * @param integer $attribute
200      * @return mixed
201      */
202     public function getAttribute($attribute)
203     {
204
205         if ($attribute >= 100) {
206             if ( ! isset($this->attributes[$attribute])) {
207                 return $this->parent->getAttribute($attribute);
208             }
209             return $this->attributes[$attribute];
210         }
211
212         if ($this->isConnected) {
213             try {
214                 return $this->dbh->getAttribute($attribute);
215             } catch(Exception $e) {
216                 throw new Doctrine_Connection_Exception('Attribute ' . $attribute . ' not found.');
217             }
218         } else {
219             if ( ! isset($this->pendingAttributes[$attribute])) {
220                 $this->connect();
221                 $this->getAttribute($attribute);
222             }
223
224             return $this->pendingAttributes[$attribute];
225         }
226     }
227     /**
228      * returns an array of available PDO drivers
229      */
230     public static function getAvailableDrivers()
231     {
232         return PDO::getAvailableDrivers();
233     }
234     /**
235      * setAttribute
236      * sets an attribute
237      *
238      * @param integer $attribute
239      * @param mixed $value
240      * @return boolean
241      */
242     public function setAttribute($attribute, $value)
243     {
244         if ($attribute >= 100) {
245             parent::setAttribute($attribute, $value);
246         } else {
247             if ($this->isConnected) {
248                 $this->dbh->setAttribute($attribute, $value);
249             } else {
250                 $this->pendingAttributes[$attribute] = $value;
251             }
252         }
253         return $this;
254     }
255     /**
256      * getName
257      * returns the name of this driver
258      *
259      * @return string           the name of this driver
260      */
261     public function getName()
262     {
263         return $this->driverName;
264     }
265     /**
266      * __get
267      * lazy loads given module and returns it
268      *
269      * @see Doctrine_DataDict
270      * @see Doctrine_Expression
271      * @see Doctrine_Export
272      * @see Doctrine_Transaction
273      * @see Doctrine_Connection::$modules       all availible modules
274      * @param string $name                      the name of the module to get
275      * @throws Doctrine_Connection_Exception    if trying to get an unknown module
276      * @return Doctrine_Connection_Module       connection module
277      */
278     public function __get($name)
279     {
280         if (isset($this->properties[$name])) {
281             return $this->properties[$name];
282         }
283
284         if ( ! isset($this->modules[$name])) {
285             throw new Doctrine_Connection_Exception('Unknown module / property ' . $name);
286         }
287         if ($this->modules[$name] === false) {
288             switch ($name) {
289                 case 'unitOfWork':
290                     $this->modules[$name] = new Doctrine_Connection_UnitOfWork($this);
291                     break;
292                 case 'formatter':
293                     $this->modules[$name] = new Doctrine_Formatter($this);
294                     break;
295                 default:
296                     $class = 'Doctrine_' . ucwords($name) . '_' . $this->getName();
297                     $this->modules[$name] = new $class($this);
298                 }
299         }
300
301         return $this->modules[$name];
302     }
303     /**
304      * returns the manager that created this connection
305      *
306      * @return Doctrine_Manager
307      */
308     public function getManager()
309     {
310         return $this->getParent();
311     }
312     /**
313      * returns the database handler of which this connection uses
314      *
315      * @return PDO              the database handler
316      */
317     public function getDbh()
318     {
319         $this->connect();
320         
321         return $this->dbh;
322     }
323     /**
324      * connect
325      * connects into database
326      *
327      * @return boolean
328      */
329     public function connect()
330     {
331
332         if ($this->isConnected) {
333             return false;
334         }
335
336         $event = new Doctrine_Event($this, Doctrine_Event::CONN_CONNECT);
337
338         $this->getListener()->preConnect($event);
339
340         $e     = explode(':', $this->options['dsn']);
341         $found = false;
342         
343         if (extension_loaded('pdo')) {
344             if (in_array($e[0], PDO::getAvailableDrivers())) {
345                 $this->dbh = new PDO($this->options['dsn'], $this->options['username'], 
346                                      $this->options['password'], $this->options['other']);
347                                      
348                 $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
349                 $found = true;
350             }
351         }
352
353         if ( ! $found) {
354             $class = 'Doctrine_Adapter_' . ucwords($e[0]);
355
356             if (class_exists($class)) {
357                 $this->dbh = new $class($this->options['dsn'], $this->options['username'], $this->options['password']);
358             } else {
359                 throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]);          
360             }
361         }
362
363         // attach the pending attributes to adapter
364         foreach($this->pendingAttributes as $attr => $value) {
365             // some drivers don't support setting this so we just skip it
366             if ($attr == Doctrine::ATTR_DRIVER_NAME) {
367                 continue;
368             }
369             $this->dbh->setAttribute($attr, $value);
370         }
371
372         $this->isConnected = true;
373
374         $this->getListener()->postConnect($event);
375         return true;
376     }
377     
378     public function incrementQueryCount() 
379     {
380         $this->_count++;
381     }
382     /**
383      * converts given driver name
384      *
385      * @param
386      */
387     public function driverName($name)
388     {
389     }
390     /**
391      * supports
392      *
393      * @param string $feature   the name of the feature
394      * @return boolean          whether or not this drivers supports given feature
395      */
396     public function supports($feature)
397     {
398         return (isset($this->supported[$feature])
399                   && ($this->supported[$feature] === 'emulated'
400                    || $this->supported[$feature]));
401     }
402     /**
403      * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
404      * query, except that if there is already a row in the table with the same
405      * key field values, the REPLACE query just updates its values instead of
406      * inserting a new row.
407      *
408      * The REPLACE type of query does not make part of the SQL standards. Since
409      * practically only MySQL and SQLIte implement it natively, this type of
410      * query isemulated through this method for other DBMS using standard types
411      * of queries inside a transaction to assure the atomicity of the operation.
412      *
413      * @param                   string  name of the table on which the REPLACE query will
414      *                          be executed.
415      *
416      * @param   array           an associative array that describes the fields and the
417      *                          values that will be inserted or updated in the specified table. The
418      *                          indexes of the array are the names of all the fields of the table.
419      *
420      *                          The values of the array are values to be assigned to the specified field.
421      *
422      * @param array $keys       an array containing all key fields (primary key fields
423      *                          or unique index fields) for this table
424      *
425      *                          the uniqueness of a row will be determined according to
426      *                          the provided key fields
427      *
428      *                          this method will fail if no key fields are specified
429      *
430      * @throws Doctrine_Connection_Exception        if this driver doesn't support replace
431      * @throws Doctrine_Connection_Exception        if some of the key values was null
432      * @throws Doctrine_Connection_Exception        if there were no key fields
433      * @throws PDOException                         if something fails at PDO level
434      * @return integer                              number of rows affected
435      */
436     public function replace($table, array $fields, array $keys)
437     {
438         //if ( ! $this->supports('replace'))
439         //    throw new Doctrine_Connection_Exception('replace query is not supported');
440
441         if (empty($keys)) {
442             throw new Doctrine_Connection_Exception('Not specified which fields are keys');
443         }
444         $condition = $values = array();
445
446         foreach ($fields as $name => $value) {
447             $values[$name] = $value;
448
449             if (in_array($name, $keys)) {
450                 if ($value === null)
451                     throw new Doctrine_Connection_Exception('key value '.$name.' may not be null');
452
453                 $condition[]       = $name . ' = ?';
454                 $conditionValues[] = $value;
455             }
456         }
457
458         $query          = 'DELETE FROM ' . $this->quoteIdentifier($table) . ' WHERE ' . implode(' AND ', $condition);
459         $affectedRows   = $this->exec($query);
460
461         $this->insert($table, $values);
462
463         $affectedRows++;
464
465
466         return $affectedRows;
467     }
468     /**
469      * Inserts a table row with specified data.
470      *
471      * @param string $table     The table to insert data into.
472      * @param array $values     An associateve array containing column-value pairs.
473      * @return boolean
474      */
475     public function insert($table, array $values = array()) {
476         if (empty($values)) {
477             return false;
478         }
479
480         // column names are specified as array keys
481         $cols = array();
482         // the query VALUES will contain either expresions (eg 'NOW()') or ?
483         $a = array();
484         foreach ($values as $k => $value) {
485             $cols[] = $this->quoteIdentifier($k);
486             if ($value instanceof Doctrine_Expression) {
487                 $a[] = $value->getSql();
488                 unset($values[$k]);
489             } else {
490                 $a[] = '?';
491             }
492         }
493
494         // build the statement
495         $query = 'INSERT INTO ' . $this->quoteIdentifier($table) 
496                . ' (' . implode(', ', $cols) . ') '
497                . 'VALUES (';
498
499         $query .= implode(', ', $a) . ')';
500         // prepare and execute the statement
501
502         $this->exec($query, array_values($values));
503
504         return true;
505     }
506     /**
507      * Set the charset on the current connection
508      *
509      * @param string    charset
510      *
511      * @return void
512      */
513     public function setCharset($charset)
514     {
515
516     }
517     /**
518      * Quote a string so it can be safely used as a table or column name
519      *
520      * Delimiting style depends on which database driver is being used.
521      *
522      * NOTE: just because you CAN use delimited identifiers doesn't mean
523      * you SHOULD use them.  In general, they end up causing way more
524      * problems than they solve.
525      *
526      * Portability is broken by using the following characters inside
527      * delimited identifiers:
528      *   + backtick (<kbd>`</kbd>) -- due to MySQL
529      *   + double quote (<kbd>"</kbd>) -- due to Oracle
530      *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
531      *
532      * Delimited identifiers are known to generally work correctly under
533      * the following drivers:
534      *   + mssql
535      *   + mysql
536      *   + mysqli
537      *   + oci8
538      *   + pgsql
539      *   + sqlite
540      *
541      * InterBase doesn't seem to be able to use delimited identifiers
542      * via PHP 4.  They work fine under PHP 5.
543      *
544      * @param string $str           identifier name to be quoted
545      * @param bool $checkOption     check the 'quote_identifier' option
546      *
547      * @return string               quoted identifier string
548      */
549     public function quoteIdentifier($str, $checkOption = true)
550     {
551         // quick fix for the identifiers that contain a dot
552         if (strpos($str, '.')) {
553             $e = explode('.', $str);
554             
555             return $this->formatter->quoteIdentifier($e[0], $checkOption) . '.' 
556                  . $this->formatter->quoteIdentifier($e[1], $checkOption);
557         }
558         return $this->formatter->quoteIdentifier($str, $checkOption);
559     }
560     /**
561      * convertBooleans
562      * some drivers need the boolean values to be converted into integers
563      * when using DQL API
564      *
565      * This method takes care of that conversion
566      *
567      * @param array $item
568      * @return void
569      */
570     public function convertBooleans($item)
571     {
572         return $this->formatter->convertBooleans($item);
573     }
574     /**
575      * quote
576      * quotes given input parameter
577      *
578      * @param mixed $input      parameter to be quoted
579      * @param string $type
580      * @return mixed
581      */
582     public function quote($input, $type = null)
583     {
584         return $this->formatter->quote($input, $type);
585     }
586     /**
587      * Set the date/time format for the current connection
588      *
589      * @param string    time format
590      *
591      * @return void
592      */
593     public function setDateFormat($format = null)
594     {
595     }
596     /**
597      * fetchAll
598      *
599      * @param string $statement         sql query to be executed
600      * @param array $params             prepared statement params
601      * @return array
602      */
603     public function fetchAll($statement, array $params = array()) 
604     {
605         return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_ASSOC);
606     }
607     /**
608      * fetchOne
609      *
610      * @param string $statement         sql query to be executed
611      * @param array $params             prepared statement params
612      * @param int $colnum               0-indexed column number to retrieve
613      * @return mixed
614      */
615     public function fetchOne($statement, array $params = array(), $colnum = 0) 
616     {
617         return $this->execute($statement, $params)->fetchColumn($colnum);
618     }
619     /**
620      * fetchRow
621      *
622      * @param string $statement         sql query to be executed
623      * @param array $params             prepared statement params
624      * @return array
625      */
626     public function fetchRow($statement, array $params = array()) 
627     {
628         return $this->execute($statement, $params)->fetch(Doctrine::FETCH_ASSOC);
629     }
630     /**
631      * fetchArray
632      *
633      * @param string $statement         sql query to be executed
634      * @param array $params             prepared statement params
635      * @return array
636      */
637     public function fetchArray($statement, array $params = array()) 
638     {
639         return $this->execute($statement, $params)->fetch(Doctrine::FETCH_NUM);
640     }
641     /**
642      * fetchColumn
643      *
644      * @param string $statement         sql query to be executed
645      * @param array $params             prepared statement params
646      * @param int $colnum               0-indexed column number to retrieve
647      * @return array
648      */
649     public function fetchColumn($statement, array $params = array(), $colnum = 0) 
650     {
651         return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_COLUMN, $colnum);
652     }
653     /**
654      * fetchAssoc
655      *
656      * @param string $statement         sql query to be executed
657      * @param array $params             prepared statement params
658      * @return array
659      */
660     public function fetchAssoc($statement, array $params = array()) 
661     {
662         return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_ASSOC);
663     }
664     /**
665      * fetchBoth
666      *
667      * @param string $statement         sql query to be executed
668      * @param array $params             prepared statement params
669      * @return array
670      */
671     public function fetchBoth($statement, array $params = array()) 
672     {
673         return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_BOTH);
674     }
675     /**
676      * query
677      * queries the database using Doctrine Query Language
678      * returns a collection of Doctrine_Record objects
679      *
680      * <code>
681      * $users = $conn->query('SELECT u.* FROM User u');
682      *
683      * $users = $conn->query('SELECT u.* FROM User u WHERE u.name LIKE ?', array('someone'));
684      * </code>
685      *
686      * @param string $query             DQL query
687      * @param array $params             query parameters
688      * @param int $hydrationMode        Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD
689      * @see Doctrine_Query
690      * @return Doctrine_Collection      Collection of Doctrine_Record objects
691      */
692     public function query($query, array $params = array(), $hydrationMode = null)
693     {
694         $parser = new Doctrine_Query($this);
695
696         return $parser->query($query, $params, $hydrationMode);
697     }
698     /**
699      * prepare
700      *
701      * @param string $statement
702      */
703     public function prepare($statement)
704     {
705         $this->connect();
706
707         try {
708             $event = new Doctrine_Event($this, Doctrine_Event::CONN_PREPARE, $statement);
709     
710             $this->getAttribute(Doctrine::ATTR_LISTENER)->prePrepare($event);
711
712             $stmt = false;
713     
714             if ( ! $event->skipOperation) {
715                 $stmt = $this->dbh->prepare($statement);
716             }
717     
718             $this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event);
719             
720             return new Doctrine_Connection_Statement($this, $stmt);
721         } catch(Doctrine_Adapter_Exception $e) {
722         } catch(PDOException $e) { }
723
724         $this->rethrowException($e, $this);
725     }
726     /**
727      * query
728      * queries the database using Doctrine Query Language and returns
729      * the first record found
730      *
731      * <code>
732      * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.id = ?', array(1));
733      *
734      * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.name LIKE ? AND u.password = ?',
735      *         array('someone', 'password')
736      *         );
737      * </code>
738      *
739      * @param string $query             DQL query
740      * @param array $params             query parameters
741      * @see Doctrine_Query
742      * @return Doctrine_Record|false    Doctrine_Record object on success,
743      *                                  boolean false on failure
744      */
745     public function queryOne($query, array $params = array()) 
746     {
747         $parser = new Doctrine_Query($this);
748
749         $coll = $parser->query($query, $params);
750         if ( ! $coll->contains(0)) {
751             return false;
752         }
753         return $coll[0];
754     }
755     /**
756      * queries the database with limit and offset
757      * added to the query and returns a Doctrine_Connection_Statement object
758      *
759      * @param string $query
760      * @param integer $limit
761      * @param integer $offset
762      * @return Doctrine_Connection_Statement
763      */
764     public function select($query, $limit = 0, $offset = 0)
765     {
766         if ($limit > 0 || $offset > 0) {
767             $query = $this->modifyLimitQuery($query, $limit, $offset);
768         }
769         return $this->execute($query);
770     }
771     /**
772      * standaloneQuery
773      *
774      * @param string $query     sql query
775      * @param array $params     query parameters
776      *
777      * @return PDOStatement|Doctrine_Adapter_Statement
778      */
779     public function standaloneQuery($query, $params = array())
780     {
781         return $this->execute($query, $params);
782     }
783     /**
784      * execute
785      * @param string $query     sql query
786      * @param array $params     query parameters
787      *
788      * @return PDOStatement|Doctrine_Adapter_Statement
789      */
790     public function execute($query, array $params = array())
791     {
792         $this->connect();
793
794         try {
795             if ( ! empty($params)) {
796                 $stmt = $this->prepare($query);
797                 $stmt->execute($params);
798                 return $stmt;
799             } else {
800                 $event = new Doctrine_Event($this, Doctrine_Event::CONN_QUERY, $query, $params);
801
802                 $this->getAttribute(Doctrine::ATTR_LISTENER)->preQuery($event);
803
804                 if ( ! $event->skipOperation) {
805                     $stmt = $this->dbh->query($query);
806
807                     $this->_count++;
808                 }
809                 $this->getAttribute(Doctrine::ATTR_LISTENER)->postQuery($event);
810
811                 return $stmt;
812             }
813         } catch(Doctrine_Adapter_Exception $e) {
814         } catch(PDOException $e) { }
815
816         $this->rethrowException($e, $this);
817     }
818     /**
819      * exec
820      * @param string $query     sql query
821      * @param array $params     query parameters
822      *
823      * @return PDOStatement|Doctrine_Adapter_Statement
824      */
825     public function exec($query, array $params = array()) {
826         $this->connect();
827
828         try {
829             if ( ! empty($params)) {
830                 $stmt = $this->prepare($query);
831                 $stmt->execute($params);
832
833                 return $stmt->rowCount();
834             } else {
835                 $event = new Doctrine_Event($this, Doctrine_Event::CONN_EXEC, $query, $params);
836
837                 $this->getAttribute(Doctrine::ATTR_LISTENER)->preExec($event);
838
839                 if ( ! $event->skipOperation) {
840                     $count = $this->dbh->exec($query);
841
842                     $this->_count++;
843                 }
844                 $this->getAttribute(Doctrine::ATTR_LISTENER)->postExec($event);
845
846                 return $count;
847             }
848         } catch(Doctrine_Adapter_Exception $e) {
849         } catch(PDOException $e) { }
850
851         $this->rethrowException($e, $this);
852     }
853     /**
854      * rethrowException
855      *
856      * @throws Doctrine_Connection_Exception
857      */
858     public function rethrowException(Exception $e, $invoker)
859     {
860         $event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR);
861
862         $this->getListener()->preError($event);
863
864         $name = 'Doctrine_Connection_' . $this->driverName . '_Exception';
865
866         $exc  = new $name($e->getMessage(), (int) $e->getCode());
867         if ( ! is_array($e->errorInfo)) {
868             $e->errorInfo = array(null, null, null, null);
869         }
870         $exc->processErrorInfo($e->errorInfo);
871
872          if ($this->getAttribute(Doctrine::ATTR_THROW_EXCEPTIONS)) {
873             throw $exc;
874         }
875         
876         $this->getListener()->postError($event);
877     }
878     /**
879      * hasTable
880      * whether or not this connection has table $name initialized
881      *
882      * @param mixed $name
883      * @return boolean
884      */
885     public function hasTable($name)
886     {
887         return isset($this->tables[$name]);
888     }
889     /**
890      * returns a table object for given component name
891      *
892      * @param string $name              component name
893      * @return object Doctrine_Table
894      */
895     public function getTable($name, $allowExport = true)
896     {
897         if (isset($this->tables[$name])) {
898             return $this->tables[$name];
899         }
900         $class = $name . 'Table';
901
902         if (class_exists($class) && in_array('Doctrine_Table', class_parents($class))) {
903             $table = new $class($name, $this);
904         } else {
905             $table = new Doctrine_Table($name, $this);
906         }
907
908         $this->tables[$name] = $table;
909
910
911         return $table;
912     }
913     /**
914      * returns an array of all initialized tables
915      *
916      * @return array
917      */
918     public function getTables()
919     {
920         return $this->tables;
921     }
922     /**
923      * returns an iterator that iterators through all
924      * initialized table objects
925      *
926      * <code>
927      * foreach ($conn as $index => $table) {
928      *      print $table;  // get a string representation of each table object
929      * }
930      * </code>
931      *
932      * @return ArrayIterator        SPL ArrayIterator object
933      */
934     public function getIterator()
935     {
936         return new ArrayIterator($this->tables);
937     }
938     /**
939      * returns the count of initialized table objects
940      *
941      * @return integer
942      */
943     public function count()
944     {
945         return $this->_count;
946     }
947     /**
948      * addTable
949      * adds a Doctrine_Table object into connection registry
950      *
951      * @param $table                a Doctrine_Table object to be added into registry
952      * @return boolean
953      */
954     public function addTable(Doctrine_Table $table)
955     {
956         $name = $table->getComponentName();
957
958         if (isset($this->tables[$name])) {
959             return false;
960         }
961         $this->tables[$name] = $table;
962         return true;
963     }
964     /**
965      * create
966      * creates a record
967      *
968      * create                       creates a record
969      * @param string $name          component name
970      * @return Doctrine_Record      Doctrine_Record object
971      */
972     public function create($name)
973     {
974         return $this->getTable($name)->create();
975     }
976     /**
977      * flush
978      * saves all the records from all tables
979      * this operation is isolated using a transaction
980      *
981      * @throws PDOException         if something went wrong at database level
982      * @return void
983      */
984     public function flush()
985     {
986         $this->beginTransaction();
987         $this->unitOfWork->saveAll();
988         $this->commit();
989     }
990     /**
991      * clear
992      * clears all repositories
993      *
994      * @return void
995      */
996     public function clear()
997     {
998         foreach ($this->tables as $k => $table) {
999             $table->getRepository()->evictAll();
1000             $table->clear();
1001         }
1002     }
1003     /**
1004      * evictTables
1005      * evicts all tables
1006      *
1007      * @return void
1008      */
1009     public function evictTables()
1010     {
1011         $this->tables = array();
1012         $this->exported = array();
1013     }
1014     /**
1015      * close
1016      * closes the connection
1017      *
1018      * @return void
1019      */
1020     public function close()
1021     {
1022         $event = new Doctrine_Event($this, Doctrine_Event::CONN_CLOSE);
1023
1024         $this->getAttribute(Doctrine::ATTR_LISTENER)->preClose($event);
1025
1026         $this->clear();
1027         
1028         unset($this->dbh);
1029         $this->isConnected = false;
1030
1031         $this->getAttribute(Doctrine::ATTR_LISTENER)->postClose($event);
1032     }
1033     /**
1034      * get the current transaction nesting level
1035      *
1036      * @return integer
1037      */
1038     public function getTransactionLevel()
1039     {
1040         return $this->transaction->getTransactionLevel();
1041     }
1042     /**
1043      * errorCode
1044      * Fetch the SQLSTATE associated with the last operation on the database handle
1045      *
1046      * @return integer
1047      */
1048     public function errorCode()
1049     {
1050         $this->connect();
1051
1052         return $this->dbh->errorCode();
1053     }
1054     /**
1055      * errorInfo
1056      * Fetch extended error information associated with the last operation on the database handle
1057      *
1058      * @return array
1059      */
1060     public function errorInfo()
1061     {
1062         $this->connect();
1063
1064         return $this->dbh->errorInfo();
1065     }
1066     /**
1067      * lastInsertId
1068      *
1069      * Returns the ID of the last inserted row, or the last value from a sequence object,
1070      * depending on the underlying driver.
1071      *
1072      * Note: This method may not return a meaningful or consistent result across different drivers, 
1073      * because the underlying database may not even support the notion of auto-increment fields or sequences.
1074      *
1075      * @param string $table     name of the table into which a new row was inserted
1076      * @param string $field     name of the field into which a new row was inserted
1077      */
1078     public function lastInsertId($table = null, $field = null)
1079     {
1080         return $this->sequence->lastInsertId($table, $field);
1081     }
1082     /**
1083      * beginTransaction
1084      * Start a transaction or set a savepoint.
1085      *
1086      * if trying to set a savepoint and there is no active transaction
1087      * a new transaction is being started
1088      *
1089      * Listeners: onPreTransactionBegin, onTransactionBegin
1090      *
1091      * @param string $savepoint                 name of a savepoint to set
1092      * @throws Doctrine_Transaction_Exception   if the transaction fails at database level
1093      * @return integer                          current transaction nesting level
1094      */
1095     public function beginTransaction($savepoint = null)
1096     {
1097         $this->transaction->beginTransaction($savepoint);
1098     }
1099     /**
1100      * commit
1101      * Commit the database changes done during a transaction that is in
1102      * progress or release a savepoint. This function may only be called when
1103      * auto-committing is disabled, otherwise it will fail.
1104      *
1105      * Listeners: onPreTransactionCommit, onTransactionCommit
1106      *
1107      * @param string $savepoint                 name of a savepoint to release
1108      * @throws Doctrine_Transaction_Exception   if the transaction fails at PDO level
1109      * @throws Doctrine_Validator_Exception     if the transaction fails due to record validations
1110      * @return boolean                          false if commit couldn't be performed, true otherwise
1111      */
1112     public function commit($savepoint = null)
1113     {
1114         $this->transaction->commit($savepoint);
1115     }
1116     /**
1117      * rollback
1118      * Cancel any database changes done during a transaction or since a specific
1119      * savepoint that is in progress. This function may only be called when
1120      * auto-committing is disabled, otherwise it will fail. Therefore, a new
1121      * transaction is implicitly started after canceling the pending changes.
1122      *
1123      * this method can be listened with onPreTransactionRollback and onTransactionRollback
1124      * eventlistener methods
1125      *
1126      * @param string $savepoint                 name of a savepoint to rollback to   
1127      * @throws Doctrine_Transaction_Exception   if the rollback operation fails at database level
1128      * @return boolean                          false if rollback couldn't be performed, true otherwise
1129      */
1130     public function rollback($savepoint = null)
1131     {
1132         $this->transaction->rollback($savepoint);
1133     }
1134
1135     /**
1136      * returns a string representation of this object
1137      * @return string
1138      */
1139     public function __toString()
1140     {
1141         return Doctrine_Lib::getConnectionAsString($this);
1142     }
1143 }